language/diem-framework/releases/artifacts/release-1.4.0-rc0/docs/modules/SlidingNonce.md
<a name="0x1_SlidingNonce"></a>
0x1::SlidingNonceAllows transactions to be executed out-of-order while ensuring that they are executed at most once. Nonces are assigned to transactions off-chain by clients submitting the transactions. It maintains a sliding window bitvector of 128 flags. A flag of 0 indicates that the transaction with that nonce has not yet been executed. When nonce X is recorded, all transactions with nonces lower then X-128 will abort.
SlidingNoncerecord_nonce_or_aborttry_record_nonce
publish<a name="0x1_SlidingNonce_SlidingNonce"></a>
SlidingNonce<a name="@Constants_0"></a>
<a name="0x1_SlidingNonce_ENONCE_ALREADY_PUBLISHED"></a>
The sliding nonce resource was already published
<pre><code><b>const</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_ALREADY_PUBLISHED">ENONCE_ALREADY_PUBLISHED</a>: u64 = 4; </code></pre><a name="0x1_SlidingNonce_ENONCE_ALREADY_RECORDED"></a>
The nonce was already recorded previously
<pre><code><b>const</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_ALREADY_RECORDED">ENONCE_ALREADY_RECORDED</a>: u64 = 3; </code></pre><a name="0x1_SlidingNonce_ENONCE_TOO_NEW"></a>
The nonce is too large - this protects against nonce exhaustion
<pre><code><b>const</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_TOO_NEW">ENONCE_TOO_NEW</a>: u64 = 2; </code></pre><a name="0x1_SlidingNonce_ENONCE_TOO_OLD"></a>
The nonce aborted because it's too old (nonce smaller than <code>min_nonce</code>)
<pre><code><b>const</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_TOO_OLD">ENONCE_TOO_OLD</a>: u64 = 1; </code></pre><a name="0x1_SlidingNonce_ESLIDING_NONCE"></a>
The <code><a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a></code> resource is in an invalid state
<pre><code><b>const</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ESLIDING_NONCE">ESLIDING_NONCE</a>: u64 = 0; </code></pre><a name="0x1_SlidingNonce_NONCE_MASK_SIZE"></a>
Size of SlidingNonce::nonce_mask in bits.
<pre><code><b>const</b> <a href="SlidingNonce.md#0x1_SlidingNonce_NONCE_MASK_SIZE">NONCE_MASK_SIZE</a>: u64 = 128; </code></pre><a name="0x1_SlidingNonce_record_nonce_or_abort"></a>
record_nonce_or_abortCalls <code>try_record_nonce</code> and aborts transaction if returned code is non-0
<pre><code><b>public</b> <b>fun</b> <a href="SlidingNonce.md#0x1_SlidingNonce_record_nonce_or_abort">record_nonce_or_abort</a>(account: &signer, seq_nonce: u64) </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b> <b>fun</b> <a href="SlidingNonce.md#0x1_SlidingNonce_record_nonce_or_abort">record_nonce_or_abort</a>(account: &signer, seq_nonce: u64) <b>acquires</b> <a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a> { <b>let</b> code = <a href="SlidingNonce.md#0x1_SlidingNonce_try_record_nonce">try_record_nonce</a>(account, seq_nonce); <b>assert</b>(code == 0, <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_invalid_argument">Errors::invalid_argument</a>(code)); } </code></pre> </details> <details> <summary>Specification</summary> <pre><code><b>include</b> <a href="SlidingNonce.md#0x1_SlidingNonce_RecordNonceAbortsIf">RecordNonceAbortsIf</a>; </code></pre><a name="0x1_SlidingNonce_RecordNonceAbortsIf"></a>
<pre><code><b>schema</b> <a href="SlidingNonce.md#0x1_SlidingNonce_RecordNonceAbortsIf">RecordNonceAbortsIf</a> { account: signer; seq_nonce: u64; <b>aborts_if</b> !<b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)) <b>with</b> <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_NOT_PUBLISHED">Errors::NOT_PUBLISHED</a>; <b>aborts_if</b> <a href="SlidingNonce.md#0x1_SlidingNonce_spec_try_record_nonce">spec_try_record_nonce</a>(account, seq_nonce) != 0 <b>with</b> <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_INVALID_ARGUMENT">Errors::INVALID_ARGUMENT</a>; } </code></pre> </details><a name="0x1_SlidingNonce_try_record_nonce"></a>
try_record_nonce<a name="@Explanation_of_the_Algorithm_1"></a>
We have an "infinite" bitvector. The <code>min_nonce</code> tells us the starting index of the window. The window size is the size of the bitmask we can represent in a 128-bit wide bitmap. The <code>seq_nonce</code> that is sent represents setting a bit at index <code>seq_nonce</code> in this infinite bitvector. Because of this, if the same <code>seq_nonce</code> is passed in multiple times, that bit will be set, and we can signal an error. In order to represent the <code>seq_nonce</code> the window we are looking at must be shifted so that <code>seq_nonce</code> lies within that 128-bit window and, since the <code>min_nonce</code> represents the left-hand-side of this window, it must be increased to be no less than <code>seq_nonce - 128</code>.
The process of shifting window will invalidate other transactions that have a <code>seq_nonce</code> number that are to the "left" of this window (i.e., less than <code>min_nonce</code>) since it cannot be represented in the current window, and the window can only move forward and never backwards.
In order to prevent shifting this window over too much at any one time, a <code>jump_limit</code> is imposed. If a <code>seq_nonce</code> is provided that would cause the <code>min_nonce</code> to need to be increased by more than the <code>jump_limit</code> this will cause a failure.
<a name="@Some_Examples_2"></a>
Let's say we start out with a clean <code><a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a></code> published under <code>account</code>, this will look like this:
000000000000000000...00000000000000000000000000000...
^ ... ^
|_____________...________________|
min_nonce = 0 min_nonce + NONCE_MASK_SIZE = 0 + 64 = 64
<a name="@Example_1:_3"></a>
Let's see what happens when we call<code><a href="SlidingNonce.md#0x1_SlidingNonce_try_record_nonce">SlidingNonce::try_record_nonce</a>(account, 8)</code>:
bit_pos = 8 - min_nonce ~~~ 8 - 0 = 8
bit_pos >= NONCE_MASK_SIZE ~~~ 8 >= 128 = FALSE
000000010000000000...00000000000000000000000000000...
^ ... ^
|_____________...________________|
min_nonce = 0 min_nonce + NONCE_MASK_SIZE = 0 + 64 = 64
<a name="@Example_2:_4"></a>
Let's see what happens when we call <code><a href="SlidingNonce.md#0x1_SlidingNonce_try_record_nonce">SlidingNonce::try_record_nonce</a>(account, 129)</code>:
bit_pos = 129 - min_nonce ~~~ 129 - 0 = 129
bit_pos >= NONCE_MASK_SIZE ~~~ 129 >= 128 = TRUE
shift = bit_pos - NONCE_MASK_SIZE + 1 ~~~ 129 - 128 + 1 = 2
3b. See if there is no overlap between the new window that we need to shift to and the old window:
shift >= NONCE_MASK_SIZE ~~~ 2 >= 128 = FALSE
3c. Since there is an overlap between the new window that we need to shift to and the previous window, shift the window over, but keep the current set bits in the overlapping part of the window:
nonce_mask = nonce_mask >> shift; min_nonce += shift;
bit_pos = seq - min_nonce ~~~ 129 - 2 = 127
00000001000000000000000...000000000000000000000000000000000000000000010...
^ ... ^^
| ... ||
| ... new_set_bit|
|_______________...____________________________________________|
min_nonce = 2 min_nonce + NONCE_MASK_SIZE = 2 + 128 = 130
<a name="@Example_3:_5"></a>
Let's see what happens when we call <code><a href="SlidingNonce.md#0x1_SlidingNonce_try_record_nonce">SlidingNonce::try_record_nonce</a>(account, 400)</code>:
bit_pos = 400 - min_nonce ~~~ 400 - 2 = 398
bit_pos >= NONCE_MASK_SIZE ~~~ 398 >= 128 = TRUE
shift = bit_pos - NONCE_MASK_SIZE + 1 ~~~ 398 - 128 + 1 = 271
3b. See if there is no overlap between the new window that we need to shift to and the old window:
shift >= NONCE_MASK_SIZE ~~~ 271 >= 128 = TRUE
3c. Since there is no overlap between the new window that we need to shift to and the previous window we zero out the bitmap, and update the <code>min_nonce</code> to start at the out-of-range <code>seq_nonce</code>:
nonce_mask = 0; min_nonce = seq_nonce + 1 - NONCE_MASK_SIZE = 273;
bit_pos = seq_nonce - min_nonce ~~~ 400 - 273 = 127
...00000001000000000000000...000000000000000000000000000000000000000000010...
^ ... ^^
| ... ||
| ... new_set_bit|
|_______________...____________________________________________|
min_nonce = 273 min_nonce + NONCE_MASK_SIZE = 273 + 128 = 401
Tries to record this nonce in the account. Returns 0 if a nonce was recorded and non-0 otherwise
<pre><code><b>public</b> <b>fun</b> <a href="SlidingNonce.md#0x1_SlidingNonce_try_record_nonce">try_record_nonce</a>(account: &signer, seq_nonce: u64): u64 </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b> <b>fun</b> <a href="SlidingNonce.md#0x1_SlidingNonce_try_record_nonce">try_record_nonce</a>(account: &signer, seq_nonce: u64): u64 <b>acquires</b> <a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a> { <b>if</b> (seq_nonce == 0) { <b>return</b> 0 }; <b>assert</b>(<b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account)), <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_not_published">Errors::not_published</a>(<a href="SlidingNonce.md#0x1_SlidingNonce_ESLIDING_NONCE">ESLIDING_NONCE</a>)); <b>let</b> t = borrow_global_mut<<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account)); // The `seq_nonce` is outside the current window <b>to</b> the "left" and is // no longer valid since we can't shift the window back. <b>if</b> (t.min_nonce > seq_nonce) { <b>return</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_TOO_OLD">ENONCE_TOO_OLD</a> }; // Don't allow giant leaps in nonce <b>to</b> protect against nonce exhaustion // If we try <b>to</b> <b>move</b> a window by more than this amount, we will fail. <b>let</b> jump_limit = 10000; <b>if</b> (t.min_nonce + jump_limit <= seq_nonce) { <b>return</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_TOO_NEW">ENONCE_TOO_NEW</a> }; // Calculate The bit position in the window that will be set in our window <b>let</b> bit_pos = seq_nonce - t.min_nonce; // See <b>if</b> the bit position that we want <b>to</b> set lies outside the current // window that we have under the current `min_nonce`. <b>if</b> (bit_pos >= <a href="SlidingNonce.md#0x1_SlidingNonce_NONCE_MASK_SIZE">NONCE_MASK_SIZE</a>) { // Determine how much we need <b>to</b> shift the current window over (<b>to</b> // the "right") so that `bit_pos` lies within the new window. <b>let</b> shift = (bit_pos - <a href="SlidingNonce.md#0x1_SlidingNonce_NONCE_MASK_SIZE">NONCE_MASK_SIZE</a> + 1); // If we are shifting the window over by more than one window's width // reset the bits in the window, and <b>update</b> the // new start of the `min_nonce` so that `seq_nonce` is the // right-most bit in the new window. <b>if</b>(shift >= <a href="SlidingNonce.md#0x1_SlidingNonce_NONCE_MASK_SIZE">NONCE_MASK_SIZE</a>) { t.nonce_mask = 0; t.min_nonce = seq_nonce + 1 - <a href="SlidingNonce.md#0x1_SlidingNonce_NONCE_MASK_SIZE">NONCE_MASK_SIZE</a>; } <b>else</b> { // We are shifting the window over by less than a windows width, // so we need <b>to</b> keep the current set bits in the window t.nonce_mask = t.nonce_mask >> (shift <b>as</b> u8); t.min_nonce = t.min_nonce + shift; } }; // The window has been (possibly) shifted over so that `seq_nonce` lies // within the window. Recompute the bit positition that needs <b>to</b> be set // within the (possibly) new window. <b>let</b> bit_pos = seq_nonce - t.min_nonce; <b>let</b> set = 1u128 << (bit_pos <b>as</b> u8); // The bit was already set, so <b>return</b> an error. <b>if</b> (t.nonce_mask & set != 0) { <b>return</b> <a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_ALREADY_RECORDED">ENONCE_ALREADY_RECORDED</a> }; t.nonce_mask = t.nonce_mask | set; 0 } </code></pre> </details> <details> <summary>Specification</summary>It is currently assumed that this function raises no arithmetic overflow/underflow.
<pre><code><b>pragma</b> opaque, verify = <b>false</b>; <b>ensures</b> result == <a href="SlidingNonce.md#0x1_SlidingNonce_spec_try_record_nonce">spec_try_record_nonce</a>(account, seq_nonce); <b>aborts_if</b> !<b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)) <b>with</b> <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_NOT_PUBLISHED">Errors::NOT_PUBLISHED</a>; <b>modifies</b> <b>global</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)); </code></pre>Note: Verification is turned off. For verifying callers, this is effectively abstracted into a function that returns arbitrary results because <code>spec_try_record_nonce</code> is uninterpreted.
Specification version of <code><a href="SlidingNonce.md#0x1_SlidingNonce_try_record_nonce">Self::try_record_nonce</a></code>.
<a name="0x1_SlidingNonce_spec_try_record_nonce"></a>
<pre><code><b>fun</b> <a href="SlidingNonce.md#0x1_SlidingNonce_spec_try_record_nonce">spec_try_record_nonce</a>(account: signer, seq_nonce: u64): u64; </code></pre> </details><a name="0x1_SlidingNonce_publish"></a>
publishPublishes nonce resource for <code>account</code> This is required before other functions in this module can be called for <code>account</code>
<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="SlidingNonce.md#0x1_SlidingNonce_publish">publish</a>(account: &signer) </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="SlidingNonce.md#0x1_SlidingNonce_publish">publish</a>(account: &signer) { <b>assert</b>(!<b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account)), <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_already_published">Errors::already_published</a>(<a href="SlidingNonce.md#0x1_SlidingNonce_ENONCE_ALREADY_PUBLISHED">ENONCE_ALREADY_PUBLISHED</a>)); move_to(account, <a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a> { min_nonce: 0, nonce_mask: 0 }); } </code></pre> </details> <details> <summary>Specification</summary> <pre><code><b>pragma</b> opaque; <b>modifies</b> <b>global</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)); <b>aborts_if</b> <b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)) <b>with</b> <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_ALREADY_PUBLISHED">Errors::ALREADY_PUBLISHED</a>; <b>ensures</b> <b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)); </code></pre> </details><a name="@Module_Specification_6"></a>
Sliding nonces are initialized at Diem root and treasury compliance addresses
<pre><code><b>invariant</b> <a href="DiemTimestamp.md#0x1_DiemTimestamp_is_operating">DiemTimestamp::is_operating</a>() ==> <b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(@DiemRoot); <b>invariant</b> <a href="DiemTimestamp.md#0x1_DiemTimestamp_is_operating">DiemTimestamp::is_operating</a>() ==> <b>exists</b><<a href="SlidingNonce.md#0x1_SlidingNonce">SlidingNonce</a>>(@TreasuryCompliance); </code></pre>