@@ -9,7 +9,7 @@ use secret_toolkit::viewing_key::{ViewingKey, ViewingKeyStore};
9
9
use secret_toolkit_crypto:: { sha_256, ContractPrng } ;
10
10
11
11
use crate :: batch;
12
- use crate :: dwb:: { random_in_range, DelayedWriteBuffer , ACCOUNT_TXS , DWB , DWB_LEN , TX_NODES } ;
12
+ use crate :: dwb:: { random_in_range, DelayedWriteBuffer , DWB , DWB_LEN , DWB_MAX_TX_EVENTS , TX_NODES } ;
13
13
use crate :: msg:: {
14
14
AllowanceGivenResult , AllowanceReceivedResult , ContractStatusLevel , ExecuteAnswer ,
15
15
ExecuteMsg , InstantiateMsg , QueryAnswer , QueryMsg , QueryWithPermit , ResponseStatus :: Success ,
@@ -1910,56 +1910,57 @@ fn perform_transfer(
1910
1910
new_entry. add_tx_node ( store, tx_id) ?;
1911
1911
new_entry. add_amount ( amount) ?;
1912
1912
1913
- // if recipient is in the buffer (non-zero index), set this value to 1, otherwise 0, in constant-time
1914
- // casting to isize will never overflow, so long as dwb length is limited to a u16 value
1915
- let recipient_in_buffer = ( ( ( recipient_index as isize | -( recipient_index as isize ) ) >> 31 ) & 1 ) as usize ;
1916
1913
1917
- if dwb. saturated ( ) {
1918
- // randomly pick an entry to exclude in case the recipient is not in the buffer
1919
- let random_exclude_index = random_in_range ( rng, 1 , DWB_LEN as u32 ) ? as usize ;
1920
-
1921
- // index of entry to exclude from selection
1922
- let exclude_index = ( recipient_index as usize * recipient_in_buffer) + ( random_exclude_index * ( 1 - recipient_in_buffer) ) ;
1914
+ // whether or not recipient is in the buffer (non-zero index)
1915
+ // casting to i32 will never overflow, so long as dwb length is limited to a u16 value
1916
+ let if_recipient_in_buffer = constant_time_is_not_zero ( recipient_index as i32 ) ;
1923
1917
1924
- // randomly select any other entry to settle in constant-time (avoiding the reserved 0th position)
1925
- let random_settle_index = ( ( ( random_in_range ( rng, 0 , DWB_LEN as u32 - 2 ) ? + exclude_index as u32 ) % ( DWB_LEN as u32 - 1 ) ) + 1 ) as usize ;
1918
+ // randomly pick an entry to exclude in case the recipient is not in the buffer
1919
+ let random_exclude_index = random_in_range ( rng, 1 , DWB_LEN as u32 ) ? as usize ;
1926
1920
1927
- // check if we have any open slots in the linked list
1928
- let open_slots = ( u16:: MAX - dwb. entries [ recipient_index] . list_len ( ) ?) as i32 ;
1929
- let list_can_grow = ( ( ( open_slots | -open_slots) >> 31 ) & 1 ) as usize ;
1921
+ // index of entry to exclude from selection
1922
+ let exclude_index = constant_time_if_else ( if_recipient_in_buffer, recipient_index, random_exclude_index) ;
1930
1923
1931
- // if we would overflow the list, just settle recipient
1932
- // TODO: see docs for attack analysis
1933
- let actual_settle_index = ( random_settle_index as usize * list_can_grow) + ( recipient_index * ( 1 - list_can_grow) ) ;
1924
+ // randomly select any other entry to settle in constant-time (avoiding the reserved 0th position)
1925
+ let random_settle_index = ( ( ( random_in_range ( rng, 0 , DWB_LEN as u32 - 2 ) ? + exclude_index as u32 ) % ( DWB_LEN as u32 - 1 ) ) + 1 ) as usize ;
1934
1926
1935
- // settle the entry
1936
- dwb. settle_entry ( store, actual_settle_index) ?;
1937
1927
1938
- // replace it with a randomly generated address (that is not currently in the buffer) and 0 amount and nil events pointer
1939
- let replacement_entry = dwb. unique_random_entry ( rng) ?;
1940
- dwb. entries [ actual_settle_index] = replacement_entry;
1928
+ // whether or not the buffer is fully saturated yet
1929
+ let if_undersaturated = constant_time_is_not_zero ( dwb. empty_space_counter as i32 ) ;
1941
1930
1942
- // pick the index to where the recipient's entry should be written
1943
- let write_index = ( recipient_index * recipient_in_buffer ) + ( actual_settle_index * ( 1 - recipient_in_buffer ) ) ;
1931
+ // find the next empty entry in the buffer
1932
+ let next_empty_index = ( DWB_LEN - dwb . empty_space_counter ) as usize ;
1944
1933
1945
- // either updates the existing recipient entry, or overwrites the random replacement entry in the settled index
1946
- dwb. entries [ write_index] = new_entry;
1947
- } else {
1948
- // TODO: revisit contract warm up with other options, e.g saturating with random address from beginning
1934
+ // if buffer is not yet saturated, settle the address at the next empty index
1935
+ let bounded_settle_index = constant_time_if_else ( if_undersaturated, next_empty_index, random_settle_index) ;
1949
1936
1950
- // find the next empty entry in the buffer
1951
- let next_index = ( DWB_LEN - dwb. empty_space_counter ) as usize ;
1952
1937
1953
- // pick the index to where the recipient's entry should be written
1954
- let write_index = ( recipient_index * recipient_in_buffer ) + ( next_index * ( 1 - recipient_in_buffer ) ) ;
1938
+ // check if we have any open slots in the linked list
1939
+ let if_list_can_grow = constant_time_is_not_zero ( ( DWB_MAX_TX_EVENTS - dwb . entries [ recipient_index ] . list_len ( ) ? ) as i32 ) ;
1955
1940
1956
- // either updates the existing recipient entry, or write the entry to the next index value
1957
- dwb. entries [ write_index] = new_entry;
1941
+ // if we would overflow the list, just settle recipient
1942
+ // TODO: see docs for attack analysis
1943
+ let actual_settle_index = constant_time_if_else ( if_list_can_grow, bounded_settle_index, recipient_index) ;
1958
1944
1959
- // decrement empty space counter if receipient is not already in buffer
1960
- let empty_space_counter_delta = ( 1 - recipient_in_buffer) as u16 ;
1961
- dwb. empty_space_counter -= empty_space_counter_delta;
1962
- }
1945
+ // settle the entry
1946
+ dwb. settle_entry ( store, actual_settle_index) ?;
1947
+
1948
+ // replace it with a randomly generated address (that is not currently in the buffer) and 0 amount and nil events pointer
1949
+ let replacement_entry = dwb. unique_random_entry ( rng) ?;
1950
+ dwb. entries [ actual_settle_index] = replacement_entry;
1951
+
1952
+ // pick the index to where the recipient's entry should be written
1953
+ let write_index = constant_time_if_else ( if_recipient_in_buffer, recipient_index, actual_settle_index) ;
1954
+
1955
+ // either updates the existing recipient entry, or overwrites the random replacement entry in the settled index
1956
+ dwb. entries [ write_index] = new_entry;
1957
+
1958
+ // decrement empty space counter if it is undersaturated and the recipient was not already in the buffer
1959
+ dwb. empty_space_counter -= constant_time_if_else (
1960
+ if_undersaturated,
1961
+ constant_time_if_else ( if_recipient_in_buffer, 0usize , 1usize ) ,
1962
+ 0usize
1963
+ ) as u16 ;
1963
1964
1964
1965
DWB . save ( store, & dwb) ?;
1965
1966
@@ -1999,6 +2000,16 @@ fn is_valid_symbol(symbol: &str) -> bool {
1999
2000
len_is_valid && symbol. bytes ( ) . all ( |byte| byte. is_ascii_alphabetic ( ) )
2000
2001
}
2001
2002
2003
+ #[ inline]
2004
+ fn constant_time_is_not_zero ( value : i32 ) -> u32 {
2005
+ return ( ( ( value | -value) >> 31 ) & 1 ) as u32 ;
2006
+ }
2007
+
2008
+ #[ inline]
2009
+ fn constant_time_if_else ( condition : u32 , then : usize , els : usize ) -> usize {
2010
+ return ( then * condition as usize ) | ( els * ( 1 - condition as usize ) ) ;
2011
+ }
2012
+
2002
2013
// pub fn migrate(
2003
2014
// _deps: DepsMut,
2004
2015
// _env: Env,
0 commit comments