language/diem-framework/modules/doc/CRSN.md
<a name="0x1_CRSN"></a>
0x1::CRSNA module implementing conflict-resistant sequence numbers (CRSNs). The specification, and formal description of the acceptance and rejection criteria, force expiration and window shifting of CRSNs are described in DIP-168.
CRSNpublishrecordcheckforce_expirehas_crsnshift_window_right<a name="0x1_CRSN_CRSN"></a>
CRSNA CRSN represents a finite slice or window of an "infinite" bitvector starting at zero with a size <code>k</code> defined dynamically at the time of publication of CRSN resource. The <code>min_nonce</code> defines the left-hand side of the slice, and the slice's state is held in <code>slots</code> and is of size <code>k</code>. Diagrammatically:
1111...000000100001000000...0100001000000...0000...
^ ... ^
|____..._____slots______...______|
min_nonce min_nonce + k - 1
<a name="@Constants_0"></a>
<a name="0x1_CRSN_ECRSN_SIZE_TOO_LARGE"></a>
The size given to the CRSN at the time of publishing was larger than the largest allowed CRSN size
<pre><code><b>const</b> <a href="CRSN.md#0x1_CRSN_ECRSN_SIZE_TOO_LARGE">ECRSN_SIZE_TOO_LARGE</a>: u64 = 3; </code></pre><a name="0x1_CRSN_EHAS_CRSN"></a>
A CRSN resource wasn't expected, but one was found
<pre><code><b>const</b> <a href="CRSN.md#0x1_CRSN_EHAS_CRSN">EHAS_CRSN</a>: u64 = 1; </code></pre><a name="0x1_CRSN_EINVALID_SHIFT"></a>
the amount to shift the CRSN window was zero
<pre><code><b>const</b> <a href="CRSN.md#0x1_CRSN_EINVALID_SHIFT">EINVALID_SHIFT</a>: u64 = 4; </code></pre><a name="0x1_CRSN_ENO_CRSN"></a>
No CRSN resource exists
<pre><code><b>const</b> <a href="CRSN.md#0x1_CRSN_ENO_CRSN">ENO_CRSN</a>: u64 = 0; </code></pre><a name="0x1_CRSN_EZERO_SIZE_CRSN"></a>
The size given to the CRSN at the time of publishing was zero, which is not supported
<pre><code><b>const</b> <a href="CRSN.md#0x1_CRSN_EZERO_SIZE_CRSN">EZERO_SIZE_CRSN</a>: u64 = 2; </code></pre><a name="0x1_CRSN_MAX_CRSN_SIZE"></a>
<pre><code><b>const</b> <a href="CRSN.md#0x1_CRSN_MAX_CRSN_SIZE">MAX_CRSN_SIZE</a>: u64 = 256; </code></pre><a name="0x1_CRSN_publish"></a>
publishPublish a DSN under <code>account</code>. Cannot already have a DSN published.
<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="CRSN.md#0x1_CRSN_publish">publish</a>(account: &signer, min_nonce: u64, size: u64) </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="CRSN.md#0x1_CRSN_publish">publish</a>(account: &signer, min_nonce: u64, size: u64) { <b>assert</b>(!<a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</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_invalid_state">Errors::invalid_state</a>(<a href="CRSN.md#0x1_CRSN_EHAS_CRSN">EHAS_CRSN</a>)); <b>assert</b>(size > 0, <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_invalid_argument">Errors::invalid_argument</a>(<a href="CRSN.md#0x1_CRSN_EZERO_SIZE_CRSN">EZERO_SIZE_CRSN</a>)); <b>assert</b>(size <= <a href="CRSN.md#0x1_CRSN_MAX_CRSN_SIZE">MAX_CRSN_SIZE</a>, <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_invalid_argument">Errors::invalid_argument</a>(<a href="CRSN.md#0x1_CRSN_ECRSN_SIZE_TOO_LARGE">ECRSN_SIZE_TOO_LARGE</a>)); move_to(account, <a href="CRSN.md#0x1_CRSN">CRSN</a> { min_nonce, size, slots: <a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_new">BitVector::new</a>(size), }) } </code></pre> </details> <details> <summary>Specification</summary> <pre><code><b>include</b> <a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_NewAbortsIf">BitVector::NewAbortsIf</a>{length: size}; <b>aborts_if</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</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_INVALID_STATE">Errors::INVALID_STATE</a>; <b>aborts_if</b> size == 0 <b>with</b> <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_INVALID_ARGUMENT">Errors::INVALID_ARGUMENT</a>; <b>aborts_if</b> size > <a href="CRSN.md#0x1_CRSN_MAX_CRSN_SIZE">MAX_CRSN_SIZE</a> <b>with</b> <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_INVALID_ARGUMENT">Errors::INVALID_ARGUMENT</a>; <b>ensures</b> <b>exists</b><<a href="CRSN.md#0x1_CRSN">CRSN</a>>(<a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)); </code></pre> </details><a name="0x1_CRSN_record"></a>
recordRecord <code>sequence_nonce</code> under the <code>account</code>. Returns true if <code>sequence_nonce</code> is accepted, returns false if the <code>sequence_nonce</code> is rejected.
<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="CRSN.md#0x1_CRSN_record">record</a>(account: &signer, sequence_nonce: u64): bool </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="CRSN.md#0x1_CRSN_record">record</a>(account: &signer, sequence_nonce: u64): bool <b>acquires</b> <a href="CRSN.md#0x1_CRSN">CRSN</a> { <b>let</b> addr = <a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account); <b>if</b> (<a href="CRSN.md#0x1_CRSN_check">check</a>(account, sequence_nonce)) { // <a href="CRSN.md#0x1_CRSN">CRSN</a> <b>exists</b> by `check`. <b>let</b> crsn = borrow_global_mut<<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr); // accept nonce <b>let</b> scaled_nonce = sequence_nonce - crsn.min_nonce; <a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_set">BitVector::set</a>(&<b>mut</b> crsn.slots, scaled_nonce); <a href="CRSN.md#0x1_CRSN_shift_window_right">shift_window_right</a>(crsn); <b>return</b> <b>true</b> } <b>else</b> <b>if</b> (<b>exists</b><<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr)) { // window was force shifted in this transaction <b>let</b> crsn = borrow_global<<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr); <b>if</b> (crsn.min_nonce > sequence_nonce) <b>return</b> <b>true</b> }; <b>false</b> } </code></pre> </details><a name="0x1_CRSN_check"></a>
checkA stateless version of <code>record</code>: returns <code><b>true</b></code> if the <code>sequence_nonce</code> will be accepted, and <code><b>false</b></code> otherwise.
<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="CRSN.md#0x1_CRSN_check">check</a>(account: &signer, sequence_nonce: u64): bool </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="CRSN.md#0x1_CRSN_check">check</a>(account: &signer, sequence_nonce: u64): bool <b>acquires</b> <a href="CRSN.md#0x1_CRSN">CRSN</a> { <b>let</b> addr = <a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account); <b>assert</b>(<a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr), <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_invalid_state">Errors::invalid_state</a>(<a href="CRSN.md#0x1_CRSN_ENO_CRSN">ENO_CRSN</a>)); <b>let</b> crsn = borrow_global_mut<<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr); // Don't accept <b>if</b> it's outside of the window <b>if</b> ((sequence_nonce < crsn.min_nonce) || ((sequence_nonce <b>as</b> u128) >= (crsn.min_nonce <b>as</b> u128) + (<a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_length">BitVector::length</a>(&crsn.slots) <b>as</b> u128))) { <b>false</b> } <b>else</b> { // scaled nonce is the index in the window <b>let</b> scaled_nonce = sequence_nonce - crsn.min_nonce; // Bit already set, reject, otherwise accept !<a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_is_index_set">BitVector::is_index_set</a>(&crsn.slots, scaled_nonce) } } </code></pre> </details> <details> <summary>Specification</summary> <pre><code><b>include</b> <a href="CRSN.md#0x1_CRSN_CheckAbortsIf">CheckAbortsIf</a>{addr: <a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_spec_address_of">Signer::spec_address_of</a>(account)}; </code></pre><a name="0x1_CRSN_CheckAbortsIf"></a>
<pre><code><b>schema</b> <a href="CRSN.md#0x1_CRSN_CheckAbortsIf">CheckAbortsIf</a> { addr: address; sequence_nonce: u64; <b>let</b> crsn = <b>global</b><<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr); <b>let</b> scaled_nonce = sequence_nonce - crsn.min_nonce; <b>aborts_if</b> !<a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr) <b>with</b> <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_INVALID_STATE">Errors::INVALID_STATE</a>; <b>include</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr) && (sequence_nonce >= crsn.min_nonce) && (sequence_nonce + crsn.min_nonce < <a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_length">BitVector::length</a>(crsn.slots)) ==> <a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_IsIndexSetAbortsIf">BitVector::IsIndexSetAbortsIf</a>{bitvector: crsn.slots, bit_index: scaled_nonce }; } </code></pre><a name="0x1_CRSN_spec_check"></a>
<pre><code><b>fun</b> <a href="CRSN.md#0x1_CRSN_spec_check">spec_check</a>(addr: address, sequence_nonce: u64): bool { <b>let</b> crsn = <b>global</b><<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr); <b>if</b> ((sequence_nonce < crsn.min_nonce) || (sequence_nonce >= crsn.min_nonce + <a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_length">BitVector::length</a>(crsn.slots))) { <b>false</b> } <b>else</b> { <b>let</b> scaled_nonce = sequence_nonce - crsn.min_nonce; !<a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_spec_is_index_set">BitVector::spec_is_index_set</a>(crsn.slots, scaled_nonce) } } </code></pre> </details><a name="0x1_CRSN_force_expire"></a>
force_expireForce expire transactions by forcibly shifting the window by <code>shift_amount</code>. After the window has been shifted by <code>shift_amount</code> it is then shifted over set bits as define by the <code>shift_window_right</code> function.
<pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_force_expire">force_expire</a>(account: &signer, shift_amount: u64) </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_force_expire">force_expire</a>(account: &signer, shift_amount: u64) <b>acquires</b> <a href="CRSN.md#0x1_CRSN">CRSN</a> { <b>assert</b>(shift_amount > 0, <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_invalid_argument">Errors::invalid_argument</a>(<a href="CRSN.md#0x1_CRSN_EINVALID_SHIFT">EINVALID_SHIFT</a>)); <b>let</b> addr = <a href="../../../../../../move-stdlib/docs/Signer.md#0x1_Signer_address_of">Signer::address_of</a>(account); <b>assert</b>(<a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr), <a href="../../../../../../move-stdlib/docs/Errors.md#0x1_Errors_invalid_state">Errors::invalid_state</a>(<a href="CRSN.md#0x1_CRSN_ENO_CRSN">ENO_CRSN</a>)); <b>let</b> crsn = borrow_global_mut<<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr); <a href="../../../../../../move-stdlib/docs/BitVector.md#0x1_BitVector_shift_left">BitVector::shift_left</a>(&<b>mut</b> crsn.slots, shift_amount); crsn.min_nonce = crsn.min_nonce + shift_amount; // shift over any set bits <a href="CRSN.md#0x1_CRSN_shift_window_right">shift_window_right</a>(crsn); } </code></pre> </details><a name="0x1_CRSN_has_crsn"></a>
has_crsnReturn whether this address has a CRSN resource published under it.
<pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr: address): bool </code></pre> <details> <summary>Implementation</summary> <pre><code><b>public</b> <b>fun</b> <a href="CRSN.md#0x1_CRSN_has_crsn">has_crsn</a>(addr: address): bool { <b>exists</b><<a href="CRSN.md#0x1_CRSN">CRSN</a>>(addr) } </code></pre> </details><a name="0x1_CRSN_shift_window_right"></a>
shift_window_right