diff --git a/Cargo.toml b/Cargo.toml index a3bc053b..e6811b27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "starcoin-framework" -version = "11.0.0" +version = "13.0.0" authors = ["Starcoin Core Dev "] license = "Apache-2.0" publish = false @@ -14,4 +14,4 @@ once_cell = "1.8.0" tempfile = "3.2.0" log = "0.4.14" -[dev-dependencies] \ No newline at end of file +[dev-dependencies] diff --git a/build/StarcoinFramework/BuildInfo.yaml b/build/StarcoinFramework/BuildInfo.yaml index 01cb339d..ab73f252 100644 --- a/build/StarcoinFramework/BuildInfo.yaml +++ b/build/StarcoinFramework/BuildInfo.yaml @@ -5,7 +5,7 @@ compiled_package_info: StarcoinAssociation: "0x0000000000000000000000000a550c18" StarcoinFramework: "0x00000000000000000000000000000001" VMReserved: "0x00000000000000000000000000000000" - source_digest: CFA067D7641D37F19CD134995335BEBEE1410E9745B0846D1E1D05149FA4D90B + source_digest: 0899528282F044F9621C6B7686759D0D2AE52CF34CFAE8BF0917D02F2699ADF9 build_flags: dev_mode: false test_mode: false diff --git a/build/StarcoinFramework/abis/OnChainConfigScripts/propose_update_flexi_dag_effective_height.abi b/build/StarcoinFramework/abis/OnChainConfigScripts/propose_update_flexi_dag_effective_height.abi new file mode 100644 index 00000000..1c33d9a5 Binary files /dev/null and b/build/StarcoinFramework/abis/OnChainConfigScripts/propose_update_flexi_dag_effective_height.abi differ diff --git a/build/StarcoinFramework/abis/StdlibUpgradeScripts/upgrade_from_v12_to_v13.abi b/build/StarcoinFramework/abis/StdlibUpgradeScripts/upgrade_from_v12_to_v13.abi new file mode 100644 index 00000000..b1ce39d4 Binary files /dev/null and b/build/StarcoinFramework/abis/StdlibUpgradeScripts/upgrade_from_v12_to_v13.abi differ diff --git a/build/StarcoinFramework/bytecode_modules/Block.mv b/build/StarcoinFramework/bytecode_modules/Block.mv index d07b44ae..e72b98b1 100644 Binary files a/build/StarcoinFramework/bytecode_modules/Block.mv and b/build/StarcoinFramework/bytecode_modules/Block.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/Epoch.mv b/build/StarcoinFramework/bytecode_modules/Epoch.mv index 2aa602ac..b9e0f1f9 100644 Binary files a/build/StarcoinFramework/bytecode_modules/Epoch.mv and b/build/StarcoinFramework/bytecode_modules/Epoch.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/FlexiDagConfig.mv b/build/StarcoinFramework/bytecode_modules/FlexiDagConfig.mv new file mode 100644 index 00000000..243f8a06 Binary files /dev/null and b/build/StarcoinFramework/bytecode_modules/FlexiDagConfig.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/Genesis.mv b/build/StarcoinFramework/bytecode_modules/Genesis.mv index 78701566..97508566 100644 Binary files a/build/StarcoinFramework/bytecode_modules/Genesis.mv and b/build/StarcoinFramework/bytecode_modules/Genesis.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/OnChainConfigScripts.mv b/build/StarcoinFramework/bytecode_modules/OnChainConfigScripts.mv index 0f740c0f..8d847cdf 100644 Binary files a/build/StarcoinFramework/bytecode_modules/OnChainConfigScripts.mv and b/build/StarcoinFramework/bytecode_modules/OnChainConfigScripts.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/StdlibUpgradeScripts.mv b/build/StarcoinFramework/bytecode_modules/StdlibUpgradeScripts.mv index b53e7f3f..cebf13e9 100644 Binary files a/build/StarcoinFramework/bytecode_modules/StdlibUpgradeScripts.mv and b/build/StarcoinFramework/bytecode_modules/StdlibUpgradeScripts.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/TransactionManager.mv b/build/StarcoinFramework/bytecode_modules/TransactionManager.mv index 17893111..799c306a 100644 Binary files a/build/StarcoinFramework/bytecode_modules/TransactionManager.mv and b/build/StarcoinFramework/bytecode_modules/TransactionManager.mv differ diff --git a/build/StarcoinFramework/docs/Block.md b/build/StarcoinFramework/docs/Block.md index 534f285d..4d2729e5 100644 --- a/build/StarcoinFramework/docs/Block.md +++ b/build/StarcoinFramework/docs/Block.md @@ -8,14 +8,19 @@ Block module provide metadata for generated blocks. - [Resource `BlockMetadata`](#0x1_Block_BlockMetadata) - [Struct `NewBlockEvent`](#0x1_Block_NewBlockEvent) +- [Resource `BlockMetadataV2`](#0x1_Block_BlockMetadataV2) +- [Struct `NewBlockEventV2`](#0x1_Block_NewBlockEventV2) - [Struct `Checkpoint`](#0x1_Block_Checkpoint) - [Resource `Checkpoints`](#0x1_Block_Checkpoints) - [Constants](#@Constants_0) - [Function `initialize`](#0x1_Block_initialize) +- [Function `initialize_blockmetadata_v2`](#0x1_Block_initialize_blockmetadata_v2) - [Function `get_current_block_number`](#0x1_Block_get_current_block_number) - [Function `get_parent_hash`](#0x1_Block_get_parent_hash) - [Function `get_current_author`](#0x1_Block_get_current_author) +- [Function `get_parents_hash`](#0x1_Block_get_parents_hash) - [Function `process_block_metadata`](#0x1_Block_process_block_metadata) +- [Function `process_block_metadata_v2`](#0x1_Block_process_block_metadata_v2) - [Function `checkpoints_init`](#0x1_Block_checkpoints_init) - [Function `checkpoint_entry`](#0x1_Block_checkpoint_entry) - [Function `checkpoint`](#0x1_Block_checkpoint) @@ -137,6 +142,116 @@ Events emitted when new block generated. + + + + +## Resource `BlockMetadataV2` + +Block metadata struct. + + +
struct BlockMetadataV2 has key
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ number of the current block +
+
+parent_hash: vector<u8> +
+
+ Hash of the parent block. +
+
+author: address +
+
+ Author of the current block. +
+
+uncles: u64 +
+
+ number of uncles. +
+
+parents_hash: vector<u8> +
+
+ An Array of the parents hash for a Dag block. +
+
+new_block_events: Event::EventHandle<Block::NewBlockEventV2> +
+
+ Handle of events when new blocks are emitted +
+
+ + +
+ + + +## Struct `NewBlockEventV2` + +Events emitted when new block generated. + + +
struct NewBlockEventV2 has drop, store
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ +
+
+author: address +
+
+ +
+
+timestamp: u64 +
+
+ +
+
+uncles: u64 +
+
+ +
+
+parents_hash: vector<u8> +
+
+ +
+
+ +
@@ -309,7 +424,7 @@ This can only be invoked by the GENESIS_ACCOUNT at genesis account, BlockMetadata { number: 0, - parent_hash: parent_hash, + parent_hash, author: CoreAddresses::GENESIS_ADDRESS(), uncles: 0, new_block_events: Event::new_event_handle<Self::NewBlockEvent>(account), @@ -333,6 +448,58 @@ This can only be invoked by the GENESIS_ACCOUNT at genesis + + + + +## Function `initialize_blockmetadata_v2` + + + +
public fun initialize_blockmetadata_v2(account: &signer)
+
+ + + +
+Implementation + + +
public fun initialize_blockmetadata_v2(account: &signer) acquires BlockMetadata {
+    CoreAddresses::assert_genesis_address(account);
+
+    let block_meta_ref = borrow_global<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+
+    // create new resource base on current block metadata
+    move_to<BlockMetadataV2>(
+        account,
+        BlockMetadataV2 {
+            number: block_meta_ref.number,
+            parent_hash: block_meta_ref.parent_hash,
+            author: block_meta_ref.author,
+            uncles: block_meta_ref.uncles,
+            parents_hash: Vector::empty(),
+            new_block_events: Event::new_event_handle<Self::NewBlockEventV2>(account),
+        });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<BlockMetadata>(Signer::address_of(account));
+ensures exists<BlockMetadataV2>(Signer::address_of(account));
+
+ + +
@@ -351,8 +518,8 @@ Get the current block number Implementation -
public fun get_current_block_number(): u64 acquires BlockMetadata {
-  borrow_global<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS()).number
+
public fun get_current_block_number(): u64 acquires BlockMetadataV2 {
+    borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).number
 }
 
@@ -365,7 +532,7 @@ Get the current block number -
aborts_if !exists<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
 
@@ -388,8 +555,8 @@ Get the hash of the parent block. Implementation -
public fun get_parent_hash(): vector<u8> acquires BlockMetadata {
-  *&borrow_global<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS()).parent_hash
+
public fun get_parent_hash(): vector<u8> acquires BlockMetadataV2 {
+    *&borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).parent_hash
 }
 
@@ -402,7 +569,7 @@ Get the hash of the parent block. -
aborts_if !exists<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
 
@@ -425,8 +592,8 @@ Gets the address of the author of the current block Implementation -
public fun get_current_author(): address acquires BlockMetadata {
-  borrow_global<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS()).author
+
public fun get_current_author(): address acquires BlockMetadataV2 {
+    borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).author
 }
 
@@ -439,7 +606,43 @@ Gets the address of the author of the current block -
aborts_if !exists<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + + + + + +## Function `get_parents_hash` + + + +
public fun get_parents_hash(): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_parents_hash(): vector<u8> acquires BlockMetadataV2 {
+    *&borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).parents_hash
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
 
@@ -462,23 +665,76 @@ Call at block prologue Implementation -
public fun process_block_metadata(account: &signer, parent_hash: vector<u8>,author: address, timestamp: u64, uncles:u64, number:u64) acquires BlockMetadata{
+
public fun process_block_metadata(account: &signer,
+                                  parent_hash: vector<u8>,
+                                  author: address,
+                                  timestamp: u64,
+                                  uncles:u64,
+                                  number:u64) acquires BlockMetadataV2{
+    Self::process_block_metadata_v2(account, parent_hash, author, timestamp, uncles, number, Vector::empty<u8>())
+
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if number != global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).number + 1;
+
+ + + +
+ + + +## Function `process_block_metadata_v2` + +Call at block prologue for flexidag + + +
public fun process_block_metadata_v2(account: &signer, parent_hash: vector<u8>, author: address, timestamp: u64, uncles: u64, number: u64, parents_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun process_block_metadata_v2(account: &signer,
+                                     parent_hash: vector<u8>,
+                                     author: address,
+                                     timestamp: u64,
+                                     uncles:u64,
+                                     number:u64,
+                                     parents_hash: vector<u8>) acquires BlockMetadataV2 {
     CoreAddresses::assert_genesis_address(account);
 
-    let block_metadata_ref = borrow_global_mut<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+    let block_metadata_ref = borrow_global_mut<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
     assert!(number == (block_metadata_ref.number + 1), Errors::invalid_argument(EBLOCK_NUMBER_MISMATCH));
     block_metadata_ref.number = number;
     block_metadata_ref.author= author;
     block_metadata_ref.parent_hash = parent_hash;
     block_metadata_ref.uncles = uncles;
+    block_metadata_ref.parents_hash = parents_hash;
 
-    Event::emit_event<NewBlockEvent>(
+    Event::emit_event<NewBlockEventV2>(
       &mut block_metadata_ref.new_block_events,
-      NewBlockEvent {
-          number: number,
-          author: author,
-          timestamp: timestamp,
-          uncles: uncles,
+      NewBlockEventV2 {
+          number,
+          author,
+          timestamp,
+          uncles,
+          parents_hash,
       }
     );
 }
@@ -494,8 +750,8 @@ Call at block prologue
 
 
 
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
-aborts_if !exists<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
-aborts_if number != global<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS()).number + 1;
+aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if number != global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).number + 1;
 
@@ -505,7 +761,7 @@ Call at block prologue
schema AbortsIfBlockMetadataNotExist {
-    aborts_if !exists<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+    aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
 }
 
@@ -573,7 +829,7 @@ Call at block prologue Implementation -
public entry fun checkpoint_entry(_account: signer) acquires BlockMetadata, Checkpoints {
+
public entry fun checkpoint_entry(_account: signer) acquires BlockMetadataV2, Checkpoints {
     checkpoint();
 }
 
@@ -609,7 +865,7 @@ Call at block prologue Implementation -
public fun checkpoint() acquires BlockMetadata, Checkpoints{
+
public fun checkpoint() acquires BlockMetadataV2, Checkpoints{
     let parent_block_number = get_current_block_number() - 1;
     let parent_block_hash   = get_parent_hash();
 
diff --git a/build/StarcoinFramework/docs/Epoch.md b/build/StarcoinFramework/docs/Epoch.md
index 17ac2e3c..d249b8d2 100644
--- a/build/StarcoinFramework/docs/Epoch.md
+++ b/build/StarcoinFramework/docs/Epoch.md
@@ -31,7 +31,6 @@ The module provide epoch functionality for starcoin.
 
 
use 0x1::ConsensusConfig;
 use 0x1::CoreAddresses;
-use 0x1::Errors;
 use 0x1::Event;
 use 0x1::Math;
 use 0x1::Option;
@@ -369,13 +368,21 @@ compute next block time_target.
 Implementation
 
 
-
public fun compute_next_block_time_target(config: &ConsensusConfig, last_epoch_time_target: u64, epoch_start_time: u64, now_milli_second: u64, start_block_number: u64, end_block_number: u64, total_uncles: u64): u64 {
+
public fun compute_next_block_time_target(
+    config: &ConsensusConfig,
+    last_epoch_time_target: u64,
+    epoch_start_time: u64,
+    now_milli_second: u64,
+    start_block_number: u64,
+    end_block_number: u64,
+    total_uncles: u64
+): u64 {
     let total_time = now_milli_second - epoch_start_time;
     let blocks = end_block_number - start_block_number;
     let avg_block_time = total_time / blocks;
     let uncles_rate = total_uncles * THOUSAND / blocks;
     let new_epoch_block_time_target = (THOUSAND + uncles_rate) * avg_block_time /
-            (ConsensusConfig::uncle_rate_target(config) + THOUSAND);
+        (ConsensusConfig::uncle_rate_target(config) + THOUSAND);
     if (new_epoch_block_time_target > last_epoch_time_target * 2) {
         new_epoch_block_time_target = last_epoch_time_target * 2;
     };
@@ -426,26 +433,43 @@ adjust_epoch try to advance to next epoch if current epoch ends.
 Implementation
 
 
-
public fun adjust_epoch(account: &signer, block_number: u64, timestamp: u64, uncles: u64, parent_gas_used:u64): u128
+
public fun adjust_epoch(
+    account: &signer,
+    block_number: u64,
+    timestamp: u64,
+    uncles: u64,
+    parent_gas_used: u64
+): u128
 acquires Epoch, EpochData {
     CoreAddresses::assert_genesis_address(account);
 
     let epoch_ref = borrow_global_mut<Epoch>(CoreAddresses::GENESIS_ADDRESS());
-    assert!(epoch_ref.max_uncles_per_block >= uncles, Errors::invalid_argument(EINVALID_UNCLES_COUNT));
+    // assert!(epoch_ref.max_uncles_per_block >= uncles, Errors::invalid_argument(EINVALID_UNCLES_COUNT));
 
     let epoch_data = borrow_global_mut<EpochData>(CoreAddresses::GENESIS_ADDRESS());
     let (new_epoch, reward_per_block) = if (block_number < epoch_ref.end_block_number) {
         (false, epoch_ref.reward_per_block)
     } else if (block_number == epoch_ref.end_block_number) {
         //start a new epoch
-        assert!(uncles == 0, Errors::invalid_argument(EINVALID_UNCLES_COUNT));
+        // assert!(uncles == 0, Errors::invalid_argument(EINVALID_UNCLES_COUNT));
         // block time target unit is milli_seconds.
         let now_milli_seconds = timestamp;
 
         let config = ConsensusConfig::get_config();
         let last_epoch_time_target = epoch_ref.block_time_target;
-        let new_epoch_block_time_target = compute_next_block_time_target(&config, last_epoch_time_target, epoch_ref.start_time, now_milli_seconds, epoch_ref.start_block_number, epoch_ref.end_block_number, epoch_data.uncles);
-        let new_reward_per_block = ConsensusConfig::do_compute_reward_per_block(&config, new_epoch_block_time_target);
+        let new_epoch_block_time_target = compute_next_block_time_target(
+            &config,
+            last_epoch_time_target,
+            epoch_ref.start_time,
+            now_milli_seconds,
+            epoch_ref.start_block_number,
+            epoch_ref.end_block_number,
+            epoch_data.uncles
+        );
+        let new_reward_per_block = ConsensusConfig::do_compute_reward_per_block(
+            &config,
+            new_epoch_block_time_target
+        );
 
         //update epoch by adjust result or config, because ConsensusConfig may be updated.
         epoch_ref.number = epoch_ref.number + 1;
@@ -461,7 +485,13 @@ adjust_epoch try to advance to next epoch if current epoch ends.
 
         epoch_data.uncles = 0;
         let last_epoch_total_gas = epoch_data.total_gas + (parent_gas_used as u128);
-        adjust_gas_limit(&config, epoch_ref, last_epoch_time_target, new_epoch_block_time_target, last_epoch_total_gas);
+        adjust_gas_limit(
+            &config,
+            epoch_ref,
+            last_epoch_time_target,
+            new_epoch_block_time_target,
+            last_epoch_total_gas
+        );
         emit_epoch_event(epoch_ref, epoch_data.total_reward);
         (true, new_reward_per_block)
     } else {
@@ -469,7 +499,7 @@ adjust_epoch try to advance to next epoch if current epoch ends.
         abort EUNREACHABLE
     };
     let reward = reward_per_block +
-            reward_per_block * (epoch_ref.reward_per_uncle_percent as u128) * (uncles as u128) / (HUNDRED as u128);
+        reward_per_block * (epoch_ref.reward_per_uncle_percent as u128) * (uncles as u128) / (HUNDRED as u128);
     update_epoch_data(epoch_data, new_epoch, reward, uncles, parent_gas_used);
     reward
 }
@@ -511,8 +541,20 @@ adjust_epoch try to advance to next epoch if current epoch ends.
 Implementation
 
 
-
fun adjust_gas_limit(config: &ConsensusConfig, epoch_ref: &mut Epoch, last_epoch_time_target: u64, new_epoch_time_target: u64, last_epoch_total_gas:u128) {
-    let new_gas_limit = compute_gas_limit(config, last_epoch_time_target, new_epoch_time_target, epoch_ref.block_gas_limit, last_epoch_total_gas);
+
fun adjust_gas_limit(
+    config: &ConsensusConfig,
+    epoch_ref: &mut Epoch,
+    last_epoch_time_target: u64,
+    new_epoch_time_target: u64,
+    last_epoch_total_gas: u128
+) {
+    let new_gas_limit = compute_gas_limit(
+        config,
+        last_epoch_time_target,
+        new_epoch_time_target,
+        epoch_ref.block_gas_limit,
+        last_epoch_total_gas
+    );
     if (Option::is_some(&new_gas_limit)) {
         epoch_ref.block_gas_limit = Option::destroy_some(new_gas_limit);
     }
@@ -551,17 +593,31 @@ Compute block's gas limit of next epoch.
 Implementation
 
 
-
public fun compute_gas_limit(config: &ConsensusConfig, last_epoch_time_target: u64, new_epoch_time_target: u64, last_epoch_block_gas_limit: u64, last_epoch_total_gas: u128) : Option::Option<u64> {
+
public fun compute_gas_limit(
+    config: &ConsensusConfig,
+    last_epoch_time_target: u64,
+    new_epoch_time_target: u64,
+    last_epoch_block_gas_limit: u64,
+    last_epoch_total_gas: u128
+): Option::Option<u64> {
     let epoch_block_count = (ConsensusConfig::epoch_block_count(config) as u128);
-    let gas_limit_threshold = (last_epoch_total_gas >= Math::mul_div((last_epoch_block_gas_limit as u128) * epoch_block_count, (80 as u128), (HUNDRED as u128)));
+    let gas_limit_threshold = (last_epoch_total_gas >= Math::mul_div(
+        (last_epoch_block_gas_limit as u128) * epoch_block_count,
+        (80 as u128),
+        (HUNDRED as u128)
+    ));
     let new_gas_limit = Option::none<u64>();
 
     let min_block_time_target = ConsensusConfig::min_block_time_target(config);
     let max_block_time_target = ConsensusConfig::max_block_time_target(config);
-    let base_block_gas_limit =  ConsensusConfig::base_block_gas_limit(config);
+    let base_block_gas_limit = ConsensusConfig::base_block_gas_limit(config);
     if (last_epoch_time_target == new_epoch_time_target) {
         if (new_epoch_time_target == min_block_time_target && gas_limit_threshold) {
-            let increase_gas_limit = in_or_decrease_gas_limit(last_epoch_block_gas_limit, 110, base_block_gas_limit);
+            let increase_gas_limit = in_or_decrease_gas_limit(
+                last_epoch_block_gas_limit,
+                110,
+                base_block_gas_limit
+            );
             new_gas_limit = Option::some(increase_gas_limit);
         } else if (new_epoch_time_target == max_block_time_target && !gas_limit_threshold) {
             let decrease_gas_limit = in_or_decrease_gas_limit(last_epoch_block_gas_limit, 90, base_block_gas_limit);
@@ -606,7 +662,7 @@ Compute block's gas limit of next epoch.
 
 
fun in_or_decrease_gas_limit(last_epoch_block_gas_limit: u64, percent: u64, min_block_gas_limit: u64): u64 {
     let tmp_gas_limit = Math::mul_div((last_epoch_block_gas_limit as u128), (percent as u128), (HUNDRED as u128));
-    let new_gas_limit = if (tmp_gas_limit > (min_block_gas_limit  as u128)) {
+    let new_gas_limit = if (tmp_gas_limit > (min_block_gas_limit as u128)) {
         (tmp_gas_limit as u64)
     } else {
         min_block_gas_limit
@@ -625,7 +681,7 @@ Compute block's gas limit of next epoch.
 
 
 
-
include Math::MulDivAbortsIf{x: last_epoch_block_gas_limit, y: percent, z: HUNDRED};
+
include Math::MulDivAbortsIf { x: last_epoch_block_gas_limit, y: percent, z: HUNDRED };
 aborts_if Math::spec_mul_div() > MAX_U64;
 
@@ -648,7 +704,13 @@ Compute block's gas limit of next epoch. Implementation -
fun update_epoch_data(epoch_data: &mut EpochData, new_epoch: bool, reward: u128, uncles: u64, parent_gas_used:u64) {
+
fun update_epoch_data(
+    epoch_data: &mut EpochData,
+    new_epoch: bool,
+    reward: u128,
+    uncles: u64,
+    parent_gas_used: u64
+) {
     if (new_epoch) {
         epoch_data.total_reward = reward;
         epoch_data.uncles = uncles;
diff --git a/build/StarcoinFramework/docs/FlexiDagConfig.md b/build/StarcoinFramework/docs/FlexiDagConfig.md
new file mode 100644
index 00000000..c8090f6e
--- /dev/null
+++ b/build/StarcoinFramework/docs/FlexiDagConfig.md
@@ -0,0 +1,167 @@
+
+
+
+# Module `0x1::FlexiDagConfig`
+
+
+
+-  [Struct `FlexiDagConfig`](#0x1_FlexiDagConfig_FlexiDagConfig)
+-  [Function `new_flexidag_config`](#0x1_FlexiDagConfig_new_flexidag_config)
+-  [Function `initialize`](#0x1_FlexiDagConfig_initialize)
+-  [Function `effective_height`](#0x1_FlexiDagConfig_effective_height)
+-  [Module Specification](#@Module_Specification_0)
+
+
+
use 0x1::Block;
+use 0x1::Config;
+use 0x1::CoreAddresses;
+
+ + + + + +## Struct `FlexiDagConfig` + +The struct to hold all config data needed for Flexidag. + + +
struct FlexiDagConfig has copy, drop, store
+
+ + + +
+Fields + + +
+
+effective_height: u64 +
+
+ +
+
+ + +
+ + + +## Function `new_flexidag_config` + +Create a new configuration for flexidag, mainly used in DAO. + + +
public fun new_flexidag_config(effective_height: u64): FlexiDagConfig::FlexiDagConfig
+
+ + + +
+Implementation + + +
public fun new_flexidag_config(effective_height: u64): FlexiDagConfig {
+    FlexiDagConfig {
+        effective_height,
+    }
+}
+
+ + + +
+ + + +## Function `initialize` + + + +
public fun initialize(account: &signer, effective_height: u64)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, effective_height: u64) {
+    CoreAddresses::assert_genesis_address(account);
+    Config::publish_new_config<FlexiDagConfig>(account, new_flexidag_config(effective_height));
+    Block::initialize_blockmetadata_v2(account);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<Config::Config<FlexiDagConfig>>(Signer::address_of(account));
+aborts_if exists<Config::ModifyConfigCapabilityHolder<FlexiDagConfig>>(Signer::address_of(account));
+ensures exists<Config::Config<FlexiDagConfig>>(Signer::address_of(account));
+ensures
+    exists<Config::ModifyConfigCapabilityHolder<FlexiDagConfig>>(
+        Signer::address_of(account),
+    );
+
+ + + +
+ + + +## Function `effective_height` + + + +
public fun effective_height(account: address): u64
+
+ + + +
+Implementation + + +
public fun effective_height(account: address): u64 {
+    let flexi_dag_config = Config::get_by_address<FlexiDagConfig>(account);
+    flexi_dag_config.effective_height
+}
+
+ + + +
+ +
+Specification + + + +
include Config::AbortsIfConfigNotExist<FlexiDagConfig> { addr: account };
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+
diff --git a/build/StarcoinFramework/docs/Genesis.md b/build/StarcoinFramework/docs/Genesis.md index b04939f0..6d449dab 100644 --- a/build/StarcoinFramework/docs/Genesis.md +++ b/build/StarcoinFramework/docs/Genesis.md @@ -509,6 +509,7 @@ The module for init Genesis }; StdlibUpgradeScripts::do_upgrade_from_v6_to_v7_with_language_version(&genesis_account, 6); StdlibUpgradeScripts::do_upgrade_from_v11_to_v12(&genesis_account); + StdlibUpgradeScripts::do_upgrade_from_v12_to_v13(&genesis_account); //Start time, Timestamp::is_genesis() will return false. this call should at the end of genesis init. Timestamp::set_time_has_started(&genesis_account); Account::release_genesis_signer(genesis_account); diff --git a/build/StarcoinFramework/docs/OnChainConfigScripts.md b/build/StarcoinFramework/docs/OnChainConfigScripts.md index f2072a84..42b2ea89 100644 --- a/build/StarcoinFramework/docs/OnChainConfigScripts.md +++ b/build/StarcoinFramework/docs/OnChainConfigScripts.md @@ -11,11 +11,13 @@ - [Function `propose_update_txn_timeout_config`](#0x1_OnChainConfigScripts_propose_update_txn_timeout_config) - [Function `propose_update_vm_config`](#0x1_OnChainConfigScripts_propose_update_vm_config) - [Function `propose_update_move_language_version`](#0x1_OnChainConfigScripts_propose_update_move_language_version) +- [Function `propose_update_flexi_dag_effective_height`](#0x1_OnChainConfigScripts_propose_update_flexi_dag_effective_height) - [Function `execute_on_chain_config_proposal`](#0x1_OnChainConfigScripts_execute_on_chain_config_proposal) - [Function `execute_on_chain_config_proposal_v2`](#0x1_OnChainConfigScripts_execute_on_chain_config_proposal_v2)
use 0x1::ConsensusConfig;
+use 0x1::FlexiDagConfig;
 use 0x1::LanguageVersion;
 use 0x1::OnChainConfigDao;
 use 0x1::RewardConfig;
@@ -303,6 +305,43 @@
 
 
 
+
+ + + +## Function `propose_update_flexi_dag_effective_height` + + + +
public entry fun propose_update_flexi_dag_effective_height(account: signer, new_height: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_flexi_dag_effective_height(account: signer, new_height: u64, exec_delay: u64) {
+    let config = FlexiDagConfig::new_flexidag_config(new_height);
+    OnChainConfigDao::propose_update<STC::STC, FlexiDagConfig::FlexiDagConfig>(&account, config, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + +
diff --git a/build/StarcoinFramework/docs/README.md b/build/StarcoinFramework/docs/README.md index 3f151f78..abbf5f43 100644 --- a/build/StarcoinFramework/docs/README.md +++ b/build/StarcoinFramework/docs/README.md @@ -43,6 +43,7 @@ This is the root document for the Move StarcoinFramework module documentation. T - [`0x1::Event`](Event.md#0x1_Event) - [`0x1::EventUtil`](EventUtil.md#0x1_EventUtil) - [`0x1::FixedPoint32`](FixedPoint32.md#0x1_FixedPoint32) +- [`0x1::FlexiDagConfig`](FlexiDagConfig.md#0x1_FlexiDagConfig) - [`0x1::FromBCS`](FromBCS.md#0x1_FromBCS) - [`0x1::GasSchedule`](GasSchedule.md#0x1_GasSchedule) - [`0x1::Genesis`](Genesis.md#0x1_Genesis) diff --git a/build/StarcoinFramework/docs/StdlibUpgradeScripts.md b/build/StarcoinFramework/docs/StdlibUpgradeScripts.md index c0cec6c5..0a8950dd 100644 --- a/build/StarcoinFramework/docs/StdlibUpgradeScripts.md +++ b/build/StarcoinFramework/docs/StdlibUpgradeScripts.md @@ -17,6 +17,8 @@ The module for StdlibUpgrade init scripts - [Function `do_upgrade_from_v7_to_v8`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v7_to_v8) - [Function `upgrade_from_v11_to_v12`](#0x1_StdlibUpgradeScripts_upgrade_from_v11_to_v12) - [Function `do_upgrade_from_v11_to_v12`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v11_to_v12) +- [Function `upgrade_from_v12_to_v13`](#0x1_StdlibUpgradeScripts_upgrade_from_v12_to_v13) +- [Function `do_upgrade_from_v12_to_v13`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v12_to_v13) - [Module Specification](#@Module_Specification_0) @@ -26,10 +28,12 @@ The module for StdlibUpgrade init scripts use 0x1::Config; use 0x1::CoreAddresses; use 0x1::EasyGas; +use 0x1::FlexiDagConfig; use 0x1::GasSchedule; use 0x1::GenesisNFT; use 0x1::GenesisSignerCapability; use 0x1::LanguageVersion; +use 0x1::Math; use 0x1::NFT; use 0x1::Offer; use 0x1::OnChainConfigDao; @@ -354,6 +358,57 @@ deprecated, use do_upgrade_from_v6_to_v7_with_language_version. + + + + +## Function `upgrade_from_v12_to_v13` + + + +
public entry fun upgrade_from_v12_to_v13(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun upgrade_from_v12_to_v13(sender: signer) {
+    do_upgrade_from_v12_to_v13(&sender);
+}
+
+ + + +
+ + + +## Function `do_upgrade_from_v12_to_v13` + + + +
public fun do_upgrade_from_v12_to_v13(sender: &signer)
+
+ + + +
+Implementation + + +
public fun do_upgrade_from_v12_to_v13(sender: &signer) {
+    {
+        FlexiDagConfig::initialize(sender, u64_max());
+        OnChainConfigDao::plugin<STC, FlexiDagConfig::FlexiDagConfig>(sender);
+    };
+}
+
+ + +
diff --git a/build/StarcoinFramework/docs/TransactionManager.md b/build/StarcoinFramework/docs/TransactionManager.md index 08ac2657..33f76b55 100644 --- a/build/StarcoinFramework/docs/TransactionManager.md +++ b/build/StarcoinFramework/docs/TransactionManager.md @@ -13,6 +13,7 @@ - [Function `epilogue`](#0x1_TransactionManager_epilogue) - [Function `epilogue_v2`](#0x1_TransactionManager_epilogue_v2) - [Function `block_prologue`](#0x1_TransactionManager_block_prologue) +- [Function `block_prologue_v2`](#0x1_TransactionManager_block_prologue_v2) - [Function `txn_prologue_v2`](#0x1_TransactionManager_txn_prologue_v2) - [Function `txn_epilogue_v3`](#0x1_TransactionManager_txn_epilogue_v3) - [Module Specification](#@Module_Specification_1) @@ -483,6 +484,56 @@ The runtime always runs this before executing the transactions in a block. number: u64, chain_id: u8, parent_gas_used: u64, +) { + Self::block_prologue_v2(account, parent_hash, timestamp, author, auth_key_vec, uncles, number, chain_id, parent_gas_used, Vector::empty<u8>()) +} +
+ + + + + +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `block_prologue_v2` + +Set the metadata for the current block and distribute transaction fees and block rewards. +The runtime always runs this before executing the transactions in a block. +For Flexidag block + + +
public fun block_prologue_v2(account: signer, parent_hash: vector<u8>, timestamp: u64, author: address, auth_key_vec: vector<u8>, uncles: u64, number: u64, chain_id: u8, parent_gas_used: u64, parents_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun block_prologue_v2(
+    account: signer,
+    parent_hash: vector<u8>,
+    timestamp: u64,
+    author: address,
+    auth_key_vec: vector<u8>,
+    uncles: u64,
+    number: u64,
+    chain_id: u8,
+    parent_gas_used: u64,
+    parents_hash: vector<u8>,
 ) {
     // Can only be invoked by genesis account
     CoreAddresses::assert_genesis_address(&account);
@@ -495,13 +546,14 @@ The runtime always runs this before executing the transactions in a block.
 
     // then deal with current block.
     Timestamp::update_global_time(&account, timestamp);
-    Block::process_block_metadata(
+    Block::process_block_metadata_v2(
         &account,
         parent_hash,
         author,
         timestamp,
         uncles,
         number,
+        parents_hash,
     );
     let reward = Epoch::adjust_epoch(&account, number, timestamp, uncles, parent_gas_used);
     // pass in previous block gas fees.
diff --git a/build/StarcoinFramework/docs/TransactionTimeout.md b/build/StarcoinFramework/docs/TransactionTimeout.md
index 9e5baaf1..01d2b3f2 100644
--- a/build/StarcoinFramework/docs/TransactionTimeout.md
+++ b/build/StarcoinFramework/docs/TransactionTimeout.md
@@ -56,7 +56,7 @@ Check whether the given timestamp is valid for transactions.
 
 
 
aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
-aborts_if !exists<Block::BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if !exists<Block::BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
 include Timestamp::AbortsIfTimestampNotExists;
 aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64();
 aborts_if Block::get_current_block_number() != 0 && !exists<Config::Config<TransactionTimeoutConfig::TransactionTimeoutConfig>>(CoreAddresses::GENESIS_ADDRESS());
@@ -70,7 +70,7 @@ Check whether the given timestamp is valid for transactions.
 
 
schema AbortsIfTimestampNotValid {
     aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
-    aborts_if !exists<Block::BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+    aborts_if !exists<Block::BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
     include Timestamp::AbortsIfTimestampNotExists;
     aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64();
     aborts_if Block::get_current_block_number() != 0 && !exists<Config::Config<TransactionTimeoutConfig::TransactionTimeoutConfig>>(CoreAddresses::GENESIS_ADDRESS());
diff --git a/build/StarcoinFramework/source_maps/Block.mvsm b/build/StarcoinFramework/source_maps/Block.mvsm
index d8622d69..63f80aa9 100644
Binary files a/build/StarcoinFramework/source_maps/Block.mvsm and b/build/StarcoinFramework/source_maps/Block.mvsm differ
diff --git a/build/StarcoinFramework/source_maps/Epoch.mvsm b/build/StarcoinFramework/source_maps/Epoch.mvsm
index b6a0521e..379bf4fa 100644
Binary files a/build/StarcoinFramework/source_maps/Epoch.mvsm and b/build/StarcoinFramework/source_maps/Epoch.mvsm differ
diff --git a/build/StarcoinFramework/source_maps/FlexiDagConfig.mvsm b/build/StarcoinFramework/source_maps/FlexiDagConfig.mvsm
new file mode 100644
index 00000000..d3ecfcbb
Binary files /dev/null and b/build/StarcoinFramework/source_maps/FlexiDagConfig.mvsm differ
diff --git a/build/StarcoinFramework/source_maps/Genesis.mvsm b/build/StarcoinFramework/source_maps/Genesis.mvsm
index e57886a2..41d1e279 100644
Binary files a/build/StarcoinFramework/source_maps/Genesis.mvsm and b/build/StarcoinFramework/source_maps/Genesis.mvsm differ
diff --git a/build/StarcoinFramework/source_maps/OnChainConfigScripts.mvsm b/build/StarcoinFramework/source_maps/OnChainConfigScripts.mvsm
index bbb6c439..69c18b0b 100644
Binary files a/build/StarcoinFramework/source_maps/OnChainConfigScripts.mvsm and b/build/StarcoinFramework/source_maps/OnChainConfigScripts.mvsm differ
diff --git a/build/StarcoinFramework/source_maps/StdlibUpgradeScripts.mvsm b/build/StarcoinFramework/source_maps/StdlibUpgradeScripts.mvsm
index 34fb9e4f..55adea0f 100644
Binary files a/build/StarcoinFramework/source_maps/StdlibUpgradeScripts.mvsm and b/build/StarcoinFramework/source_maps/StdlibUpgradeScripts.mvsm differ
diff --git a/build/StarcoinFramework/source_maps/TransactionManager.mvsm b/build/StarcoinFramework/source_maps/TransactionManager.mvsm
index 7857e070..ae0c7182 100644
Binary files a/build/StarcoinFramework/source_maps/TransactionManager.mvsm and b/build/StarcoinFramework/source_maps/TransactionManager.mvsm differ
diff --git a/build/StarcoinFramework/source_maps/TransactionTimeout.mvsm b/build/StarcoinFramework/source_maps/TransactionTimeout.mvsm
index 31b8e697..8aab5565 100644
Binary files a/build/StarcoinFramework/source_maps/TransactionTimeout.mvsm and b/build/StarcoinFramework/source_maps/TransactionTimeout.mvsm differ
diff --git a/integration-tests/block/block_metadata.move b/integration-tests/block/block_metadata.move
index d759ca9e..5a5d47a4 100644
--- a/integration-tests/block/block_metadata.move
+++ b/integration-tests/block/block_metadata.move
@@ -4,6 +4,7 @@
 
 //# run --signers alice
 script {
+    use StarcoinFramework::Option;
     use StarcoinFramework::Block;
     use StarcoinFramework::Debug;
 
@@ -11,5 +12,10 @@ script {
         let hash = Block::get_parent_hash();
         Debug::print>(&hash);
     }
+
+    fun get_parents_hash(_account: signer) {
+        let hash = Block::get_parents_hash();
+        Debug::print>>(&hash);
+    }
 }
 // check: EXECUTED
diff --git a/release/StarcoinFramework.v0.1.0.blob b/release/StarcoinFramework.v0.1.0.blob
new file mode 100644
index 00000000..422b9ad2
Binary files /dev/null and b/release/StarcoinFramework.v0.1.0.blob differ
diff --git a/release/v13/BuildInfo.yaml b/release/v13/BuildInfo.yaml
new file mode 100644
index 00000000..ab73f252
--- /dev/null
+++ b/release/v13/BuildInfo.yaml
@@ -0,0 +1,20 @@
+---
+compiled_package_info:
+  package_name: StarcoinFramework
+  address_alias_instantiation:
+    StarcoinAssociation: "0x0000000000000000000000000a550c18"
+    StarcoinFramework: "0x00000000000000000000000000000001"
+    VMReserved: "0x00000000000000000000000000000000"
+  source_digest: 0899528282F044F9621C6B7686759D0D2AE52CF34CFAE8BF0917D02F2699ADF9
+  build_flags:
+    dev_mode: false
+    test_mode: false
+    generate_docs: true
+    generate_abis: true
+    install_dir: ~
+    force_recompilation: true
+    additional_named_addresses: {}
+    architecture: ~
+    fetch_deps_only: false
+    skip_fetch_latest_git_deps: false
+dependencies: []
diff --git a/release/v13/abis/Account/accept_token.abi b/release/v13/abis/Account/accept_token.abi
new file mode 100644
index 00000000..9d200103
Binary files /dev/null and b/release/v13/abis/Account/accept_token.abi differ
diff --git a/release/v13/abis/Account/accept_token_entry.abi b/release/v13/abis/Account/accept_token_entry.abi
new file mode 100644
index 00000000..2b482dca
Binary files /dev/null and b/release/v13/abis/Account/accept_token_entry.abi differ
diff --git a/release/v13/abis/Account/create_account_with_initial_amount.abi b/release/v13/abis/Account/create_account_with_initial_amount.abi
new file mode 100644
index 00000000..3a0f339c
Binary files /dev/null and b/release/v13/abis/Account/create_account_with_initial_amount.abi differ
diff --git a/release/v13/abis/Account/create_account_with_initial_amount_entry.abi b/release/v13/abis/Account/create_account_with_initial_amount_entry.abi
new file mode 100644
index 00000000..85e0e210
Binary files /dev/null and b/release/v13/abis/Account/create_account_with_initial_amount_entry.abi differ
diff --git a/release/v13/abis/Account/create_account_with_initial_amount_v2.abi b/release/v13/abis/Account/create_account_with_initial_amount_v2.abi
new file mode 100644
index 00000000..4c1f4529
Binary files /dev/null and b/release/v13/abis/Account/create_account_with_initial_amount_v2.abi differ
diff --git a/release/v13/abis/Account/remove_zero_balance_entry.abi b/release/v13/abis/Account/remove_zero_balance_entry.abi
new file mode 100644
index 00000000..fbf4971e
Binary files /dev/null and b/release/v13/abis/Account/remove_zero_balance_entry.abi differ
diff --git a/release/v13/abis/Account/rotate_authentication_key.abi b/release/v13/abis/Account/rotate_authentication_key.abi
new file mode 100644
index 00000000..d69bd371
Binary files /dev/null and b/release/v13/abis/Account/rotate_authentication_key.abi differ
diff --git a/release/v13/abis/Account/rotate_authentication_key_entry.abi b/release/v13/abis/Account/rotate_authentication_key_entry.abi
new file mode 100644
index 00000000..81a17ade
Binary files /dev/null and b/release/v13/abis/Account/rotate_authentication_key_entry.abi differ
diff --git a/release/v13/abis/Account/set_auto_accept_token_entry.abi b/release/v13/abis/Account/set_auto_accept_token_entry.abi
new file mode 100644
index 00000000..9b5dab25
Binary files /dev/null and b/release/v13/abis/Account/set_auto_accept_token_entry.abi differ
diff --git a/release/v13/abis/AccountScripts/disable_auto_accept_token.abi b/release/v13/abis/AccountScripts/disable_auto_accept_token.abi
new file mode 100644
index 00000000..3661e680
Binary files /dev/null and b/release/v13/abis/AccountScripts/disable_auto_accept_token.abi differ
diff --git a/release/v13/abis/AccountScripts/enable_auto_accept_token.abi b/release/v13/abis/AccountScripts/enable_auto_accept_token.abi
new file mode 100644
index 00000000..2cb1e7b5
Binary files /dev/null and b/release/v13/abis/AccountScripts/enable_auto_accept_token.abi differ
diff --git a/release/v13/abis/AccountScripts/remove_zero_balance.abi b/release/v13/abis/AccountScripts/remove_zero_balance.abi
new file mode 100644
index 00000000..f0ffaf79
Binary files /dev/null and b/release/v13/abis/AccountScripts/remove_zero_balance.abi differ
diff --git a/release/v13/abis/Block/checkpoint_entry.abi b/release/v13/abis/Block/checkpoint_entry.abi
new file mode 100644
index 00000000..951dc0d5
Binary files /dev/null and b/release/v13/abis/Block/checkpoint_entry.abi differ
diff --git a/release/v13/abis/Block/update_state_root_entry.abi b/release/v13/abis/Block/update_state_root_entry.abi
new file mode 100644
index 00000000..b9e52802
Binary files /dev/null and b/release/v13/abis/Block/update_state_root_entry.abi differ
diff --git a/release/v13/abis/Dao/destroy_terminated_proposal.abi b/release/v13/abis/Dao/destroy_terminated_proposal.abi
new file mode 100644
index 00000000..ce2f1889
Binary files /dev/null and b/release/v13/abis/Dao/destroy_terminated_proposal.abi differ
diff --git a/release/v13/abis/Dao/queue_proposal_action.abi b/release/v13/abis/Dao/queue_proposal_action.abi
new file mode 100644
index 00000000..afbffcd7
Binary files /dev/null and b/release/v13/abis/Dao/queue_proposal_action.abi differ
diff --git a/release/v13/abis/DaoVoteScripts/cast_vote.abi b/release/v13/abis/DaoVoteScripts/cast_vote.abi
new file mode 100644
index 00000000..4b294bf3
Binary files /dev/null and b/release/v13/abis/DaoVoteScripts/cast_vote.abi differ
diff --git a/release/v13/abis/DaoVoteScripts/flip_vote.abi b/release/v13/abis/DaoVoteScripts/flip_vote.abi
new file mode 100644
index 00000000..5aca87b2
Binary files /dev/null and b/release/v13/abis/DaoVoteScripts/flip_vote.abi differ
diff --git a/release/v13/abis/DaoVoteScripts/revoke_vote.abi b/release/v13/abis/DaoVoteScripts/revoke_vote.abi
new file mode 100644
index 00000000..66a558bb
Binary files /dev/null and b/release/v13/abis/DaoVoteScripts/revoke_vote.abi differ
diff --git a/release/v13/abis/DaoVoteScripts/revoke_vote_of_power.abi b/release/v13/abis/DaoVoteScripts/revoke_vote_of_power.abi
new file mode 100644
index 00000000..23389794
Binary files /dev/null and b/release/v13/abis/DaoVoteScripts/revoke_vote_of_power.abi differ
diff --git a/release/v13/abis/DaoVoteScripts/unstake_vote.abi b/release/v13/abis/DaoVoteScripts/unstake_vote.abi
new file mode 100644
index 00000000..2d215ed5
Binary files /dev/null and b/release/v13/abis/DaoVoteScripts/unstake_vote.abi differ
diff --git a/release/v13/abis/DummyToken/mint.abi b/release/v13/abis/DummyToken/mint.abi
new file mode 100644
index 00000000..0174dc62
Binary files /dev/null and b/release/v13/abis/DummyToken/mint.abi differ
diff --git a/release/v13/abis/EasyGas/deposit.abi b/release/v13/abis/EasyGas/deposit.abi
new file mode 100644
index 00000000..75afce1c
Binary files /dev/null and b/release/v13/abis/EasyGas/deposit.abi differ
diff --git a/release/v13/abis/EasyGas/init_data_source.abi b/release/v13/abis/EasyGas/init_data_source.abi
new file mode 100644
index 00000000..90a0e498
Binary files /dev/null and b/release/v13/abis/EasyGas/init_data_source.abi differ
diff --git a/release/v13/abis/EasyGas/register.abi b/release/v13/abis/EasyGas/register.abi
new file mode 100644
index 00000000..aebcb5f8
Binary files /dev/null and b/release/v13/abis/EasyGas/register.abi differ
diff --git a/release/v13/abis/EasyGas/update.abi b/release/v13/abis/EasyGas/update.abi
new file mode 100644
index 00000000..19dc8d01
Binary files /dev/null and b/release/v13/abis/EasyGas/update.abi differ
diff --git a/release/v13/abis/EasyGas/withdraw_gas_fee_entry.abi b/release/v13/abis/EasyGas/withdraw_gas_fee_entry.abi
new file mode 100644
index 00000000..b6fcd89d
Binary files /dev/null and b/release/v13/abis/EasyGas/withdraw_gas_fee_entry.abi differ
diff --git a/release/v13/abis/EmptyScripts/empty_script.abi b/release/v13/abis/EmptyScripts/empty_script.abi
new file mode 100644
index 00000000..2be29aba
Binary files /dev/null and b/release/v13/abis/EmptyScripts/empty_script.abi differ
diff --git a/release/v13/abis/Genesis/initialize.abi b/release/v13/abis/Genesis/initialize.abi
new file mode 100644
index 00000000..a2f881ae
Binary files /dev/null and b/release/v13/abis/Genesis/initialize.abi differ
diff --git a/release/v13/abis/Genesis/initialize_v2.abi b/release/v13/abis/Genesis/initialize_v2.abi
new file mode 100644
index 00000000..3c0fe67b
Binary files /dev/null and b/release/v13/abis/Genesis/initialize_v2.abi differ
diff --git a/release/v13/abis/GenesisNFT/mint.abi b/release/v13/abis/GenesisNFT/mint.abi
new file mode 100644
index 00000000..b6a74ac4
Binary files /dev/null and b/release/v13/abis/GenesisNFT/mint.abi differ
diff --git a/release/v13/abis/GenesisNFT/mint_entry.abi b/release/v13/abis/GenesisNFT/mint_entry.abi
new file mode 100644
index 00000000..874e3fa2
Binary files /dev/null and b/release/v13/abis/GenesisNFT/mint_entry.abi differ
diff --git a/release/v13/abis/ModifyDaoConfigProposal/execute.abi b/release/v13/abis/ModifyDaoConfigProposal/execute.abi
new file mode 100644
index 00000000..a32f5574
Binary files /dev/null and b/release/v13/abis/ModifyDaoConfigProposal/execute.abi differ
diff --git a/release/v13/abis/ModifyDaoConfigProposal/propose.abi b/release/v13/abis/ModifyDaoConfigProposal/propose.abi
new file mode 100644
index 00000000..9dea6390
Binary files /dev/null and b/release/v13/abis/ModifyDaoConfigProposal/propose.abi differ
diff --git a/release/v13/abis/ModuleUpgradeScripts/cancel_upgrade_plan.abi b/release/v13/abis/ModuleUpgradeScripts/cancel_upgrade_plan.abi
new file mode 100644
index 00000000..f39e40a4
Binary files /dev/null and b/release/v13/abis/ModuleUpgradeScripts/cancel_upgrade_plan.abi differ
diff --git a/release/v13/abis/ModuleUpgradeScripts/execute_module_upgrade_plan_propose.abi b/release/v13/abis/ModuleUpgradeScripts/execute_module_upgrade_plan_propose.abi
new file mode 100644
index 00000000..0f59d6f0
Binary files /dev/null and b/release/v13/abis/ModuleUpgradeScripts/execute_module_upgrade_plan_propose.abi differ
diff --git a/release/v13/abis/ModuleUpgradeScripts/propose_module_upgrade_v2.abi b/release/v13/abis/ModuleUpgradeScripts/propose_module_upgrade_v2.abi
new file mode 100644
index 00000000..a949b260
Binary files /dev/null and b/release/v13/abis/ModuleUpgradeScripts/propose_module_upgrade_v2.abi differ
diff --git a/release/v13/abis/ModuleUpgradeScripts/submit_module_upgrade_plan.abi b/release/v13/abis/ModuleUpgradeScripts/submit_module_upgrade_plan.abi
new file mode 100644
index 00000000..6b11cc23
Binary files /dev/null and b/release/v13/abis/ModuleUpgradeScripts/submit_module_upgrade_plan.abi differ
diff --git a/release/v13/abis/ModuleUpgradeScripts/submit_upgrade_plan.abi b/release/v13/abis/ModuleUpgradeScripts/submit_upgrade_plan.abi
new file mode 100644
index 00000000..1198af01
Binary files /dev/null and b/release/v13/abis/ModuleUpgradeScripts/submit_upgrade_plan.abi differ
diff --git a/release/v13/abis/ModuleUpgradeScripts/update_module_upgrade_strategy.abi b/release/v13/abis/ModuleUpgradeScripts/update_module_upgrade_strategy.abi
new file mode 100644
index 00000000..a166e001
Binary files /dev/null and b/release/v13/abis/ModuleUpgradeScripts/update_module_upgrade_strategy.abi differ
diff --git a/release/v13/abis/ModuleUpgradeScripts/update_module_upgrade_strategy_with_min_time.abi b/release/v13/abis/ModuleUpgradeScripts/update_module_upgrade_strategy_with_min_time.abi
new file mode 100644
index 00000000..f8e8ad1c
Binary files /dev/null and b/release/v13/abis/ModuleUpgradeScripts/update_module_upgrade_strategy_with_min_time.abi differ
diff --git a/release/v13/abis/NFT/accept.abi b/release/v13/abis/NFT/accept.abi
new file mode 100644
index 00000000..8e678ebd
Binary files /dev/null and b/release/v13/abis/NFT/accept.abi differ
diff --git a/release/v13/abis/NFT/accept_entry.abi b/release/v13/abis/NFT/accept_entry.abi
new file mode 100644
index 00000000..6bbac000
Binary files /dev/null and b/release/v13/abis/NFT/accept_entry.abi differ
diff --git a/release/v13/abis/NFT/destroy_empty.abi b/release/v13/abis/NFT/destroy_empty.abi
new file mode 100644
index 00000000..456cb71a
Binary files /dev/null and b/release/v13/abis/NFT/destroy_empty.abi differ
diff --git a/release/v13/abis/NFT/destroy_empty_entry.abi b/release/v13/abis/NFT/destroy_empty_entry.abi
new file mode 100644
index 00000000..808b5de8
Binary files /dev/null and b/release/v13/abis/NFT/destroy_empty_entry.abi differ
diff --git a/release/v13/abis/NFT/remove_empty_gallery.abi b/release/v13/abis/NFT/remove_empty_gallery.abi
new file mode 100644
index 00000000..17b89be0
Binary files /dev/null and b/release/v13/abis/NFT/remove_empty_gallery.abi differ
diff --git a/release/v13/abis/NFT/remove_empty_gallery_entry.abi b/release/v13/abis/NFT/remove_empty_gallery_entry.abi
new file mode 100644
index 00000000..b1fbfa40
Binary files /dev/null and b/release/v13/abis/NFT/remove_empty_gallery_entry.abi differ
diff --git a/release/v13/abis/NFT/transfer.abi b/release/v13/abis/NFT/transfer.abi
new file mode 100644
index 00000000..492b218d
Binary files /dev/null and b/release/v13/abis/NFT/transfer.abi differ
diff --git a/release/v13/abis/NFT/transfer_entry.abi b/release/v13/abis/NFT/transfer_entry.abi
new file mode 100644
index 00000000..8d60d00a
Binary files /dev/null and b/release/v13/abis/NFT/transfer_entry.abi differ
diff --git a/release/v13/abis/Offer/take_offer.abi b/release/v13/abis/Offer/take_offer.abi
new file mode 100644
index 00000000..a9ad0fa8
Binary files /dev/null and b/release/v13/abis/Offer/take_offer.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/execute_on_chain_config_proposal.abi b/release/v13/abis/OnChainConfigScripts/execute_on_chain_config_proposal.abi
new file mode 100644
index 00000000..884f2421
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/execute_on_chain_config_proposal.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/execute_on_chain_config_proposal_v2.abi b/release/v13/abis/OnChainConfigScripts/execute_on_chain_config_proposal_v2.abi
new file mode 100644
index 00000000..4fa4c35a
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/execute_on_chain_config_proposal_v2.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/propose_update_consensus_config.abi b/release/v13/abis/OnChainConfigScripts/propose_update_consensus_config.abi
new file mode 100644
index 00000000..543cd101
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/propose_update_consensus_config.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/propose_update_flexi_dag_effective_height.abi b/release/v13/abis/OnChainConfigScripts/propose_update_flexi_dag_effective_height.abi
new file mode 100644
index 00000000..1c33d9a5
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/propose_update_flexi_dag_effective_height.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/propose_update_move_language_version.abi b/release/v13/abis/OnChainConfigScripts/propose_update_move_language_version.abi
new file mode 100644
index 00000000..20cc2fa3
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/propose_update_move_language_version.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/propose_update_reward_config.abi b/release/v13/abis/OnChainConfigScripts/propose_update_reward_config.abi
new file mode 100644
index 00000000..8a2a050d
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/propose_update_reward_config.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/propose_update_txn_publish_option.abi b/release/v13/abis/OnChainConfigScripts/propose_update_txn_publish_option.abi
new file mode 100644
index 00000000..1f80407e
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/propose_update_txn_publish_option.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/propose_update_txn_timeout_config.abi b/release/v13/abis/OnChainConfigScripts/propose_update_txn_timeout_config.abi
new file mode 100644
index 00000000..7dbba62f
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/propose_update_txn_timeout_config.abi differ
diff --git a/release/v13/abis/OnChainConfigScripts/propose_update_vm_config.abi b/release/v13/abis/OnChainConfigScripts/propose_update_vm_config.abi
new file mode 100644
index 00000000..2a990bca
Binary files /dev/null and b/release/v13/abis/OnChainConfigScripts/propose_update_vm_config.abi differ
diff --git a/release/v13/abis/Oracle/init_data_source.abi b/release/v13/abis/Oracle/init_data_source.abi
new file mode 100644
index 00000000..c5e6b0be
Binary files /dev/null and b/release/v13/abis/Oracle/init_data_source.abi differ
diff --git a/release/v13/abis/Oracle/init_data_source_entry.abi b/release/v13/abis/Oracle/init_data_source_entry.abi
new file mode 100644
index 00000000..b7777020
Binary files /dev/null and b/release/v13/abis/Oracle/init_data_source_entry.abi differ
diff --git a/release/v13/abis/Oracle/register_oracle.abi b/release/v13/abis/Oracle/register_oracle.abi
new file mode 100644
index 00000000..b9d93a29
Binary files /dev/null and b/release/v13/abis/Oracle/register_oracle.abi differ
diff --git a/release/v13/abis/Oracle/register_oracle_entry.abi b/release/v13/abis/Oracle/register_oracle_entry.abi
new file mode 100644
index 00000000..14527ed7
Binary files /dev/null and b/release/v13/abis/Oracle/register_oracle_entry.abi differ
diff --git a/release/v13/abis/Oracle/update.abi b/release/v13/abis/Oracle/update.abi
new file mode 100644
index 00000000..acbc5310
Binary files /dev/null and b/release/v13/abis/Oracle/update.abi differ
diff --git a/release/v13/abis/Oracle/update_entry.abi b/release/v13/abis/Oracle/update_entry.abi
new file mode 100644
index 00000000..6ad3fa90
Binary files /dev/null and b/release/v13/abis/Oracle/update_entry.abi differ
diff --git a/release/v13/abis/PackageTxnManager/convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2.abi b/release/v13/abis/PackageTxnManager/convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2.abi
new file mode 100644
index 00000000..179a1e05
Binary files /dev/null and b/release/v13/abis/PackageTxnManager/convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2.abi differ
diff --git a/release/v13/abis/StdlibUpgradeScripts/take_linear_withdraw_capability.abi b/release/v13/abis/StdlibUpgradeScripts/take_linear_withdraw_capability.abi
new file mode 100644
index 00000000..aa00d595
Binary files /dev/null and b/release/v13/abis/StdlibUpgradeScripts/take_linear_withdraw_capability.abi differ
diff --git a/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v11_to_v12.abi b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v11_to_v12.abi
new file mode 100644
index 00000000..4e6f60b3
Binary files /dev/null and b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v11_to_v12.abi differ
diff --git a/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v12_to_v13.abi b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v12_to_v13.abi
new file mode 100644
index 00000000..b1ce39d4
Binary files /dev/null and b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v12_to_v13.abi differ
diff --git a/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v2_to_v3.abi b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v2_to_v3.abi
new file mode 100644
index 00000000..824b629f
Binary files /dev/null and b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v2_to_v3.abi differ
diff --git a/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v5_to_v6.abi b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v5_to_v6.abi
new file mode 100644
index 00000000..4556e0de
Binary files /dev/null and b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v5_to_v6.abi differ
diff --git a/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v6_to_v7.abi b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v6_to_v7.abi
new file mode 100644
index 00000000..285bf84a
Binary files /dev/null and b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v6_to_v7.abi differ
diff --git a/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v7_to_v8.abi b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v7_to_v8.abi
new file mode 100644
index 00000000..6795d809
Binary files /dev/null and b/release/v13/abis/StdlibUpgradeScripts/upgrade_from_v7_to_v8.abi differ
diff --git a/release/v13/abis/TransferScripts/batch_peer_to_peer.abi b/release/v13/abis/TransferScripts/batch_peer_to_peer.abi
new file mode 100644
index 00000000..f6a196ff
Binary files /dev/null and b/release/v13/abis/TransferScripts/batch_peer_to_peer.abi differ
diff --git a/release/v13/abis/TransferScripts/batch_peer_to_peer_v2.abi b/release/v13/abis/TransferScripts/batch_peer_to_peer_v2.abi
new file mode 100644
index 00000000..77a837ee
Binary files /dev/null and b/release/v13/abis/TransferScripts/batch_peer_to_peer_v2.abi differ
diff --git a/release/v13/abis/TransferScripts/peer_to_peer.abi b/release/v13/abis/TransferScripts/peer_to_peer.abi
new file mode 100644
index 00000000..29e6b70d
Binary files /dev/null and b/release/v13/abis/TransferScripts/peer_to_peer.abi differ
diff --git a/release/v13/abis/TransferScripts/peer_to_peer_batch.abi b/release/v13/abis/TransferScripts/peer_to_peer_batch.abi
new file mode 100644
index 00000000..0e6a00b0
Binary files /dev/null and b/release/v13/abis/TransferScripts/peer_to_peer_batch.abi differ
diff --git a/release/v13/abis/TransferScripts/peer_to_peer_v2.abi b/release/v13/abis/TransferScripts/peer_to_peer_v2.abi
new file mode 100644
index 00000000..b3e5683a
Binary files /dev/null and b/release/v13/abis/TransferScripts/peer_to_peer_v2.abi differ
diff --git a/release/v13/abis/TransferScripts/peer_to_peer_with_metadata.abi b/release/v13/abis/TransferScripts/peer_to_peer_with_metadata.abi
new file mode 100644
index 00000000..1520ef4a
Binary files /dev/null and b/release/v13/abis/TransferScripts/peer_to_peer_with_metadata.abi differ
diff --git a/release/v13/abis/TransferScripts/peer_to_peer_with_metadata_v2.abi b/release/v13/abis/TransferScripts/peer_to_peer_with_metadata_v2.abi
new file mode 100644
index 00000000..461b7ede
Binary files /dev/null and b/release/v13/abis/TransferScripts/peer_to_peer_with_metadata_v2.abi differ
diff --git a/release/v13/abis/TreasuryScripts/execute_withdraw_proposal.abi b/release/v13/abis/TreasuryScripts/execute_withdraw_proposal.abi
new file mode 100644
index 00000000..c718e096
Binary files /dev/null and b/release/v13/abis/TreasuryScripts/execute_withdraw_proposal.abi differ
diff --git a/release/v13/abis/TreasuryScripts/propose_withdraw.abi b/release/v13/abis/TreasuryScripts/propose_withdraw.abi
new file mode 100644
index 00000000..471d3f1c
Binary files /dev/null and b/release/v13/abis/TreasuryScripts/propose_withdraw.abi differ
diff --git a/release/v13/abis/TreasuryScripts/withdraw_and_split_lt_withdraw_cap.abi b/release/v13/abis/TreasuryScripts/withdraw_and_split_lt_withdraw_cap.abi
new file mode 100644
index 00000000..2aa69118
Binary files /dev/null and b/release/v13/abis/TreasuryScripts/withdraw_and_split_lt_withdraw_cap.abi differ
diff --git a/release/v13/abis/TreasuryScripts/withdraw_token_with_linear_withdraw_capability.abi b/release/v13/abis/TreasuryScripts/withdraw_token_with_linear_withdraw_capability.abi
new file mode 100644
index 00000000..7e0f00e1
Binary files /dev/null and b/release/v13/abis/TreasuryScripts/withdraw_token_with_linear_withdraw_capability.abi differ
diff --git a/release/v13/bytecode_modules/ACL.mv b/release/v13/bytecode_modules/ACL.mv
new file mode 100644
index 00000000..773134f2
Binary files /dev/null and b/release/v13/bytecode_modules/ACL.mv differ
diff --git a/release/v13/bytecode_modules/Account.mv b/release/v13/bytecode_modules/Account.mv
new file mode 100644
index 00000000..1a346558
Binary files /dev/null and b/release/v13/bytecode_modules/Account.mv differ
diff --git a/release/v13/bytecode_modules/AccountScripts.mv b/release/v13/bytecode_modules/AccountScripts.mv
new file mode 100644
index 00000000..3ea86f9c
Binary files /dev/null and b/release/v13/bytecode_modules/AccountScripts.mv differ
diff --git a/release/v13/bytecode_modules/Arith.mv b/release/v13/bytecode_modules/Arith.mv
new file mode 100644
index 00000000..61d6433f
Binary files /dev/null and b/release/v13/bytecode_modules/Arith.mv differ
diff --git a/release/v13/bytecode_modules/Authenticator.mv b/release/v13/bytecode_modules/Authenticator.mv
new file mode 100644
index 00000000..c5a74c50
Binary files /dev/null and b/release/v13/bytecode_modules/Authenticator.mv differ
diff --git a/release/v13/bytecode_modules/BCS.mv b/release/v13/bytecode_modules/BCS.mv
new file mode 100644
index 00000000..d66fd297
Binary files /dev/null and b/release/v13/bytecode_modules/BCS.mv differ
diff --git a/release/v13/bytecode_modules/BitOperators.mv b/release/v13/bytecode_modules/BitOperators.mv
new file mode 100644
index 00000000..5def61d4
Binary files /dev/null and b/release/v13/bytecode_modules/BitOperators.mv differ
diff --git a/release/v13/bytecode_modules/Block.mv b/release/v13/bytecode_modules/Block.mv
new file mode 100644
index 00000000..e72b98b1
Binary files /dev/null and b/release/v13/bytecode_modules/Block.mv differ
diff --git a/release/v13/bytecode_modules/BlockReward.mv b/release/v13/bytecode_modules/BlockReward.mv
new file mode 100644
index 00000000..f2b27297
Binary files /dev/null and b/release/v13/bytecode_modules/BlockReward.mv differ
diff --git a/release/v13/bytecode_modules/ChainId.mv b/release/v13/bytecode_modules/ChainId.mv
new file mode 100644
index 00000000..fb643fe9
Binary files /dev/null and b/release/v13/bytecode_modules/ChainId.mv differ
diff --git a/release/v13/bytecode_modules/Collection.mv b/release/v13/bytecode_modules/Collection.mv
new file mode 100644
index 00000000..306c01fc
Binary files /dev/null and b/release/v13/bytecode_modules/Collection.mv differ
diff --git a/release/v13/bytecode_modules/Collection2.mv b/release/v13/bytecode_modules/Collection2.mv
new file mode 100644
index 00000000..ab314639
Binary files /dev/null and b/release/v13/bytecode_modules/Collection2.mv differ
diff --git a/release/v13/bytecode_modules/Compare.mv b/release/v13/bytecode_modules/Compare.mv
new file mode 100644
index 00000000..e3e7ef4a
Binary files /dev/null and b/release/v13/bytecode_modules/Compare.mv differ
diff --git a/release/v13/bytecode_modules/Config.mv b/release/v13/bytecode_modules/Config.mv
new file mode 100644
index 00000000..5107abba
Binary files /dev/null and b/release/v13/bytecode_modules/Config.mv differ
diff --git a/release/v13/bytecode_modules/ConsensusConfig.mv b/release/v13/bytecode_modules/ConsensusConfig.mv
new file mode 100644
index 00000000..0baf06dc
Binary files /dev/null and b/release/v13/bytecode_modules/ConsensusConfig.mv differ
diff --git a/release/v13/bytecode_modules/ConsensusStrategy.mv b/release/v13/bytecode_modules/ConsensusStrategy.mv
new file mode 100644
index 00000000..911f9d5d
Binary files /dev/null and b/release/v13/bytecode_modules/ConsensusStrategy.mv differ
diff --git a/release/v13/bytecode_modules/CoreAddresses.mv b/release/v13/bytecode_modules/CoreAddresses.mv
new file mode 100644
index 00000000..8977cc44
Binary files /dev/null and b/release/v13/bytecode_modules/CoreAddresses.mv differ
diff --git a/release/v13/bytecode_modules/Dao.mv b/release/v13/bytecode_modules/Dao.mv
new file mode 100644
index 00000000..6f6bedf1
Binary files /dev/null and b/release/v13/bytecode_modules/Dao.mv differ
diff --git a/release/v13/bytecode_modules/DaoVoteScripts.mv b/release/v13/bytecode_modules/DaoVoteScripts.mv
new file mode 100644
index 00000000..1ad7b59e
Binary files /dev/null and b/release/v13/bytecode_modules/DaoVoteScripts.mv differ
diff --git a/release/v13/bytecode_modules/Debug.mv b/release/v13/bytecode_modules/Debug.mv
new file mode 100644
index 00000000..06446cdf
Binary files /dev/null and b/release/v13/bytecode_modules/Debug.mv differ
diff --git a/release/v13/bytecode_modules/DummyToken.mv b/release/v13/bytecode_modules/DummyToken.mv
new file mode 100644
index 00000000..27c9e6f5
Binary files /dev/null and b/release/v13/bytecode_modules/DummyToken.mv differ
diff --git a/release/v13/bytecode_modules/DummyTokenScripts.mv b/release/v13/bytecode_modules/DummyTokenScripts.mv
new file mode 100644
index 00000000..cc64f15f
Binary files /dev/null and b/release/v13/bytecode_modules/DummyTokenScripts.mv differ
diff --git a/release/v13/bytecode_modules/EVMAddress.mv b/release/v13/bytecode_modules/EVMAddress.mv
new file mode 100644
index 00000000..9cb4eceb
Binary files /dev/null and b/release/v13/bytecode_modules/EVMAddress.mv differ
diff --git a/release/v13/bytecode_modules/EasyGas.mv b/release/v13/bytecode_modules/EasyGas.mv
new file mode 100644
index 00000000..9194df2c
Binary files /dev/null and b/release/v13/bytecode_modules/EasyGas.mv differ
diff --git a/release/v13/bytecode_modules/EasyGasScript.mv b/release/v13/bytecode_modules/EasyGasScript.mv
new file mode 100644
index 00000000..16d8f183
Binary files /dev/null and b/release/v13/bytecode_modules/EasyGasScript.mv differ
diff --git a/release/v13/bytecode_modules/EmptyScripts.mv b/release/v13/bytecode_modules/EmptyScripts.mv
new file mode 100644
index 00000000..1f874d05
Binary files /dev/null and b/release/v13/bytecode_modules/EmptyScripts.mv differ
diff --git a/release/v13/bytecode_modules/Epoch.mv b/release/v13/bytecode_modules/Epoch.mv
new file mode 100644
index 00000000..b9e0f1f9
Binary files /dev/null and b/release/v13/bytecode_modules/Epoch.mv differ
diff --git a/release/v13/bytecode_modules/Errors.mv b/release/v13/bytecode_modules/Errors.mv
new file mode 100644
index 00000000..8d51430c
Binary files /dev/null and b/release/v13/bytecode_modules/Errors.mv differ
diff --git a/release/v13/bytecode_modules/Event.mv b/release/v13/bytecode_modules/Event.mv
new file mode 100644
index 00000000..a4b3a1b8
Binary files /dev/null and b/release/v13/bytecode_modules/Event.mv differ
diff --git a/release/v13/bytecode_modules/EventUtil.mv b/release/v13/bytecode_modules/EventUtil.mv
new file mode 100644
index 00000000..bbbc6f34
Binary files /dev/null and b/release/v13/bytecode_modules/EventUtil.mv differ
diff --git a/release/v13/bytecode_modules/FixedPoint32.mv b/release/v13/bytecode_modules/FixedPoint32.mv
new file mode 100644
index 00000000..2ecc1abb
Binary files /dev/null and b/release/v13/bytecode_modules/FixedPoint32.mv differ
diff --git a/release/v13/bytecode_modules/FlexiDagConfig.mv b/release/v13/bytecode_modules/FlexiDagConfig.mv
new file mode 100644
index 00000000..243f8a06
Binary files /dev/null and b/release/v13/bytecode_modules/FlexiDagConfig.mv differ
diff --git a/release/v13/bytecode_modules/FromBCS.mv b/release/v13/bytecode_modules/FromBCS.mv
new file mode 100644
index 00000000..6291eb75
Binary files /dev/null and b/release/v13/bytecode_modules/FromBCS.mv differ
diff --git a/release/v13/bytecode_modules/GasSchedule.mv b/release/v13/bytecode_modules/GasSchedule.mv
new file mode 100644
index 00000000..a88bc854
Binary files /dev/null and b/release/v13/bytecode_modules/GasSchedule.mv differ
diff --git a/release/v13/bytecode_modules/Genesis.mv b/release/v13/bytecode_modules/Genesis.mv
new file mode 100644
index 00000000..97508566
Binary files /dev/null and b/release/v13/bytecode_modules/Genesis.mv differ
diff --git a/release/v13/bytecode_modules/GenesisNFT.mv b/release/v13/bytecode_modules/GenesisNFT.mv
new file mode 100644
index 00000000..803cfbad
Binary files /dev/null and b/release/v13/bytecode_modules/GenesisNFT.mv differ
diff --git a/release/v13/bytecode_modules/GenesisNFTScripts.mv b/release/v13/bytecode_modules/GenesisNFTScripts.mv
new file mode 100644
index 00000000..fe06059a
Binary files /dev/null and b/release/v13/bytecode_modules/GenesisNFTScripts.mv differ
diff --git a/release/v13/bytecode_modules/GenesisSignerCapability.mv b/release/v13/bytecode_modules/GenesisSignerCapability.mv
new file mode 100644
index 00000000..0b2013c1
Binary files /dev/null and b/release/v13/bytecode_modules/GenesisSignerCapability.mv differ
diff --git a/release/v13/bytecode_modules/Hash.mv b/release/v13/bytecode_modules/Hash.mv
new file mode 100644
index 00000000..67a4a8bf
Binary files /dev/null and b/release/v13/bytecode_modules/Hash.mv differ
diff --git a/release/v13/bytecode_modules/IdentifierNFT.mv b/release/v13/bytecode_modules/IdentifierNFT.mv
new file mode 100644
index 00000000..44d5f272
Binary files /dev/null and b/release/v13/bytecode_modules/IdentifierNFT.mv differ
diff --git a/release/v13/bytecode_modules/IdentifierNFTScripts.mv b/release/v13/bytecode_modules/IdentifierNFTScripts.mv
new file mode 100644
index 00000000..f495cd76
Binary files /dev/null and b/release/v13/bytecode_modules/IdentifierNFTScripts.mv differ
diff --git a/release/v13/bytecode_modules/LanguageVersion.mv b/release/v13/bytecode_modules/LanguageVersion.mv
new file mode 100644
index 00000000..0c130d72
Binary files /dev/null and b/release/v13/bytecode_modules/LanguageVersion.mv differ
diff --git a/release/v13/bytecode_modules/Math.mv b/release/v13/bytecode_modules/Math.mv
new file mode 100644
index 00000000..034dad6d
Binary files /dev/null and b/release/v13/bytecode_modules/Math.mv differ
diff --git a/release/v13/bytecode_modules/MerkleNFTDistributor.mv b/release/v13/bytecode_modules/MerkleNFTDistributor.mv
new file mode 100644
index 00000000..bb8c651a
Binary files /dev/null and b/release/v13/bytecode_modules/MerkleNFTDistributor.mv differ
diff --git a/release/v13/bytecode_modules/MerkleProof.mv b/release/v13/bytecode_modules/MerkleProof.mv
new file mode 100644
index 00000000..27c43e89
Binary files /dev/null and b/release/v13/bytecode_modules/MerkleProof.mv differ
diff --git a/release/v13/bytecode_modules/MintDaoProposal.mv b/release/v13/bytecode_modules/MintDaoProposal.mv
new file mode 100644
index 00000000..2f4e07a5
Binary files /dev/null and b/release/v13/bytecode_modules/MintDaoProposal.mv differ
diff --git a/release/v13/bytecode_modules/MintScripts.mv b/release/v13/bytecode_modules/MintScripts.mv
new file mode 100644
index 00000000..e0d82f1a
Binary files /dev/null and b/release/v13/bytecode_modules/MintScripts.mv differ
diff --git a/release/v13/bytecode_modules/ModifyDaoConfigProposal.mv b/release/v13/bytecode_modules/ModifyDaoConfigProposal.mv
new file mode 100644
index 00000000..390caae2
Binary files /dev/null and b/release/v13/bytecode_modules/ModifyDaoConfigProposal.mv differ
diff --git a/release/v13/bytecode_modules/ModuleUpgradeScripts.mv b/release/v13/bytecode_modules/ModuleUpgradeScripts.mv
new file mode 100644
index 00000000..f2d215e2
Binary files /dev/null and b/release/v13/bytecode_modules/ModuleUpgradeScripts.mv differ
diff --git a/release/v13/bytecode_modules/NFT.mv b/release/v13/bytecode_modules/NFT.mv
new file mode 100644
index 00000000..cf97fbd3
Binary files /dev/null and b/release/v13/bytecode_modules/NFT.mv differ
diff --git a/release/v13/bytecode_modules/NFTGallery.mv b/release/v13/bytecode_modules/NFTGallery.mv
new file mode 100644
index 00000000..bda70d9e
Binary files /dev/null and b/release/v13/bytecode_modules/NFTGallery.mv differ
diff --git a/release/v13/bytecode_modules/NFTGalleryScripts.mv b/release/v13/bytecode_modules/NFTGalleryScripts.mv
new file mode 100644
index 00000000..e9736e40
Binary files /dev/null and b/release/v13/bytecode_modules/NFTGalleryScripts.mv differ
diff --git a/release/v13/bytecode_modules/Offer.mv b/release/v13/bytecode_modules/Offer.mv
new file mode 100644
index 00000000..297fc8eb
Binary files /dev/null and b/release/v13/bytecode_modules/Offer.mv differ
diff --git a/release/v13/bytecode_modules/OnChainConfigDao.mv b/release/v13/bytecode_modules/OnChainConfigDao.mv
new file mode 100644
index 00000000..cccbe130
Binary files /dev/null and b/release/v13/bytecode_modules/OnChainConfigDao.mv differ
diff --git a/release/v13/bytecode_modules/OnChainConfigScripts.mv b/release/v13/bytecode_modules/OnChainConfigScripts.mv
new file mode 100644
index 00000000..8d847cdf
Binary files /dev/null and b/release/v13/bytecode_modules/OnChainConfigScripts.mv differ
diff --git a/release/v13/bytecode_modules/Option.mv b/release/v13/bytecode_modules/Option.mv
new file mode 100644
index 00000000..340a58f5
Binary files /dev/null and b/release/v13/bytecode_modules/Option.mv differ
diff --git a/release/v13/bytecode_modules/Oracle.mv b/release/v13/bytecode_modules/Oracle.mv
new file mode 100644
index 00000000..348476de
Binary files /dev/null and b/release/v13/bytecode_modules/Oracle.mv differ
diff --git a/release/v13/bytecode_modules/PackageTxnManager.mv b/release/v13/bytecode_modules/PackageTxnManager.mv
new file mode 100644
index 00000000..75e05951
Binary files /dev/null and b/release/v13/bytecode_modules/PackageTxnManager.mv differ
diff --git a/release/v13/bytecode_modules/PriceOracle.mv b/release/v13/bytecode_modules/PriceOracle.mv
new file mode 100644
index 00000000..b8584e77
Binary files /dev/null and b/release/v13/bytecode_modules/PriceOracle.mv differ
diff --git a/release/v13/bytecode_modules/PriceOracleAggregator.mv b/release/v13/bytecode_modules/PriceOracleAggregator.mv
new file mode 100644
index 00000000..51dc465f
Binary files /dev/null and b/release/v13/bytecode_modules/PriceOracleAggregator.mv differ
diff --git a/release/v13/bytecode_modules/PriceOracleScripts.mv b/release/v13/bytecode_modules/PriceOracleScripts.mv
new file mode 100644
index 00000000..9fc3054e
Binary files /dev/null and b/release/v13/bytecode_modules/PriceOracleScripts.mv differ
diff --git a/release/v13/bytecode_modules/RewardConfig.mv b/release/v13/bytecode_modules/RewardConfig.mv
new file mode 100644
index 00000000..91147060
Binary files /dev/null and b/release/v13/bytecode_modules/RewardConfig.mv differ
diff --git a/release/v13/bytecode_modules/Ring.mv b/release/v13/bytecode_modules/Ring.mv
new file mode 100644
index 00000000..870e433e
Binary files /dev/null and b/release/v13/bytecode_modules/Ring.mv differ
diff --git a/release/v13/bytecode_modules/SIP_2.mv b/release/v13/bytecode_modules/SIP_2.mv
new file mode 100644
index 00000000..b495fd3d
Binary files /dev/null and b/release/v13/bytecode_modules/SIP_2.mv differ
diff --git a/release/v13/bytecode_modules/SIP_3.mv b/release/v13/bytecode_modules/SIP_3.mv
new file mode 100644
index 00000000..3885df58
Binary files /dev/null and b/release/v13/bytecode_modules/SIP_3.mv differ
diff --git a/release/v13/bytecode_modules/STC.mv b/release/v13/bytecode_modules/STC.mv
new file mode 100644
index 00000000..b462ed90
Binary files /dev/null and b/release/v13/bytecode_modules/STC.mv differ
diff --git a/release/v13/bytecode_modules/STCUSDOracle.mv b/release/v13/bytecode_modules/STCUSDOracle.mv
new file mode 100644
index 00000000..2a49c599
Binary files /dev/null and b/release/v13/bytecode_modules/STCUSDOracle.mv differ
diff --git a/release/v13/bytecode_modules/Secp256k1.mv b/release/v13/bytecode_modules/Secp256k1.mv
new file mode 100644
index 00000000..5f0dd612
Binary files /dev/null and b/release/v13/bytecode_modules/Secp256k1.mv differ
diff --git a/release/v13/bytecode_modules/SharedEd25519PublicKey.mv b/release/v13/bytecode_modules/SharedEd25519PublicKey.mv
new file mode 100644
index 00000000..aa92ddbc
Binary files /dev/null and b/release/v13/bytecode_modules/SharedEd25519PublicKey.mv differ
diff --git a/release/v13/bytecode_modules/Signature.mv b/release/v13/bytecode_modules/Signature.mv
new file mode 100644
index 00000000..e37f2baf
Binary files /dev/null and b/release/v13/bytecode_modules/Signature.mv differ
diff --git a/release/v13/bytecode_modules/SignedInteger64.mv b/release/v13/bytecode_modules/SignedInteger64.mv
new file mode 100644
index 00000000..e3b62651
Binary files /dev/null and b/release/v13/bytecode_modules/SignedInteger64.mv differ
diff --git a/release/v13/bytecode_modules/Signer.mv b/release/v13/bytecode_modules/Signer.mv
new file mode 100644
index 00000000..a84a73d5
Binary files /dev/null and b/release/v13/bytecode_modules/Signer.mv differ
diff --git a/release/v13/bytecode_modules/SimpleMap.mv b/release/v13/bytecode_modules/SimpleMap.mv
new file mode 100644
index 00000000..0effeda9
Binary files /dev/null and b/release/v13/bytecode_modules/SimpleMap.mv differ
diff --git a/release/v13/bytecode_modules/StarcoinVerifier.mv b/release/v13/bytecode_modules/StarcoinVerifier.mv
new file mode 100644
index 00000000..684d44fe
Binary files /dev/null and b/release/v13/bytecode_modules/StarcoinVerifier.mv differ
diff --git a/release/v13/bytecode_modules/StdlibUpgradeScripts.mv b/release/v13/bytecode_modules/StdlibUpgradeScripts.mv
new file mode 100644
index 00000000..cebf13e9
Binary files /dev/null and b/release/v13/bytecode_modules/StdlibUpgradeScripts.mv differ
diff --git a/release/v13/bytecode_modules/String.mv b/release/v13/bytecode_modules/String.mv
new file mode 100644
index 00000000..4b51f437
Binary files /dev/null and b/release/v13/bytecode_modules/String.mv differ
diff --git a/release/v13/bytecode_modules/StructuredHash.mv b/release/v13/bytecode_modules/StructuredHash.mv
new file mode 100644
index 00000000..41c88588
Binary files /dev/null and b/release/v13/bytecode_modules/StructuredHash.mv differ
diff --git a/release/v13/bytecode_modules/Table.mv b/release/v13/bytecode_modules/Table.mv
new file mode 100644
index 00000000..297bb011
Binary files /dev/null and b/release/v13/bytecode_modules/Table.mv differ
diff --git a/release/v13/bytecode_modules/Timestamp.mv b/release/v13/bytecode_modules/Timestamp.mv
new file mode 100644
index 00000000..815d9907
Binary files /dev/null and b/release/v13/bytecode_modules/Timestamp.mv differ
diff --git a/release/v13/bytecode_modules/Token.mv b/release/v13/bytecode_modules/Token.mv
new file mode 100644
index 00000000..4217dbb8
Binary files /dev/null and b/release/v13/bytecode_modules/Token.mv differ
diff --git a/release/v13/bytecode_modules/TransactionFee.mv b/release/v13/bytecode_modules/TransactionFee.mv
new file mode 100644
index 00000000..f209ddeb
Binary files /dev/null and b/release/v13/bytecode_modules/TransactionFee.mv differ
diff --git a/release/v13/bytecode_modules/TransactionManager.mv b/release/v13/bytecode_modules/TransactionManager.mv
new file mode 100644
index 00000000..799c306a
Binary files /dev/null and b/release/v13/bytecode_modules/TransactionManager.mv differ
diff --git a/release/v13/bytecode_modules/TransactionPublishOption.mv b/release/v13/bytecode_modules/TransactionPublishOption.mv
new file mode 100644
index 00000000..814bd5ae
Binary files /dev/null and b/release/v13/bytecode_modules/TransactionPublishOption.mv differ
diff --git a/release/v13/bytecode_modules/TransactionTimeout.mv b/release/v13/bytecode_modules/TransactionTimeout.mv
new file mode 100644
index 00000000..0e53dcbf
Binary files /dev/null and b/release/v13/bytecode_modules/TransactionTimeout.mv differ
diff --git a/release/v13/bytecode_modules/TransactionTimeoutConfig.mv b/release/v13/bytecode_modules/TransactionTimeoutConfig.mv
new file mode 100644
index 00000000..f54deb34
Binary files /dev/null and b/release/v13/bytecode_modules/TransactionTimeoutConfig.mv differ
diff --git a/release/v13/bytecode_modules/TransferScripts.mv b/release/v13/bytecode_modules/TransferScripts.mv
new file mode 100644
index 00000000..5a5eaf79
Binary files /dev/null and b/release/v13/bytecode_modules/TransferScripts.mv differ
diff --git a/release/v13/bytecode_modules/Treasury.mv b/release/v13/bytecode_modules/Treasury.mv
new file mode 100644
index 00000000..58818122
Binary files /dev/null and b/release/v13/bytecode_modules/Treasury.mv differ
diff --git a/release/v13/bytecode_modules/TreasuryScripts.mv b/release/v13/bytecode_modules/TreasuryScripts.mv
new file mode 100644
index 00000000..23b7501a
Binary files /dev/null and b/release/v13/bytecode_modules/TreasuryScripts.mv differ
diff --git a/release/v13/bytecode_modules/TreasuryWithdrawDaoProposal.mv b/release/v13/bytecode_modules/TreasuryWithdrawDaoProposal.mv
new file mode 100644
index 00000000..9b09f0d0
Binary files /dev/null and b/release/v13/bytecode_modules/TreasuryWithdrawDaoProposal.mv differ
diff --git a/release/v13/bytecode_modules/TypeInfo.mv b/release/v13/bytecode_modules/TypeInfo.mv
new file mode 100644
index 00000000..1be6ae92
Binary files /dev/null and b/release/v13/bytecode_modules/TypeInfo.mv differ
diff --git a/release/v13/bytecode_modules/U256.mv b/release/v13/bytecode_modules/U256.mv
new file mode 100644
index 00000000..5114e77f
Binary files /dev/null and b/release/v13/bytecode_modules/U256.mv differ
diff --git a/release/v13/bytecode_modules/UpgradeModuleDaoProposal.mv b/release/v13/bytecode_modules/UpgradeModuleDaoProposal.mv
new file mode 100644
index 00000000..083a94d1
Binary files /dev/null and b/release/v13/bytecode_modules/UpgradeModuleDaoProposal.mv differ
diff --git a/release/v13/bytecode_modules/VMConfig.mv b/release/v13/bytecode_modules/VMConfig.mv
new file mode 100644
index 00000000..d4a4038b
Binary files /dev/null and b/release/v13/bytecode_modules/VMConfig.mv differ
diff --git a/release/v13/bytecode_modules/Vector.mv b/release/v13/bytecode_modules/Vector.mv
new file mode 100644
index 00000000..fca0c13f
Binary files /dev/null and b/release/v13/bytecode_modules/Vector.mv differ
diff --git a/release/v13/bytecode_modules/Version.mv b/release/v13/bytecode_modules/Version.mv
new file mode 100644
index 00000000..e08ee09a
Binary files /dev/null and b/release/v13/bytecode_modules/Version.mv differ
diff --git a/release/v13/bytecode_modules/YieldFarming.mv b/release/v13/bytecode_modules/YieldFarming.mv
new file mode 100644
index 00000000..50950090
Binary files /dev/null and b/release/v13/bytecode_modules/YieldFarming.mv differ
diff --git a/release/v13/bytecode_modules/YieldFarmingV2.mv b/release/v13/bytecode_modules/YieldFarmingV2.mv
new file mode 100644
index 00000000..e0e4f01a
Binary files /dev/null and b/release/v13/bytecode_modules/YieldFarmingV2.mv differ
diff --git a/release/v13/docs/ACL.md b/release/v13/docs/ACL.md
new file mode 100644
index 00000000..acad22c6
--- /dev/null
+++ b/release/v13/docs/ACL.md
@@ -0,0 +1,205 @@
+
+
+
+# Module `0x1::ACL`
+
+Access control list (acl) module. An acl is a list of account addresses who
+have the access permission to a certain object.
+This module uses a vector to represent the list, but can be refactored to
+use a "set" instead when it's available in the language in the future.
+
+
+-  [Struct `ACL`](#0x1_ACL_ACL)
+-  [Constants](#@Constants_0)
+-  [Function `empty`](#0x1_ACL_empty)
+-  [Function `add`](#0x1_ACL_add)
+-  [Function `remove`](#0x1_ACL_remove)
+-  [Function `contains`](#0x1_ACL_contains)
+-  [Function `assert_contains`](#0x1_ACL_assert_contains)
+
+
+
use 0x1::Errors;
+use 0x1::Vector;
+
+ + + + + +## Struct `ACL` + + + +
struct ACL has copy, drop, store
+
+ + + +
+Fields + + +
+
+list: vector<address> +
+
+ +
+
+ + +
+ + + +## Constants + + + + +The ACL already contains the address. + + +
const ECONTAIN: u64 = 0;
+
+ + + + + +The ACL does not contain the address. + + +
const ENOT_CONTAIN: u64 = 1;
+
+ + + + + +## Function `empty` + +Return an empty ACL. + + +
public fun empty(): ACL::ACL
+
+ + + +
+Implementation + + +
public fun empty(): ACL {
+    ACL{ list: Vector::empty<address>() }
+}
+
+ + + +
+ + + +## Function `add` + +Add the address to the ACL. + + +
public fun add(acl: &mut ACL::ACL, addr: address)
+
+ + + +
+Implementation + + +
public fun add(acl: &mut ACL, addr: address) {
+    assert!(!Vector::contains(&mut acl.list, &addr), Errors::invalid_argument(ECONTAIN));
+    Vector::push_back(&mut acl.list, addr);
+}
+
+ + + +
+ + + +## Function `remove` + +Remove the address from the ACL. + + +
public fun remove(acl: &mut ACL::ACL, addr: address)
+
+ + + +
+Implementation + + +
public fun remove(acl: &mut ACL, addr: address) {
+    let (found, index) = Vector::index_of(&mut acl.list, &addr);
+    assert!(found, Errors::invalid_argument(ENOT_CONTAIN));
+    Vector::remove(&mut acl.list, index);
+}
+
+ + + +
+ + + +## Function `contains` + +Return true iff the ACL contains the address. + + +
public fun contains(acl: &ACL::ACL, addr: address): bool
+
+ + + +
+Implementation + + +
public fun contains(acl: &ACL, addr: address): bool {
+    Vector::contains(&acl.list, &addr)
+}
+
+ + + +
+ + + +## Function `assert_contains` + +assert! that the ACL has the address. + + +
public fun assert_contains(acl: &ACL::ACL, addr: address)
+
+ + + +
+Implementation + + +
public fun assert_contains(acl: &ACL, addr: address) {
+    assert!(contains(acl, addr), Errors::invalid_argument(ENOT_CONTAIN));
+}
+
+ + + +
diff --git a/release/v13/docs/Account.md b/release/v13/docs/Account.md new file mode 100644 index 00000000..cd98d41a --- /dev/null +++ b/release/v13/docs/Account.md @@ -0,0 +1,3676 @@ + + + +# Module `0x1::Account` + +The module for the account resource that governs every account + + +- [Resource `Account`](#0x1_Account_Account) +- [Resource `Balance`](#0x1_Account_Balance) +- [Struct `WithdrawCapability`](#0x1_Account_WithdrawCapability) +- [Struct `KeyRotationCapability`](#0x1_Account_KeyRotationCapability) +- [Struct `WithdrawEvent`](#0x1_Account_WithdrawEvent) +- [Struct `DepositEvent`](#0x1_Account_DepositEvent) +- [Struct `AcceptTokenEvent`](#0x1_Account_AcceptTokenEvent) +- [Resource `SignerDelegated`](#0x1_Account_SignerDelegated) +- [Struct `SignerCapability`](#0x1_Account_SignerCapability) +- [Resource `AutoAcceptToken`](#0x1_Account_AutoAcceptToken) +- [Struct `RotateAuthKeyEvent`](#0x1_Account_RotateAuthKeyEvent) +- [Struct `ExtractWithdrawCapEvent`](#0x1_Account_ExtractWithdrawCapEvent) +- [Struct `SignerDelegateEvent`](#0x1_Account_SignerDelegateEvent) +- [Resource `EventStore`](#0x1_Account_EventStore) +- [Constants](#@Constants_0) +- [Function `remove_signer_capability`](#0x1_Account_remove_signer_capability) +- [Function `create_signer_with_cap`](#0x1_Account_create_signer_with_cap) +- [Function `destroy_signer_cap`](#0x1_Account_destroy_signer_cap) +- [Function `signer_address`](#0x1_Account_signer_address) +- [Function `is_signer_delegated`](#0x1_Account_is_signer_delegated) +- [Function `create_genesis_account`](#0x1_Account_create_genesis_account) +- [Function `release_genesis_signer`](#0x1_Account_release_genesis_signer) +- [Function `create_account`](#0x1_Account_create_account) +- [Function `create_account_with_address`](#0x1_Account_create_account_with_address) +- [Function `make_account`](#0x1_Account_make_account) +- [Function `create_signer`](#0x1_Account_create_signer) +- [Function `create_account_with_initial_amount`](#0x1_Account_create_account_with_initial_amount) +- [Function `create_account_with_initial_amount_v2`](#0x1_Account_create_account_with_initial_amount_v2) +- [Function `create_account_with_initial_amount_entry`](#0x1_Account_create_account_with_initial_amount_entry) +- [Function `create_delegate_account`](#0x1_Account_create_delegate_account) +- [Function `deposit_to_self`](#0x1_Account_deposit_to_self) +- [Function `deposit`](#0x1_Account_deposit) +- [Function `deposit_with_metadata`](#0x1_Account_deposit_with_metadata) +- [Function `deposit_to_balance`](#0x1_Account_deposit_to_balance) +- [Function `withdraw_from_balance_v2`](#0x1_Account_withdraw_from_balance_v2) +- [Function `set_sequence_number`](#0x1_Account_set_sequence_number) +- [Function `set_authentication_key`](#0x1_Account_set_authentication_key) +- [Function `withdraw_from_balance`](#0x1_Account_withdraw_from_balance) +- [Function `withdraw`](#0x1_Account_withdraw) +- [Function `withdraw_with_metadata`](#0x1_Account_withdraw_with_metadata) +- [Function `withdraw_with_capability`](#0x1_Account_withdraw_with_capability) +- [Function `withdraw_with_capability_and_metadata`](#0x1_Account_withdraw_with_capability_and_metadata) +- [Function `extract_withdraw_capability`](#0x1_Account_extract_withdraw_capability) +- [Function `restore_withdraw_capability`](#0x1_Account_restore_withdraw_capability) +- [Function `emit_account_withdraw_event`](#0x1_Account_emit_account_withdraw_event) +- [Function `emit_account_deposit_event`](#0x1_Account_emit_account_deposit_event) +- [Function `pay_from_capability`](#0x1_Account_pay_from_capability) +- [Function `pay_from_with_metadata`](#0x1_Account_pay_from_with_metadata) +- [Function `pay_from`](#0x1_Account_pay_from) +- [Function `rotate_authentication_key_with_capability`](#0x1_Account_rotate_authentication_key_with_capability) +- [Function `extract_key_rotation_capability`](#0x1_Account_extract_key_rotation_capability) +- [Function `restore_key_rotation_capability`](#0x1_Account_restore_key_rotation_capability) +- [Function `destroy_key_rotation_capability`](#0x1_Account_destroy_key_rotation_capability) +- [Function `rotate_authentication_key`](#0x1_Account_rotate_authentication_key) +- [Function `rotate_authentication_key_entry`](#0x1_Account_rotate_authentication_key_entry) +- [Function `do_rotate_authentication_key`](#0x1_Account_do_rotate_authentication_key) +- [Function `balance_for`](#0x1_Account_balance_for) +- [Function `balance`](#0x1_Account_balance) +- [Function `do_accept_token`](#0x1_Account_do_accept_token) +- [Function `accept_token`](#0x1_Account_accept_token) +- [Function `accept_token_entry`](#0x1_Account_accept_token_entry) +- [Function `is_accepts_token`](#0x1_Account_is_accepts_token) +- [Function `is_accept_token`](#0x1_Account_is_accept_token) +- [Function `can_auto_accept_token`](#0x1_Account_can_auto_accept_token) +- [Function `set_auto_accept_token_entry`](#0x1_Account_set_auto_accept_token_entry) +- [Function `set_auto_accept_token`](#0x1_Account_set_auto_accept_token) +- [Function `try_accept_token`](#0x1_Account_try_accept_token) +- [Function `sequence_number_for_account`](#0x1_Account_sequence_number_for_account) +- [Function `sequence_number`](#0x1_Account_sequence_number) +- [Function `authentication_key`](#0x1_Account_authentication_key) +- [Function `delegated_key_rotation_capability`](#0x1_Account_delegated_key_rotation_capability) +- [Function `delegated_withdraw_capability`](#0x1_Account_delegated_withdraw_capability) +- [Function `withdraw_capability_address`](#0x1_Account_withdraw_capability_address) +- [Function `key_rotation_capability_address`](#0x1_Account_key_rotation_capability_address) +- [Function `exists_at`](#0x1_Account_exists_at) +- [Function `is_dummy_auth_key`](#0x1_Account_is_dummy_auth_key) +- [Function `is_dummy_auth_key_v2`](#0x1_Account_is_dummy_auth_key_v2) +- [Function `txn_prologue`](#0x1_Account_txn_prologue) +- [Function `txn_prologue_v2`](#0x1_Account_txn_prologue_v2) +- [Function `txn_epilogue`](#0x1_Account_txn_epilogue) +- [Function `transaction_fee_simulate`](#0x1_Account_transaction_fee_simulate) +- [Function `txn_epilogue_v2`](#0x1_Account_txn_epilogue_v2) +- [Function `txn_epilogue_v3`](#0x1_Account_txn_epilogue_v3) +- [Function `remove_zero_balance_entry`](#0x1_Account_remove_zero_balance_entry) +- [Function `remove_zero_balance`](#0x1_Account_remove_zero_balance) +- [Function `make_event_store_if_not_exist`](#0x1_Account_make_event_store_if_not_exist) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Authenticator;
+use 0x1::BCS;
+use 0x1::CoreAddresses;
+use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Hash;
+use 0x1::Math;
+use 0x1::Option;
+use 0x1::STC;
+use 0x1::Signer;
+use 0x1::Timestamp;
+use 0x1::Token;
+use 0x1::TransactionFee;
+use 0x1::Vector;
+
+ + + + + +## Resource `Account` + +Every account has a Account::Account resource + + +
struct Account has key
+
+ + + +
+Fields + + +
+
+authentication_key: vector<u8> +
+
+ The current authentication key. + This can be different than the key used to create the account +
+
+withdrawal_capability: Option::Option<Account::WithdrawCapability> +
+
+ A withdrawal_capability allows whoever holds this capability + to withdraw from the account. At the time of account creation + this capability is stored in this option. It can later be + "extracted" from this field via extract_withdraw_capability, + and can also be restored via restore_withdraw_capability. +
+
+key_rotation_capability: Option::Option<Account::KeyRotationCapability> +
+
+ A key_rotation_capability allows whoever holds this capability + the ability to rotate the authentication key for the account. At + the time of account creation this capability is stored in this + option. It can later be "extracted" from this field via + extract_key_rotation_capability, and can also be restored via + restore_key_rotation_capability. +
+
+withdraw_events: Event::EventHandle<Account::WithdrawEvent> +
+
+ event handle for account balance withdraw event +
+
+deposit_events: Event::EventHandle<Account::DepositEvent> +
+
+ event handle for account balance deposit event +
+
+accept_token_events: Event::EventHandle<Account::AcceptTokenEvent> +
+
+ Event handle for accept_token event +
+
+sequence_number: u64 +
+
+ The current sequence number. + Incremented by one each time a transaction is submitted +
+
+ + +
+ + + +## Resource `Balance` + +A resource that holds the tokens stored in this account + + +
struct Balance<TokenType> has key
+
+ + + +
+Fields + + +
+
+token: Token::Token<TokenType> +
+
+ +
+
+ + +
+ + + +## Struct `WithdrawCapability` + +The holder of WithdrawCapability for account_address can withdraw Token from +account_address/Account::Account/balance. +There is at most one WithdrawCapability in existence for a given address. + + +
struct WithdrawCapability has store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+ + +
+ + + +## Struct `KeyRotationCapability` + +The holder of KeyRotationCapability for account_address can rotate the authentication key for +account_address (i.e., write to account_address/Account::Account/authentication_key). +There is at most one KeyRotationCapability in existence for a given address. + + +
struct KeyRotationCapability has store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+ + +
+ + + +## Struct `WithdrawEvent` + +Message for balance withdraw event. + + +
struct WithdrawEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u128 +
+
+ The amount of Token sent +
+
+token_code: Token::TokenCode +
+
+ The code symbol for the token that was sent +
+
+metadata: vector<u8> +
+
+ Metadata associated with the withdraw +
+
+ + +
+ + + +## Struct `DepositEvent` + +Message for balance deposit event. + + +
struct DepositEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u128 +
+
+ The amount of Token sent +
+
+token_code: Token::TokenCode +
+
+ The code symbol for the token that was sent +
+
+metadata: vector<u8> +
+
+ Metadata associated with the deposit +
+
+ + +
+ + + +## Struct `AcceptTokenEvent` + +Message for accept token events + + +
struct AcceptTokenEvent has drop, store
+
+ + + +
+Fields + + +
+
+token_code: Token::TokenCode +
+
+ +
+
+ + +
+ + + +## Resource `SignerDelegated` + + + +
struct SignerDelegated has key
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `SignerCapability` + + + +
struct SignerCapability has store
+
+ + + +
+Fields + + +
+
+addr: address +
+
+ +
+
+ + +
+ + + +## Resource `AutoAcceptToken` + + + +
struct AutoAcceptToken has key
+
+ + + +
+Fields + + +
+
+enable: bool +
+
+ +
+
+ + +
+ + + +## Struct `RotateAuthKeyEvent` + +Message for rotate_authentication_key events + + +
struct RotateAuthKeyEvent has drop, store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+new_auth_key: vector<u8> +
+
+ +
+
+ + +
+ + + +## Struct `ExtractWithdrawCapEvent` + +Message for extract_withdraw_capability events + + +
struct ExtractWithdrawCapEvent has drop, store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+ + +
+ + + +## Struct `SignerDelegateEvent` + +Message for SignerDelegate events + + +
struct SignerDelegateEvent has drop, store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+ + +
+ + + +## Resource `EventStore` + + + +
struct EventStore has key
+
+ + + +
+Fields + + +
+
+rotate_auth_key_events: Event::EventHandle<Account::RotateAuthKeyEvent> +
+
+ Event handle for rotate_authentication_key event +
+
+extract_withdraw_cap_events: Event::EventHandle<Account::ExtractWithdrawCapEvent> +
+
+ Event handle for extract_withdraw_capability event +
+
+signer_delegate_events: Event::EventHandle<Account::SignerDelegateEvent> +
+
+ Event handle for signer delegated event +
+
+ + +
+ + + +## Constants + + + + + + +
const MAX_U64: u128 = 18446744073709551615;
+
+ + + + + + + +
const EDEPRECATED_FUNCTION: u64 = 19;
+
+ + + + + + + +
const EPROLOGUE_ACCOUNT_DOES_NOT_EXIST: u64 = 0;
+
+ + + + + +The address bytes length + + +
const ADDRESS_LENGTH: u64 = 16;
+
+ + + + + + + +
const CONTRACT_ACCOUNT_AUTH_KEY_PLACEHOLDER: vector<u8> = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
+
+ + + + + + + +
const DUMMY_AUTH_KEY: vector<u8> = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+ + + + + + + +
const EADDRESS_AND_AUTH_KEY_MISMATCH: u64 = 105;
+
+ + + + + + + +
const EADDRESS_PUBLIC_KEY_INCONSISTENT: u64 = 104;
+
+ + + + + + + +
const EBAD_TRANSACTION_FEE_TOKEN: u64 = 18;
+
+ + + + + + + +
const ECOIN_DEPOSIT_IS_ZERO: u64 = 15;
+
+ + + + + + + +
const EINSUFFICIENT_BALANCE: u64 = 10;
+
+ + + + + + + +
const EKEY_ROTATION_CAPABILITY_ALREADY_EXTRACTED: u64 = 103;
+
+ + + + + + + +
const EMALFORMED_AUTHENTICATION_KEY: u64 = 102;
+
+ + + + + + + +
const EPROLOGUE_CANT_PAY_GAS_DEPOSIT: u64 = 4;
+
+ + + + + + + +
const EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY: u64 = 1;
+
+ + + + + + + +
const EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG: u64 = 9;
+
+ + + + + + + +
const EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW: u64 = 3;
+
+ + + + + + + +
const EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD: u64 = 2;
+
+ + + + + + + +
const EPROLOGUE_SIGNER_ALREADY_DELEGATED: u64 = 200;
+
+ + + + + + + +
const ERR_SIGNER_ALREADY_DELEGATED: u64 = 107;
+
+ + + + + + + +
const ERR_TOKEN_NOT_ACCEPT: u64 = 106;
+
+ + + + + + + +
const EWITHDRAWAL_CAPABILITY_ALREADY_EXTRACTED: u64 = 101;
+
+ + + + + +## Function `remove_signer_capability` + +A one-way action, once SignerCapability is removed from signer, the address cannot send txns anymore. +This function can only called once by signer. + + +
public fun remove_signer_capability(signer: &signer): Account::SignerCapability
+
+ + + +
+Implementation + + +
public fun remove_signer_capability(signer: &signer): SignerCapability
+acquires Account, EventStore {
+    let signer_addr = Signer::address_of(signer);
+    assert!(!is_signer_delegated(signer_addr), Errors::invalid_state(ERR_SIGNER_ALREADY_DELEGATED));
+
+    // set to account auth key to noop.
+    {
+        let key_rotation_capability = extract_key_rotation_capability(signer);
+        rotate_authentication_key_with_capability(&key_rotation_capability, CONTRACT_ACCOUNT_AUTH_KEY_PLACEHOLDER);
+        destroy_key_rotation_capability(key_rotation_capability);
+        move_to(signer, SignerDelegated {});
+
+        make_event_store_if_not_exist(signer);
+        let event_store = borrow_global_mut<EventStore>(signer_addr);
+        Event::emit_event<SignerDelegateEvent>(
+            &mut event_store.signer_delegate_events,
+            SignerDelegateEvent {
+                account_address: signer_addr
+            }
+        );
+    };
+
+    let signer_cap = SignerCapability {addr: signer_addr };
+    signer_cap
+}
+
+ + + +
+ + + +## Function `create_signer_with_cap` + + + +
public fun create_signer_with_cap(cap: &Account::SignerCapability): signer
+
+ + + +
+Implementation + + +
public fun create_signer_with_cap(cap: &SignerCapability): signer {
+    create_signer(cap.addr)
+}
+
+ + + +
+ + + +## Function `destroy_signer_cap` + + + +
public fun destroy_signer_cap(cap: Account::SignerCapability)
+
+ + + +
+Implementation + + +
public fun destroy_signer_cap(cap: SignerCapability) {
+    let SignerCapability {addr: _} = cap;
+}
+
+ + + +
+ + + +## Function `signer_address` + + + +
public fun signer_address(cap: &Account::SignerCapability): address
+
+ + + +
+Implementation + + +
public fun signer_address(cap: &SignerCapability): address {
+    cap.addr
+}
+
+ + + +
+ + + +## Function `is_signer_delegated` + + + +
public fun is_signer_delegated(addr: address): bool
+
+ + + +
+Implementation + + +
public fun is_signer_delegated(addr: address): bool {
+    exists<SignerDelegated>(addr)
+}
+
+ + + +
+ + + +## Function `create_genesis_account` + +Create an genesis account at new_account_address and return signer. +Genesis authentication_key is zero bytes. + + +
public fun create_genesis_account(new_account_address: address): signer
+
+ + + +
+Implementation + + +
public fun create_genesis_account(
+    new_account_address: address,
+) :signer {
+    Timestamp::assert_genesis();
+    let new_account = create_signer(new_account_address);
+    make_account(&new_account, DUMMY_AUTH_KEY);
+    new_account
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if len(DUMMY_AUTH_KEY) != 32;
+aborts_if exists<Account>(new_account_address);
+
+ + + +
+ + + +## Function `release_genesis_signer` + +Release genesis account signer + + +
public fun release_genesis_signer(_genesis_account: signer)
+
+ + + +
+Implementation + + +
public fun release_genesis_signer(_genesis_account: signer){
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `create_account` + +Deprecated since @v5 + + +
public fun create_account<TokenType: store>(_authentication_key: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun create_account<TokenType: store>(_authentication_key: vector<u8>): address {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if true;
+
+ + + +
+ + + +## Function `create_account_with_address` + +Creates a new account at fresh_address with a balance of zero and empty auth key, the address as init auth key for check transaction. +Creating an account at address StarcoinFramework will cause runtime failure as it is a +reserved address for the MoveVM. + + +
public fun create_account_with_address<TokenType: store>(fresh_address: address)
+
+ + + +
+Implementation + + +
public fun create_account_with_address<TokenType: store>(fresh_address: address) acquires Account {
+    let new_account = create_signer(fresh_address);
+    make_account(&new_account, DUMMY_AUTH_KEY);
+    // Make sure all account accept STC.
+    if (!STC::is_stc<TokenType>()){
+        do_accept_token<STC>(&new_account);
+    };
+    do_accept_token<TokenType>(&new_account);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if exists<Account>(fresh_address);
+aborts_if Token::spec_token_code<TokenType>() != Token::spec_token_code<STC>() && exists<Balance<STC>>(fresh_address);
+aborts_if exists<Balance<TokenType>>(fresh_address);
+ensures exists_at(fresh_address);
+ensures exists<Balance<TokenType>>(fresh_address);
+
+ + + +
+ + + +## Function `make_account` + + + +
fun make_account(new_account: &signer, authentication_key: vector<u8>)
+
+ + + +
+Implementation + + +
fun make_account(
+    new_account: &signer,
+    authentication_key: vector<u8>,
+) {
+    assert!(Vector::length(&authentication_key) == 32, Errors::invalid_argument(EMALFORMED_AUTHENTICATION_KEY));
+    let new_account_addr = Signer::address_of(new_account);
+    Event::publish_generator(new_account);
+    move_to(new_account, Account {
+          authentication_key,
+          withdrawal_capability: Option::some(
+              WithdrawCapability {
+                  account_address: new_account_addr
+          }),
+          key_rotation_capability: Option::some(
+              KeyRotationCapability {
+                  account_address: new_account_addr
+          }),
+          withdraw_events: Event::new_event_handle<WithdrawEvent>(new_account),
+          deposit_events: Event::new_event_handle<DepositEvent>(new_account),
+          accept_token_events: Event::new_event_handle<AcceptTokenEvent>(new_account),
+          sequence_number: 0,
+    });
+    move_to(new_account, AutoAcceptToken{enable: true});
+    move_to(new_account, EventStore {
+          rotate_auth_key_events: Event::new_event_handle<RotateAuthKeyEvent>(new_account),
+          extract_withdraw_cap_events: Event::new_event_handle<ExtractWithdrawCapEvent>(new_account),
+          signer_delegate_events: Event::new_event_handle<SignerDelegateEvent>(new_account),
+    });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if len(authentication_key) != 32;
+aborts_if exists<Account>(Signer::address_of(new_account));
+aborts_if exists<AutoAcceptToken>(Signer::address_of(new_account));
+ensures exists_at(Signer::address_of(new_account));
+
+ + + +
+ + + +## Function `create_signer` + + + +
fun create_signer(addr: address): signer
+
+ + + +
+Implementation + + +
native fun create_signer(addr: address): signer;
+
+ + + +
+ + + +## Function `create_account_with_initial_amount` + + + +
public entry fun create_account_with_initial_amount<TokenType: store>(account: signer, fresh_address: address, _auth_key: vector<u8>, initial_amount: u128)
+
+ + + +
+Implementation + + +
public entry fun create_account_with_initial_amount<TokenType: store>(account: signer, fresh_address: address, _auth_key: vector<u8>, initial_amount: u128)
+acquires Account, Balance, AutoAcceptToken {
+    create_account_with_initial_amount_entry<TokenType>(account, fresh_address, initial_amount);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `create_account_with_initial_amount_v2` + + + +
public entry fun create_account_with_initial_amount_v2<TokenType: store>(account: signer, fresh_address: address, initial_amount: u128)
+
+ + + +
+Implementation + + +
public entry fun create_account_with_initial_amount_v2<TokenType: store>(account: signer, fresh_address: address, initial_amount: u128)
+acquires Account, Balance, AutoAcceptToken {
+    create_account_with_initial_amount_entry<TokenType>(account, fresh_address, initial_amount);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `create_account_with_initial_amount_entry` + + + +
public entry fun create_account_with_initial_amount_entry<TokenType: store>(account: signer, fresh_address: address, initial_amount: u128)
+
+ + + +
+Implementation + + +
public entry fun create_account_with_initial_amount_entry<TokenType: store>(account: signer, fresh_address: address, initial_amount: u128)
+acquires Account, Balance, AutoAcceptToken {
+    create_account_with_address<TokenType>(fresh_address);
+    if (initial_amount > 0) {
+        pay_from<TokenType>(&account, fresh_address, initial_amount);
+    };
+}
+
+ + + +
+ + + +## Function `create_delegate_account` + +Generate an new address and create a new account, then delegate the account and return the new account address and SignerCapability + + +
public fun create_delegate_account(sender: &signer): (address, Account::SignerCapability)
+
+ + + +
+Implementation + + +
public fun create_delegate_account(sender: &signer) : (address, SignerCapability) acquires Balance, Account, EventStore {
+    let sender_address = Signer::address_of(sender);
+    let sequence_number = Self::sequence_number(sender_address);
+    // use stc balance as part of seed, just for new address more random.
+    let stc_balance = Self::balance<STC>(sender_address);
+
+    let seed_bytes = BCS::to_bytes(&sender_address);
+    Vector::append(&mut seed_bytes, BCS::to_bytes(&sequence_number));
+    Vector::append(&mut seed_bytes, BCS::to_bytes(&stc_balance));
+
+    let seed_hash = Hash::sha3_256(seed_bytes);
+    let i = 0;
+    let address_bytes = Vector::empty();
+    while (i < ADDRESS_LENGTH) {
+        Vector::push_back(&mut address_bytes, *Vector::borrow(&seed_hash,i));
+        i = i + 1;
+    };
+    let new_address = BCS::to_address(address_bytes);
+    Self::create_account_with_address<STC>(new_address);
+    let new_signer = Self::create_signer(new_address);
+    (new_address, Self::remove_signer_capability(&new_signer))
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deposit_to_self` + +Deposits the to_deposit token into the self's account balance + + +
public fun deposit_to_self<TokenType: store>(account: &signer, to_deposit: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun deposit_to_self<TokenType: store>(account: &signer, to_deposit: Token<TokenType>)
+acquires Account, Balance, AutoAcceptToken {
+    let account_address = Signer::address_of(account);
+    if (!is_accepts_token<TokenType>(account_address)){
+        do_accept_token<TokenType>(account);
+    };
+    deposit(account_address, to_deposit);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if to_deposit.value == 0;
+let is_accepts_token = exists<Balance<TokenType>>(Signer::address_of(account));
+aborts_if is_accepts_token && global<Balance<TokenType>>(Signer::address_of(account)).token.value + to_deposit.value > max_u128();
+aborts_if !exists<Account>(Signer::address_of(account));
+ensures exists<Balance<TokenType>>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `deposit` + +Deposits the to_deposit token into the receiver's account balance with the no metadata +It's a reverse operation of withdraw. + + +
public fun deposit<TokenType: store>(receiver: address, to_deposit: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun deposit<TokenType: store>(
+    receiver: address,
+    to_deposit: Token<TokenType>,
+) acquires Account, Balance, AutoAcceptToken {
+    deposit_with_metadata<TokenType>(receiver, to_deposit, x"")
+}
+
+ + + +
+ +
+Specification + + + +
include DepositWithMetadataAbortsIf<TokenType>;
+
+ + + +
+ + + +## Function `deposit_with_metadata` + +Deposits the to_deposit token into the receiver's account balance with the attached metadata +It's a reverse operation of withdraw_with_metadata. + + +
public fun deposit_with_metadata<TokenType: store>(receiver: address, to_deposit: Token::Token<TokenType>, metadata: vector<u8>)
+
+ + + +
+Implementation + + +
public fun deposit_with_metadata<TokenType: store>(
+    receiver: address,
+    to_deposit: Token<TokenType>,
+    metadata: vector<u8>,
+) acquires Account, Balance, AutoAcceptToken {
+
+    if (!exists_at(receiver)) {
+        create_account_with_address<TokenType>(receiver);
+    };
+
+    try_accept_token<TokenType>(receiver);
+
+    let deposit_value = Token::value(&to_deposit);
+    if (deposit_value > 0u128) {
+        // Deposit the `to_deposit` token
+        deposit_to_balance<TokenType>(borrow_global_mut<Balance<TokenType>>(receiver), to_deposit);
+
+        // emit deposit event
+        emit_account_deposit_event<TokenType>(receiver, deposit_value, metadata);
+    } else {
+        Token::destroy_zero(to_deposit);
+    };
+}
+
+ + + +
+ +
+Specification + + + +
include DepositWithMetadataAbortsIf<TokenType>;
+ensures exists<Balance<TokenType>>(receiver);
+ensures old(global<Balance<TokenType>>(receiver)).token.value + to_deposit.value == global<Balance<TokenType>>(receiver).token.value;
+
+ + + + + + + +
schema DepositWithMetadataAbortsIf<TokenType> {
+    receiver: address;
+    to_deposit: Token<TokenType>;
+    aborts_if to_deposit.value == 0;
+    aborts_if !exists<Account>(receiver);
+    aborts_if !exists<Balance<TokenType>>(receiver);
+    aborts_if global<Balance<TokenType>>(receiver).token.value + to_deposit.value > max_u128();
+}
+
+ + + +
+ + + +## Function `deposit_to_balance` + +Helper to deposit amount to the given account balance + + +
fun deposit_to_balance<TokenType: store>(balance: &mut Account::Balance<TokenType>, token: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
fun deposit_to_balance<TokenType: store>(balance: &mut Balance<TokenType>, token: Token::Token<TokenType>) {
+    Token::deposit(&mut balance.token, token)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if balance.token.value + token.value > MAX_U128;
+
+ + + +
+ + + +## Function `withdraw_from_balance_v2` + + + +
public(friend) fun withdraw_from_balance_v2<TokenType: store>(sender: address, amount: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public(friend) fun withdraw_from_balance_v2<TokenType: store>(sender:address, amount: u128): Token<TokenType> acquires Balance {
+    let balance = borrow_global_mut<Balance<TokenType>>(sender);
+    Token::withdraw(&mut balance.token, amount)
+}
+
+ + + +
+ + + +## Function `set_sequence_number` + + + +
public(friend) fun set_sequence_number(sender: address, sequence_number: u64)
+
+ + + +
+Implementation + + +
public (friend) fun set_sequence_number(sender: address, sequence_number: u64) acquires Account {
+    let account = borrow_global_mut<Account>(sender);
+    account.sequence_number = sequence_number;
+}
+
+ + + +
+ + + +## Function `set_authentication_key` + + + +
public(friend) fun set_authentication_key(sender: address, auth_key: vector<u8>)
+
+ + + +
+Implementation + + +
public (friend) fun set_authentication_key(sender:address,auth_key:vector<u8>) acquires Account{
+    let account = borrow_global_mut<Account>(sender);
+    account.authentication_key = auth_key;
+}
+
+ + + +
+ + + +## Function `withdraw_from_balance` + +Helper to withdraw amount from the given account balance and return the withdrawn Token + + +
public fun withdraw_from_balance<TokenType: store>(balance: &mut Account::Balance<TokenType>, amount: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun withdraw_from_balance<TokenType: store>(balance: &mut Balance<TokenType>, amount: u128): Token<TokenType>{
+    Token::withdraw(&mut balance.token, amount)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if balance.token.value < amount;
+
+ + + +
+ + + +## Function `withdraw` + +Withdraw amount Token from the account balance + + +
public fun withdraw<TokenType: store>(account: &signer, amount: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun withdraw<TokenType: store>(account: &signer, amount: u128): Token<TokenType>
+acquires Account, Balance {
+    withdraw_with_metadata<TokenType>(account, amount, x"")
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Balance<TokenType>>(Signer::address_of(account));
+aborts_if !exists<Account>(Signer::address_of(account));
+aborts_if global<Balance<TokenType>>(Signer::address_of(account)).token.value < amount;
+aborts_if Option::is_none(global<Account>(Signer::address_of(account)).withdrawal_capability);
+
+ + + +
+ + + +## Function `withdraw_with_metadata` + +Withdraw amount tokens from signer with given metadata. + + +
public fun withdraw_with_metadata<TokenType: store>(account: &signer, amount: u128, metadata: vector<u8>): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun withdraw_with_metadata<TokenType: store>(account: &signer, amount: u128, metadata: vector<u8>): Token<TokenType>
+acquires Account, Balance {
+    let sender_addr = Signer::address_of(account);
+    let sender_balance = borrow_global_mut<Balance<TokenType>>(sender_addr);
+    // The sender_addr has delegated the privilege to withdraw from her account elsewhere--abort.
+    assert!(!delegated_withdraw_capability(sender_addr), Errors::invalid_state(EWITHDRAWAL_CAPABILITY_ALREADY_EXTRACTED));
+    if (amount == 0){
+        return Token::zero()
+    };
+    emit_account_withdraw_event<TokenType>(sender_addr, amount, metadata);
+    // The sender_addr has retained her withdrawal privileges--proceed.
+    withdraw_from_balance<TokenType>(sender_balance, amount)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Balance<TokenType>>(Signer::address_of(account));
+aborts_if !exists<Account>(Signer::address_of(account));
+aborts_if global<Balance<TokenType>>(Signer::address_of(account)).token.value < amount;
+aborts_if Option::is_none(global<Account>(Signer::address_of(account)).withdrawal_capability);
+
+ + + + + + + +
fun spec_withdraw<TokenType>(account: signer, amount: u128): Token<TokenType> {
+   Token<TokenType> { value: amount }
+}
+
+ + + +
+ + + +## Function `withdraw_with_capability` + +Withdraw amount Token from the account under cap.account_address with no metadata + + +
public fun withdraw_with_capability<TokenType: store>(cap: &Account::WithdrawCapability, amount: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun withdraw_with_capability<TokenType: store>(
+    cap: &WithdrawCapability, amount: u128
+): Token<TokenType> acquires Balance, Account {
+    withdraw_with_capability_and_metadata<TokenType>(cap, amount, x"")
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Balance<TokenType>>(cap.account_address);
+aborts_if !exists<Account>(cap.account_address);
+aborts_if global<Balance<TokenType>>(cap.account_address).token.value < amount;
+
+ + + +
+ + + +## Function `withdraw_with_capability_and_metadata` + +Withdraw amount Token from the account under cap.account_address with metadata + + +
public fun withdraw_with_capability_and_metadata<TokenType: store>(cap: &Account::WithdrawCapability, amount: u128, metadata: vector<u8>): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun withdraw_with_capability_and_metadata<TokenType: store>(
+    cap: &WithdrawCapability, amount: u128, metadata: vector<u8>
+): Token<TokenType> acquires Balance, Account {
+    let balance = borrow_global_mut<Balance<TokenType>>(cap.account_address);
+    emit_account_withdraw_event<TokenType>(cap.account_address, amount, metadata);
+    withdraw_from_balance<TokenType>(balance , amount)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Balance<TokenType>>(cap.account_address);
+aborts_if !exists<Account>(cap.account_address);
+aborts_if global<Balance<TokenType>>(cap.account_address).token.value < amount;
+
+ + + +
+ + + +## Function `extract_withdraw_capability` + +Return a unique capability granting permission to withdraw from the sender's account balance. + + +
public fun extract_withdraw_capability(sender: &signer): Account::WithdrawCapability
+
+ + + +
+Implementation + + +
public fun extract_withdraw_capability(
+    sender: &signer
+): WithdrawCapability acquires Account, EventStore {
+    let sender_addr = Signer::address_of(sender);
+    // Abort if we already extracted the unique withdraw capability for this account.
+    assert!(!delegated_withdraw_capability(sender_addr), Errors::invalid_state(EWITHDRAWAL_CAPABILITY_ALREADY_EXTRACTED));
+
+    make_event_store_if_not_exist(sender);
+    let event_store = borrow_global_mut<EventStore>(sender_addr);
+    Event::emit_event<ExtractWithdrawCapEvent>(
+        &mut event_store.extract_withdraw_cap_events,
+        ExtractWithdrawCapEvent {
+            account_address: sender_addr,
+        }
+    );
+    let account = borrow_global_mut<Account>(sender_addr);
+    Option::extract(&mut account.withdrawal_capability)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(Signer::address_of(sender));
+aborts_if Option::is_none(global<Account>( Signer::address_of(sender)).withdrawal_capability);
+
+ + + +
+ + + +## Function `restore_withdraw_capability` + +Return the withdraw capability to the account it originally came from + + +
public fun restore_withdraw_capability(cap: Account::WithdrawCapability)
+
+ + + +
+Implementation + + +
public fun restore_withdraw_capability(cap: WithdrawCapability)
+   acquires Account {
+       let account = borrow_global_mut<Account>(cap.account_address);
+       Option::fill(&mut account.withdrawal_capability, cap)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Option::is_some(global<Account>(cap.account_address).withdrawal_capability);
+aborts_if !exists<Account>(cap.account_address);
+
+ + + +
+ + + +## Function `emit_account_withdraw_event` + + + +
fun emit_account_withdraw_event<TokenType: store>(account: address, amount: u128, metadata: vector<u8>)
+
+ + + +
+Implementation + + +
fun emit_account_withdraw_event<TokenType: store>(account: address, amount: u128, metadata: vector<u8>)
+acquires Account {
+    // emit withdraw event
+    let account = borrow_global_mut<Account>(account);
+
+    Event::emit_event<WithdrawEvent>(&mut account.withdraw_events, WithdrawEvent {
+        amount,
+        token_code: Token::token_code<TokenType>(),
+        metadata,
+    });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(account);
+
+ + + +
+ + + +## Function `emit_account_deposit_event` + + + +
fun emit_account_deposit_event<TokenType: store>(account: address, amount: u128, metadata: vector<u8>)
+
+ + + +
+Implementation + + +
fun emit_account_deposit_event<TokenType: store>(account: address, amount: u128, metadata: vector<u8>)
+acquires Account {
+    // emit withdraw event
+    let account = borrow_global_mut<Account>(account);
+
+    Event::emit_event<DepositEvent>(&mut account.deposit_events, DepositEvent {
+        amount,
+        token_code: Token::token_code<TokenType>(),
+        metadata,
+    });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(account);
+
+ + + +
+ + + +## Function `pay_from_capability` + +Withdraws amount Token using the passed in WithdrawCapability, and deposits it +into the payee's account balance. Creates the payee account if it doesn't exist. + + +
public fun pay_from_capability<TokenType: store>(cap: &Account::WithdrawCapability, payee: address, amount: u128, metadata: vector<u8>)
+
+ + + +
+Implementation + + +
public fun pay_from_capability<TokenType: store>(
+    cap: &WithdrawCapability,
+    payee: address,
+    amount: u128,
+    metadata: vector<u8>,
+) acquires Account, Balance, AutoAcceptToken {
+    let tokens = withdraw_with_capability_and_metadata<TokenType>(cap, amount, *&metadata);
+    deposit_with_metadata<TokenType>(
+        payee,
+        tokens,
+        metadata,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Balance<TokenType>>(cap.account_address);
+aborts_if !exists<Account>(cap.account_address);
+aborts_if global<Balance<TokenType>>(cap.account_address).token.value < amount;
+aborts_if amount == 0;
+aborts_if !exists<Account>(payee);
+aborts_if !exists<Balance<TokenType>>(payee);
+aborts_if cap.account_address != payee && global<Balance<TokenType>>(payee).token.value + amount > MAX_U128;
+
+ + + +
+ + + +## Function `pay_from_with_metadata` + +Withdraw amount Token from the transaction sender's +account balance and send the token to the payee address with the +attached metadata Creates the payee account if it does not exist + + +
public fun pay_from_with_metadata<TokenType: store>(account: &signer, payee: address, amount: u128, metadata: vector<u8>)
+
+ + + +
+Implementation + + +
public fun pay_from_with_metadata<TokenType: store>(
+    account: &signer,
+    payee: address,
+    amount: u128,
+    metadata: vector<u8>,
+) acquires Account, Balance, AutoAcceptToken {
+    let tokens = withdraw_with_metadata<TokenType>(account, amount, *&metadata);
+    deposit_with_metadata<TokenType>(
+        payee,
+        tokens,
+        metadata,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Balance<TokenType>>(Signer::address_of(account));
+aborts_if !exists<Account>(Signer::address_of(account));
+aborts_if global<Balance<TokenType>>(Signer::address_of(account)).token.value < amount;
+aborts_if Option::is_none(global<Account>(Signer::address_of(account)).withdrawal_capability);
+aborts_if amount == 0;
+aborts_if !exists<Account>(payee);
+aborts_if !exists<Balance<TokenType>>(payee);
+aborts_if Signer::address_of(account) != payee && global<Balance<TokenType>>(payee).token.value + amount > max_u128();
+
+ + + + + + + +
schema DepositWithPayerAndMetadataAbortsIf<TokenType> {
+    payer: address;
+    payee: address;
+    to_deposit: Token<TokenType>;
+    aborts_if to_deposit.value == 0;
+    aborts_if !exists<Account>(payer);
+    aborts_if !exists<Account>(payee);
+    aborts_if !exists<Balance<TokenType>>(payee);
+    aborts_if global<Balance<TokenType>>(payee).token.value + to_deposit.value > max_u128();
+}
+
+ + + +
+ + + +## Function `pay_from` + +Withdraw amount Token from the transaction sender's +account balance and send the token to the payee address +Creates the payee account if it does not exist + + +
public fun pay_from<TokenType: store>(account: &signer, payee: address, amount: u128)
+
+ + + +
+Implementation + + +
public fun pay_from<TokenType: store>(
+    account: &signer,
+    payee: address,
+    amount: u128
+) acquires Account, Balance, AutoAcceptToken {
+    pay_from_with_metadata<TokenType>(account, payee, amount, x"");
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Balance<TokenType>>(Signer::address_of(account));
+aborts_if !exists<Account>(Signer::address_of(account));
+aborts_if global<Balance<TokenType>>(Signer::address_of(account)).token.value < amount;
+aborts_if Option::is_none(global<Account>(Signer::address_of(account)).withdrawal_capability);
+aborts_if amount == 0;
+aborts_if !exists<Account>(payee);
+aborts_if !exists<Balance<TokenType>>(payee);
+aborts_if Signer::address_of(account) != payee && global<Balance<TokenType>>(payee).token.value + amount > max_u128();
+
+ + + +
+ + + +## Function `rotate_authentication_key_with_capability` + +Rotate the authentication key for the account under cap.account_address + + +
public fun rotate_authentication_key_with_capability(cap: &Account::KeyRotationCapability, new_authentication_key: vector<u8>)
+
+ + + +
+Implementation + + +
public fun rotate_authentication_key_with_capability(
+    cap: &KeyRotationCapability,
+    new_authentication_key: vector<u8>,
+) acquires Account  {
+    let sender_account_resource = borrow_global_mut<Account>(cap.account_address);
+    // Don't allow rotating to clearly invalid key
+    assert!(Vector::length(&new_authentication_key) == 32, Errors::invalid_argument(EMALFORMED_AUTHENTICATION_KEY));
+    sender_account_resource.authentication_key = new_authentication_key;
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(cap.account_address);
+aborts_if len(new_authentication_key) != 32;
+ensures global<Account>(cap.account_address).authentication_key == new_authentication_key;
+
+ + + + + + + +
fun spec_rotate_authentication_key_with_capability(addr: address, new_authentication_key: vector<u8>): bool {
+   global<Account>(addr).authentication_key == new_authentication_key
+}
+
+ + + +
+ + + +## Function `extract_key_rotation_capability` + +Return a unique capability granting permission to rotate the sender's authentication key + + +
public fun extract_key_rotation_capability(account: &signer): Account::KeyRotationCapability
+
+ + + +
+Implementation + + +
public fun extract_key_rotation_capability(account: &signer): KeyRotationCapability
+acquires Account {
+    let account_address = Signer::address_of(account);
+    // Abort if we already extracted the unique key rotation capability for this account.
+    assert!(!delegated_key_rotation_capability(account_address), Errors::invalid_state(EKEY_ROTATION_CAPABILITY_ALREADY_EXTRACTED));
+    let account = borrow_global_mut<Account>(account_address);
+    Option::extract(&mut account.key_rotation_capability)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(Signer::address_of(account));
+aborts_if Option::is_none(global<Account>(Signer::address_of(account)).key_rotation_capability);
+
+ + + +
+ + + +## Function `restore_key_rotation_capability` + +Return the key rotation capability to the account it originally came from + + +
public fun restore_key_rotation_capability(cap: Account::KeyRotationCapability)
+
+ + + +
+Implementation + + +
public fun restore_key_rotation_capability(cap: KeyRotationCapability)
+acquires Account {
+    let account = borrow_global_mut<Account>(cap.account_address);
+    Option::fill(&mut account.key_rotation_capability, cap)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Option::is_some(global<Account>(cap.account_address).key_rotation_capability);
+aborts_if !exists<Account>(cap.account_address);
+
+ + + +
+ + + +## Function `destroy_key_rotation_capability` + + + +
public fun destroy_key_rotation_capability(cap: Account::KeyRotationCapability)
+
+ + + +
+Implementation + + +
public fun destroy_key_rotation_capability(cap: KeyRotationCapability) {
+    let KeyRotationCapability {account_address: _} = cap;
+}
+
+ + + +
+ + + +## Function `rotate_authentication_key` + + + +
public entry fun rotate_authentication_key(account: signer, new_key: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun rotate_authentication_key(account: signer, new_key: vector<u8>) acquires Account, EventStore {
+    rotate_authentication_key_entry(account, new_key);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `rotate_authentication_key_entry` + + + +
public entry fun rotate_authentication_key_entry(account: signer, new_key: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun rotate_authentication_key_entry(account: signer, new_key: vector<u8>) acquires Account, EventStore {
+    do_rotate_authentication_key(&account, new_key);
+}
+
+ + + +
+ + + +## Function `do_rotate_authentication_key` + + + +
public fun do_rotate_authentication_key(account: &signer, new_key: vector<u8>)
+
+ + + +
+Implementation + + +
public fun do_rotate_authentication_key(account: &signer, new_key: vector<u8>) acquires Account, EventStore {
+    let key_rotation_capability = extract_key_rotation_capability(account);
+    rotate_authentication_key_with_capability(&key_rotation_capability, copy new_key);
+    restore_key_rotation_capability(key_rotation_capability);
+
+    make_event_store_if_not_exist(account);
+    let signer_addr = Signer::address_of(account);
+    let event_store = borrow_global_mut<EventStore>(signer_addr);
+    Event::emit_event<RotateAuthKeyEvent>(
+        &mut event_store.rotate_auth_key_events,
+        RotateAuthKeyEvent {
+            account_address: signer_addr,
+            new_auth_key: new_key,
+        }
+    );
+}
+
+ + + +
+ + + +## Function `balance_for` + +Helper to return the u128 value of the balance for account + + +
fun balance_for<TokenType: store>(balance: &Account::Balance<TokenType>): u128
+
+ + + +
+Implementation + + +
fun balance_for<TokenType: store>(balance: &Balance<TokenType>): u128 {
+    Token::value<TokenType>(&balance.token)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `balance` + +Return the current TokenType balance of the account at addr. + + +
public fun balance<TokenType: store>(addr: address): u128
+
+ + + +
+Implementation + + +
public fun balance<TokenType: store>(addr: address): u128 acquires Balance {
+    if (exists<Balance<TokenType>>(addr)) {
+        balance_for(borrow_global<Balance<TokenType>>(addr))
+    } else {
+        0u128
+    }
+}
+
+ + + +
+ + + +## Function `do_accept_token` + +Add a balance of Token type to the sending account. + + +
public fun do_accept_token<TokenType: store>(account: &signer)
+
+ + + +
+Implementation + + +
public fun do_accept_token<TokenType: store>(account: &signer) acquires Account {
+    move_to(account, Balance<TokenType>{ token: Token::zero<TokenType>() });
+    let token_code = Token::token_code<TokenType>();
+    // Load the sender's account
+    let sender_account_ref = borrow_global_mut<Account>(Signer::address_of(account));
+    // Log a sent event
+    Event::emit_event<AcceptTokenEvent>(
+        &mut sender_account_ref.accept_token_events,
+        AcceptTokenEvent {
+            token_code:  token_code,
+        },
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if exists<Balance<TokenType>>(Signer::address_of(account));
+aborts_if !exists<Account>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `accept_token` + + + +
public entry fun accept_token<TokenType: store>(account: signer)
+
+ + + +
+Implementation + + +
public entry fun accept_token<TokenType: store>(account: signer) acquires Account {
+    accept_token_entry<TokenType>(account);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `accept_token_entry` + + + +
public entry fun accept_token_entry<TokenType: store>(account: signer)
+
+ + + +
+Implementation + + +
public entry fun accept_token_entry<TokenType: store>(account: signer) acquires Account {
+    do_accept_token<TokenType>(&account);
+}
+
+ + + +
+ + + +## Function `is_accepts_token` + +This is a alias of is_accept_token + + +
public fun is_accepts_token<TokenType: store>(addr: address): bool
+
+ + + +
+Implementation + + +
public fun is_accepts_token<TokenType: store>(addr: address): bool acquires AutoAcceptToken {
+    Self::is_accept_token<TokenType>(addr)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `is_accept_token` + +Return whether the account at addr accept Token type tokens + + +
public fun is_accept_token<TokenType: store>(addr: address): bool
+
+ + + +
+Implementation + + +
public fun is_accept_token<TokenType: store>(addr: address): bool acquires AutoAcceptToken {
+    if (can_auto_accept_token(addr)) {
+        true
+    } else {
+        exists<Balance<TokenType>>(addr)
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `can_auto_accept_token` + +Check whether the address can auto accept token. + + +
public fun can_auto_accept_token(addr: address): bool
+
+ + + +
+Implementation + + +
public fun can_auto_accept_token(addr: address): bool acquires AutoAcceptToken {
+    if (exists<AutoAcceptToken>(addr)) {
+        borrow_global<AutoAcceptToken>(addr).enable
+    } else {
+        false
+    }
+}
+
+ + + +
+ + + +## Function `set_auto_accept_token_entry` + + + +
public entry fun set_auto_accept_token_entry(account: signer, enable: bool)
+
+ + + +
+Implementation + + +
public entry fun set_auto_accept_token_entry(account: signer, enable: bool) acquires AutoAcceptToken {
+    set_auto_accept_token(&account, enable);
+}
+
+ + + +
+ + + +## Function `set_auto_accept_token` + +Configure whether auto-accept tokens. + + +
public fun set_auto_accept_token(account: &signer, enable: bool)
+
+ + + +
+Implementation + + +
public fun set_auto_accept_token(account: &signer, enable: bool) acquires AutoAcceptToken {
+    let addr = Signer::address_of(account);
+    if (exists<AutoAcceptToken>(addr)) {
+        let config = borrow_global_mut<AutoAcceptToken>(addr);
+        config.enable = enable;
+    } else {
+        move_to(account, AutoAcceptToken{enable});
+    };
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `try_accept_token` + +try to accept token for addr. + + +
fun try_accept_token<TokenType: store>(addr: address)
+
+ + + +
+Implementation + + +
fun try_accept_token<TokenType: store>(addr: address) acquires AutoAcceptToken, Account {
+    if (!exists<Balance<TokenType>>(addr)) {
+        if (can_auto_accept_token(addr)) {
+            let signer = create_signer(addr);
+            do_accept_token<TokenType>(&signer);
+        }else{
+            abort Errors::not_published(ERR_TOKEN_NOT_ACCEPT)
+        }
+    };
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `sequence_number_for_account` + +Helper to return the sequence number field for given account + + +
fun sequence_number_for_account(account: &Account::Account): u64
+
+ + + +
+Implementation + + +
fun sequence_number_for_account(account: &Account): u64 {
+    account.sequence_number
+}
+
+ + + +
+ + + +## Function `sequence_number` + +Return the current sequence number at addr + + +
public fun sequence_number(addr: address): u64
+
+ + + +
+Implementation + + +
public fun sequence_number(addr: address): u64 acquires Account {
+    sequence_number_for_account(borrow_global<Account>(addr))
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(addr);
+
+ + + +
+ + + +## Function `authentication_key` + +Return the authentication key for this account + + +
public fun authentication_key(addr: address): vector<u8>
+
+ + + +
+Implementation + + +
public fun authentication_key(addr: address): vector<u8> acquires Account {
+    *&borrow_global<Account>(addr).authentication_key
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(addr);
+
+ + + +
+ + + +## Function `delegated_key_rotation_capability` + +Return true if the account at addr has delegated its key rotation capability + + +
public fun delegated_key_rotation_capability(addr: address): bool
+
+ + + +
+Implementation + + +
public fun delegated_key_rotation_capability(addr: address): bool
+acquires Account {
+    Option::is_none(&borrow_global<Account>(addr).key_rotation_capability)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(addr);
+
+ + + +
+ + + +## Function `delegated_withdraw_capability` + +Return true if the account at addr has delegated its withdraw capability + + +
public fun delegated_withdraw_capability(addr: address): bool
+
+ + + +
+Implementation + + +
public fun delegated_withdraw_capability(addr: address): bool
+acquires Account {
+    Option::is_none(&borrow_global<Account>(addr).withdrawal_capability)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account>(addr);
+
+ + + +
+ + + +## Function `withdraw_capability_address` + +Return a reference to the address associated with the given withdraw capability + + +
public fun withdraw_capability_address(cap: &Account::WithdrawCapability): &address
+
+ + + +
+Implementation + + +
public fun withdraw_capability_address(cap: &WithdrawCapability): &address {
+    &cap.account_address
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `key_rotation_capability_address` + +Return a reference to the address associated with the given key rotation capability + + +
public fun key_rotation_capability_address(cap: &Account::KeyRotationCapability): &address
+
+ + + +
+Implementation + + +
public fun key_rotation_capability_address(cap: &KeyRotationCapability): &address {
+    &cap.account_address
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `exists_at` + +Checks if an account exists at check_addr + + +
public fun exists_at(check_addr: address): bool
+
+ + + +
+Implementation + + +
public fun exists_at(check_addr: address): bool {
+    exists<Account>(check_addr)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `is_dummy_auth_key` + + + +
fun is_dummy_auth_key(account: &Account::Account): bool
+
+ + + +
+Implementation + + +
fun is_dummy_auth_key(account: &Account): bool {
+    *&account.authentication_key == DUMMY_AUTH_KEY
+}
+
+ + + +
+ + + +## Function `is_dummy_auth_key_v2` + + + +
public fun is_dummy_auth_key_v2(account: address): bool
+
+ + + +
+Implementation + + +
public fun is_dummy_auth_key_v2(account: address): bool acquires Account {
+    let account = borrow_global_mut<Account>(account);
+    account.authentication_key == DUMMY_AUTH_KEY
+}
+
+ + + +
+ + + +## Function `txn_prologue` + +The prologue is invoked at the beginning of every transaction +It verifies: +- The account's auth key matches the transaction's public key +- That the account has enough balance to pay for all of the gas +- That the sequence number matches the transaction's sequence key + + +
public fun txn_prologue<TokenType: store>(account: &signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64)
+
+ + + +
+Implementation + + +
public fun txn_prologue<TokenType: store>(
+    account: &signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+) acquires Account, Balance {
+    txn_prologue_v2<TokenType>(
+        account,
+        txn_sender,
+        txn_sequence_number,
+        txn_authentication_key_preimage,
+        txn_gas_price,
+        txn_max_gas_units,
+        1,
+        1,
+    )
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<Account>(txn_sender);
+aborts_if global<Account>(txn_sender).authentication_key == DUMMY_AUTH_KEY && Authenticator::spec_derived_address(Hash::sha3_256(txn_authentication_key_preimage)) != txn_sender;
+aborts_if global<Account>(txn_sender).authentication_key != DUMMY_AUTH_KEY && Hash::sha3_256(txn_authentication_key_preimage) != global<Account>(txn_sender).authentication_key;
+aborts_if txn_sequence_number < global<Account>(txn_sender).sequence_number;
+
+ + + +
+ + + +## Function `txn_prologue_v2` + + + +
public fun txn_prologue_v2<TokenType: store>(account: &signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, stc_price: u128, stc_price_scaling: u128)
+
+ + + +
+Implementation + + +
public fun txn_prologue_v2<TokenType: store>(
+    account: &signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    stc_price: u128,
+    stc_price_scaling: u128
+) acquires Account, Balance {
+    CoreAddresses::assert_genesis_address(account);
+
+    // Verify that the transaction sender's account exists
+    assert!(exists_at(txn_sender), Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST));
+    // Verify the account has not delegate its signer cap.
+    assert!(!is_signer_delegated(txn_sender), Errors::invalid_state(EPROLOGUE_SIGNER_ALREADY_DELEGATED));
+
+    // Load the transaction sender's account
+    let sender_account = borrow_global_mut<Account>(txn_sender);
+
+    if (is_dummy_auth_key(sender_account)){
+        // if sender's auth key is empty, use address as auth key for check transaction.
+        assert!(
+            Authenticator::derived_address(Hash::sha3_256(txn_authentication_key_preimage)) == txn_sender,
+            Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY)
+        );
+    }else{
+        // Check that the hash of the transaction's public key matches the account's auth key
+        assert!(
+            Hash::sha3_256(txn_authentication_key_preimage) == *&sender_account.authentication_key,
+            Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY)
+        );
+    };
+    // Check that the account has enough balance for all of the gas
+    let (max_transaction_fee_stc,max_transaction_fee_token) = transaction_fee_simulate(txn_gas_price,txn_max_gas_units,0, stc_price, stc_price_scaling);
+    assert!(
+        max_transaction_fee_stc <= MAX_U64,
+        Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT),
+    );
+    if (max_transaction_fee_stc > 0) {
+        assert!(
+            (txn_sequence_number as u128) < MAX_U64,
+            Errors::limit_exceeded(EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG)
+        );
+        let balance_amount_token = balance<TokenType>(txn_sender);
+        assert!(balance_amount_token >= max_transaction_fee_token, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT));
+        if (!is_stc<TokenType>()){
+            let balance_amount_stc= balance<STC>(CoreAddresses::GENESIS_ADDRESS());
+            assert!(balance_amount_stc >= max_transaction_fee_stc, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT));
+        }
+    };
+    // Check that the transaction sequence number matches the sequence number of the account
+    assert!(txn_sequence_number >= sender_account.sequence_number, Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD));
+    assert!(txn_sequence_number == sender_account.sequence_number, Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW));
+
+}
+
+ + + +
+ + + +## Function `txn_epilogue` + +The epilogue is invoked at the end of transactions. +It collects gas and bumps the sequence number + + +
public fun txn_epilogue<TokenType: store>(account: &signer, txn_sender: address, txn_sequence_number: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64)
+
+ + + +
+Implementation + + +
public fun txn_epilogue<TokenType: store>(
+    account: &signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    gas_units_remaining: u64,
+) acquires Account, Balance {
+    txn_epilogue_v3<TokenType>(account, txn_sender, txn_sequence_number, Vector::empty(), txn_gas_price, txn_max_gas_units, gas_units_remaining,1,1)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `transaction_fee_simulate` + + + +
public fun transaction_fee_simulate(txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, stc_price: u128, stc_price_scaling: u128): (u128, u128)
+
+ + + +
+Implementation + + +
public fun transaction_fee_simulate(
+    txn_gas_price:u64,
+    txn_max_gas_units: u64,
+    gas_units_remaining:u64,
+    stc_price: u128,
+    stc_price_scaling: u128,
+): (u128, u128){
+    let transaction_fee_stc =(txn_gas_price * (txn_max_gas_units - gas_units_remaining) as u128);
+    let transaction_fee_token= Math::mul_div((transaction_fee_stc as u128), stc_price, stc_price_scaling);
+    transaction_fee_token = if (transaction_fee_token == 0 && transaction_fee_stc > 0 ) { 1 } else { transaction_fee_token};
+    (transaction_fee_stc, transaction_fee_token)
+}
+
+ + + +
+ + + +## Function `txn_epilogue_v2` + +The epilogue is invoked at the end of transactions. +It collects gas and bumps the sequence number + + +
public fun txn_epilogue_v2<TokenType: store>(account: &signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64)
+
+ + + +
+Implementation + + +
public fun txn_epilogue_v2<TokenType: store>(
+    account: &signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    gas_units_remaining: u64,
+) acquires Account, Balance {
+    txn_epilogue_v3<TokenType>(
+        account,
+        txn_sender,
+        txn_sequence_number,
+        txn_authentication_key_preimage,
+        txn_gas_price,
+        txn_max_gas_units,
+        gas_units_remaining,1,1)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<Account>(txn_sender);
+aborts_if !exists<Balance<TokenType>>(txn_sender);
+aborts_if txn_max_gas_units < gas_units_remaining;
+let transaction_fee_amount = txn_gas_price * (txn_max_gas_units - gas_units_remaining);
+aborts_if transaction_fee_amount > max_u128();
+aborts_if global<Balance<TokenType>>(txn_sender).token.value < transaction_fee_amount;
+aborts_if txn_sequence_number + 1 > max_u64();
+aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > 0 &&
+          global<Balance<TokenType>>(txn_sender).token.value  < txn_gas_price * (txn_max_gas_units - gas_units_remaining);
+aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > 0 &&
+          !exists<TransactionFee::TransactionFee<TokenType>>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > 0 &&
+          global<TransactionFee::TransactionFee<TokenType>>(CoreAddresses::GENESIS_ADDRESS()).fee.value + txn_gas_price * (txn_max_gas_units - gas_units_remaining) > max_u128();
+
+ + + + +
pragma verify = false;
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<Account>(txn_sender);
+aborts_if !exists<Balance<TokenType>>(txn_sender);
+aborts_if txn_sequence_number + 1 > max_u64();
+aborts_if !exists<Balance<TokenType>>(txn_sender);
+aborts_if txn_max_gas_units < gas_units_remaining;
+
+ + + +
+ + + +## Function `txn_epilogue_v3` + +The epilogue is invoked at the end of transactions. +It collects gas and bumps the sequence number + + +
public fun txn_epilogue_v3<TokenType: store>(account: &signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, stc_price: u128, stc_price_scaling: u128)
+
+ + + +
+Implementation + + +
public fun txn_epilogue_v3<TokenType: store>(
+    account: &signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    gas_units_remaining: u64,
+    stc_price: u128,
+    stc_price_scaling: u128,
+) acquires Account, Balance {
+    CoreAddresses::assert_genesis_address(account);
+    // Charge for gas
+    let (transaction_fee_amount_stc,transaction_fee_amount_token) = transaction_fee_simulate(
+        txn_gas_price,
+        txn_max_gas_units,
+        gas_units_remaining,
+        stc_price,
+        stc_price_scaling);
+    assert!(
+        balance<TokenType>(txn_sender) >= transaction_fee_amount_token,
+        Errors::limit_exceeded(EINSUFFICIENT_BALANCE)
+    );
+    if (!is_stc<TokenType>()){
+        let genesis_balance_amount_stc=balance<STC>(CoreAddresses::GENESIS_ADDRESS());
+        assert!(genesis_balance_amount_stc >= transaction_fee_amount_stc,
+            Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT)
+        );
+    };
+    // Load the transaction sender's account and balance resources
+    let sender_account = borrow_global_mut<Account>(txn_sender);
+    // Bump the sequence number
+    sender_account.sequence_number = txn_sequence_number + 1;
+    // Set auth key when user send transaction first.
+    if (is_dummy_auth_key(sender_account) && !Vector::is_empty(&txn_authentication_key_preimage)){
+        sender_account.authentication_key = Hash::sha3_256(txn_authentication_key_preimage);
+    };
+    if (transaction_fee_amount_stc > 0) {
+        let transaction_fee_token = withdraw_from_balance(
+        borrow_global_mut<Balance<TokenType>>(txn_sender),
+            transaction_fee_amount_token
+        );
+        deposit_to_balance(borrow_global_mut<Balance<TokenType>>(CoreAddresses::GENESIS_ADDRESS()), transaction_fee_token);
+        let stc_fee_token = withdraw_from_balance(borrow_global_mut<Balance<STC>>(CoreAddresses::GENESIS_ADDRESS()), transaction_fee_amount_stc);
+        TransactionFee::pay_fee(stc_fee_token);
+    };
+}
+
+ + + +
+ + + +## Function `remove_zero_balance_entry` + + + +
public entry fun remove_zero_balance_entry<TokenType: store>(account: signer)
+
+ + + +
+Implementation + + +
public entry fun remove_zero_balance_entry<TokenType: store>(account: signer) acquires Balance {
+    remove_zero_balance<TokenType>(&account);
+}
+
+ + + +
+ + + +## Function `remove_zero_balance` + +Remove zero Balance + + +
public fun remove_zero_balance<TokenType: store>(account: &signer)
+
+ + + +
+Implementation + + +
public fun remove_zero_balance<TokenType: store>(account: &signer) acquires Balance {
+    let addr: address = Signer::address_of(account);
+    let Balance<TokenType> { token } = move_from<Balance<TokenType>>(addr);
+    Token::destroy_zero<TokenType>(token);
+}
+
+ + + +
+ +
+Specification + + + +
let addr = Signer::address_of(account);
+aborts_if !exists<Balance<TokenType>>(addr);
+ensures !exists<Balance<TokenType>>(addr);
+
+ + + +
+ + + +## Function `make_event_store_if_not_exist` + +Make a event store if it's not exist. + + +
fun make_event_store_if_not_exist(account: &signer)
+
+ + + +
+Implementation + + +
fun make_event_store_if_not_exist(account: &signer) {
+    if (!exists<EventStore>(Signer::address_of(account))) {
+        move_to(account, EventStore {
+          rotate_auth_key_events: Event::new_event_handle<RotateAuthKeyEvent>(account),
+          extract_withdraw_cap_events: Event::new_event_handle<ExtractWithdrawCapEvent>(account),
+          signer_delegate_events: Event::new_event_handle<SignerDelegateEvent>(account),
+        })
+    };
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/AccountScripts.md b/release/v13/docs/AccountScripts.md new file mode 100644 index 00000000..0b0181fa --- /dev/null +++ b/release/v13/docs/AccountScripts.md @@ -0,0 +1,93 @@ + + + +# Module `0x1::AccountScripts` + + + +- [Function `enable_auto_accept_token`](#0x1_AccountScripts_enable_auto_accept_token) +- [Function `disable_auto_accept_token`](#0x1_AccountScripts_disable_auto_accept_token) +- [Function `remove_zero_balance`](#0x1_AccountScripts_remove_zero_balance) + + +
use 0x1::Account;
+
+ + + + + +## Function `enable_auto_accept_token` + +Enable account's auto-accept-token feature. +The script function is reenterable. + + +
public entry fun enable_auto_accept_token(account: signer)
+
+ + + +
+Implementation + + +
public entry fun enable_auto_accept_token(account: signer) {
+    Account::set_auto_accept_token_entry(account, true);
+}
+
+ + + +
+ + + +## Function `disable_auto_accept_token` + +Disable account's auto-accept-token feature. +The script function is reenterable. + + +
public entry fun disable_auto_accept_token(account: signer)
+
+ + + +
+Implementation + + +
public entry fun disable_auto_accept_token(account: signer) {
+    Account::set_auto_accept_token_entry(account, false);
+}
+
+ + + +
+ + + +## Function `remove_zero_balance` + +Remove zero Balance + + +
public entry fun remove_zero_balance<TokenType: store>(account: signer)
+
+ + + +
+Implementation + + +
public entry fun remove_zero_balance<TokenType: store>(account: signer) {
+    Account::remove_zero_balance_entry<TokenType>(account);
+}
+
+ + + +
diff --git a/release/v13/docs/Authenticator.md b/release/v13/docs/Authenticator.md new file mode 100644 index 00000000..baad0a7d --- /dev/null +++ b/release/v13/docs/Authenticator.md @@ -0,0 +1,448 @@ + + + +# Module `0x1::Authenticator` + +Move representation of the authenticator types +- Ed25519 (single-sig) +- MultiEd25519 (K-of-N multisig) + + +- [Struct `MultiEd25519PublicKey`](#0x1_Authenticator_MultiEd25519PublicKey) +- [Constants](#@Constants_0) +- [Function `create_multi_ed25519`](#0x1_Authenticator_create_multi_ed25519) +- [Function `ed25519_authentication_key`](#0x1_Authenticator_ed25519_authentication_key) +- [Function `derived_address`](#0x1_Authenticator_derived_address) +- [Function `multi_ed25519_authentication_key`](#0x1_Authenticator_multi_ed25519_authentication_key) +- [Function `public_keys`](#0x1_Authenticator_public_keys) +- [Function `threshold`](#0x1_Authenticator_threshold) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::BCS;
+use 0x1::Errors;
+use 0x1::Hash;
+use 0x1::Vector;
+
+ + + + + +## Struct `MultiEd25519PublicKey` + +A multi-ed25519 public key + + +
struct MultiEd25519PublicKey has copy, drop, store
+
+ + + +
+Fields + + +
+
+public_keys: vector<vector<u8>> +
+
+ vector of ed25519 public keys +
+
+threshold: u8 +
+
+ approval threshold +
+
+ + +
+ + + +## Constants + + + + + + +
const AUTHENTICATION_KEY_LENGTH: u64 = 32;
+
+ + + + + + + +
const ED25519_SCHEME_ID: u8 = 0;
+
+ + + + + +Not enough keys were provided for the specified threshold when creating an MultiEd25519 key + + +
const ENOT_ENOUGH_KEYS_FOR_THRESHOLD: u64 = 103;
+
+ + + + + +Too many keys were provided for the specified threshold when creating an MultiEd25519 key + + +
const ENUM_KEYS_ABOVE_MAX_THRESHOLD: u64 = 104;
+
+ + + + + + + +
const EWRONG_AUTHENTICATION_KEY_LENGTH: u64 = 101;
+
+ + + + + +Threshold provided was 0 which can't be used to create a MultiEd25519 key + + +
const EZERO_THRESHOLD: u64 = 102;
+
+ + + + + +Maximum number of keys allowed in a MultiEd25519 public/private key + + +
const MAX_MULTI_ED25519_KEYS: u64 = 32;
+
+ + + + + + + +
const MULTI_ED25519_SCHEME_ID: u8 = 1;
+
+ + + + + +## Function `create_multi_ed25519` + +Create a a multisig policy from a vector of ed25519 public keys and a threshold. +Note: this does *not* check uniqueness of keys. Repeated keys are convenient to +encode weighted multisig policies. For example Alice AND 1 of Bob or Carol is +public_key: {alice_key, alice_key, bob_key, carol_key}, threshold: 3 +Aborts if threshold is zero or bigger than the length of public_keys. + + +
public fun create_multi_ed25519(public_keys: vector<vector<u8>>, threshold: u8): Authenticator::MultiEd25519PublicKey
+
+ + + +
+Implementation + + +
public fun create_multi_ed25519(
+    public_keys: vector<vector<u8>>,
+    threshold: u8
+): MultiEd25519PublicKey {
+    // check threshold requirements
+    let len = Vector::length(&public_keys);
+    assert!(threshold != 0, Errors::invalid_argument(EZERO_THRESHOLD));
+    assert!(
+        (threshold as u64) <= len,
+        Errors::invalid_argument(ENOT_ENOUGH_KEYS_FOR_THRESHOLD)
+    );
+    // the multied25519 signature scheme allows at most 32 keys
+    assert!(
+        len <= MAX_MULTI_ED25519_KEYS,
+        Errors::invalid_argument(ENUM_KEYS_ABOVE_MAX_THRESHOLD)
+    );
+
+    MultiEd25519PublicKey { public_keys, threshold }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if threshold == 0;
+aborts_if threshold > Vector::length(public_keys);
+aborts_if Vector::length(public_keys) > 32;
+
+ + + +
+ + + +## Function `ed25519_authentication_key` + +Compute an authentication key for the ed25519 public key public_key + + +
public fun ed25519_authentication_key(public_key: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun ed25519_authentication_key(public_key: vector<u8>): vector<u8> {
+    Vector::push_back(&mut public_key, ED25519_SCHEME_ID);
+    Hash::sha3_256(public_key)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures [abstract] result == spec_ed25519_authentication_key(public_key);
+
+ + +We use an uninterpreted function to represent the result of key construction. The actual value +does not matter for the verification of callers. + + + + + +
fun spec_ed25519_authentication_key(public_key: vector<u8>): vector<u8>;
+
+ + + +
+ + + +## Function `derived_address` + +convert authentication key to address + + +
public fun derived_address(authentication_key: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun derived_address(authentication_key: vector<u8>): address {
+    assert!(Vector::length(&authentication_key) == AUTHENTICATION_KEY_LENGTH, Errors::invalid_argument(EWRONG_AUTHENTICATION_KEY_LENGTH));
+    let address_bytes = Vector::empty<u8>();
+
+    let i = 16;
+    while (i < 32) {
+        let b = *Vector::borrow(&authentication_key, i);
+        Vector::push_back(&mut address_bytes, b);
+        i = i + 1;
+    };
+
+    BCS::to_address(address_bytes)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if len(authentication_key) != 32;
+ensures [abstract] result == spec_derived_address(authentication_key);
+
+ + +We use an uninterpreted function to represent the result of derived address. The actual value +does not matter for the verification of callers. + + + + + +
fun spec_derived_address(authentication_key: vector<u8>): address;
+
+ + + +
+ + + +## Function `multi_ed25519_authentication_key` + +Compute a multied25519 account authentication key for the policy k + + +
public fun multi_ed25519_authentication_key(k: &Authenticator::MultiEd25519PublicKey): vector<u8>
+
+ + + +
+Implementation + + +
public fun multi_ed25519_authentication_key(k: &MultiEd25519PublicKey): vector<u8> {
+    let public_keys = &k.public_keys;
+    let len = Vector::length(public_keys);
+    let authentication_key_preimage = Vector::empty();
+    let i = 0;
+    while (i < len) {
+        let public_key = *Vector::borrow(public_keys, i);
+        Vector::append(
+            &mut authentication_key_preimage,
+            public_key
+        );
+        i = i + 1;
+    };
+    Vector::append(&mut authentication_key_preimage, BCS::to_bytes(&k.threshold));
+    Vector::push_back(&mut authentication_key_preimage, MULTI_ED25519_SCHEME_ID);
+    Hash::sha3_256(authentication_key_preimage)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `public_keys` + +Return the public keys involved in the multisig policy k + + +
public fun public_keys(k: &Authenticator::MultiEd25519PublicKey): &vector<vector<u8>>
+
+ + + +
+Implementation + + +
public fun public_keys(k: &MultiEd25519PublicKey): &vector<vector<u8>> {
+    &k.public_keys
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `threshold` + +Return the threshold for the multisig policy k + + +
public fun threshold(k: &Authenticator::MultiEd25519PublicKey): u8
+
+ + + +
+Implementation + + +
public fun threshold(k: &MultiEd25519PublicKey): u8 {
+    *&k.threshold
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/BCS.md b/release/v13/docs/BCS.md new file mode 100644 index 00000000..9e684aac --- /dev/null +++ b/release/v13/docs/BCS.md @@ -0,0 +1,1599 @@ + + + +# Module `0x1::BCS` + +Utility for converting a Move value to its binary representation in BCS (Diem Canonical +Serialization). BCS is the binary encoding for Move resources and other non-module values +published on-chain. + + +- [Constants](#@Constants_0) +- [Function `to_bytes`](#0x1_BCS_to_bytes) +- [Function `to_address`](#0x1_BCS_to_address) +- [Function `deserialize_option_bytes_vector`](#0x1_BCS_deserialize_option_bytes_vector) +- [Function `deserialize_bytes_vector`](#0x1_BCS_deserialize_bytes_vector) +- [Function `deserialize_u64_vector`](#0x1_BCS_deserialize_u64_vector) +- [Function `deserialize_u128_vector`](#0x1_BCS_deserialize_u128_vector) +- [Function `deserialize_option_bytes`](#0x1_BCS_deserialize_option_bytes) +- [Function `deserialize_address`](#0x1_BCS_deserialize_address) +- [Function `deserialize_16_bytes`](#0x1_BCS_deserialize_16_bytes) +- [Function `deserialize_bytes`](#0x1_BCS_deserialize_bytes) +- [Function `deserialize_u128`](#0x1_BCS_deserialize_u128) +- [Function `deserialize_u64`](#0x1_BCS_deserialize_u64) +- [Function `deserialize_u32`](#0x1_BCS_deserialize_u32) +- [Function `deserialize_u16`](#0x1_BCS_deserialize_u16) +- [Function `deserialize_u8`](#0x1_BCS_deserialize_u8) +- [Function `deserialize_option_tag`](#0x1_BCS_deserialize_option_tag) +- [Function `deserialize_len`](#0x1_BCS_deserialize_len) +- [Function `deserialize_bool`](#0x1_BCS_deserialize_bool) +- [Function `get_byte`](#0x1_BCS_get_byte) +- [Function `get_n_bytes`](#0x1_BCS_get_n_bytes) +- [Function `get_n_bytes_as_u128`](#0x1_BCS_get_n_bytes_as_u128) +- [Function `deserialize_uleb128_as_u32`](#0x1_BCS_deserialize_uleb128_as_u32) +- [Function `serialize_u32_as_uleb128`](#0x1_BCS_serialize_u32_as_uleb128) +- [Function `skip_option_bytes_vector`](#0x1_BCS_skip_option_bytes_vector) +- [Function `skip_option_bytes`](#0x1_BCS_skip_option_bytes) +- [Function `skip_bytes_vector`](#0x1_BCS_skip_bytes_vector) +- [Function `skip_bytes`](#0x1_BCS_skip_bytes) +- [Function `skip_n_bytes`](#0x1_BCS_skip_n_bytes) +- [Function `skip_u64_vector`](#0x1_BCS_skip_u64_vector) +- [Function `skip_u128_vector`](#0x1_BCS_skip_u128_vector) +- [Function `skip_u256`](#0x1_BCS_skip_u256) +- [Function `skip_u128`](#0x1_BCS_skip_u128) +- [Function `skip_u64`](#0x1_BCS_skip_u64) +- [Function `skip_u32`](#0x1_BCS_skip_u32) +- [Function `skip_u16`](#0x1_BCS_skip_u16) +- [Function `skip_address`](#0x1_BCS_skip_address) +- [Function `skip_bool`](#0x1_BCS_skip_bool) +- [Function `can_skip`](#0x1_BCS_can_skip) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Option;
+
+ + + + + +## Constants + + + + + + +
const ERR_INPUT_NOT_LARGE_ENOUGH: u64 = 201;
+
+ + + + + + + +
const ERR_INVALID_ULEB128_NUMBER_UNEXPECTED_ZERO_DIGIT: u64 = 207;
+
+ + + + + + + +
const ERR_OVERFLOW_PARSING_ULEB128_ENCODED_UINT32: u64 = 206;
+
+ + + + + + + +
const ERR_UNEXPECTED_BOOL_VALUE: u64 = 205;
+
+ + + + + + + +
const INTEGER32_MAX_VALUE: u64 = 2147483647;
+
+ + + + + +## Function `to_bytes` + +Return the binary representation of v in BCS (Starcoin Canonical Serialization) format + + +
public fun to_bytes<MoveValue>(v: &MoveValue): vector<u8>
+
+ + + +
+Implementation + + +
native public fun to_bytes<MoveValue>(v: &MoveValue): vector<u8>;
+
+ + + +
+ + + +## Function `to_address` + +Return the address of key bytes + + +
public fun to_address(key_bytes: vector<u8>): address
+
+ + + +
+Implementation + + +
native public fun to_address(key_bytes: vector<u8>): address;
+
+ + + +
+ + + +## Function `deserialize_option_bytes_vector` + + + +
public fun deserialize_option_bytes_vector(input: &vector<u8>, offset: u64): (vector<Option::Option<vector<u8>>>, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_option_bytes_vector(input: &vector<u8>, offset: u64): (vector<Option::Option<vector<u8>>>, u64) {
+    let (len, new_offset) = deserialize_len(input, offset);
+    let i = 0;
+    let vec = Vector::empty<Option::Option<vector<u8>>>();
+    while (i < len) {
+        let (opt_bs, o) = deserialize_option_bytes(input, new_offset);
+        Vector::push_back(&mut vec, opt_bs);
+        new_offset = o;
+        i = i + 1;
+    };
+    (vec, new_offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_bytes_vector` + + + +
public fun deserialize_bytes_vector(input: &vector<u8>, offset: u64): (vector<vector<u8>>, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_bytes_vector(input: &vector<u8>, offset: u64): (vector<vector<u8>>, u64) {
+    let (len, new_offset) = deserialize_len(input, offset);
+    let i = 0;
+    let vec = Vector::empty<vector<u8>>();
+    while (i < len) {
+        let (opt_bs, o) = deserialize_bytes(input, new_offset);
+        Vector::push_back(&mut vec, opt_bs);
+        new_offset = o;
+        i = i + 1;
+    };
+    (vec, new_offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_u64_vector` + + + +
public fun deserialize_u64_vector(input: &vector<u8>, offset: u64): (vector<u64>, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_u64_vector(input: &vector<u8>, offset: u64): (vector<u64>, u64) {
+    let (len, new_offset) = deserialize_len(input, offset);
+    let i = 0;
+    let vec = Vector::empty<u64>();
+    while (i < len) {
+        let (opt_bs, o) = deserialize_u64(input, new_offset);
+        Vector::push_back(&mut vec, opt_bs);
+        new_offset = o;
+        i = i + 1;
+    };
+    (vec, new_offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_u128_vector` + + + +
public fun deserialize_u128_vector(input: &vector<u8>, offset: u64): (vector<u128>, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_u128_vector(input: &vector<u8>, offset: u64): (vector<u128>, u64) {
+    let (len, new_offset) = deserialize_len(input, offset);
+    let i = 0;
+    let vec = Vector::empty<u128>();
+    while (i < len) {
+        let (opt_bs, o) = deserialize_u128(input, new_offset);
+        Vector::push_back(&mut vec, opt_bs);
+        new_offset = o;
+        i = i + 1;
+    };
+    (vec, new_offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_option_bytes` + + + +
public fun deserialize_option_bytes(input: &vector<u8>, offset: u64): (Option::Option<vector<u8>>, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_option_bytes(input: &vector<u8>, offset: u64): (Option::Option<vector<u8>>, u64) {
+    let (tag, new_offset) = deserialize_option_tag(input, offset);
+    if (!tag) {
+        return (Option::none<vector<u8>>(), new_offset)
+    } else {
+        let (bs, new_offset) = deserialize_bytes(input, new_offset);
+        return (Option::some<vector<u8>>(bs), new_offset)
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_address` + + + +
public fun deserialize_address(input: &vector<u8>, offset: u64): (address, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_address(input: &vector<u8>, offset: u64): (address, u64) {
+    let (content, new_offset) = deserialize_16_bytes(input, offset);
+    (BCS::to_address(content), new_offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_16_bytes` + + + +
public fun deserialize_16_bytes(input: &vector<u8>, offset: u64): (vector<u8>, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_16_bytes(input: &vector<u8>, offset: u64): (vector<u8>, u64) {
+    let content = get_n_bytes(input, offset, 16);
+    (content, offset + 16)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_bytes` + + + +
public fun deserialize_bytes(input: &vector<u8>, offset: u64): (vector<u8>, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_bytes(input: &vector<u8>, offset: u64): (vector<u8>, u64) {
+    let (len, new_offset) = deserialize_len(input, offset);
+    let content = get_n_bytes(input, new_offset, len);
+    (content, new_offset + len)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_u128` + + + +
public fun deserialize_u128(input: &vector<u8>, offset: u64): (u128, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_u128(input: &vector<u8>, offset: u64): (u128, u64) {
+    let u = get_n_bytes_as_u128(input, offset, 16);
+    (u, offset + 16)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_u64` + + + +
public fun deserialize_u64(input: &vector<u8>, offset: u64): (u64, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_u64(input: &vector<u8>, offset: u64): (u64, u64) {
+    let u = get_n_bytes_as_u128(input, offset, 8);
+    ((u as u64), offset + 8)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_u32` + + + +
public fun deserialize_u32(input: &vector<u8>, offset: u64): (u64, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_u32(input: &vector<u8>, offset: u64): (u64, u64) {
+    let u = get_n_bytes_as_u128(input, offset, 4);
+    ((u as u64), offset + 4)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_u16` + + + +
public fun deserialize_u16(input: &vector<u8>, offset: u64): (u64, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_u16(input: &vector<u8>, offset: u64): (u64, u64) {
+    let u = get_n_bytes_as_u128(input, offset, 2);
+    ((u as u64), offset + 2)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_u8` + + + +
public fun deserialize_u8(input: &vector<u8>, offset: u64): (u8, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_u8(input: &vector<u8>, offset: u64): (u8, u64) {
+    let u = get_byte(input, offset);
+    (u, offset + 1)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_option_tag` + + + +
public fun deserialize_option_tag(input: &vector<u8>, offset: u64): (bool, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_option_tag(input: &vector<u8>, offset: u64): (bool, u64) {
+    deserialize_bool(input, offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_len` + + + +
public fun deserialize_len(input: &vector<u8>, offset: u64): (u64, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_len(input: &vector<u8>, offset: u64): (u64, u64) {
+    deserialize_uleb128_as_u32(input, offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_bool` + + + +
public fun deserialize_bool(input: &vector<u8>, offset: u64): (bool, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_bool(input: &vector<u8>, offset: u64): (bool, u64) {
+    let b = get_byte(input, offset);
+    if (b == 1) {
+        return (true, offset + 1)
+    } else if (b == 0) {
+        return (false, offset + 1)
+    } else {
+        abort ERR_UNEXPECTED_BOOL_VALUE
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `get_byte` + + + +
fun get_byte(input: &vector<u8>, offset: u64): u8
+
+ + + +
+Implementation + + +
fun get_byte(input: &vector<u8>, offset: u64): u8 {
+    assert!(((offset + 1) <= Vector::length(input)) && (offset < offset + 1), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH));
+    *Vector::borrow(input, offset)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `get_n_bytes` + + + +
fun get_n_bytes(input: &vector<u8>, offset: u64, n: u64): vector<u8>
+
+ + + +
+Implementation + + +
fun get_n_bytes(input: &vector<u8>, offset: u64, n: u64): vector<u8> {
+    assert!(((offset + n) <= Vector::length(input)) && (offset < offset + n), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH));
+    let i = 0;
+    let content = Vector::empty<u8>();
+    while (i < n) {
+        let b = *Vector::borrow(input, offset + i);
+        Vector::push_back(&mut content, b);
+        i = i + 1;
+    };
+    content
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `get_n_bytes_as_u128` + + + +
fun get_n_bytes_as_u128(input: &vector<u8>, offset: u64, n: u64): u128
+
+ + + +
+Implementation + + +
fun get_n_bytes_as_u128(input: &vector<u8>, offset: u64, n: u64): u128 {
+    assert!(((offset + n) <= Vector::length(input)) && (offset < offset + n), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH));
+    let number: u128 = 0;
+    let i = 0;
+    while (i < n) {
+        let byte = *Vector::borrow(input, offset + i);
+        let s = (i as u8) * 8;
+        number = number + ((byte as u128) << s);
+        i = i + 1;
+    };
+    number
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `deserialize_uleb128_as_u32` + + + +
public fun deserialize_uleb128_as_u32(input: &vector<u8>, offset: u64): (u64, u64)
+
+ + + +
+Implementation + + +
public fun deserialize_uleb128_as_u32(input: &vector<u8>, offset: u64): (u64, u64) {
+    let value: u64 = 0;
+    let shift = 0;
+    let new_offset = offset;
+    while (shift < 32) {
+        let x = get_byte(input, new_offset);
+        new_offset = new_offset + 1;
+        let digit: u8 = x & 0x7F;
+        value = value | (digit as u64) << shift;
+        if ((value < 0) || (value > INTEGER32_MAX_VALUE)) {
+            abort ERR_OVERFLOW_PARSING_ULEB128_ENCODED_UINT32
+        };
+        if (digit == x) {
+            if (shift > 0 && digit == 0) {
+                abort ERR_INVALID_ULEB128_NUMBER_UNEXPECTED_ZERO_DIGIT
+            };
+            return (value, new_offset)
+        };
+        shift = shift + 7
+    };
+    abort ERR_OVERFLOW_PARSING_ULEB128_ENCODED_UINT32
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+pragma verify = false;
+
+ + + +
+ + + +## Function `serialize_u32_as_uleb128` + + + +
fun serialize_u32_as_uleb128(value: u64): vector<u8>
+
+ + + +
+Implementation + + +
fun serialize_u32_as_uleb128(value: u64): vector<u8> {
+    let output = Vector::empty<u8>();
+    while ((value >> 7) != 0) {
+        Vector::push_back(&mut output, (((value & 0x7f) | 0x80) as u8));
+        value = value >> 7;
+    };
+    Vector::push_back(&mut output, (value as u8));
+    output
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_option_bytes_vector` + + + +
public fun skip_option_bytes_vector(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_option_bytes_vector(input: &vector<u8>, offset: u64): u64 {
+    let (len, new_offset) = deserialize_len(input, offset);
+    let i = 0;
+    while (i < len) {
+        new_offset = skip_option_bytes(input, new_offset);
+        i = i + 1;
+    };
+    new_offset
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_option_bytes` + + + +
public fun skip_option_bytes(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_option_bytes(input: &vector<u8>, offset: u64):  u64 {
+    let (tag, new_offset) = deserialize_option_tag(input, offset);
+    if (!tag) {
+        new_offset
+    } else {
+        skip_bytes(input, new_offset)
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_bytes_vector` + + + +
public fun skip_bytes_vector(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_bytes_vector(input: &vector<u8>, offset: u64): u64 {
+    let (len, new_offset) = deserialize_len(input, offset);
+    let i = 0;
+    while (i < len) {
+        new_offset = skip_bytes(input, new_offset);
+        i = i + 1;
+    };
+    new_offset
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_bytes` + + + +
public fun skip_bytes(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_bytes(input: &vector<u8>, offset: u64): u64 {
+    let (len, new_offset) = deserialize_len(input, offset);
+    new_offset + len
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_n_bytes` + + + +
public fun skip_n_bytes(input: &vector<u8>, offset: u64, n: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_n_bytes(input: &vector<u8>, offset: u64, n:u64): u64 {
+    can_skip(input, offset, n );
+    offset + n
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_u64_vector` + + + +
public fun skip_u64_vector(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_u64_vector(input: &vector<u8>, offset: u64): u64 {
+    let (len, new_offset) = deserialize_len(input, offset);
+    can_skip(input, new_offset, len * 8);
+    new_offset + len * 8
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_u128_vector` + + + +
public fun skip_u128_vector(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_u128_vector(input: &vector<u8>, offset: u64): u64 {
+    let (len, new_offset) = deserialize_len(input, offset);
+    can_skip(input, new_offset, len * 16);
+    new_offset + len * 16
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_u256` + + + +
public fun skip_u256(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_u256(input: &vector<u8>, offset: u64): u64 {
+    can_skip(input, offset, 32 );
+    offset + 32
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_u128` + + + +
public fun skip_u128(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_u128(input: &vector<u8>, offset: u64): u64 {
+    can_skip(input, offset, 16 );
+    offset + 16
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_u64` + + + +
public fun skip_u64(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_u64(input: &vector<u8>, offset: u64): u64 {
+    can_skip(input, offset, 8 );
+    offset + 8
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_u32` + + + +
public fun skip_u32(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_u32(input: &vector<u8>, offset: u64): u64 {
+    can_skip(input, offset, 4 );
+    offset + 4
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_u16` + + + +
public fun skip_u16(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_u16(input: &vector<u8>, offset: u64): u64 {
+    can_skip(input, offset, 2 );
+    offset + 2
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_address` + + + +
public fun skip_address(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_address(input: &vector<u8>, offset: u64): u64 {
+    skip_n_bytes(input, offset, 16)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `skip_bool` + + + +
public fun skip_bool(input: &vector<u8>, offset: u64): u64
+
+ + + +
+Implementation + + +
public fun skip_bool(input: &vector<u8>, offset: u64):  u64{
+    can_skip(input, offset, 1);
+    offset + 1
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `can_skip` + + + +
fun can_skip(input: &vector<u8>, offset: u64, n: u64)
+
+ + + +
+Implementation + + +
fun can_skip(input: &vector<u8>, offset: u64, n: u64){
+    assert!(((offset + n) <= Vector::length(input)) && (offset < offset + n), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH));
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
+ + + + + + + +
native fun serialize<MoveValue>(v: &MoveValue): vector<u8>;
+
diff --git a/release/v13/docs/Bitwise.md b/release/v13/docs/Bitwise.md new file mode 100644 index 00000000..74d8a7ca --- /dev/null +++ b/release/v13/docs/Bitwise.md @@ -0,0 +1,179 @@ + + + +# Module `0x1::BitOperators` + +Functions for bit operations. + + +- [Function `and`](#0x1_BitOperators_and) +- [Function `or`](#0x1_BitOperators_or) +- [Function `xor`](#0x1_BitOperators_xor) +- [Function `not`](#0x1_BitOperators_not) +- [Function `lshift`](#0x1_BitOperators_lshift) +- [Function `rshift`](#0x1_BitOperators_rshift) +- [Module Specification](#@Module_Specification_0) + + +
+ + + + + +## Function `and` + +bit and: x & y + + +
public fun and(x: u64, y: u64): u64
+
+ + + +
+Implementation + + +
public fun and(x: u64, y: u64): u64 {
+    (x & y as u64)
+}
+
+ + + +
+ + + +## Function `or` + +bit or: x | y + + +
public fun or(x: u64, y: u64): u64
+
+ + + +
+Implementation + + +
public fun or(x: u64, y: u64): u64 {
+    (x | y as u64)
+}
+
+ + + +
+ + + +## Function `xor` + +bit xor: x ^ y + + +
public fun xor(x: u64, y: u64): u64
+
+ + + +
+Implementation + + +
public fun xor(x: u64, y: u64): u64 {
+    (x ^ y as u64)
+}
+
+ + + +
+ + + +## Function `not` + +bit not: !x + + +
public fun not(x: u64): u64
+
+ + + +
+Implementation + + +
public fun not(x: u64): u64 {
+   (x ^ 18446744073709551615u64 as u64)
+}
+
+ + + +
+ + + +## Function `lshift` + +left shift n bits. + + +
public fun lshift(x: u64, n: u8): u64
+
+ + + +
+Implementation + + +
public fun lshift(x: u64, n: u8): u64 {
+    (x << n  as u64)
+}
+
+ + + +
+ + + +## Function `rshift` + +right shift n bits. + + +
public fun rshift(x: u64, n: u8): u64
+
+ + + +
+Implementation + + +
public fun rshift(x: u64, n: u8): u64 {
+    (x >> n  as u64)
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+
diff --git a/release/v13/docs/Block.md b/release/v13/docs/Block.md new file mode 100644 index 00000000..4d2729e5 --- /dev/null +++ b/release/v13/docs/Block.md @@ -0,0 +1,1197 @@ + + + +# Module `0x1::Block` + +Block module provide metadata for generated blocks. + + +- [Resource `BlockMetadata`](#0x1_Block_BlockMetadata) +- [Struct `NewBlockEvent`](#0x1_Block_NewBlockEvent) +- [Resource `BlockMetadataV2`](#0x1_Block_BlockMetadataV2) +- [Struct `NewBlockEventV2`](#0x1_Block_NewBlockEventV2) +- [Struct `Checkpoint`](#0x1_Block_Checkpoint) +- [Resource `Checkpoints`](#0x1_Block_Checkpoints) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_Block_initialize) +- [Function `initialize_blockmetadata_v2`](#0x1_Block_initialize_blockmetadata_v2) +- [Function `get_current_block_number`](#0x1_Block_get_current_block_number) +- [Function `get_parent_hash`](#0x1_Block_get_parent_hash) +- [Function `get_current_author`](#0x1_Block_get_current_author) +- [Function `get_parents_hash`](#0x1_Block_get_parents_hash) +- [Function `process_block_metadata`](#0x1_Block_process_block_metadata) +- [Function `process_block_metadata_v2`](#0x1_Block_process_block_metadata_v2) +- [Function `checkpoints_init`](#0x1_Block_checkpoints_init) +- [Function `checkpoint_entry`](#0x1_Block_checkpoint_entry) +- [Function `checkpoint`](#0x1_Block_checkpoint) +- [Function `base_checkpoint`](#0x1_Block_base_checkpoint) +- [Function `latest_state_root`](#0x1_Block_latest_state_root) +- [Function `base_latest_state_root`](#0x1_Block_base_latest_state_root) +- [Function `update_state_root_entry`](#0x1_Block_update_state_root_entry) +- [Function `update_state_root`](#0x1_Block_update_state_root) +- [Function `base_update_state_root`](#0x1_Block_base_update_state_root) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::BCS;
+use 0x1::CoreAddresses;
+use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Hash;
+use 0x1::Option;
+use 0x1::Ring;
+use 0x1::Timestamp;
+use 0x1::Vector;
+
+ + + + + +## Resource `BlockMetadata` + +Block metadata struct. + + +
struct BlockMetadata has key
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ number of the current block +
+
+parent_hash: vector<u8> +
+
+ Hash of the parent block. +
+
+author: address +
+
+ Author of the current block. +
+
+uncles: u64 +
+
+ number of uncles. +
+
+new_block_events: Event::EventHandle<Block::NewBlockEvent> +
+
+ Handle of events when new blocks are emitted +
+
+ + +
+ + + +## Struct `NewBlockEvent` + +Events emitted when new block generated. + + +
struct NewBlockEvent has drop, store
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ +
+
+author: address +
+
+ +
+
+timestamp: u64 +
+
+ +
+
+uncles: u64 +
+
+ +
+
+ + +
+ + + +## Resource `BlockMetadataV2` + +Block metadata struct. + + +
struct BlockMetadataV2 has key
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ number of the current block +
+
+parent_hash: vector<u8> +
+
+ Hash of the parent block. +
+
+author: address +
+
+ Author of the current block. +
+
+uncles: u64 +
+
+ number of uncles. +
+
+parents_hash: vector<u8> +
+
+ An Array of the parents hash for a Dag block. +
+
+new_block_events: Event::EventHandle<Block::NewBlockEventV2> +
+
+ Handle of events when new blocks are emitted +
+
+ + +
+ + + +## Struct `NewBlockEventV2` + +Events emitted when new block generated. + + +
struct NewBlockEventV2 has drop, store
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ +
+
+author: address +
+
+ +
+
+timestamp: u64 +
+
+ +
+
+uncles: u64 +
+
+ +
+
+parents_hash: vector<u8> +
+
+ +
+
+ + +
+ + + +## Struct `Checkpoint` + + + +
struct Checkpoint has copy, drop, store
+
+ + + +
+Fields + + +
+
+block_number: u64 +
+
+ +
+
+block_hash: vector<u8> +
+
+ +
+
+state_root: Option::Option<vector<u8>> +
+
+ +
+
+ + +
+ + + +## Resource `Checkpoints` + + + +
struct Checkpoints has store, key
+
+ + + +
+Fields + + +
+
+checkpoints: Ring::Ring<Block::Checkpoint> +
+
+ +
+
+index: u64 +
+
+ +
+
+last_number: u64 +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const BLOCK_HEADER_LENGTH: u64 = 247;
+
+ + + + + + + +
const BLOCK_INTERVAL_NUMBER: u64 = 5;
+
+ + + + + + + +
const CHECKPOINT_LENGTH: u64 = 60;
+
+ + + + + + + +
const EBLOCK_NUMBER_MISMATCH: u64 = 17;
+
+ + + + + + + +
const ERROR_INTERVAL_TOO_LITTLE: u64 = 20;
+
+ + + + + + + +
const ERROR_NOT_BLOCK_HEADER: u64 = 19;
+
+ + + + + + + +
const ERROR_NO_HAVE_CHECKPOINT: u64 = 18;
+
+ + + + + +## Function `initialize` + +This can only be invoked by the GENESIS_ACCOUNT at genesis + + +
public fun initialize(account: &signer, parent_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, parent_hash: vector<u8>) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+
+    move_to<BlockMetadata>(
+        account,
+        BlockMetadata {
+            number: 0,
+            parent_hash,
+            author: CoreAddresses::GENESIS_ADDRESS(),
+            uncles: 0,
+            new_block_events: Event::new_event_handle<Self::NewBlockEvent>(account),
+        });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<BlockMetadata>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `initialize_blockmetadata_v2` + + + +
public fun initialize_blockmetadata_v2(account: &signer)
+
+ + + +
+Implementation + + +
public fun initialize_blockmetadata_v2(account: &signer) acquires BlockMetadata {
+    CoreAddresses::assert_genesis_address(account);
+
+    let block_meta_ref = borrow_global<BlockMetadata>(CoreAddresses::GENESIS_ADDRESS());
+
+    // create new resource base on current block metadata
+    move_to<BlockMetadataV2>(
+        account,
+        BlockMetadataV2 {
+            number: block_meta_ref.number,
+            parent_hash: block_meta_ref.parent_hash,
+            author: block_meta_ref.author,
+            uncles: block_meta_ref.uncles,
+            parents_hash: Vector::empty(),
+            new_block_events: Event::new_event_handle<Self::NewBlockEventV2>(account),
+        });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<BlockMetadata>(Signer::address_of(account));
+ensures exists<BlockMetadataV2>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `get_current_block_number` + +Get the current block number + + +
public fun get_current_block_number(): u64
+
+ + + +
+Implementation + + +
public fun get_current_block_number(): u64 acquires BlockMetadataV2 {
+    borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).number
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `get_parent_hash` + +Get the hash of the parent block. + + +
public fun get_parent_hash(): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_parent_hash(): vector<u8> acquires BlockMetadataV2 {
+    *&borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).parent_hash
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `get_current_author` + +Gets the address of the author of the current block + + +
public fun get_current_author(): address
+
+ + + +
+Implementation + + +
public fun get_current_author(): address acquires BlockMetadataV2 {
+    borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).author
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `get_parents_hash` + + + +
public fun get_parents_hash(): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_parents_hash(): vector<u8> acquires BlockMetadataV2 {
+    *&borrow_global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).parents_hash
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `process_block_metadata` + +Call at block prologue + + +
public fun process_block_metadata(account: &signer, parent_hash: vector<u8>, author: address, timestamp: u64, uncles: u64, number: u64)
+
+ + + +
+Implementation + + +
public fun process_block_metadata(account: &signer,
+                                  parent_hash: vector<u8>,
+                                  author: address,
+                                  timestamp: u64,
+                                  uncles:u64,
+                                  number:u64) acquires BlockMetadataV2{
+    Self::process_block_metadata_v2(account, parent_hash, author, timestamp, uncles, number, Vector::empty<u8>())
+
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if number != global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).number + 1;
+
+ + + +
+ + + +## Function `process_block_metadata_v2` + +Call at block prologue for flexidag + + +
public fun process_block_metadata_v2(account: &signer, parent_hash: vector<u8>, author: address, timestamp: u64, uncles: u64, number: u64, parents_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun process_block_metadata_v2(account: &signer,
+                                     parent_hash: vector<u8>,
+                                     author: address,
+                                     timestamp: u64,
+                                     uncles:u64,
+                                     number:u64,
+                                     parents_hash: vector<u8>) acquires BlockMetadataV2 {
+    CoreAddresses::assert_genesis_address(account);
+
+    let block_metadata_ref = borrow_global_mut<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+    assert!(number == (block_metadata_ref.number + 1), Errors::invalid_argument(EBLOCK_NUMBER_MISMATCH));
+    block_metadata_ref.number = number;
+    block_metadata_ref.author= author;
+    block_metadata_ref.parent_hash = parent_hash;
+    block_metadata_ref.uncles = uncles;
+    block_metadata_ref.parents_hash = parents_hash;
+
+    Event::emit_event<NewBlockEventV2>(
+      &mut block_metadata_ref.new_block_events,
+      NewBlockEventV2 {
+          number,
+          author,
+          timestamp,
+          uncles,
+          parents_hash,
+      }
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if number != global<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS()).number + 1;
+
+ + + + + + + +
schema AbortsIfBlockMetadataNotExist {
+    aborts_if !exists<BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+}
+
+ + + +
+ + + +## Function `checkpoints_init` + + + +
public fun checkpoints_init(account: &signer)
+
+ + + +
+Implementation + + +
public fun checkpoints_init(account: &signer){
+    CoreAddresses::assert_genesis_address(account);
+
+    let checkpoints = Ring::create_with_capacity<Checkpoint>(CHECKPOINT_LENGTH);
+    move_to<Checkpoints>(
+        account,
+        Checkpoints {
+           checkpoints,
+           index        : 0,
+           last_number  : 0,
+    });
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `checkpoint_entry` + + + +
public entry fun checkpoint_entry(_account: signer)
+
+ + + +
+Implementation + + +
public entry fun checkpoint_entry(_account: signer) acquires BlockMetadataV2, Checkpoints {
+    checkpoint();
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `checkpoint` + + + +
public fun checkpoint()
+
+ + + +
+Implementation + + +
public fun checkpoint() acquires BlockMetadataV2, Checkpoints{
+    let parent_block_number = get_current_block_number() - 1;
+    let parent_block_hash   = get_parent_hash();
+
+    let checkpoints = borrow_global_mut<Checkpoints>(CoreAddresses::GENESIS_ADDRESS());
+    base_checkpoint(checkpoints, parent_block_number, parent_block_hash);
+
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `base_checkpoint` + + + +
fun base_checkpoint(checkpoints: &mut Block::Checkpoints, parent_block_number: u64, parent_block_hash: vector<u8>)
+
+ + + +
+Implementation + + +
fun base_checkpoint(checkpoints: &mut Checkpoints, parent_block_number: u64, parent_block_hash:vector<u8>){
+    assert!(checkpoints.last_number + BLOCK_INTERVAL_NUMBER <= parent_block_number || checkpoints.last_number == 0, Errors::invalid_argument(ERROR_INTERVAL_TOO_LITTLE));
+
+    checkpoints.index = checkpoints.index + 1;
+    checkpoints.last_number = parent_block_number;
+    let op_checkpoint = Ring::push<Checkpoint>(&mut checkpoints.checkpoints, Checkpoint {
+                                                            block_number: parent_block_number,
+                                                            block_hash: parent_block_hash,
+                                                            state_root: Option::none<vector<u8>>(),
+                                                        } );
+    if(Option::is_some(&op_checkpoint)){
+        Option::destroy_some(op_checkpoint);
+    }else{
+        Option::destroy_none(op_checkpoint);
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `latest_state_root` + + + +
public fun latest_state_root(): (u64, vector<u8>)
+
+ + + +
+Implementation + + +
public fun latest_state_root():(u64,vector<u8>) acquires  Checkpoints{
+    let checkpoints = borrow_global<Checkpoints>(CoreAddresses::GENESIS_ADDRESS());
+    base_latest_state_root(checkpoints)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `base_latest_state_root` + + + +
fun base_latest_state_root(checkpoints: &Block::Checkpoints): (u64, vector<u8>)
+
+ + + +
+Implementation + + +
fun base_latest_state_root(checkpoints: &Checkpoints):(u64,vector<u8>){
+    let len = Ring::capacity<Checkpoint>(&checkpoints.checkpoints);
+    let j = if(checkpoints.index < len - 1){
+        checkpoints.index
+    }else{
+        len
+    };
+    let i = checkpoints.index;
+    while( j > 0){
+        let op_checkpoint = Ring::borrow(&checkpoints.checkpoints, i - 1 );
+        if( Option::is_some(op_checkpoint) && Option::is_some(&Option::borrow(op_checkpoint).state_root) ) {
+            let state_root = Option::borrow(&Option::borrow(op_checkpoint).state_root);
+            return (Option::borrow(op_checkpoint).block_number, *state_root)
+        };
+        j = j - 1;
+        i = i - 1;
+    };
+
+    abort Errors::invalid_state(ERROR_NO_HAVE_CHECKPOINT)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `update_state_root_entry` + + + +
public entry fun update_state_root_entry(_account: signer, header: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun update_state_root_entry(_account: signer , header: vector<u8>)
+acquires Checkpoints {
+    update_state_root(header);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `update_state_root` + + + +
public fun update_state_root(header: vector<u8>)
+
+ + + +
+Implementation + + +
public fun update_state_root(header: vector<u8>) acquires  Checkpoints {
+    let checkpoints = borrow_global_mut<Checkpoints>(CoreAddresses::GENESIS_ADDRESS());
+    base_update_state_root(checkpoints, header);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `base_update_state_root` + + + +
fun base_update_state_root(checkpoints: &mut Block::Checkpoints, header: vector<u8>)
+
+ + + +
+Implementation + + +
fun base_update_state_root(checkpoints: &mut Checkpoints, header: vector<u8>){
+    let prefix = Hash::sha3_256(b"STARCOIN::BlockHeader");
+
+    //parent_hash
+    let new_offset = BCS::skip_bytes(&header,0);
+    //timestamp
+    let new_offset = BCS::skip_u64(&header,new_offset);
+    //number
+    let (number,new_offset) = BCS::deserialize_u64(&header,new_offset);
+    //author
+    new_offset = BCS::skip_address(&header,new_offset);
+    //author_auth_key
+    new_offset = BCS::skip_option_bytes(&header,new_offset);
+    //txn_accumulator_root
+    new_offset = BCS::skip_bytes(&header,new_offset);
+    //block_accumulator_root
+    new_offset = BCS::skip_bytes(&header,new_offset);
+    //state_root
+    let (state_root,_new_offset) = BCS::deserialize_bytes(&header,new_offset);
+
+    Vector::append(&mut prefix,header);
+    let block_hash = Hash::sha3_256(prefix);
+
+    let len = Ring::capacity<Checkpoint>(&checkpoints.checkpoints);
+    let j = if(checkpoints.index < len - 1){
+        checkpoints.index
+    }else{
+        len
+    };
+    let i = checkpoints.index;
+    while( j > 0){
+        let op_checkpoint = Ring::borrow_mut(&mut checkpoints.checkpoints, i - 1);
+
+        if( Option::is_some(op_checkpoint) && &Option::borrow(op_checkpoint).block_hash == &block_hash && Option::borrow<Checkpoint>(op_checkpoint).block_number == number) {
+
+            let op_state_root = &mut Option::borrow_mut<Checkpoint>(op_checkpoint).state_root;
+            if(Option::is_some(op_state_root)){
+                Option::swap(op_state_root, state_root);
+            }else{
+                Option::fill(op_state_root, state_root);
+            };
+            return
+        };
+        j = j - 1;
+        i = i - 1;
+    };
+
+    abort Errors::invalid_state(ERROR_NO_HAVE_CHECKPOINT)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/BlockReward.md b/release/v13/docs/BlockReward.md new file mode 100644 index 00000000..213d255d --- /dev/null +++ b/release/v13/docs/BlockReward.md @@ -0,0 +1,397 @@ + + + +# Module `0x1::BlockReward` + +The module provide block rewarding calculation logic. + + +- [Resource `RewardQueue`](#0x1_BlockReward_RewardQueue) +- [Struct `RewardInfo`](#0x1_BlockReward_RewardInfo) +- [Struct `BlockRewardEvent`](#0x1_BlockReward_BlockRewardEvent) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_BlockReward_initialize) +- [Function `process_block_reward`](#0x1_BlockReward_process_block_reward) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Account;
+use 0x1::CoreAddresses;
+use 0x1::Errors;
+use 0x1::Event;
+use 0x1::RewardConfig;
+use 0x1::STC;
+use 0x1::Timestamp;
+use 0x1::Token;
+use 0x1::Treasury;
+use 0x1::TreasuryWithdrawDaoProposal;
+use 0x1::Vector;
+
+ + + + + +## Resource `RewardQueue` + +Queue of rewards distributed to miners. + + +
struct RewardQueue has key
+
+ + + +
+Fields + + +
+
+reward_number: u64 +
+
+ How many block rewards has been handled. +
+
+infos: vector<BlockReward::RewardInfo> +
+
+ informations about the reward distribution. +
+
+reward_events: Event::EventHandle<BlockReward::BlockRewardEvent> +
+
+ event handle used to emit block reward event. +
+
+ + +
+ + + +## Struct `RewardInfo` + +Reward info of miners. + + +
struct RewardInfo has store
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ number of the block miner minted. +
+
+reward: u128 +
+
+ how many stc rewards. +
+
+miner: address +
+
+ miner who mint the block. +
+
+gas_fees: Token::Token<STC::STC> +
+
+ store the gas fee that users consumed. +
+
+ + +
+ + + +## Struct `BlockRewardEvent` + +block reward event + + +
struct BlockRewardEvent has drop, store
+
+ + + +
+Fields + + +
+
+block_number: u64 +
+
+ block number +
+
+block_reward: u128 +
+
+ STC reward. +
+
+gas_fees: u128 +
+
+ gas fees in STC. +
+
+miner: address +
+
+ block miner +
+
+ + +
+ + + +## Constants + + + + + + +
const EAUTHOR_ADDRESS_AND_AUTH_KEY_MISMATCH: u64 = 105;
+
+ + + + + + + +
const EAUTHOR_AUTH_KEY_IS_EMPTY: u64 = 101;
+
+ + + + + + + +
const ECURRENT_NUMBER_IS_WRONG: u64 = 102;
+
+ + + + + + + +
const EMINER_EXIST: u64 = 104;
+
+ + + + + + + +
const EREWARD_NUMBER_IS_WRONG: u64 = 103;
+
+ + + + + +## Function `initialize` + +Initialize the module, should be called in genesis. + + +
public fun initialize(account: &signer, reward_delay: u64)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, reward_delay: u64) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+
+    RewardConfig::initialize(account, reward_delay);
+    move_to<RewardQueue>(account, RewardQueue {
+        reward_number: 0,
+        infos: Vector::empty(),
+        reward_events: Event::new_event_handle<Self::BlockRewardEvent>(account),
+    });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+include Config::PublishNewConfigAbortsIf<RewardConfig::RewardConfig>;
+include Config::PublishNewConfigEnsures<RewardConfig::RewardConfig>;
+aborts_if exists<RewardQueue>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<RewardQueue>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `process_block_reward` + +Process the given block rewards. + + +
public fun process_block_reward(account: &signer, current_number: u64, current_reward: u128, current_author: address, _auth_key_vec: vector<u8>, previous_block_gas_fees: Token::Token<STC::STC>)
+
+ + + +
+Implementation + + +
public fun process_block_reward(account: &signer, current_number: u64, current_reward: u128,
+                                current_author: address, _auth_key_vec: vector<u8>,
+                                previous_block_gas_fees: Token::Token<STC>) acquires RewardQueue {
+    CoreAddresses::assert_genesis_address(account);
+    if (current_number == 0) {
+        Token::destroy_zero(previous_block_gas_fees);
+        return
+    };
+
+    let rewards = borrow_global_mut<RewardQueue>(CoreAddresses::GENESIS_ADDRESS());
+    let len = Vector::length(&rewards.infos);
+    assert!((current_number == (rewards.reward_number + len + 1)), Errors::invalid_argument(ECURRENT_NUMBER_IS_WRONG));
+
+    // distribute gas fee to last block reward info.
+    // if not last block reward info, the passed in gas fee must be zero.
+    if (len == 0) {
+        Token::destroy_zero(previous_block_gas_fees);
+    } else {
+        let reward_info = Vector::borrow_mut(&mut rewards.infos, len - 1);
+        assert!(current_number == reward_info.number + 1, Errors::invalid_argument(ECURRENT_NUMBER_IS_WRONG));
+        Token::deposit(&mut reward_info.gas_fees, previous_block_gas_fees);
+    };
+
+    let reward_delay = RewardConfig::reward_delay();
+    if (len >= reward_delay) {//pay and remove
+        let i = len;
+        while (i > 0 && i >= reward_delay) {
+            let RewardInfo { number: reward_block_number, reward: block_reward, gas_fees, miner } = Vector::remove(&mut rewards.infos, 0);
+
+            let gas_fee_value = Token::value(&gas_fees);
+            let total_reward = gas_fees;
+            // add block reward to total.
+            if (block_reward > 0) {
+                // if no STC in Treasury, BlockReward will been 0.
+                let treasury_balance = Treasury::balance<STC>();
+                if (treasury_balance < block_reward) {
+                    block_reward = treasury_balance;
+                };
+                if (block_reward > 0) {
+                    let reward = TreasuryWithdrawDaoProposal::withdraw_for_block_reward<STC>(account, block_reward);
+                    Token::deposit(&mut total_reward, reward);
+                };
+            };
+            // distribute total.
+            if (Token::value(&total_reward) > 0) {
+                Account::deposit<STC>(miner, total_reward);
+            } else {
+                Token::destroy_zero(total_reward);
+            };
+            // emit reward event.
+            Event::emit_event<BlockRewardEvent>(
+                &mut rewards.reward_events,
+                BlockRewardEvent {
+                    block_number: reward_block_number,
+                    block_reward: block_reward,
+                    gas_fees: gas_fee_value,
+                    miner,
+                }
+            );
+
+            rewards.reward_number = rewards.reward_number + 1;
+            i = i - 1;
+        }
+    };
+
+    if (!Account::exists_at(current_author)) {
+        Account::create_account_with_address<STC>(current_author);
+    };
+    let current_info = RewardInfo {
+        number: current_number,
+        reward: current_reward,
+        miner: current_author,
+        gas_fees: Token::zero<STC>(),
+    };
+    Vector::push_back(&mut rewards.infos, current_info);
+
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if current_number == 0 && Token::value(previous_block_gas_fees) != 0;
+aborts_if current_number > 0 && !exists<RewardQueue>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if current_number > 0 && (global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).reward_number + Vector::length(global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).infos) + 1) != current_number;
+aborts_if current_number > 0 && !exists<Config::Config<RewardConfig::RewardConfig>>(CoreAddresses::GENESIS_ADDRESS());
+let reward_info_length = Vector::length(global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).infos);
+aborts_if current_number > 0 && reward_info_length == 0 && Token::value(previous_block_gas_fees) != 0;
+aborts_if current_number > 0 && reward_info_length != 0 && Vector::borrow(global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).infos, reward_info_length - 1).number != current_number - 1;
+aborts_if current_number > 0 && Vector::length(global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).infos) >= global<Config::Config<RewardConfig::RewardConfig>>(CoreAddresses::GENESIS_ADDRESS()).payload.reward_delay
+&& (global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).reward_number + 1) != Vector::borrow(global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).infos, 0).number;
+aborts_if current_number > 0 && Vector::length(global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).infos) >= global<Config::Config<RewardConfig::RewardConfig>>(CoreAddresses::GENESIS_ADDRESS()).payload.reward_delay
+        && (global<RewardQueue>(CoreAddresses::GENESIS_ADDRESS()).reward_number + 1) > max_u64();
+aborts_if current_number > 0 && !Account::exists_at(current_author) ;
+pragma verify = false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/ChainId.md b/release/v13/docs/ChainId.md new file mode 100644 index 00000000..89ab9fd3 --- /dev/null +++ b/release/v13/docs/ChainId.md @@ -0,0 +1,425 @@ + + + +# Module `0x1::ChainId` + +The module provides chain id information. + + +- [Resource `ChainId`](#0x1_ChainId_ChainId) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_ChainId_initialize) +- [Function `get`](#0x1_ChainId_get) +- [Function `is_dev`](#0x1_ChainId_is_dev) +- [Function `is_test`](#0x1_ChainId_is_test) +- [Function `is_halley`](#0x1_ChainId_is_halley) +- [Function `is_proxima`](#0x1_ChainId_is_proxima) +- [Function `is_barnard`](#0x1_ChainId_is_barnard) +- [Function `is_main`](#0x1_ChainId_is_main) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::CoreAddresses;
+use 0x1::Timestamp;
+
+ + + + + +## Resource `ChainId` + +chain id data structure. + + +
struct ChainId has key
+
+ + + +
+Fields + + +
+
+id: u8 +
+
+ real id. +
+
+ + +
+ + + +## Constants + + + + + + +
const BARNARD_CHAIN_ID: u8 = 251;
+
+ + + + + + + +
const DEV_CHAIN_ID: u8 = 254;
+
+ + + + + + + +
const HALLEY_CHAIN_ID: u8 = 253;
+
+ + + + + + + +
const MAIN_CHAIN_ID: u8 = 1;
+
+ + + + + + + +
const PROXIMA_CHAIN_ID: u8 = 252;
+
+ + + + + + + +
const TEST_CHAIN_ID: u8 = 255;
+
+ + + + + +## Function `initialize` + +Publish the chain ID under the genesis account + + +
public fun initialize(account: &signer, id: u8)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, id: u8) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+    move_to(account, ChainId { id });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<ChainId>(Signer::address_of(account));
+ensures exists<ChainId>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `get` + +Return the chain ID of this chain + + +
public fun get(): u8
+
+ + + +
+Implementation + + +
public fun get(): u8 acquires ChainId {
+    borrow_global<ChainId>(CoreAddresses::GENESIS_ADDRESS()).id
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `is_dev` + + + +
public fun is_dev(): bool
+
+ + + +
+Implementation + + +
public fun is_dev(): bool acquires ChainId {
+    get() == DEV_CHAIN_ID
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `is_test` + + + +
public fun is_test(): bool
+
+ + + +
+Implementation + + +
public fun is_test(): bool acquires ChainId {
+    get() == TEST_CHAIN_ID
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `is_halley` + + + +
public fun is_halley(): bool
+
+ + + +
+Implementation + + +
public fun is_halley(): bool acquires ChainId {
+    get() == HALLEY_CHAIN_ID
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `is_proxima` + + + +
public fun is_proxima(): bool
+
+ + + +
+Implementation + + +
public fun is_proxima(): bool acquires ChainId {
+    get() == PROXIMA_CHAIN_ID
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `is_barnard` + + + +
public fun is_barnard(): bool
+
+ + + +
+Implementation + + +
public fun is_barnard(): bool acquires ChainId {
+    get() == BARNARD_CHAIN_ID
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `is_main` + + + +
public fun is_main(): bool
+
+ + + +
+Implementation + + +
public fun is_main(): bool acquires ChainId {
+    get() == MAIN_CHAIN_ID
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+ensures exists<ChainId>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Collection.md b/release/v13/docs/Collection.md new file mode 100644 index 00000000..6716cbb4 --- /dev/null +++ b/release/v13/docs/Collection.md @@ -0,0 +1,430 @@ + + + +# Module `0x1::Collection` + +Deprecated since @v3 please use Collection2 +Provide a account based vector for save resource. + + +- [Struct `Collection`](#0x1_Collection_Collection) +- [Resource `CollectionStore`](#0x1_Collection_CollectionStore) +- [Constants](#@Constants_0) +- [Function `borrow`](#0x1_Collection_borrow) +- [Function `pop_back`](#0x1_Collection_pop_back) +- [Function `exists_at`](#0x1_Collection_exists_at) +- [Function `put`](#0x1_Collection_put) +- [Function `take`](#0x1_Collection_take) +- [Function `borrow_collection`](#0x1_Collection_borrow_collection) +- [Function `return_collection`](#0x1_Collection_return_collection) +- [Function `destroy_empty`](#0x1_Collection_destroy_empty) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Option;
+use 0x1::Signer;
+use 0x1::Vector;
+
+ + + + + +## Struct `Collection` + +Collection in memory, can not drop & store. + + +
struct Collection<T>
+
+ + + +
+Fields + + +
+
+items: vector<T> +
+
+ +
+
+owner: address +
+
+ the owner of Collection. +
+
+ + +
+ + + +## Resource `CollectionStore` + +Collection in global store. + + +
struct CollectionStore<T: store> has key
+
+ + + +
+Fields + + +
+
+items: Option::Option<vector<T>> +
+
+ items in the CollectionStore. + use Option at here is for temporary take away from store to construct Collection. +
+
+ + +
+ + + +## Constants + + + + + + +
const EDEPRECATED_FUNCTION: u64 = 19;
+
+ + + + + + + +
const ECOLLECTION_NOT_EXIST: u64 = 101;
+
+ + + + + +The operator require the collection owner. + + +
const ECOLLECTION_NOT_OWNER: u64 = 102;
+
+ + + + + +## Function `borrow` + +Acquire an immutable reference to the ith element of the collection c. +Aborts if i is out of bounds. + + +
public fun borrow<T>(c: &Collection::Collection<T>, i: u64): &T
+
+ + + +
+Implementation + + +
public fun borrow<T>(c: &Collection<T>, i: u64): &T{
+    Vector::borrow(&c.items, i)
+}
+
+ + + +
+ + + +## Function `pop_back` + +Pop an element from the end of vector v. +Aborts if v is empty. + + +
public fun pop_back<T>(account: &signer, c: &mut Collection::Collection<T>): T
+
+ + + +
+Implementation + + +
public fun pop_back<T>(account: &signer, c: &mut Collection<T>): T {
+    assert!(Signer::address_of(account) == c.owner, Errors::requires_address(ECOLLECTION_NOT_OWNER));
+    Vector::pop_back<T>(&mut c.items)
+}
+
+ + + +
+ + + +## Function `exists_at` + +check the Collection exists in addr + + +
fun exists_at<T: store>(addr: address): bool
+
+ + + +
+Implementation + + +
fun exists_at<T: store>(addr: address): bool{
+    exists<CollectionStore<T>>(addr)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `put` + +Deprecated since @v3 +Put items to account's Collection last position. + + +
public fun put<T: store>(_account: &signer, _item: T)
+
+ + + +
+Implementation + + +
public fun put<T: store>(_account: &signer, _item: T) {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `take` + +Take last item from account's Collection of T. + + +
public fun take<T: store>(account: &signer): T
+
+ + + +
+Implementation + + +
public fun take<T: store>(account: &signer): T acquires CollectionStore{
+    let addr = Signer::address_of(account);
+    let c = borrow_collection<T>(addr);
+    let item = pop_back(account, &mut c);
+    return_collection(c);
+    item
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `borrow_collection` + +Borrow collection of T from addr + + +
public fun borrow_collection<T: store>(addr: address): Collection::Collection<T>
+
+ + + +
+Implementation + + +
public fun borrow_collection<T: store>(addr: address): Collection<T> acquires CollectionStore{
+    assert!(exists_at<T>(addr), Errors::invalid_state(ECOLLECTION_NOT_EXIST));
+    let c = borrow_global_mut<CollectionStore<T>>(addr);
+    let items = Option::extract(&mut c.items);
+    Collection{
+        items,
+        owner: addr
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `return_collection` + +Return the Collection of T + + +
public fun return_collection<T: store>(c: Collection::Collection<T>)
+
+ + + +
+Implementation + + +
public fun return_collection<T: store>(c: Collection<T>) acquires CollectionStore{
+    let Collection{ items, owner } = c;
+    if (Vector::is_empty(&items)) {
+        let c = move_from<CollectionStore<T>>(owner);
+        destroy_empty(c);
+        Vector::destroy_empty(items);
+    }else{
+        let c = borrow_global_mut<CollectionStore<T>>(owner);
+        Option::fill(&mut c.items, items);
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `destroy_empty` + + + +
fun destroy_empty<T: store>(c: Collection::CollectionStore<T>)
+
+ + + +
+Implementation + + +
fun destroy_empty<T: store>(c: CollectionStore<T>){
+    let CollectionStore{ items } = c;
+    Option::destroy_none(items);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = false;
+
diff --git a/release/v13/docs/Collection2.md b/release/v13/docs/Collection2.md new file mode 100644 index 00000000..42e6ff21 --- /dev/null +++ b/release/v13/docs/Collection2.md @@ -0,0 +1,878 @@ + + + +# Module `0x1::Collection2` + +Provide a account based vector for save resource item. +The resource in CollectionStore can borrowed by anyone, anyone can get immutable ref of item. +and the owner of Collection can allow others to add item to Collection or get mut ref from Collection.git + + +- [Struct `Collection`](#0x1_Collection2_Collection) +- [Resource `CollectionStore`](#0x1_Collection2_CollectionStore) +- [Constants](#@Constants_0) +- [Function `length`](#0x1_Collection2_length) +- [Function `borrow`](#0x1_Collection2_borrow) +- [Function `push_back`](#0x1_Collection2_push_back) +- [Function `borrow_mut`](#0x1_Collection2_borrow_mut) +- [Function `pop_back`](#0x1_Collection2_pop_back) +- [Function `remove`](#0x1_Collection2_remove) +- [Function `append`](#0x1_Collection2_append) +- [Function `append_all`](#0x1_Collection2_append_all) +- [Function `exists_at`](#0x1_Collection2_exists_at) +- [Function `is_accept`](#0x1_Collection2_is_accept) +- [Function `accept`](#0x1_Collection2_accept) +- [Function `put`](#0x1_Collection2_put) +- [Function `put_all`](#0x1_Collection2_put_all) +- [Function `take`](#0x1_Collection2_take) +- [Function `create_collection`](#0x1_Collection2_create_collection) +- [Function `length_of`](#0x1_Collection2_length_of) +- [Function `borrow_collection`](#0x1_Collection2_borrow_collection) +- [Function `return_collection`](#0x1_Collection2_return_collection) +- [Function `destroy_collection`](#0x1_Collection2_destroy_collection) +- [Function `destroy_empty`](#0x1_Collection2_destroy_empty) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Option;
+use 0x1::Signer;
+use 0x1::Vector;
+
+ + + + + +## Struct `Collection` + +Collection in memory, can not drop & store. + + +
struct Collection<T>
+
+ + + +
+Fields + + +
+
+items: vector<T> +
+
+ +
+
+owner: address +
+
+ +
+
+can_put: bool +
+
+ +
+
+can_mut: bool +
+
+ +
+
+can_take: bool +
+
+ +
+
+ + +
+ + + +## Resource `CollectionStore` + +Collection in global store. + + +
struct CollectionStore<T: store> has key
+
+ + + +
+Fields + + +
+
+items: Option::Option<vector<T>> +
+
+ items in the CollectionStore. + use Option at here is for temporary take away from store to construct Collection. +
+
+anyone_can_put: bool +
+
+ +
+
+anyone_can_mut: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + +The operator require the collection owner or collection set anyone_can_put to true. + + +
const ERR_COLLECTION_CAN_NOT_ADD: u64 = 102;
+
+ + + + + +The operator require the collection owner or collection set anyone_can_mut to true. + + +
const ERR_COLLECTION_CAN_NOT_MUT: u64 = 103;
+
+ + + + + +The operator require the collection owner + + +
const ERR_COLLECTION_CAN_NOT_TAKE: u64 = 104;
+
+ + + + + + + +
const ERR_COLLECTION_INVALID_BORROW_STATE: u64 = 105;
+
+ + + + + + + +
const ERR_COLLECTION_IS_NOT_EMPTY: u64 = 106;
+
+ + + + + + + +
const ERR_COLLECTION_NOT_EXIST: u64 = 101;
+
+ + + + + +## Function `length` + +Return the length of the collection. + + +
public fun length<T>(c: &Collection2::Collection<T>): u64
+
+ + + +
+Implementation + + +
public fun length<T>(c: &Collection<T>): u64{
+    Vector::length(&c.items)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `borrow` + +Acquire an immutable reference to the ith element of the collection c. +Aborts if i is out of bounds. + + +
public fun borrow<T>(c: &Collection2::Collection<T>, i: u64): &T
+
+ + + +
+Implementation + + +
public fun borrow<T>(c: &Collection<T>, i: u64): &T{
+    Vector::borrow(&c.items, i)
+}
+
+ + + +
+ + + +## Function `push_back` + +Add item v to the end of the collection c. +require owner of Collection. + + +
public fun push_back<T>(c: &mut Collection2::Collection<T>, t: T)
+
+ + + +
+Implementation + + +
public fun push_back<T>(c: &mut Collection<T>, t: T){
+    assert!(c.can_put, Errors::requires_address(ERR_COLLECTION_CAN_NOT_ADD));
+    Vector::push_back<T>(&mut c.items, t);
+}
+
+ + + +
+ + + +## Function `borrow_mut` + +Return a mutable reference to the ith item in the Collection c. +Aborts if i is out of bounds. + + +
public fun borrow_mut<T>(c: &mut Collection2::Collection<T>, i: u64): &mut T
+
+ + + +
+Implementation + + +
public fun borrow_mut<T>(c: &mut Collection<T>, i: u64): &mut T{
+    assert!(c.can_mut, Errors::requires_address(ERR_COLLECTION_CAN_NOT_MUT));
+    Vector::borrow_mut<T>(&mut c.items, i)
+}
+
+ + + +
+ + + +## Function `pop_back` + +Pop an element from the end of vector v. +Aborts if v is empty. + + +
public fun pop_back<T>(c: &mut Collection2::Collection<T>): T
+
+ + + +
+Implementation + + +
public fun pop_back<T>(c: &mut Collection<T>): T {
+    assert!(c.can_take, Errors::requires_address(ERR_COLLECTION_CAN_NOT_TAKE));
+    Vector::pop_back<T>(&mut c.items)
+}
+
+ + + +
+ + + +## Function `remove` + + + +
public fun remove<T>(c: &mut Collection2::Collection<T>, i: u64): T
+
+ + + +
+Implementation + + +
public fun remove<T>(c: &mut Collection<T>, i: u64): T{
+    assert!(c.can_take, Errors::requires_address(ERR_COLLECTION_CAN_NOT_TAKE));
+    Vector::remove<T>(&mut c.items, i)
+}
+
+ + + +
+ + + +## Function `append` + + + +
public fun append<T>(c: &mut Collection2::Collection<T>, other: T)
+
+ + + +
+Implementation + + +
public fun append<T>(c: &mut Collection<T>, other: T) {
+    assert!(c.can_put, Errors::requires_address(ERR_COLLECTION_CAN_NOT_ADD));
+    Vector::append<T>(&mut c.items, Vector::singleton(other))
+}
+
+ + + +
+ + + +## Function `append_all` + + + +
public fun append_all<T>(c: &mut Collection2::Collection<T>, other: vector<T>)
+
+ + + +
+Implementation + + +
public fun append_all<T>(c: &mut Collection<T>, other: vector<T>) {
+    assert!(c.can_put, Errors::requires_address(ERR_COLLECTION_CAN_NOT_ADD));
+    Vector::append<T>(&mut c.items, other)
+}
+
+ + + +
+ + + +## Function `exists_at` + +check the Collection exists in addr + + +
public fun exists_at<T: store>(addr: address): bool
+
+ + + +
+Implementation + + +
public fun exists_at<T: store>(addr: address): bool{
+    exists<CollectionStore<T>>(addr)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `is_accept` + +check addr is accept T and other can send T to addr, +it means exists a Collection of T at addr and anyone_can_put of the Collection is true + + +
public fun is_accept<T: store>(addr: address): bool
+
+ + + +
+Implementation + + +
public fun is_accept<T: store>(addr: address): bool acquires CollectionStore {
+    if (!exists<CollectionStore<T>>(addr)){
+        return false
+    };
+    let cs = borrow_global<CollectionStore<T>>(addr);
+    cs.anyone_can_put
+}
+
+ + + +
+ + + +## Function `accept` + +signer allow other send T to self +create a Collection of T and set anyone_can_put to true +if the Collection exists, just update anyone_can_put to true + + +
public fun accept<T: store>(signer: &signer)
+
+ + + +
+Implementation + + +
public fun accept<T: store>(signer: &signer) acquires CollectionStore {
+     let addr = Signer::address_of(signer);
+    if (!exists<CollectionStore<T>>(addr)){
+        Self::create_collection<T>(signer, true, false);
+    };
+    let cs = borrow_global_mut<CollectionStore<T>>(addr);
+    if (!cs.anyone_can_put) {
+        cs.anyone_can_put = true;
+    }
+}
+
+ + + +
+ + + +## Function `put` + +Put items to to_addr's Collection of T +put = borrow_collection + append + return_collection. + + +
public fun put<T: store>(signer: &signer, owner: address, item: T)
+
+ + + +
+Implementation + + +
public fun put<T: store>(signer: &signer, owner: address, item: T) acquires CollectionStore{
+    let c = Self::borrow_collection(signer, owner);
+    Self::append(&mut c, item);
+    Self::return_collection(c);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `put_all` + +Put all items to owner's collection of T. + + +
public fun put_all<T: store>(signer: &signer, owner: address, items: vector<T>)
+
+ + + +
+Implementation + + +
public fun put_all<T: store>(signer: &signer, owner: address, items: vector<T>) acquires CollectionStore{
+    let c = Self::borrow_collection(signer, owner);
+    Self::append_all(&mut c, items);
+    Self::return_collection(c);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `take` + +Take last item from signer's Collection of T. + + +
public fun take<T: store>(signer: &signer): T
+
+ + + +
+Implementation + + +
public fun take<T: store>(signer: &signer): T acquires CollectionStore{
+    let addr = Signer::address_of(signer);
+    let c = borrow_collection<T>(signer, addr);
+    let item = pop_back(&mut c);
+    return_collection(c);
+    item
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `create_collection` + + + +
public fun create_collection<T: store>(signer: &signer, anyone_can_put: bool, anyone_can_mut: bool)
+
+ + + +
+Implementation + + +
public fun create_collection<T:store>(signer: &signer, anyone_can_put: bool, anyone_can_mut: bool) {
+    move_to(signer, CollectionStore<T>{items: Option::some(Vector::empty<T>()), anyone_can_put, anyone_can_mut})
+}
+
+ + + +
+ + + +## Function `length_of` + +Return the length of Collection from owner, if collection do not exist, return 0. + + +
public fun length_of<T: store>(owner: address): u64
+
+ + + +
+Implementation + + +
public fun length_of<T: store>(owner: address) : u64 acquires CollectionStore{
+    if (exists_at<T>(owner)){
+        let cs = borrow_global_mut<CollectionStore<T>>(owner);
+        //if items is None, indicate it is borrowed
+        assert!(!Option::is_none(&cs.items), Errors::invalid_state(ERR_COLLECTION_INVALID_BORROW_STATE));
+        let items = Option::borrow(&cs.items);
+        Vector::length(items)
+    }else{
+        0
+    }
+}
+
+ + + +
+ + + +## Function `borrow_collection` + +Borrow collection of T from owner, auto detected the collection's can_put|can_mut|can_take by the sender and Collection config. + + +
public fun borrow_collection<T: store>(sender: &signer, owner: address): Collection2::Collection<T>
+
+ + + +
+Implementation + + +
public fun borrow_collection<T: store>(sender: &signer, owner: address): Collection<T> acquires CollectionStore{
+    assert!(exists_at<T>(owner), Errors::invalid_state(ERR_COLLECTION_NOT_EXIST));
+    let cs = borrow_global_mut<CollectionStore<T>>(owner);
+    //if items is None, indicate it is borrowed
+    assert!(!Option::is_none(&cs.items), Errors::invalid_state(ERR_COLLECTION_INVALID_BORROW_STATE));
+    let items = Option::extract(&mut cs.items);
+    let is_owner = owner == Signer::address_of(sender);
+    let can_put = cs.anyone_can_put || is_owner;
+    let can_mut = cs.anyone_can_mut || is_owner;
+    let can_take = is_owner;
+    Collection{
+        items,
+        owner,
+        can_put,
+        can_mut,
+        can_take,
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `return_collection` + +Return the Collection of T + + +
public fun return_collection<T: store>(c: Collection2::Collection<T>)
+
+ + + +
+Implementation + + +
public fun return_collection<T: store>(c: Collection<T>) acquires CollectionStore{
+    let Collection{ items, owner, can_put:_, can_mut:_, can_take:_ } = c;
+    let cs = borrow_global_mut<CollectionStore<T>>(owner);
+    assert!(Option::is_none(&cs.items), Errors::invalid_state(ERR_COLLECTION_INVALID_BORROW_STATE));
+    Option::fill(&mut cs.items, items);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `destroy_collection` + + + +
public fun destroy_collection<T: store>(signer: &signer)
+
+ + + +
+Implementation + + +
public fun destroy_collection<T: store>(signer: &signer) acquires CollectionStore{
+    let c = move_from<CollectionStore<T>>(Signer::address_of(signer));
+    destroy_empty(c);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `destroy_empty` + + + +
fun destroy_empty<T: store>(c: Collection2::CollectionStore<T>)
+
+ + + +
+Implementation + + +
fun destroy_empty<T: store>(c: CollectionStore<T>){
+    let CollectionStore{ items, anyone_can_put:_, anyone_can_mut:_,} = c;
+    if (Option::is_some(&items)) {
+        let item_vec = Option::extract(&mut items);
+        assert!(Vector::is_empty(&item_vec), Errors::invalid_state(ERR_COLLECTION_IS_NOT_EMPTY));
+        Vector::destroy_empty(item_vec);
+        Option::destroy_none(items);
+    }else{
+        Option::destroy_none(items);
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = false;
+
diff --git a/release/v13/docs/Compare.md b/release/v13/docs/Compare.md new file mode 100644 index 00000000..998884cc --- /dev/null +++ b/release/v13/docs/Compare.md @@ -0,0 +1,340 @@ + + + +# Module `0x1::Compare` + + + +- [Constants](#@Constants_0) +- [Function `cmp_bcs_bytes`](#0x1_Compare_cmp_bcs_bytes) +- [Function `cmp_bytes`](#0x1_Compare_cmp_bytes) +- [Function `cmp_u64`](#0x1_Compare_cmp_u64) +- [Function `is_equal`](#0x1_Compare_is_equal) +- [Function `is_less_than`](#0x1_Compare_is_less_than) +- [Function `is_greater_than`](#0x1_Compare_is_greater_than) +- [Module Specification](#@Module_Specification_1) + + +
+ + + + + +## Constants + + + + + + +
const EQUAL: u8 = 0;
+
+ + + + + + + +
const GREATER_THAN: u8 = 2;
+
+ + + + + + + +
const LESS_THAN: u8 = 1;
+
+ + + + + +## Function `cmp_bcs_bytes` + +Compare v1 and v2 using +(1) byte-by-byte comparison from right to left until we reach the end of the shorter vector, +then +(2) vector length to break ties. +Returns either EQUAL (0u8), LESS_THAN (1u8), or GREATER_THAN (2u8). +This function is designed to compare BCS (Starcoin Canonical Serialization)-encoded values +(i.e., vectors produced by BCS::to_bytes). A typical client will call +Compare::cmp_bcs_bytes(BCS::to_bytes(&t1), BCS::to_bytes(&t2)). The comparison provides the +following guarantees w.r.t the original values t1 and t2: +- cmp_bcs_bytes(bcs_ext(t1), bcs_ext(t2)) == LESS_THAN iff cmp_bcs_bytes(t2, t1) == GREATER_THAN +- Compare::cmp<T>(t1, t2) == EQUAL iff t1 == t2 and (similarly) +Compare::cmp<T>(t1, t2) != EQUAL iff t1 != t2, where == and != denote the Move +bytecode operations for polymorphic equality. +- for all primitive types T with < and > comparison operators exposed in Move bytecode +(u8, u64, u128), we have +compare_bcs_bytes(bcs_ext(t1), bcs_ext(t2)) == LESS_THAN iff t1 < t2 and (similarly) +compare_bcs_bytes(bcs_ext(t1), bcs_ext(t2)) == LESS_THAN iff t1 > t2. +For all other types, the order is whatever the BCS encoding of the type and the comparison +strategy above gives you. One case where the order might be surprising is the address type. +CoreAddresses are 16 byte hex values that BCS encodes with the identity function. The right to +left, byte-by-byte comparison means that (for example) +compare_bcs_bytes(bcs_ext(0x01), bcs_ext(0x10)) == LESS_THAN (as you'd expect), but +compare_bcs_bytes(bcs_ext(0x100), bcs_ext(0x001)) == LESS_THAN (as you probably wouldn't expect). +Keep this in mind when using this function to compare addresses. + + +
public fun cmp_bcs_bytes(v1: &vector<u8>, v2: &vector<u8>): u8
+
+ + + +
+Implementation + + +
public fun cmp_bcs_bytes(v1: &vector<u8>, v2: &vector<u8>): u8 {
+    let i1 = Vector::length(v1);
+    let i2 = Vector::length(v2);
+    let len_cmp = cmp_u64(i1, i2);
+
+    // BCS uses little endian encoding for all integer types, so we choose to compare from left
+    // to right. Going right to left would make the behavior of Compare.cmp diverge from the
+    // bytecode operators < and > on integer values (which would be confusing).
+    while (i1 > 0 && i2 > 0) {
+        i1 = i1 - 1;
+        i2 = i2 - 1;
+        let v1 = *Vector::borrow(v1, i1);
+        let v2 = *Vector::borrow(v2, i2);
+        let elem_cmp = if (v1 == v2) EQUAL
+            else if (v1 < v2) LESS_THAN
+            else GREATER_THAN;
+        if (elem_cmp != 0) return elem_cmp
+        // else, compare next element
+    };
+    // all compared elements equal; use length comparison to break the tie
+    len_cmp
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `cmp_bytes` + + + +
public fun cmp_bytes(v1: &vector<u8>, v2: &vector<u8>): u8
+
+ + + +
+Implementation + + +
public fun cmp_bytes(v1: &vector<u8>, v2: &vector<u8>): u8 {
+    let l1 = Vector::length(v1);
+    let l2 = Vector::length(v2);
+    let len_cmp = cmp_u64(l1, l2);
+    let i = 0;
+    while (i < l1 && i < l2) {
+        let v1 = *Vector::borrow(v1, i);
+        let v2 = *Vector::borrow(v2, i);
+        let elem_cmp = if (v1 == v2) EQUAL
+            else if (v1 < v2) LESS_THAN
+            else GREATER_THAN;
+        if (elem_cmp != 0) {
+            return elem_cmp
+        };
+        // else, compare next element
+        i = i + 1;
+    };
+    // all compared elements equal; use length comparison to break the tie
+    len_cmp
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `cmp_u64` + + + +
fun cmp_u64(i1: u64, i2: u64): u8
+
+ + + +
+Implementation + + +
fun cmp_u64(i1: u64, i2: u64): u8 {
+    if (i1 == i2) EQUAL
+    else if (i1 < i2) LESS_THAN
+    else GREATER_THAN
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `is_equal` + + + +
public fun is_equal(result: u8): bool
+
+ + + +
+Implementation + + +
public fun is_equal(result: u8): bool {
+    result == EQUAL
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `is_less_than` + + + +
public fun is_less_than(result: u8): bool
+
+ + + +
+Implementation + + +
public fun is_less_than(result: u8): bool {
+    result == LESS_THAN
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `is_greater_than` + + + +
public fun is_greater_than(result: u8): bool
+
+ + + +
+Implementation + + +
public fun is_greater_than(result: u8): bool {
+    result == GREATER_THAN
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Config.md b/release/v13/docs/Config.md new file mode 100644 index 00000000..cdffd8bb --- /dev/null +++ b/release/v13/docs/Config.md @@ -0,0 +1,773 @@ + + + +# Module `0x1::Config` + +The module provides a general implmentation of configuration for onchain contracts. + + +- [Resource `Config`](#0x1_Config_Config) +- [Struct `ModifyConfigCapability`](#0x1_Config_ModifyConfigCapability) +- [Resource `ModifyConfigCapabilityHolder`](#0x1_Config_ModifyConfigCapabilityHolder) +- [Struct `ConfigChangeEvent`](#0x1_Config_ConfigChangeEvent) +- [Constants](#@Constants_0) +- [Function `get_by_address`](#0x1_Config_get_by_address) +- [Function `config_exist_by_address`](#0x1_Config_config_exist_by_address) +- [Function `set`](#0x1_Config_set) +- [Function `set_with_capability`](#0x1_Config_set_with_capability) +- [Function `publish_new_config_with_capability`](#0x1_Config_publish_new_config_with_capability) +- [Function `publish_new_config`](#0x1_Config_publish_new_config) +- [Function `extract_modify_config_capability`](#0x1_Config_extract_modify_config_capability) +- [Function `restore_modify_config_capability`](#0x1_Config_restore_modify_config_capability) +- [Function `destroy_modify_config_capability`](#0x1_Config_destroy_modify_config_capability) +- [Function `account_address`](#0x1_Config_account_address) +- [Function `emit_config_change_event`](#0x1_Config_emit_config_change_event) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Option;
+use 0x1::Signer;
+
+ + + + + +## Resource `Config` + +A generic singleton resource that holds a value of a specific type. + + +
struct Config<ConfigValue: copy, drop, store> has key
+
+ + + +
+Fields + + +
+
+payload: ConfigValue +
+
+ +
+
+ + +
+ + + +## Struct `ModifyConfigCapability` + +Accounts with this privilege can modify config of type ConfigValue under account_address + + +
struct ModifyConfigCapability<ConfigValue: copy, drop, store> has store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+events: Event::EventHandle<Config::ConfigChangeEvent<ConfigValue>> +
+
+ +
+
+ + +
+ + + +## Resource `ModifyConfigCapabilityHolder` + +A holder for ModifyConfigCapability, for extraction and restoration of ModifyConfigCapability. + + +
struct ModifyConfigCapabilityHolder<ConfigValue: copy, drop, store> has store, key
+
+ + + +
+Fields + + +
+
+cap: Option::Option<Config::ModifyConfigCapability<ConfigValue>> +
+
+ +
+
+ + +
+ + + +## Struct `ConfigChangeEvent` + +Event emitted when config value is changed. + + +
struct ConfigChangeEvent<ConfigValue: copy, drop, store> has drop, store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+value: ConfigValue +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ECAPABILITY_HOLDER_NOT_EXISTS: u64 = 101;
+
+ + + + + + + +
const ECONFIG_VALUE_DOES_NOT_EXIST: u64 = 13;
+
+ + + + + +## Function `get_by_address` + +Get a copy of ConfigValue value stored under addr. + + +
public fun get_by_address<ConfigValue: copy, drop, store>(addr: address): ConfigValue
+
+ + + +
+Implementation + + +
public fun get_by_address<ConfigValue: copy + drop + store>(addr: address): ConfigValue acquires Config {
+    assert!(exists<Config<ConfigValue>>(addr), Errors::invalid_state(ECONFIG_VALUE_DOES_NOT_EXIST));
+    *&borrow_global<Config<ConfigValue>>(addr).payload
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Config<ConfigValue>>(addr);
+ensures exists<Config<ConfigValue>>(addr);
+ensures result == spec_get<ConfigValue>(addr);
+
+ + + +
+ + + +## Function `config_exist_by_address` + +Check whether the config of ConfigValue type exists under addr. + + +
public fun config_exist_by_address<ConfigValue: copy, drop, store>(addr: address): bool
+
+ + + +
+Implementation + + +
public fun config_exist_by_address<ConfigValue: copy + drop + store>(addr: address): bool {
+    exists<Config<ConfigValue>>(addr)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == exists<Config<ConfigValue>>(addr);
+
+ + + +
+ + + +## Function `set` + +Set a config item to a new value with capability stored under signer + + +
public fun set<ConfigValue: copy, drop, store>(account: &signer, payload: ConfigValue)
+
+ + + +
+Implementation + + +
public fun set<ConfigValue: copy + drop + store>(
+    account: &signer,
+    payload: ConfigValue,
+) acquires Config, ModifyConfigCapabilityHolder {
+    let signer_address = Signer::address_of(account);
+    assert!(
+        exists<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address),
+        Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS),
+    );
+    let cap_holder = borrow_global_mut<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address);
+    assert!(Option::is_some(&cap_holder.cap), Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS));
+    set_with_capability(Option::borrow_mut(&mut cap_holder.cap), payload);
+}
+
+ + + +
+ +
+Specification + + + +
let addr = Signer::address_of(account);
+let cap_opt = spec_cap<ConfigValue>(addr);
+let cap = Option::borrow(spec_cap<ConfigValue>(Signer::address_of(account)));
+aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(addr);
+aborts_if Option::is_none<ModifyConfigCapability<ConfigValue>>(cap_opt);
+ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(addr);
+pragma aborts_if_is_partial;
+ensures exists<Config<ConfigValue>>(
+    Option::borrow(spec_cap<ConfigValue>(Signer::address_of(account))).account_address,
+);
+ensures global<Config<ConfigValue>>(
+    Option::borrow(spec_cap<ConfigValue>(Signer::address_of(account))).account_address,
+).payload == payload;
+
+ + + + + + + +
fun spec_cap<ConfigValue>(addr: address): Option<ModifyConfigCapability<ConfigValue>> {
+   global<ModifyConfigCapabilityHolder<ConfigValue>>(addr).cap
+}
+
+ + + +
+ + + +## Function `set_with_capability` + +Set a config item to a new value with cap. + + +
public fun set_with_capability<ConfigValue: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<ConfigValue>, payload: ConfigValue)
+
+ + + +
+Implementation + + +
public fun set_with_capability<ConfigValue: copy + drop + store>(
+    cap: &mut ModifyConfigCapability<ConfigValue>,
+    payload: ConfigValue,
+) acquires Config {
+    let addr = cap.account_address;
+    assert!(exists<Config<ConfigValue>>(addr), Errors::invalid_state(ECONFIG_VALUE_DOES_NOT_EXIST));
+    let config = borrow_global_mut<Config<ConfigValue>>(addr);
+    config.payload = copy payload;
+    emit_config_change_event(cap, payload);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Config<ConfigValue>>(cap.account_address);
+ensures exists<Config<ConfigValue>>(cap.account_address);
+ensures global<Config<ConfigValue>>(cap.account_address).payload == payload;
+
+ + + +
+ + + +## Function `publish_new_config_with_capability` + +Publish a new config item. The caller will use the returned ModifyConfigCapability to specify the access control +policy for who can modify the config. + + +
public fun publish_new_config_with_capability<ConfigValue: copy, drop, store>(account: &signer, payload: ConfigValue): Config::ModifyConfigCapability<ConfigValue>
+
+ + + +
+Implementation + + +
public fun publish_new_config_with_capability<ConfigValue: copy + drop + store>(
+    account: &signer,
+    payload: ConfigValue,
+): ModifyConfigCapability<ConfigValue> acquires ModifyConfigCapabilityHolder{
+    publish_new_config<ConfigValue>(account, payload);
+    extract_modify_config_capability<ConfigValue>(account)
+}
+
+ + + +
+ +
+Specification + + + +
include PublishNewConfigAbortsIf<ConfigValue>;
+ensures exists<Config<ConfigValue>>(Signer::address_of(account));
+ensures global<Config<ConfigValue>>(Signer::address_of(account)).payload == payload;
+ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
+ensures Option::is_none(global<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account)).cap);
+
+ + + +
+ + + +## Function `publish_new_config` + +Publish a new config item under account address. + + +
public fun publish_new_config<ConfigValue: copy, drop, store>(account: &signer, payload: ConfigValue)
+
+ + + +
+Implementation + + +
public fun publish_new_config<ConfigValue: copy + drop + store>(account: &signer, payload: ConfigValue) {
+    move_to(account, Config<ConfigValue>{ payload });
+    let cap = ModifyConfigCapability<ConfigValue> {
+        account_address: Signer::address_of(account),
+        events: Event::new_event_handle<ConfigChangeEvent<ConfigValue>>(account),
+    };
+    move_to(account, ModifyConfigCapabilityHolder{cap: Option::some(cap)});
+}
+
+ + + +
+ +
+Specification + + + +
include PublishNewConfigAbortsIf<ConfigValue>;
+ensures exists<Config<ConfigValue>>(Signer::address_of(account));
+ensures global<Config<ConfigValue>>(Signer::address_of(account)).payload == payload;
+ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
+ensures Option::is_some(global<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account)).cap);
+
+ + + + + + + +
schema PublishNewConfigAbortsIf<ConfigValue> {
+    account: signer;
+    aborts_if exists<Config<ConfigValue>>(Signer::address_of(account));
+    aborts_if exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
+}
+
+ + + + + + + +
schema AbortsIfConfigNotExist<ConfigValue> {
+    addr: address;
+    aborts_if !exists<Config<ConfigValue>>(addr);
+}
+
+ + + + + + + +
schema AbortsIfConfigOrCapabilityNotExist<ConfigValue> {
+    addr: address;
+    aborts_if !exists<Config<ConfigValue>>(addr);
+    aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(addr);
+}
+
+ + + + + + + +
schema PublishNewConfigEnsures<ConfigValue> {
+    account: signer;
+    ensures exists<Config<ConfigValue>>(Signer::address_of(account));
+    ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(Signer::address_of(account));
+}
+
+ + + + + + + +
schema AbortsIfCapNotExist<ConfigValue> {
+    address: address;
+    aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(address);
+    aborts_if Option::is_none<ModifyConfigCapability<ConfigValue>>(
+        global<ModifyConfigCapabilityHolder<ConfigValue>>(address).cap,
+    );
+}
+
+ + + +
+ + + +## Function `extract_modify_config_capability` + +Extract account's ModifyConfigCapability for ConfigValue type + + +
public fun extract_modify_config_capability<ConfigValue: copy, drop, store>(account: &signer): Config::ModifyConfigCapability<ConfigValue>
+
+ + + +
+Implementation + + +
public fun extract_modify_config_capability<ConfigValue: copy + drop + store>(
+    account: &signer,
+): ModifyConfigCapability<ConfigValue> acquires ModifyConfigCapabilityHolder {
+    let signer_address = Signer::address_of(account);
+    assert!(
+        exists<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address),
+        Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS)
+    );
+    let cap_holder = borrow_global_mut<ModifyConfigCapabilityHolder<ConfigValue>>(signer_address);
+    Option::extract(&mut cap_holder.cap)
+}
+
+ + + +
+ +
+Specification + + + +
let address = Signer::address_of(account);
+include AbortsIfCapNotExist<ConfigValue>;
+ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(address);
+ensures Option::is_none<ModifyConfigCapability<ConfigValue>>(
+    global<ModifyConfigCapabilityHolder<ConfigValue>>(address).cap
+);
+ensures result == old(Option::borrow(global<ModifyConfigCapabilityHolder<ConfigValue>>(address).cap));
+
+ + + +
+ + + +## Function `restore_modify_config_capability` + +Restore account's ModifyConfigCapability + + +
public fun restore_modify_config_capability<ConfigValue: copy, drop, store>(cap: Config::ModifyConfigCapability<ConfigValue>)
+
+ + + +
+Implementation + + +
public fun restore_modify_config_capability<ConfigValue: copy + drop + store>(
+    cap: ModifyConfigCapability<ConfigValue>,
+) acquires ModifyConfigCapabilityHolder {
+    let cap_holder = borrow_global_mut<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address);
+    Option::fill(&mut cap_holder.cap, cap);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address);
+aborts_if Option::is_some(global<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address).cap);
+ensures exists<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address);
+ensures Option::is_some(global<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address).cap);
+ensures Option::borrow(global<ModifyConfigCapabilityHolder<ConfigValue>>(cap.account_address).cap) == cap;
+
+ + + +
+ + + +## Function `destroy_modify_config_capability` + +Destroy the given ModifyConfigCapability + + +
public fun destroy_modify_config_capability<ConfigValue: copy, drop, store>(cap: Config::ModifyConfigCapability<ConfigValue>)
+
+ + + +
+Implementation + + +
public fun destroy_modify_config_capability<ConfigValue: copy + drop + store>(
+    cap: ModifyConfigCapability<ConfigValue>,
+) {
+    let ModifyConfigCapability{account_address:_, events} = cap;
+    Event::destroy_handle(events)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `account_address` + +Return the address of the given ModifyConfigCapability + + +
public fun account_address<ConfigValue: copy, drop, store>(cap: &Config::ModifyConfigCapability<ConfigValue>): address
+
+ + + +
+Implementation + + +
public fun account_address<ConfigValue: copy + drop + store>(cap: &ModifyConfigCapability<ConfigValue>): address {
+    cap.account_address
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == cap.account_address;
+
+ + + +
+ + + +## Function `emit_config_change_event` + +Emit a config change event. + + +
fun emit_config_change_event<ConfigValue: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<ConfigValue>, value: ConfigValue)
+
+ + + +
+Implementation + + +
fun emit_config_change_event<ConfigValue: copy + drop + store>(
+    cap: &mut ModifyConfigCapability<ConfigValue>,
+    value: ConfigValue,
+) {
+    Event::emit_event<ConfigChangeEvent<ConfigValue>>(
+        &mut cap.events,
+        ConfigChangeEvent {
+            account_address: cap.account_address,
+            value,
+        },
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
+ + + + + + + +
fun spec_get<ConfigValue>(addr: address): ConfigValue {
+   global<Config<ConfigValue>>(addr).payload
+}
+
diff --git a/release/v13/docs/ConsensusConfig.md b/release/v13/docs/ConsensusConfig.md new file mode 100644 index 00000000..fc33060d --- /dev/null +++ b/release/v13/docs/ConsensusConfig.md @@ -0,0 +1,700 @@ + + + +# Module `0x1::ConsensusConfig` + +The module provide configuration of consensus parameters. + + +- [Struct `ConsensusConfig`](#0x1_ConsensusConfig_ConsensusConfig) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_ConsensusConfig_initialize) +- [Function `new_consensus_config`](#0x1_ConsensusConfig_new_consensus_config) +- [Function `get_config`](#0x1_ConsensusConfig_get_config) +- [Function `uncle_rate_target`](#0x1_ConsensusConfig_uncle_rate_target) +- [Function `base_block_time_target`](#0x1_ConsensusConfig_base_block_time_target) +- [Function `base_reward_per_block`](#0x1_ConsensusConfig_base_reward_per_block) +- [Function `epoch_block_count`](#0x1_ConsensusConfig_epoch_block_count) +- [Function `base_block_difficulty_window`](#0x1_ConsensusConfig_base_block_difficulty_window) +- [Function `base_reward_per_uncle_percent`](#0x1_ConsensusConfig_base_reward_per_uncle_percent) +- [Function `min_block_time_target`](#0x1_ConsensusConfig_min_block_time_target) +- [Function `max_block_time_target`](#0x1_ConsensusConfig_max_block_time_target) +- [Function `base_max_uncles_per_block`](#0x1_ConsensusConfig_base_max_uncles_per_block) +- [Function `base_block_gas_limit`](#0x1_ConsensusConfig_base_block_gas_limit) +- [Function `strategy`](#0x1_ConsensusConfig_strategy) +- [Function `compute_reward_per_block`](#0x1_ConsensusConfig_compute_reward_per_block) +- [Function `do_compute_reward_per_block`](#0x1_ConsensusConfig_do_compute_reward_per_block) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::CoreAddresses;
+use 0x1::Errors;
+use 0x1::Math;
+use 0x1::Timestamp;
+
+ + + + + +## Struct `ConsensusConfig` + +consensus configurations. + + +
struct ConsensusConfig has copy, drop, store
+
+ + + +
+Fields + + +
+
+uncle_rate_target: u64 +
+
+ Uncle block rate per epoch +
+
+base_block_time_target: u64 +
+
+ Average target time to calculate a block's difficulty +
+
+base_reward_per_block: u128 +
+
+ Rewards per block +
+
+base_reward_per_uncle_percent: u64 +
+
+ Percentage of base_reward_per_block to reward a uncle block +
+
+epoch_block_count: u64 +
+
+ Blocks each epoch +
+
+base_block_difficulty_window: u64 +
+
+ How many ancestor blocks which use to calculate next block's difficulty +
+
+min_block_time_target: u64 +
+
+ Minimum target time to calculate a block's difficulty +
+
+max_block_time_target: u64 +
+
+ Maximum target time to calculate a block's difficulty +
+
+base_max_uncles_per_block: u64 +
+
+ Maximum number of uncle block per block +
+
+base_block_gas_limit: u64 +
+
+ Maximum gases per block +
+
+strategy: u8 +
+
+ Strategy to calculate difficulty +
+
+ + +
+ + + +## Constants + + + + + + +
const EINVALID_ARGUMENT: u64 = 18;
+
+ + + + + +## Function `initialize` + +Initialization of the module. + + +
public fun initialize(account: &signer, uncle_rate_target: u64, epoch_block_count: u64, base_block_time_target: u64, base_block_difficulty_window: u64, base_reward_per_block: u128, base_reward_per_uncle_percent: u64, min_block_time_target: u64, max_block_time_target: u64, base_max_uncles_per_block: u64, base_block_gas_limit: u64, strategy: u8)
+
+ + + +
+Implementation + + +
public fun initialize(
+    account: &signer,
+    uncle_rate_target: u64,
+    epoch_block_count: u64,
+    base_block_time_target: u64,
+    base_block_difficulty_window: u64,
+    base_reward_per_block: u128,
+    base_reward_per_uncle_percent: u64,
+    min_block_time_target: u64,
+    max_block_time_target: u64,
+    base_max_uncles_per_block: u64,
+    base_block_gas_limit: u64,
+    strategy: u8,
+) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+
+    Config::publish_new_config<Self::ConsensusConfig>(
+        account,
+        new_consensus_config(
+            uncle_rate_target,
+            base_block_time_target,
+            base_reward_per_block,
+            base_reward_per_uncle_percent,
+            epoch_block_count,
+            base_block_difficulty_window,
+            min_block_time_target,
+            max_block_time_target,
+            base_max_uncles_per_block,
+            base_block_gas_limit,
+            strategy,
+        ),
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if uncle_rate_target == 0;
+aborts_if epoch_block_count == 0;
+aborts_if base_reward_per_block == 0;
+aborts_if base_block_time_target == 0;
+aborts_if base_block_difficulty_window == 0;
+aborts_if min_block_time_target == 0;
+aborts_if max_block_time_target < min_block_time_target;
+include Config::PublishNewConfigAbortsIf<ConsensusConfig>;
+include Config::PublishNewConfigEnsures<ConsensusConfig>;
+
+ + + +
+ + + +## Function `new_consensus_config` + +Create a new consensus config mainly used in DAO. + + +
public fun new_consensus_config(uncle_rate_target: u64, base_block_time_target: u64, base_reward_per_block: u128, base_reward_per_uncle_percent: u64, epoch_block_count: u64, base_block_difficulty_window: u64, min_block_time_target: u64, max_block_time_target: u64, base_max_uncles_per_block: u64, base_block_gas_limit: u64, strategy: u8): ConsensusConfig::ConsensusConfig
+
+ + + +
+Implementation + + +
public fun new_consensus_config(uncle_rate_target: u64,
+                                base_block_time_target: u64,
+                                base_reward_per_block: u128,
+                                base_reward_per_uncle_percent: u64,
+                                epoch_block_count: u64,
+                                base_block_difficulty_window: u64,
+                                min_block_time_target: u64,
+                                max_block_time_target: u64,
+                                base_max_uncles_per_block: u64,
+                                base_block_gas_limit: u64,
+                                strategy: u8,): ConsensusConfig {
+    assert!(uncle_rate_target > 0, Errors::invalid_argument(EINVALID_ARGUMENT));
+    assert!(base_block_time_target > 0, Errors::invalid_argument(EINVALID_ARGUMENT));
+    assert!(base_reward_per_block > 0, Errors::invalid_argument(EINVALID_ARGUMENT));
+    assert!(epoch_block_count > 0, Errors::invalid_argument(EINVALID_ARGUMENT));
+    assert!(base_block_difficulty_window > 0, Errors::invalid_argument(EINVALID_ARGUMENT));
+    // base_reward_per_uncle_percent can been zero.
+    assert!(min_block_time_target > 0, Errors::invalid_argument(EINVALID_ARGUMENT));
+    assert!(max_block_time_target >= min_block_time_target, Errors::invalid_argument(EINVALID_ARGUMENT));
+
+    ConsensusConfig {
+        uncle_rate_target,
+        base_block_time_target,
+        base_reward_per_block,
+        epoch_block_count,
+        base_block_difficulty_window,
+        base_reward_per_uncle_percent,
+        min_block_time_target,
+        max_block_time_target,
+        base_max_uncles_per_block,
+        base_block_gas_limit,
+        strategy,
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if uncle_rate_target == 0;
+aborts_if epoch_block_count == 0;
+aborts_if base_reward_per_block == 0;
+aborts_if base_block_time_target == 0;
+aborts_if base_block_difficulty_window == 0;
+aborts_if min_block_time_target == 0;
+aborts_if max_block_time_target < min_block_time_target;
+
+ + + +
+ + + +## Function `get_config` + +Get config data. + + +
public fun get_config(): ConsensusConfig::ConsensusConfig
+
+ + + +
+Implementation + + +
public fun get_config(): ConsensusConfig {
+    Config::get_by_address<ConsensusConfig>(CoreAddresses::GENESIS_ADDRESS())
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Config::Config<ConsensusConfig>>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + + + + + +
fun spec_get_config(): ConsensusConfig {
+   global<Config::Config<ConsensusConfig>>(CoreAddresses::GENESIS_ADDRESS()).payload
+}
+
+ + + +
+ + + +## Function `uncle_rate_target` + +Get uncle_rate_target + + +
public fun uncle_rate_target(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun uncle_rate_target(config: &ConsensusConfig): u64 {
+    config.uncle_rate_target
+}
+
+ + + +
+ + + +## Function `base_block_time_target` + +Get base_block_time_target + + +
public fun base_block_time_target(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun base_block_time_target(config: &ConsensusConfig): u64 {
+    config.base_block_time_target
+}
+
+ + + +
+ + + +## Function `base_reward_per_block` + +Get base_reward_per_block + + +
public fun base_reward_per_block(config: &ConsensusConfig::ConsensusConfig): u128
+
+ + + +
+Implementation + + +
public fun base_reward_per_block(config: &ConsensusConfig): u128 {
+    config.base_reward_per_block
+}
+
+ + + +
+ + + +## Function `epoch_block_count` + +Get epoch_block_count + + +
public fun epoch_block_count(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun epoch_block_count(config: &ConsensusConfig): u64 {
+    config.epoch_block_count
+}
+
+ + + +
+ + + +## Function `base_block_difficulty_window` + +Get base_block_difficulty_window + + +
public fun base_block_difficulty_window(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun base_block_difficulty_window(config: &ConsensusConfig): u64 {
+    config.base_block_difficulty_window
+}
+
+ + + +
+ + + +## Function `base_reward_per_uncle_percent` + +Get base_reward_per_uncle_percent + + +
public fun base_reward_per_uncle_percent(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun base_reward_per_uncle_percent(config: &ConsensusConfig): u64 {
+    config.base_reward_per_uncle_percent
+}
+
+ + + +
+ + + +## Function `min_block_time_target` + +Get min_block_time_target + + +
public fun min_block_time_target(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun min_block_time_target(config: &ConsensusConfig): u64 {
+    config.min_block_time_target
+}
+
+ + + +
+ + + +## Function `max_block_time_target` + +Get max_block_time_target + + +
public fun max_block_time_target(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun max_block_time_target(config: &ConsensusConfig): u64 {
+    config.max_block_time_target
+}
+
+ + + +
+ + + +## Function `base_max_uncles_per_block` + +Get base_max_uncles_per_block + + +
public fun base_max_uncles_per_block(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun base_max_uncles_per_block(config: &ConsensusConfig): u64 {
+    config.base_max_uncles_per_block
+}
+
+ + + +
+ + + +## Function `base_block_gas_limit` + +Get base_block_gas_limit + + +
public fun base_block_gas_limit(config: &ConsensusConfig::ConsensusConfig): u64
+
+ + + +
+Implementation + + +
public fun base_block_gas_limit(config: &ConsensusConfig): u64 {
+    config.base_block_gas_limit
+}
+
+ + + +
+ + + +## Function `strategy` + +Get strategy + + +
public fun strategy(config: &ConsensusConfig::ConsensusConfig): u8
+
+ + + +
+Implementation + + +
public fun strategy(config: &ConsensusConfig): u8 {
+    config.strategy
+}
+
+ + + +
+ + + +## Function `compute_reward_per_block` + +Compute block reward given the new_epoch_block_time_target. + + +
public fun compute_reward_per_block(new_epoch_block_time_target: u64): u128
+
+ + + +
+Implementation + + +
public fun compute_reward_per_block(new_epoch_block_time_target: u64): u128 {
+    let config = get_config();
+    do_compute_reward_per_block(&config, new_epoch_block_time_target)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Config::Config<ConsensusConfig>>(CoreAddresses::GENESIS_ADDRESS());
+include Math::MulDivAbortsIf{x: spec_get_config().base_reward_per_block, y: new_epoch_block_time_target, z: spec_get_config().base_block_time_target};
+
+ + + +
+ + + +## Function `do_compute_reward_per_block` + +Compute block reward given the new_epoch_block_time_target, and the consensus config. + + +
public fun do_compute_reward_per_block(config: &ConsensusConfig::ConsensusConfig, new_epoch_block_time_target: u64): u128
+
+ + + +
+Implementation + + +
public fun do_compute_reward_per_block(config: &ConsensusConfig, new_epoch_block_time_target: u64): u128 {
+    Math::mul_div(config.base_reward_per_block, (new_epoch_block_time_target as u128), (config.base_block_time_target as u128))
+}
+
+ + + +
+ +
+Specification + + + +
include Math::MulDivAbortsIf{x: config.base_reward_per_block, y: new_epoch_block_time_target, z: config.base_block_time_target};
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/ConsensusStrategy.md b/release/v13/docs/ConsensusStrategy.md new file mode 100644 index 00000000..f8f72b50 --- /dev/null +++ b/release/v13/docs/ConsensusStrategy.md @@ -0,0 +1,143 @@ + + + +# Module `0x1::ConsensusStrategy` + +The module provides the information of current consensus strategy. + + +- [Struct `ConsensusStrategy`](#0x1_ConsensusStrategy_ConsensusStrategy) +- [Function `initialize`](#0x1_ConsensusStrategy_initialize) +- [Function `get`](#0x1_ConsensusStrategy_get) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::Config;
+use 0x1::CoreAddresses;
+use 0x1::Timestamp;
+
+ + + + + +## Struct `ConsensusStrategy` + +ConsensusStrategy data. + + +
struct ConsensusStrategy has copy, drop, store
+
+ + + +
+Fields + + +
+
+value: u8 +
+
+ Value of strategy +
+
+ + +
+ + + +## Function `initialize` + +Publish the chain ID under the genesis account + + +
public fun initialize(account: &signer, consensus_strategy: u8)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, consensus_strategy: u8) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+    let cap = Config::publish_new_config_with_capability<ConsensusStrategy>(
+        account,
+        ConsensusStrategy { value:consensus_strategy }
+    );
+    //destroy the cap, so ConsensusStrategy can not been change.
+    Config::destroy_modify_config_capability(cap);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<Config::Config<ConsensusStrategy>>(Signer::address_of(account));
+aborts_if exists<Config::ModifyConfigCapabilityHolder<ConsensusStrategy>>(Signer::address_of(account));
+ensures exists<Config::Config<ConsensusStrategy>>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `get` + +Return the consensus strategy type of this chain + + +
public fun get(): u8
+
+ + + +
+Implementation + + +
public fun get(): u8 {
+    Config::get_by_address<ConsensusStrategy>(CoreAddresses::GENESIS_ADDRESS()).value
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Config::Config<ConsensusStrategy>>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/CoreAddresses.md b/release/v13/docs/CoreAddresses.md new file mode 100644 index 00000000..70f433be --- /dev/null +++ b/release/v13/docs/CoreAddresses.md @@ -0,0 +1,178 @@ + + + +# Module `0x1::CoreAddresses` + +The module provide addresses used in stdlib. + + +- [Constants](#@Constants_0) +- [Function `GENESIS_ADDRESS`](#0x1_CoreAddresses_GENESIS_ADDRESS) +- [Function `assert_genesis_address`](#0x1_CoreAddresses_assert_genesis_address) +- [Function `ASSOCIATION_ROOT_ADDRESS`](#0x1_CoreAddresses_ASSOCIATION_ROOT_ADDRESS) +- [Function `VM_RESERVED_ADDRESS`](#0x1_CoreAddresses_VM_RESERVED_ADDRESS) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Signer;
+
+ + + + + +## Constants + + + + + + +
const ENOT_GENESIS_ACCOUNT: u64 = 11;
+
+ + + + + +## Function `GENESIS_ADDRESS` + +The address of the genesis + + +
public fun GENESIS_ADDRESS(): address
+
+ + + +
+Implementation + + +
public fun GENESIS_ADDRESS(): address {
+    @0x1
+}
+
+ + + +
+ + + +## Function `assert_genesis_address` + +Assert signer is genesis. + + +
public fun assert_genesis_address(account: &signer)
+
+ + + +
+Implementation + + +
public fun assert_genesis_address(account: &signer) {
+    assert!(Signer::address_of(account) == GENESIS_ADDRESS(),
+            Errors::requires_address(ENOT_GENESIS_ACCOUNT))
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+include AbortsIfNotGenesisAddress;
+
+ + +Specifies that a function aborts if the account does not have the Diem root address. + + + + + +
schema AbortsIfNotGenesisAddress {
+    account: signer;
+    aborts_if Signer::address_of(account) != GENESIS_ADDRESS();
+}
+
+ + + +
+ + + +## Function `ASSOCIATION_ROOT_ADDRESS` + +The address of the root association account. This account is +created in genesis, and cannot be changed. This address has +ultimate authority over the permissions granted (or removed) from +accounts on-chain. + + +
public fun ASSOCIATION_ROOT_ADDRESS(): address
+
+ + + +
+Implementation + + +
public fun ASSOCIATION_ROOT_ADDRESS(): address {
+    @0xA550C18
+}
+
+ + + +
+ + + +## Function `VM_RESERVED_ADDRESS` + +The reserved address for transactions inserted by the VM into blocks (e.g. +block metadata transactions). Because the transaction is sent from +the VM, an account _cannot_ exist at the 0x0 address since there +is no signer for the transaction. + + +
public fun VM_RESERVED_ADDRESS(): address
+
+ + + +
+Implementation + + +
public fun VM_RESERVED_ADDRESS(): address {
+    @0x0
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Dao.md b/release/v13/docs/Dao.md new file mode 100644 index 00000000..db025649 --- /dev/null +++ b/release/v13/docs/Dao.md @@ -0,0 +1,2330 @@ + + + +# Module `0x1::Dao` + + + +- [Resource `DaoGlobalInfo`](#0x1_Dao_DaoGlobalInfo) +- [Struct `DaoConfig`](#0x1_Dao_DaoConfig) +- [Struct `ProposalCreatedEvent`](#0x1_Dao_ProposalCreatedEvent) +- [Struct `VoteChangedEvent`](#0x1_Dao_VoteChangedEvent) +- [Resource `Proposal`](#0x1_Dao_Proposal) +- [Resource `Vote`](#0x1_Dao_Vote) +- [Constants](#@Constants_0) +- [Function `plugin`](#0x1_Dao_plugin) +- [Function `new_dao_config`](#0x1_Dao_new_dao_config) +- [Function `propose`](#0x1_Dao_propose) +- [Function `cast_vote`](#0x1_Dao_cast_vote) +- [Function `do_cast_vote`](#0x1_Dao_do_cast_vote) +- [Function `change_vote`](#0x1_Dao_change_vote) +- [Function `do_flip_vote`](#0x1_Dao_do_flip_vote) +- [Function `revoke_vote`](#0x1_Dao_revoke_vote) +- [Function `do_revoke_vote`](#0x1_Dao_do_revoke_vote) +- [Function `unstake_votes`](#0x1_Dao_unstake_votes) +- [Function `queue_proposal_action`](#0x1_Dao_queue_proposal_action) +- [Function `extract_proposal_action`](#0x1_Dao_extract_proposal_action) +- [Function `destroy_terminated_proposal`](#0x1_Dao_destroy_terminated_proposal) +- [Function `proposal_exists`](#0x1_Dao_proposal_exists) +- [Function `proposal_state`](#0x1_Dao_proposal_state) +- [Function `do_proposal_state`](#0x1_Dao_do_proposal_state) +- [Function `proposal_info`](#0x1_Dao_proposal_info) +- [Function `vote_of`](#0x1_Dao_vote_of) +- [Function `has_vote`](#0x1_Dao_has_vote) +- [Function `generate_next_proposal_id`](#0x1_Dao_generate_next_proposal_id) +- [Function `voting_delay`](#0x1_Dao_voting_delay) +- [Function `voting_period`](#0x1_Dao_voting_period) +- [Function `quorum_votes`](#0x1_Dao_quorum_votes) +- [Function `voting_quorum_rate`](#0x1_Dao_voting_quorum_rate) +- [Function `min_action_delay`](#0x1_Dao_min_action_delay) +- [Function `get_config`](#0x1_Dao_get_config) +- [Function `modify_dao_config`](#0x1_Dao_modify_dao_config) +- [Function `set_voting_delay`](#0x1_Dao_set_voting_delay) +- [Function `set_voting_period`](#0x1_Dao_set_voting_period) +- [Function `set_voting_quorum_rate`](#0x1_Dao_set_voting_quorum_rate) +- [Function `set_min_action_delay`](#0x1_Dao_set_min_action_delay) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Option;
+use 0x1::Signer;
+use 0x1::Timestamp;
+use 0x1::Token;
+use 0x1::Treasury;
+
+ + + + + +## Resource `DaoGlobalInfo` + +global DAO info of the specified token type Token. + + +
struct DaoGlobalInfo<Token: store> has key
+
+ + + +
+Fields + + +
+
+next_proposal_id: u64 +
+
+ next proposal id. +
+
+proposal_create_event: Event::EventHandle<Dao::ProposalCreatedEvent> +
+
+ proposal creating event. +
+
+vote_changed_event: Event::EventHandle<Dao::VoteChangedEvent> +
+
+ voting event. +
+
+ + +
+ + + +## Struct `DaoConfig` + +Configuration of the Token's DAO. + + +
struct DaoConfig<TokenT: copy, drop, store> has copy, drop, store
+
+ + + +
+Fields + + +
+
+voting_delay: u64 +
+
+ after proposal created, how long use should wait before he can vote (in milliseconds) +
+
+voting_period: u64 +
+
+ how long the voting window is (in milliseconds). +
+
+voting_quorum_rate: u8 +
+
+ the quorum rate to agree on the proposal. + if 50% votes needed, then the voting_quorum_rate should be 50. + it should between (0, 100]. +
+
+min_action_delay: u64 +
+
+ how long the proposal should wait before it can be executed (in milliseconds). +
+
+ + +
+ +
+Specification + + + +
invariant voting_quorum_rate > 0 && voting_quorum_rate <= 100;
+invariant voting_delay > 0;
+invariant voting_period > 0;
+invariant min_action_delay > 0;
+
+ + + +
+ + + +## Struct `ProposalCreatedEvent` + +emitted when proposal created. + + +
struct ProposalCreatedEvent has drop, store
+
+ + + +
+Fields + + +
+
+proposal_id: u64 +
+
+ the proposal id. +
+
+proposer: address +
+
+ proposer is the user who create the proposal. +
+
+ + +
+ + + +## Struct `VoteChangedEvent` + +emitted when user vote/revoke_vote. + + +
struct VoteChangedEvent has drop, store
+
+ + + +
+Fields + + +
+
+proposal_id: u64 +
+
+ the proposal id. +
+
+voter: address +
+
+ the voter. +
+
+proposer: address +
+
+ creator of the proposal. +
+
+agree: bool +
+
+ agree with the proposal or not +
+
+vote: u128 +
+
+ latest vote count of the voter. +
+
+ + +
+ + + +## Resource `Proposal` + +Proposal data struct. + + +
struct Proposal<Token: store, Action: store> has key
+
+ + + +
+Fields + + +
+
+id: u64 +
+
+ id of the proposal +
+
+proposer: address +
+
+ creator of the proposal +
+
+start_time: u64 +
+
+ when voting begins. +
+
+end_time: u64 +
+
+ when voting ends. +
+
+for_votes: u128 +
+
+ count of voters who agree with the proposal +
+
+against_votes: u128 +
+
+ count of voters who're against the proposal +
+
+eta: u64 +
+
+ executable after this time. +
+
+action_delay: u64 +
+
+ after how long, the agreed proposal can be executed. +
+
+quorum_votes: u128 +
+
+ how many votes to reach to make the proposal pass. +
+
+action: Option::Option<Action> +
+
+ proposal action. +
+
+ + +
+ + + +## Resource `Vote` + +User vote info. + + +
struct Vote<TokenT: store> has key
+
+ + + +
+Fields + + +
+
+proposer: address +
+
+ vote for the proposal under the proposer. +
+
+id: u64 +
+
+ proposal id. +
+
+stake: Token::Token<TokenT> +
+
+ how many tokens to stake. +
+
+agree: bool +
+
+ vote for or vote against. +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_NOT_AUTHORIZED: u64 = 1401;
+
+ + + + + + + +
const ACTIVE: u8 = 2;
+
+ + + + + + + +
const AGREED: u8 = 4;
+
+ + + + + + + +
const DEFEATED: u8 = 3;
+
+ + + + + + + +
const ERR_ACTION_DELAY_TOO_SMALL: u64 = 1402;
+
+ + + + + + + +
const ERR_ACTION_MUST_EXIST: u64 = 1409;
+
+ + + + + + + +
const ERR_CONFIG_PARAM_INVALID: u64 = 1407;
+
+ + + + + + + +
const ERR_PROPOSAL_ID_MISMATCH: u64 = 1404;
+
+ + + + + + + +
const ERR_PROPOSAL_STATE_INVALID: u64 = 1403;
+
+ + + + + + + +
const ERR_PROPOSER_MISMATCH: u64 = 1405;
+
+ + + + + + + +
const ERR_QUORUM_RATE_INVALID: u64 = 1406;
+
+ + + + + + + +
const ERR_VOTED_OTHERS_ALREADY: u64 = 1410;
+
+ + + + + + + +
const ERR_VOTE_STATE_MISMATCH: u64 = 1408;
+
+ + + + + + + +
const EXECUTABLE: u8 = 6;
+
+ + + + + + + +
const EXTRACTED: u8 = 7;
+
+ + + + + +Proposal state + + +
const PENDING: u8 = 1;
+
+ + + + + + + +
const QUEUED: u8 = 5;
+
+ + + + + +## Function `plugin` + +plugin function, can only be called by token issuer. +Any token who wants to have gov functionality +can optin this module by call this register function. + + +
public fun plugin<TokenT: copy, drop, store>(signer: &signer, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64)
+
+ + + +
+Implementation + + +
public fun plugin<TokenT: copy + drop + store>(
+    signer: &signer,
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+) {
+    let token_issuer = Token::token_address<TokenT>();
+    assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    // let proposal_id = ProposalId {next: 0};
+    let gov_info = DaoGlobalInfo<TokenT> {
+        next_proposal_id: 0,
+        proposal_create_event: Event::new_event_handle<ProposalCreatedEvent>(signer),
+        vote_changed_event: Event::new_event_handle<VoteChangedEvent>(signer),
+    };
+    move_to(signer, gov_info);
+    let config = new_dao_config<TokenT>(
+        voting_delay,
+        voting_period,
+        voting_quorum_rate,
+        min_action_delay,
+    );
+    Config::publish_new_config(signer, config);
+}
+
+ + + +
+ +
+Specification + + + +
let sender = Signer::address_of(signer);
+aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS();
+include NewDaoConfigParamSchema<TokenT>;
+include Config::PublishNewConfigAbortsIf<DaoConfig<TokenT>>{account: signer};
+aborts_if exists<DaoGlobalInfo<TokenT>>(sender);
+
+ + + + + + + +
schema RequirePluginDao<TokenT> {
+    let token_addr = Token::SPEC_TOKEN_TEST_ADDRESS();
+    aborts_if !exists<DaoGlobalInfo<TokenT>>(token_addr);
+    aborts_if !exists<Config::Config<DaoConfig<TokenT>>>(token_addr);
+}
+
+ + + + + + + +
schema AbortIfDaoInfoNotExist<TokenT> {
+    let token_addr = Token::SPEC_TOKEN_TEST_ADDRESS();
+    aborts_if !exists<DaoGlobalInfo<TokenT>>(token_addr);
+}
+
+ + + + + + + +
schema AbortIfDaoConfigNotExist<TokenT> {
+    let token_addr = Token::SPEC_TOKEN_TEST_ADDRESS();
+    aborts_if !exists<Config::Config<DaoConfig<TokenT>>>(token_addr);
+}
+
+ + + + + + + +
schema AbortIfTimestampNotExist {
+    aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+}
+
+ + + + +
apply
+    AbortIfDaoInfoNotExist<TokenT>
+to
+    generate_next_proposal_id<TokenT>;
+apply
+    AbortIfDaoConfigNotExist<TokenT>
+to
+    get_config<TokenT>,
+    voting_delay<TokenT>,
+    voting_period<TokenT>,
+    voting_quorum_rate<TokenT>,
+    min_action_delay<TokenT>,
+    quorum_votes<TokenT>;
+
+ + + +
+ + + +## Function `new_dao_config` + +create a dao config + + +
public fun new_dao_config<TokenT: copy, drop, store>(voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64): Dao::DaoConfig<TokenT>
+
+ + + +
+Implementation + + +
public fun new_dao_config<TokenT: copy + drop + store>(
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+): DaoConfig<TokenT> {
+    assert!(voting_delay > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID));
+    assert!(voting_period > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID));
+    assert!(
+        voting_quorum_rate > 0 && voting_quorum_rate <= 100,
+        Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID),
+    );
+    assert!(min_action_delay > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID));
+    DaoConfig { voting_delay, voting_period, voting_quorum_rate, min_action_delay }
+}
+
+ + + +
+ +
+Specification + + + +
include NewDaoConfigParamSchema<TokenT>;
+
+ + + + + + + +
schema NewDaoConfigParamSchema<TokenT> {
+    voting_delay: u64;
+    voting_period: u64;
+    voting_quorum_rate: u8;
+    min_action_delay: u64;
+    aborts_if voting_delay == 0;
+    aborts_if voting_period == 0;
+    aborts_if voting_quorum_rate == 0 || voting_quorum_rate > 100;
+    aborts_if min_action_delay == 0;
+}
+
+ + + +
+ + + +## Function `propose` + +propose a proposal. +action: the actual action to execute. +action_delay: the delay to execute after the proposal is agreed + + +
public fun propose<TokenT: copy, drop, store, ActionT: copy, drop, store>(signer: &signer, action: ActionT, action_delay: u64)
+
+ + + +
+Implementation + + +
public fun propose<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    signer: &signer,
+    action: ActionT,
+    action_delay: u64,
+) acquires DaoGlobalInfo {
+    if (action_delay == 0) {
+        action_delay = min_action_delay<TokenT>();
+    } else {
+        assert!(action_delay >= min_action_delay<TokenT>(), Errors::invalid_argument(ERR_ACTION_DELAY_TOO_SMALL));
+    };
+    let proposal_id = generate_next_proposal_id<TokenT>();
+    let proposer = Signer::address_of(signer);
+    let start_time = Timestamp::now_milliseconds() + voting_delay<TokenT>();
+    let quorum_votes = quorum_votes<TokenT>();
+    let proposal = Proposal<TokenT, ActionT> {
+        id: proposal_id,
+        proposer,
+        start_time,
+        end_time: start_time + voting_period<TokenT>(),
+        for_votes: 0,
+        against_votes: 0,
+        eta: 0,
+        action_delay,
+        quorum_votes,
+        action: Option::some(action),
+    };
+    move_to(signer, proposal);
+    // emit event
+    let gov_info = borrow_global_mut<DaoGlobalInfo<TokenT>>(Token::token_address<TokenT>());
+    Event::emit_event(
+        &mut gov_info.proposal_create_event,
+        ProposalCreatedEvent { proposal_id, proposer },
+    );
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+let proposer = Signer::address_of(signer);
+include GenerateNextProposalIdSchema<TokenT>;
+pragma addition_overflow_unchecked = true;
+include AbortIfDaoConfigNotExist<TokenT>;
+include AbortIfDaoInfoNotExist<TokenT>;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if action_delay > 0 && action_delay < spec_dao_config<TokenT>().min_action_delay;
+include CheckQuorumVotes<TokenT>;
+let sender = Signer::address_of(signer);
+aborts_if exists<Proposal<TokenT, ActionT>>(sender);
+modifies global<DaoGlobalInfo<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+ensures exists<Proposal<TokenT, ActionT>>(sender);
+
+ + + +
+ + + +## Function `cast_vote` + +votes for a proposal. +User can only vote once, then the stake is locked, +which can only be unstaked by user after the proposal is expired, or cancelled, or executed. +So think twice before casting vote. + + +
public fun cast_vote<TokenT: copy, drop, store, ActionT: copy, drop, store>(signer: &signer, proposer_address: address, proposal_id: u64, stake: Token::Token<TokenT>, agree: bool)
+
+ + + +
+Implementation + + +
public fun cast_vote<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    signer: &signer,
+    proposer_address: address,
+    proposal_id: u64,
+    stake: Token::Token<TokenT>,
+    agree: bool,
+) acquires Proposal, DaoGlobalInfo, Vote {
+    {
+        let state = proposal_state<TokenT, ActionT>(proposer_address, proposal_id);
+        // only when proposal is active, use can cast vote.
+        assert!(state == ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID));
+    };
+    let proposal = borrow_global_mut<Proposal<TokenT, ActionT>>(proposer_address);
+    assert!(proposal.id == proposal_id, Errors::invalid_argument(ERR_PROPOSAL_ID_MISMATCH));
+    let sender = Signer::address_of(signer);
+    let total_voted = if (exists<Vote<TokenT>>(sender)) {
+        let my_vote = borrow_global_mut<Vote<TokenT>>(sender);
+        assert!(my_vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY));
+        assert!(my_vote.agree == agree, Errors::invalid_state(ERR_VOTE_STATE_MISMATCH));
+
+        do_cast_vote(proposal, my_vote, stake);
+        Token::value(&my_vote.stake)
+    } else {
+        let my_vote = Vote<TokenT> {
+            proposer: proposer_address,
+            id: proposal_id,
+            stake: Token::zero(),
+            agree,
+        };
+        do_cast_vote(proposal, &mut my_vote, stake);
+        let total_voted = Token::value(&my_vote.stake);
+        move_to(signer, my_vote);
+        total_voted
+    };
+
+    // emit event
+    let gov_info = borrow_global_mut<DaoGlobalInfo<TokenT>>(Token::token_address<TokenT>());
+    Event::emit_event(
+        &mut gov_info.vote_changed_event,
+        VoteChangedEvent {
+            proposal_id,
+            proposer: proposer_address,
+            voter: sender,
+            agree,
+            vote: total_voted,
+        },
+    );
+}
+
+ + + +
+ +
+Specification + + + +
pragma addition_overflow_unchecked = true;
+include AbortIfDaoInfoNotExist<TokenT>;
+let expected_states = vec(ACTIVE);
+include CheckProposalStates<TokenT, ActionT> {expected_states};
+let sender = Signer::address_of(signer);
+let vote_exists = exists<Vote<TokenT>>(sender);
+include vote_exists ==> CheckVoteOnCast<TokenT, ActionT> {
+    voter: sender,
+    proposal_id: proposal_id,
+    agree: agree,
+    stake_value: stake.value,
+};
+modifies global<Proposal<TokenT, ActionT>>(proposer_address);
+ensures !vote_exists ==> global<Vote<TokenT>>(sender).stake.value == stake.value;
+
+ + + +
+ + + +## Function `do_cast_vote` + + + +
fun do_cast_vote<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposal: &mut Dao::Proposal<TokenT, ActionT>, vote: &mut Dao::Vote<TokenT>, stake: Token::Token<TokenT>)
+
+ + + +
+Implementation + + +
fun do_cast_vote<TokenT: copy + drop + store, ActionT: copy + drop + store>(proposal: &mut Proposal<TokenT, ActionT>, vote: &mut Vote<TokenT>, stake: Token::Token<TokenT>) {
+    let stake_value = Token::value(&stake);
+    Token::deposit(&mut vote.stake, stake);
+    if (vote.agree) {
+        proposal.for_votes = proposal.for_votes + stake_value;
+    } else {
+        proposal.against_votes = proposal.against_votes + stake_value;
+    };
+}
+
+ + + +
+ +
+Specification + + + +
pragma addition_overflow_unchecked = true;
+aborts_if vote.stake.value + stake.value > MAX_U128;
+ensures vote.stake.value == old(vote).stake.value + stake.value;
+ensures vote.agree ==> old(proposal).for_votes + stake.value == proposal.for_votes;
+ensures vote.agree ==> old(proposal).against_votes == proposal.against_votes;
+ensures !vote.agree ==> old(proposal).against_votes + stake.value == proposal.against_votes;
+ensures !vote.agree ==> old(proposal).for_votes == proposal.for_votes;
+
+ + + +
+ + + +## Function `change_vote` + +Let user change their vote during the voting time. + + +
public fun change_vote<TokenT: copy, drop, store, ActionT: copy, drop, store>(signer: &signer, proposer_address: address, proposal_id: u64, agree: bool)
+
+ + + +
+Implementation + + +
public fun change_vote<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    signer: &signer,
+    proposer_address: address,
+    proposal_id: u64,
+    agree: bool,
+) acquires Proposal, DaoGlobalInfo, Vote {
+    {
+        let state = proposal_state<TokenT, ActionT>(proposer_address, proposal_id);
+        // only when proposal is active, user can change vote.
+        assert!(state == ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID));
+    };
+    let proposal = borrow_global_mut<Proposal<TokenT, ActionT>>(proposer_address);
+    assert!(proposal.id == proposal_id, Errors::invalid_argument(ERR_PROPOSAL_ID_MISMATCH));
+    let my_vote = borrow_global_mut<Vote<TokenT>>(Signer::address_of(signer));
+    {
+        assert!(my_vote.proposer == proposer_address, Errors::invalid_argument(ERR_PROPOSER_MISMATCH));
+        assert!(my_vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY));
+    };
+
+    // flip the vote
+    if (my_vote.agree != agree) {
+        let total_voted = do_flip_vote(my_vote, proposal);
+        // emit event
+        let gov_info = borrow_global_mut<DaoGlobalInfo<TokenT>>(Token::token_address<TokenT>());
+        Event::emit_event(
+            &mut gov_info.vote_changed_event,
+            VoteChangedEvent {
+                proposal_id,
+                proposer: proposer_address,
+                voter: Signer::address_of(signer),
+                agree,
+                vote: total_voted,
+            },
+        );
+    };
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+let expected_states = vec(ACTIVE);
+include CheckProposalStates<TokenT, ActionT>{expected_states};
+let sender = Signer::address_of(signer);
+aborts_if !exists<Vote<TokenT>>(sender);
+let vote = global<Vote<TokenT>>(sender);
+include CheckVoteOnProposal<TokenT>{vote, proposer_address, proposal_id};
+include vote.agree != agree ==> CheckChangeVote<TokenT, ActionT>{vote, proposer_address};
+ensures vote.agree != agree ==> vote.agree == agree;
+
+ + + +
+ + + +## Function `do_flip_vote` + + + +
fun do_flip_vote<TokenT: copy, drop, store, ActionT: copy, drop, store>(my_vote: &mut Dao::Vote<TokenT>, proposal: &mut Dao::Proposal<TokenT, ActionT>): u128
+
+ + + +
+Implementation + + +
fun do_flip_vote<TokenT: copy + drop + store, ActionT: copy + drop + store>(my_vote: &mut Vote<TokenT>, proposal: &mut Proposal<TokenT, ActionT>): u128 {
+    my_vote.agree = !my_vote.agree;
+    let total_voted = Token::value(&my_vote.stake);
+    if (my_vote.agree) {
+        proposal.for_votes = proposal.for_votes + total_voted;
+        proposal.against_votes = proposal.against_votes - total_voted;
+    } else {
+        proposal.for_votes = proposal.for_votes - total_voted;
+        proposal.against_votes = proposal.against_votes + total_voted;
+    };
+    total_voted
+}
+
+ + + +
+ +
+Specification + + + +
include CheckFlipVote<TokenT, ActionT>;
+ensures my_vote.agree == !old(my_vote).agree;
+
+ + + +
+ + + +## Function `revoke_vote` + +Revoke some voting powers from vote on proposal_id of proposer_address. + + +
public fun revoke_vote<TokenT: copy, drop, store, ActionT: copy, drop, store>(signer: &signer, proposer_address: address, proposal_id: u64, voting_power: u128): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
public fun revoke_vote<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    signer: &signer,
+    proposer_address: address,
+    proposal_id: u64,
+    voting_power: u128,
+): Token::Token<TokenT> acquires Proposal, Vote, DaoGlobalInfo {
+    {
+        let state = proposal_state<TokenT, ActionT>(proposer_address, proposal_id);
+        // only when proposal is active, user can revoke vote.
+        assert!(state == ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID));
+    };
+    // get proposal
+    let proposal = borrow_global_mut<Proposal<TokenT, ActionT>>(proposer_address);
+
+    // get vote
+    let my_vote = move_from<Vote<TokenT>>(Signer::address_of(signer));
+    {
+        assert!(my_vote.proposer == proposer_address, Errors::invalid_argument(ERR_PROPOSER_MISMATCH));
+        assert!(my_vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY));
+    };
+    // revoke vote on proposal
+    let reverted_stake =do_revoke_vote(proposal, &mut my_vote, voting_power);
+    // emit vote changed event
+    let gov_info = borrow_global_mut<DaoGlobalInfo<TokenT>>(Token::token_address<TokenT>());
+    Event::emit_event(
+        &mut gov_info.vote_changed_event,
+        VoteChangedEvent {
+            proposal_id,
+            proposer: proposer_address,
+            voter: Signer::address_of(signer),
+            agree: my_vote.agree,
+            vote: Token::value(&my_vote.stake),
+        },
+    );
+
+    // if user has no stake, destroy his vote. resolve https://github.com/starcoinorg/starcoin/issues/2925.
+    if (Token::value(&my_vote.stake) == 0u128) {
+        let Vote {stake, proposer: _, id: _, agree: _} = my_vote;
+        Token::destroy_zero(stake);
+    } else {
+        move_to(signer, my_vote);
+    };
+
+    reverted_stake
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+include AbortIfDaoInfoNotExist<TokenT>;
+let expected_states = vec(ACTIVE);
+include CheckProposalStates<TokenT, ActionT> {expected_states};
+let sender = Signer::address_of(signer);
+aborts_if !exists<Vote<TokenT>>(sender);
+let vote = global<Vote<TokenT>>(sender);
+include CheckVoteOnProposal<TokenT> {vote, proposer_address, proposal_id};
+include CheckRevokeVote<TokenT, ActionT> {
+    vote,
+    proposal: global<Proposal<TokenT, ActionT>>(proposer_address),
+    to_revoke: voting_power,
+};
+modifies global<Vote<TokenT>>(sender);
+modifies global<Proposal<TokenT, ActionT>>(proposer_address);
+modifies global<DaoGlobalInfo<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+ensures global<Vote<TokenT>>(sender).stake.value + result.value == old(global<Vote<TokenT>>(sender)).stake.value;
+ensures result.value == voting_power;
+
+ + + +
+ + + +## Function `do_revoke_vote` + + + +
fun do_revoke_vote<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposal: &mut Dao::Proposal<TokenT, ActionT>, vote: &mut Dao::Vote<TokenT>, to_revoke: u128): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
fun do_revoke_vote<TokenT: copy + drop + store, ActionT: copy + drop + store>(proposal: &mut Proposal<TokenT, ActionT>, vote: &mut Vote<TokenT>, to_revoke: u128): Token::Token<TokenT> {
+    spec {
+        assume vote.stake.value >= to_revoke;
+    };
+    let reverted_stake = Token::withdraw(&mut vote.stake, to_revoke);
+    if (vote.agree) {
+        proposal.for_votes = proposal.for_votes - to_revoke;
+    } else {
+        proposal.against_votes = proposal.against_votes - to_revoke;
+    };
+    spec {
+        assert Token::value(reverted_stake) == to_revoke;
+    };
+    reverted_stake
+}
+
+ + + +
+ +
+Specification + + + +
include CheckRevokeVote<TokenT, ActionT>;
+ensures vote.agree ==> old(proposal).for_votes == proposal.for_votes + to_revoke;
+ensures !vote.agree ==> old(proposal).against_votes == proposal.against_votes + to_revoke;
+ensures result.value == to_revoke;
+
+ + + +
+ + + +## Function `unstake_votes` + +Retrieve back my staked token voted for a proposal. + + +
public fun unstake_votes<TokenT: copy, drop, store, ActionT: copy, drop, store>(signer: &signer, proposer_address: address, proposal_id: u64): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
public fun unstake_votes<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    signer: &signer,
+    proposer_address: address,
+    proposal_id: u64,
+): Token::Token<TokenT> acquires Proposal, Vote {
+    // only check state when proposal exists.
+    // because proposal can be destroyed after it ends in DEFEATED or EXTRACTED state.
+    if (proposal_exists<TokenT, ActionT>(proposer_address, proposal_id)) {
+        let state = proposal_state<TokenT, ActionT>(proposer_address, proposal_id);
+        // Only after vote period end, user can unstake his votes.
+        assert!(state > ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID));
+    };
+    let Vote { proposer, id, stake, agree: _ } = move_from<Vote<TokenT>>(
+        Signer::address_of(signer),
+    );
+    // these checks are still required.
+    assert!(proposer == proposer_address, Errors::requires_address(ERR_PROPOSER_MISMATCH));
+    assert!(id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY));
+    stake
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+let expected_states = vec(DEFEATED);
+let expected_states1 = concat(expected_states,vec(AGREED));
+let expected_states2 = concat(expected_states1,vec(QUEUED));
+let expected_states3 = concat(expected_states2,vec(EXECUTABLE));
+let expected_states4 = concat(expected_states3,vec(EXTRACTED));
+aborts_if expected_states4[0] != DEFEATED;
+aborts_if expected_states4[1] != AGREED;
+aborts_if expected_states4[2] != QUEUED;
+aborts_if expected_states4[3] != EXECUTABLE;
+aborts_if expected_states4[4] != EXTRACTED;
+include spec_proposal_exists<TokenT, ActionT>(proposer_address, proposal_id) ==>
+            CheckProposalStates<TokenT, ActionT>{expected_states: expected_states4};
+let sender = Signer::address_of(signer);
+aborts_if !exists<Vote<TokenT>>(sender);
+let vote = global<Vote<TokenT>>(sender);
+include CheckVoteOnProposal<TokenT>{vote, proposer_address, proposal_id};
+ensures !exists<Vote<TokenT>>(sender);
+ensures result.value == old(vote).stake.value;
+
+ + + +
+ + + +## Function `queue_proposal_action` + +queue agreed proposal to execute. + + +
public entry fun queue_proposal_action<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun queue_proposal_action<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+) acquires Proposal {
+    // Only agreed proposal can be submitted.
+    assert!(
+        proposal_state<TokenT, ActionT>(proposer_address, proposal_id) == AGREED,
+        Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID)
+    );
+    let proposal = borrow_global_mut<Proposal<TokenT, ActionT>>(proposer_address);
+    proposal.eta = Timestamp::now_milliseconds() + proposal.action_delay;
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+let expected_states = vec(AGREED);
+include CheckProposalStates<TokenT, ActionT>{expected_states};
+let proposal = global<Proposal<TokenT, ActionT>>(proposer_address);
+aborts_if Timestamp::spec_now_millseconds() + proposal.action_delay > MAX_U64;
+ensures proposal.eta >= Timestamp::spec_now_millseconds();
+
+ + + +
+ + + +## Function `extract_proposal_action` + +extract proposal action to execute. + + +
public fun extract_proposal_action<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposer_address: address, proposal_id: u64): ActionT
+
+ + + +
+Implementation + + +
public fun extract_proposal_action<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+): ActionT acquires Proposal {
+    // Only executable proposal's action can be extracted.
+    assert!(
+        proposal_state<TokenT, ActionT>(proposer_address, proposal_id) == EXECUTABLE,
+        Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID),
+    );
+    let proposal = borrow_global_mut<Proposal<TokenT, ActionT>>(proposer_address);
+    let action: ActionT = Option::extract(&mut proposal.action);
+    action
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+let expected_states = vec(EXECUTABLE);
+include CheckProposalStates<TokenT, ActionT>{expected_states};
+modifies global<Proposal<TokenT, ActionT>>(proposer_address);
+ensures Option::is_none(global<Proposal<TokenT, ActionT>>(proposer_address).action);
+
+ + + +
+ + + +## Function `destroy_terminated_proposal` + +remove terminated proposal from proposer + + +
public entry fun destroy_terminated_proposal<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun destroy_terminated_proposal<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+) acquires Proposal {
+    let proposal_state = proposal_state<TokenT, ActionT>(proposer_address, proposal_id);
+    assert!(
+        proposal_state == DEFEATED || proposal_state == EXTRACTED,
+        Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID),
+    );
+    let Proposal {
+        id: _,
+        proposer: _,
+        start_time: _,
+        end_time: _,
+        for_votes: _,
+        against_votes: _,
+        eta: _,
+        action_delay: _,
+        quorum_votes: _,
+        action,
+    } = move_from<Proposal<TokenT, ActionT>>(proposer_address);
+    if (proposal_state == DEFEATED) {
+        let _ = Option::extract(&mut action);
+    };
+    Option::destroy_none(action);
+}
+
+ + + +
+ +
+Specification + + + +
let expected_states = concat(vec(DEFEATED), vec(EXTRACTED));
+aborts_if len(expected_states) != 2;
+aborts_if expected_states[0] != DEFEATED;
+aborts_if expected_states[1] != EXTRACTED;
+aborts_if !exists<Proposal<TokenT, ActionT>>(proposer_address);
+let proposal = global<Proposal<TokenT, ActionT>>(proposer_address);
+aborts_if proposal.id != proposal_id;
+include AbortIfTimestampNotExist;
+let current_time = Timestamp::spec_now_millseconds();
+let state = do_proposal_state(proposal, current_time);
+aborts_if (forall s in expected_states : s != state);
+aborts_if state == DEFEATED && Option::is_none(global<Proposal<TokenT, ActionT>>(proposer_address).action);
+aborts_if state == EXTRACTED && Option::is_some(global<Proposal<TokenT, ActionT>>(proposer_address).action);
+modifies global<Proposal<TokenT, ActionT>>(proposer_address);
+
+ + + +
+ + + +## Function `proposal_exists` + +check whether a proposal exists in proposer_address with id proposal_id. + + +
public fun proposal_exists<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposer_address: address, proposal_id: u64): bool
+
+ + + +
+Implementation + + +
public fun proposal_exists<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+): bool acquires Proposal {
+    if (exists<Proposal<TokenT, ActionT>>(proposer_address)) {
+        let proposal = borrow_global<Proposal<TokenT, ActionT>>(proposer_address);
+        return proposal.id == proposal_id
+    };
+    false
+}
+
+ + + +
+ +
+Specification + + + +
ensures exists<Proposal<TokenT, ActionT>>(proposer_address) &&
+            borrow_global<Proposal<TokenT, ActionT>>(proposer_address).id == proposal_id ==>
+            result;
+
+ + + + + + + +
fun spec_proposal_exists<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+   proposer_address: address,
+   proposal_id: u64,
+): bool {
+   if (exists<Proposal<TokenT, ActionT>>(proposer_address)) {
+       let proposal = global<Proposal<TokenT, ActionT>>(proposer_address);
+       proposal.id == proposal_id
+   } else {
+       false
+   }
+}
+
+ + + +
+ + + +## Function `proposal_state` + +Get the proposal state. + + +
public fun proposal_state<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposer_address: address, proposal_id: u64): u8
+
+ + + +
+Implementation + + +
public fun proposal_state<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+): u8 acquires Proposal {
+    let proposal = borrow_global<Proposal<TokenT, ActionT>>(proposer_address);
+    assert!(proposal.id == proposal_id, Errors::invalid_argument(ERR_PROPOSAL_ID_MISMATCH));
+    let current_time = Timestamp::now_milliseconds();
+    do_proposal_state(proposal, current_time)
+}
+
+ + + +
+ +
+Specification + + + +
include AbortIfTimestampNotExist;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if !exists<Proposal<TokenT, ActionT>>(proposer_address);
+let proposal = global<Proposal<TokenT, ActionT>>(proposer_address);
+aborts_if proposal.id != proposal_id;
+
+ + + +
+ + + +## Function `do_proposal_state` + + + +
fun do_proposal_state<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposal: &Dao::Proposal<TokenT, ActionT>, current_time: u64): u8
+
+ + + +
+Implementation + + +
fun do_proposal_state<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    proposal: &Proposal<TokenT, ActionT>,
+    current_time: u64,
+): u8 {
+    if (current_time < proposal.start_time) {
+        // Pending
+        PENDING
+    } else if (current_time <= proposal.end_time) {
+        // Active
+        ACTIVE
+    } else if (proposal.for_votes <= proposal.against_votes ||
+        proposal.for_votes < proposal.quorum_votes) {
+        // Defeated
+        DEFEATED
+    } else if (proposal.eta == 0) {
+        // Agreed.
+        AGREED
+    } else if (current_time < proposal.eta) {
+        // Queued, waiting to execute
+        QUEUED
+    } else if (Option::is_some(&proposal.action)) {
+        EXECUTABLE
+    } else {
+        EXTRACTED
+    }
+}
+
+ + + +
+ + + +## Function `proposal_info` + +get proposal's information. +return: (id, start_time, end_time, for_votes, against_votes). + + +
public fun proposal_info<TokenT: copy, drop, store, ActionT: copy, drop, store>(proposer_address: address): (u64, u64, u64, u128, u128)
+
+ + + +
+Implementation + + +
public fun proposal_info<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    proposer_address: address,
+): (u64, u64, u64, u128, u128) acquires Proposal {
+    let proposal = borrow_global<Proposal<TokenT, ActionT>>(proposer_address);
+    (proposal.id, proposal.start_time, proposal.end_time, proposal.for_votes, proposal.against_votes)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Proposal<TokenT, ActionT>>(proposer_address);
+
+ + + +
+ + + +## Function `vote_of` + +Get voter's vote info on proposal with proposal_id of proposer_address. + + +
public fun vote_of<TokenT: copy, drop, store>(voter: address, proposer_address: address, proposal_id: u64): (bool, u128)
+
+ + + +
+Implementation + + +
public fun vote_of<TokenT: copy + drop + store>(
+    voter: address,
+    proposer_address: address,
+    proposal_id: u64,
+): (bool, u128) acquires Vote {
+    let vote = borrow_global<Vote<TokenT>>(voter);
+    assert!(vote.proposer == proposer_address, Errors::requires_address(ERR_PROPOSER_MISMATCH));
+    assert!(vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY));
+    (vote.agree, Token::value(&vote.stake))
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Vote<TokenT>>(voter);
+let vote = global<Vote<TokenT>>(voter);
+include CheckVoteOnProposal<TokenT>{vote, proposer_address, proposal_id};
+
+ + + +
+ + + +## Function `has_vote` + +Check whether voter has voted on proposal with proposal_id of proposer_address. + + +
public fun has_vote<TokenT: copy, drop, store>(voter: address, proposer_address: address, proposal_id: u64): bool
+
+ + + +
+Implementation + + +
public fun has_vote<TokenT: copy + drop + store>(
+    voter: address,
+    proposer_address: address,
+    proposal_id: u64,
+): bool acquires Vote {
+    if (!exists<Vote<TokenT>>(voter)) {
+        return false
+    };
+
+    let vote = borrow_global<Vote<TokenT>>(voter);
+    vote.proposer == proposer_address && vote.id == proposal_id
+}
+
+ + + +
+ + + +## Function `generate_next_proposal_id` + + + +
fun generate_next_proposal_id<TokenT: store>(): u64
+
+ + + +
+Implementation + + +
fun generate_next_proposal_id<TokenT: store>(): u64 acquires DaoGlobalInfo {
+    let gov_info = borrow_global_mut<DaoGlobalInfo<TokenT>>(Token::token_address<TokenT>());
+    let proposal_id = gov_info.next_proposal_id;
+    gov_info.next_proposal_id = proposal_id + 1;
+    proposal_id
+}
+
+ + + +
+ +
+Specification + + + +
include GenerateNextProposalIdSchema<TokenT>;
+ensures result == old(global<DaoGlobalInfo<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id);
+
+ + + + + + + +
schema GenerateNextProposalIdSchema<TokenT> {
+    aborts_if global<DaoGlobalInfo<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id >= MAX_U64;
+    modifies global<DaoGlobalInfo<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+    ensures
+        global<DaoGlobalInfo<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id ==
+        old(global<DaoGlobalInfo<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id) + 1;
+}
+
+ + + +
+ + + +## Function `voting_delay` + +get default voting delay of the DAO. + + +
public fun voting_delay<TokenT: copy, drop, store>(): u64
+
+ + + +
+Implementation + + +
public fun voting_delay<TokenT: copy + drop + store>(): u64 {
+    get_config<TokenT>().voting_delay
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `voting_period` + +get the default voting period of the DAO. + + +
public fun voting_period<TokenT: copy, drop, store>(): u64
+
+ + + +
+Implementation + + +
public fun voting_period<TokenT: copy + drop + store>(): u64 {
+    get_config<TokenT>().voting_period
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `quorum_votes` + +Quorum votes to make proposal pass. + + +
public fun quorum_votes<TokenT: copy, drop, store>(): u128
+
+ + + +
+Implementation + + +
public fun quorum_votes<TokenT: copy + drop + store>(): u128 {
+    let market_cap = Token::market_cap<TokenT>();
+    let balance_in_treasury = Treasury::balance<TokenT>();
+    let supply = market_cap - balance_in_treasury;
+    let rate = voting_quorum_rate<TokenT>();
+    let rate = (rate as u128);
+    supply * rate / 100
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+include CheckQuorumVotes<TokenT>;
+
+ + + + + + + +
fun spec_quorum_votes<TokenT: copy + drop + store>(): u128 {
+   let supply = Token::spec_abstract_total_value<TokenT>() - Treasury::spec_balance<TokenT>();
+   supply * spec_dao_config<TokenT>().voting_quorum_rate / 100
+}
+
+ + + +
+ + + +## Function `voting_quorum_rate` + +Get the quorum rate in percent. + + +
public fun voting_quorum_rate<TokenT: copy, drop, store>(): u8
+
+ + + +
+Implementation + + +
public fun voting_quorum_rate<TokenT: copy + drop + store>(): u8 {
+    get_config<TokenT>().voting_quorum_rate
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == global<Config::Config<DaoConfig<TokenT>>>((Token::SPEC_TOKEN_TEST_ADDRESS())).payload.voting_quorum_rate;
+
+ + + +
+ + + +## Function `min_action_delay` + +Get the min_action_delay of the DAO. + + +
public fun min_action_delay<TokenT: copy, drop, store>(): u64
+
+ + + +
+Implementation + + +
public fun min_action_delay<TokenT: copy + drop + store>(): u64 {
+    get_config<TokenT>().min_action_delay
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == spec_dao_config<TokenT>().min_action_delay;
+
+ + + +
+ + + +## Function `get_config` + + + +
fun get_config<TokenT: copy, drop, store>(): Dao::DaoConfig<TokenT>
+
+ + + +
+Implementation + + +
fun get_config<TokenT: copy + drop + store>(): DaoConfig<TokenT> {
+    let token_issuer = Token::token_address<TokenT>();
+    Config::get_by_address<DaoConfig<TokenT>>(token_issuer)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == global<Config::Config<DaoConfig<TokenT>>>(Token::SPEC_TOKEN_TEST_ADDRESS()).payload;
+
+ + + + + + + +
fun spec_dao_config<TokenT: copy + drop + store>(): DaoConfig<TokenT> {
+   global<Config::Config<DaoConfig<TokenT>>>((Token::SPEC_TOKEN_TEST_ADDRESS())).payload
+}
+
+ + + + + + + +
schema CheckModifyConfigWithCap<TokenT> {
+    cap: Config::ModifyConfigCapability<DaoConfig<TokenT>>;
+    aborts_if cap.account_address != Token::SPEC_TOKEN_TEST_ADDRESS();
+    aborts_if !exists<Config::Config<DaoConfig<TokenT>>>(cap.account_address);
+}
+
+ + + +
+ + + +## Function `modify_dao_config` + +update function, modify dao config. +if any param is 0, it means no change to that param. + + +
public fun modify_dao_config<TokenT: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<Dao::DaoConfig<TokenT>>, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64)
+
+ + + +
+Implementation + + +
public fun modify_dao_config<TokenT: copy + drop + store>(
+    cap: &mut Config::ModifyConfigCapability<DaoConfig<TokenT>>,
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+) {
+    assert!(Config::account_address(cap) == Token::token_address<TokenT>(), Errors::invalid_argument(ERR_NOT_AUTHORIZED));
+    let config = get_config<TokenT>();
+    if (voting_period > 0) {
+        config.voting_period = voting_period;
+    };
+    if (voting_delay > 0) {
+        config.voting_delay = voting_delay;
+    };
+    if (voting_quorum_rate > 0) {
+        assert!(voting_quorum_rate <= 100, Errors::invalid_argument(ERR_QUORUM_RATE_INVALID));
+        config.voting_quorum_rate = voting_quorum_rate;
+    };
+    if (min_action_delay > 0) {
+        config.min_action_delay = min_action_delay;
+    };
+    Config::set_with_capability<DaoConfig<TokenT>>(cap, config);
+}
+
+ + + +
+ +
+Specification + + + +
include CheckModifyConfigWithCap<TokenT>;
+aborts_if voting_quorum_rate > 0 && voting_quorum_rate > 100;
+
+ + + +
+ + + +## Function `set_voting_delay` + +set voting delay + + +
public fun set_voting_delay<TokenT: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<Dao::DaoConfig<TokenT>>, value: u64)
+
+ + + +
+Implementation + + +
public fun set_voting_delay<TokenT: copy + drop + store>(
+    cap: &mut Config::ModifyConfigCapability<DaoConfig<TokenT>>,
+    value: u64,
+) {
+    assert!(Config::account_address(cap) == Token::token_address<TokenT>(), Errors::invalid_argument(ERR_NOT_AUTHORIZED));
+    assert!(value > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID));
+    let config = get_config<TokenT>();
+    config.voting_delay = value;
+    Config::set_with_capability<DaoConfig<TokenT>>(cap, config);
+}
+
+ + + +
+ +
+Specification + + + +
include CheckModifyConfigWithCap<TokenT>;
+aborts_if value == 0;
+
+ + + +
+ + + +## Function `set_voting_period` + +set voting period + + +
public fun set_voting_period<TokenT: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<Dao::DaoConfig<TokenT>>, value: u64)
+
+ + + +
+Implementation + + +
public fun set_voting_period<TokenT: copy + drop + store>(
+    cap: &mut Config::ModifyConfigCapability<DaoConfig<TokenT>>,
+    value: u64,
+) {
+    assert!(Config::account_address(cap) == Token::token_address<TokenT>(), Errors::invalid_argument(ERR_NOT_AUTHORIZED));
+    assert!(value > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID));
+    let config = get_config<TokenT>();
+    config.voting_period = value;
+    Config::set_with_capability<DaoConfig<TokenT>>(cap, config);
+}
+
+ + + +
+ +
+Specification + + + +
include CheckModifyConfigWithCap<TokenT>;
+aborts_if value == 0;
+
+ + + +
+ + + +## Function `set_voting_quorum_rate` + +set voting quorum rate + + +
public fun set_voting_quorum_rate<TokenT: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<Dao::DaoConfig<TokenT>>, value: u8)
+
+ + + +
+Implementation + + +
public fun set_voting_quorum_rate<TokenT: copy + drop + store>(
+    cap: &mut Config::ModifyConfigCapability<DaoConfig<TokenT>>,
+    value: u8,
+) {
+    assert!(Config::account_address(cap) == Token::token_address<TokenT>(), Errors::invalid_argument(ERR_NOT_AUTHORIZED));
+    assert!(value <= 100 && value > 0, Errors::invalid_argument(ERR_QUORUM_RATE_INVALID));
+    let config = get_config<TokenT>();
+    config.voting_quorum_rate = value;
+    Config::set_with_capability<DaoConfig<TokenT>>(cap, config);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !(value > 0 && value <= 100);
+include CheckModifyConfigWithCap<TokenT>;
+
+ + + +
+ + + +## Function `set_min_action_delay` + +set min action delay + + +
public fun set_min_action_delay<TokenT: copy, drop, store>(cap: &mut Config::ModifyConfigCapability<Dao::DaoConfig<TokenT>>, value: u64)
+
+ + + +
+Implementation + + +
public fun set_min_action_delay<TokenT: copy + drop + store>(
+    cap: &mut Config::ModifyConfigCapability<DaoConfig<TokenT>>,
+    value: u64,
+) {
+    assert!(Config::account_address(cap) == Token::token_address<TokenT>(), Errors::invalid_argument(ERR_NOT_AUTHORIZED));
+    assert!(value > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID));
+    let config = get_config<TokenT>();
+    config.min_action_delay = value;
+    Config::set_with_capability<DaoConfig<TokenT>>(cap, config);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if value == 0;
+include CheckModifyConfigWithCap<TokenT>;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/DaoVoteScripts.md b/release/v13/docs/DaoVoteScripts.md new file mode 100644 index 00000000..7bd67431 --- /dev/null +++ b/release/v13/docs/DaoVoteScripts.md @@ -0,0 +1,197 @@ + + + +# Module `0x1::DaoVoteScripts` + + + +- [Function `cast_vote`](#0x1_DaoVoteScripts_cast_vote) +- [Function `revoke_vote`](#0x1_DaoVoteScripts_revoke_vote) +- [Function `flip_vote`](#0x1_DaoVoteScripts_flip_vote) +- [Function `revoke_vote_of_power`](#0x1_DaoVoteScripts_revoke_vote_of_power) +- [Function `unstake_vote`](#0x1_DaoVoteScripts_unstake_vote) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::Account;
+use 0x1::Dao;
+use 0x1::Signer;
+use 0x1::Token;
+
+ + + + + +## Function `cast_vote` + + + +
public entry fun cast_vote<Token: copy, drop, store, ActionT: copy, drop, store>(signer: signer, proposer_address: address, proposal_id: u64, agree: bool, votes: u128)
+
+ + + +
+Implementation + + +
public entry fun cast_vote<Token: copy + drop + store, ActionT: copy + drop + store>(
+    signer: signer,
+    proposer_address: address,
+    proposal_id: u64,
+    agree: bool,
+    votes: u128,
+) {
+    let sender = Signer::address_of(&signer);
+    if (Dao::has_vote<Token>(sender, proposer_address, proposal_id)) {
+        // if already voted, and vote is not same as the current cast, change the existing vote.
+        // resolve https://github.com/starcoinorg/starcoin/issues/2925.
+        let (agree_voted, _) = Dao::vote_of<Token>(sender, proposer_address, proposal_id);
+        if (agree_voted != agree) {
+            Dao::change_vote<Token, ActionT>(&signer, proposer_address, proposal_id, agree);
+        }
+    };
+
+    let votes = Account::withdraw<Token>(&signer, votes);
+    Dao::cast_vote<Token, ActionT>(&signer, proposer_address, proposal_id, votes, agree);
+}
+
+ + + +
+ + + +## Function `revoke_vote` + +revoke all votes on a proposal + + +
public entry fun revoke_vote<Token: copy, drop, store, Action: copy, drop, store>(signer: signer, proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun revoke_vote<Token: copy + drop + store, Action: copy + drop + store>(
+    signer: signer,
+    proposer_address: address,
+    proposal_id: u64,
+) {
+    let sender = Signer::address_of(&signer);
+    let (_, power) = Dao::vote_of<Token>(sender, proposer_address, proposal_id);
+    let my_token = Dao::revoke_vote<Token, Action>(&signer, proposer_address, proposal_id, power);
+    Account::deposit(sender, my_token);
+}
+
+ + + +
+ + + +## Function `flip_vote` + +Let user change their vote during the voting time. + + +
public entry fun flip_vote<TokenT: copy, drop, store, ActionT: copy, drop, store>(signer: signer, proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun flip_vote<TokenT: copy + drop + store, ActionT: copy + drop + store>(
+    signer: signer,
+    proposer_address: address,
+    proposal_id: u64,
+) {
+    let (agree, _) = Dao::vote_of<TokenT>(Signer::address_of(&signer), proposer_address, proposal_id);
+    Dao::change_vote<TokenT, ActionT>(&signer, proposer_address, proposal_id, !agree);
+}
+
+ + + +
+ + + +## Function `revoke_vote_of_power` + +revoke some votes on a proposal + + +
public entry fun revoke_vote_of_power<Token: copy, drop, store, Action: copy, drop, store>(signer: signer, proposer_address: address, proposal_id: u64, power: u128)
+
+ + + +
+Implementation + + +
public entry fun revoke_vote_of_power<Token: copy + drop + store, Action: copy + drop + store>(
+    signer: signer,
+    proposer_address: address,
+    proposal_id: u64,
+    power: u128,
+) {
+    let sender = Signer::address_of(&signer);
+    let my_token = Dao::revoke_vote<Token, Action>(&signer, proposer_address, proposal_id, power);
+    Account::deposit(sender, my_token);
+}
+
+ + + +
+ + + +## Function `unstake_vote` + + + +
public entry fun unstake_vote<Token: copy, drop, store, Action: copy, drop, store>(signer: signer, proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun unstake_vote<Token: copy + drop + store, Action: copy + drop + store>(
+    signer: signer,
+    proposer_address: address,
+    proposal_id: u64,
+) {
+    let my_token = Dao::unstake_votes<Token, Action>(&signer, proposer_address, proposal_id);
+    Account::deposit(Signer::address_of(&signer), my_token);
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_partial = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/Debug.md b/release/v13/docs/Debug.md new file mode 100644 index 00000000..9d714ba9 --- /dev/null +++ b/release/v13/docs/Debug.md @@ -0,0 +1,72 @@ + + + +# Module `0x1::Debug` + +The module provide debug print for Move. + + +- [Function `print`](#0x1_Debug_print) +- [Function `print_stack_trace`](#0x1_Debug_print_stack_trace) +- [Module Specification](#@Module_Specification_0) + + +
+ + + + + +## Function `print` + +Print data of Type T. + + +
public fun print<T: store>(x: &T)
+
+ + + +
+Implementation + + +
native public fun print<T: store>(x: &T);
+
+ + + +
+ + + +## Function `print_stack_trace` + +Print current stack. + + +
public fun print_stack_trace()
+
+ + + +
+Implementation + + +
native public fun print_stack_trace();
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/DummyToken.md b/release/v13/docs/DummyToken.md new file mode 100644 index 00000000..ebf1331d --- /dev/null +++ b/release/v13/docs/DummyToken.md @@ -0,0 +1,46 @@ + + + +# Module `0x1::DummyTokenScripts` + + + +- [Function `mint`](#0x1_DummyTokenScripts_mint) + + +
use 0x1::Account;
+use 0x1::DummyToken;
+use 0x1::Signer;
+use 0x1::Token;
+
+ + + + + +## Function `mint` + + + +
public entry fun mint(sender: signer, amount: u128)
+
+ + + +
+Implementation + + +
public entry fun mint(sender: signer, amount: u128){
+    let token = DummyToken::mint(&sender, amount);
+    let sender_addr = Signer::address_of(&sender);
+    if(Account::is_accept_token<DummyToken>(sender_addr)){
+        Account::do_accept_token<DummyToken>(&sender);
+    };
+    Account::deposit(sender_addr, token);
+}
+
+ + + +
diff --git a/release/v13/docs/EasyGas.md b/release/v13/docs/EasyGas.md new file mode 100644 index 00000000..13225827 --- /dev/null +++ b/release/v13/docs/EasyGas.md @@ -0,0 +1,140 @@ + + + +# Module `0x1::EasyGasScript` + + + +- [Function `register`](#0x1_EasyGasScript_register) +- [Function `init_data_source`](#0x1_EasyGasScript_init_data_source) +- [Function `update`](#0x1_EasyGasScript_update) +- [Function `withdraw_gas_fee_entry`](#0x1_EasyGasScript_withdraw_gas_fee_entry) +- [Function `deposit`](#0x1_EasyGasScript_deposit) + + +
use 0x1::EasyGas;
+use 0x1::TransferScripts;
+
+ + + + + +## Function `register` + + + +
public entry fun register<TokenType: store>(sender: signer, precision: u8)
+
+ + + +
+Implementation + + +
public entry fun register<TokenType: store>(sender: signer, precision: u8) {
+    EasyGas::register_oracle<TokenType>(&sender, precision)
+}
+
+ + + +
+ + + +## Function `init_data_source` + + + +
public entry fun init_data_source<TokenType: store>(sender: signer, init_value: u128)
+
+ + + +
+Implementation + + +
public entry fun init_data_source<TokenType: store>(sender: signer, init_value: u128) {
+    EasyGas::init_oracle_source<TokenType>(&sender, init_value);
+}
+
+ + + +
+ + + +## Function `update` + + + +
public entry fun update<TokenType: store>(sender: signer, value: u128)
+
+ + + +
+Implementation + + +
public entry fun update<TokenType: store>(sender: signer, value: u128) {
+    EasyGas::update_oracle<TokenType>(&sender, value)
+}
+
+ + + +
+ + + +## Function `withdraw_gas_fee_entry` + + + +
public entry fun withdraw_gas_fee_entry<TokenType: store>(sender: signer, amount: u128)
+
+ + + +
+Implementation + + +
public entry fun withdraw_gas_fee_entry<TokenType: store>(sender: signer, amount: u128) {
+    EasyGas::withdraw_gas_fee<TokenType>(&sender, amount);
+}
+
+ + + +
+ + + +## Function `deposit` + + + +
public entry fun deposit<TokenType: store>(sender: signer, amount: u128)
+
+ + + +
+Implementation + + +
public entry fun deposit<TokenType: store>(sender: signer, amount:u128)  {
+    let address = EasyGas::get_gas_fee_address();
+    peer_to_peer_v2<TokenType>(sender, address, amount)
+}
+
+ + + +
diff --git a/release/v13/docs/EmptyScripts.md b/release/v13/docs/EmptyScripts.md new file mode 100644 index 00000000..08c13a3c --- /dev/null +++ b/release/v13/docs/EmptyScripts.md @@ -0,0 +1,48 @@ + + + +# Module `0x1::EmptyScripts` + + + +- [Function `empty_script`](#0x1_EmptyScripts_empty_script) +- [Module Specification](#@Module_Specification_0) + + +
+ + + + + +## Function `empty_script` + + + +
public entry fun empty_script()
+
+ + + +
+Implementation + + +
public entry fun empty_script() {
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_partial = false;
+pragma aborts_if_is_strict = false;
+
diff --git a/release/v13/docs/Epoch.md b/release/v13/docs/Epoch.md new file mode 100644 index 00000000..d249b8d2 --- /dev/null +++ b/release/v13/docs/Epoch.md @@ -0,0 +1,1103 @@ + + + +# Module `0x1::Epoch` + +The module provide epoch functionality for starcoin. + + +- [Resource `Epoch`](#0x1_Epoch_Epoch) +- [Struct `NewEpochEvent`](#0x1_Epoch_NewEpochEvent) +- [Resource `EpochData`](#0x1_Epoch_EpochData) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_Epoch_initialize) +- [Function `compute_next_block_time_target`](#0x1_Epoch_compute_next_block_time_target) +- [Function `adjust_epoch`](#0x1_Epoch_adjust_epoch) +- [Function `adjust_gas_limit`](#0x1_Epoch_adjust_gas_limit) +- [Function `compute_gas_limit`](#0x1_Epoch_compute_gas_limit) +- [Function `in_or_decrease_gas_limit`](#0x1_Epoch_in_or_decrease_gas_limit) +- [Function `update_epoch_data`](#0x1_Epoch_update_epoch_data) +- [Function `emit_epoch_event`](#0x1_Epoch_emit_epoch_event) +- [Function `start_time`](#0x1_Epoch_start_time) +- [Function `uncles`](#0x1_Epoch_uncles) +- [Function `total_gas`](#0x1_Epoch_total_gas) +- [Function `block_gas_limit`](#0x1_Epoch_block_gas_limit) +- [Function `start_block_number`](#0x1_Epoch_start_block_number) +- [Function `end_block_number`](#0x1_Epoch_end_block_number) +- [Function `number`](#0x1_Epoch_number) +- [Function `block_time_target`](#0x1_Epoch_block_time_target) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::ConsensusConfig;
+use 0x1::CoreAddresses;
+use 0x1::Event;
+use 0x1::Math;
+use 0x1::Option;
+use 0x1::Timestamp;
+
+ + + + + +## Resource `Epoch` + +Current epoch info. + + +
struct Epoch has key
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ Number of current epoch +
+
+start_time: u64 +
+
+ Start time of current epoch +
+
+start_block_number: u64 +
+
+ Start block's number of current epoch +
+
+end_block_number: u64 +
+
+ End block's number of current epoch +
+
+block_time_target: u64 +
+
+ Average target time to calculate a block's difficulty in current epoch +
+
+reward_per_block: u128 +
+
+ Rewards per block in current epoch +
+
+reward_per_uncle_percent: u64 +
+
+ Percentage of reward_per_block to reward a uncle block in current epoch +
+
+block_difficulty_window: u64 +
+
+ How many ancestor blocks which use to calculate next block's difficulty in current epoch +
+
+max_uncles_per_block: u64 +
+
+ Maximum number of uncle block per block in current epoch +
+
+block_gas_limit: u64 +
+
+ Maximum gases per block in current epoch +
+
+strategy: u8 +
+
+ Strategy to calculate difficulty in current epoch +
+
+new_epoch_events: Event::EventHandle<Epoch::NewEpochEvent> +
+
+ Switch Epoch Event +
+
+ + +
+ + + +## Struct `NewEpochEvent` + +New epoch event. + + +
struct NewEpochEvent has drop, store
+
+ + + +
+Fields + + +
+
+number: u64 +
+
+ Epoch::number +
+
+start_time: u64 +
+
+ Epoch::start_time +
+
+start_block_number: u64 +
+
+ Epoch::start_block_number +
+
+end_block_number: u64 +
+
+ Epoch::end_block_number +
+
+block_time_target: u64 +
+
+ Epoch::block_time_target +
+
+reward_per_block: u128 +
+
+ Epoch::reward_per_block +
+
+previous_epoch_total_reward: u128 +
+
+ Total rewards during previous epoch +
+
+ + +
+ + + +## Resource `EpochData` + +Epoch data. + + +
struct EpochData has key
+
+ + + +
+Fields + + +
+
+uncles: u64 +
+
+ Up to now, Number of uncle block during current epoch +
+
+total_reward: u128 +
+
+ Up to now, Total rewards during current epoch +
+
+total_gas: u128 +
+
+ Up to now, Total gases during current epoch +
+
+ + +
+ + + +## Constants + + + + + + +
const EINVALID_UNCLES_COUNT: u64 = 101;
+
+ + + + + + + +
const EUNREACHABLE: u64 = 19;
+
+ + + + + + + +
const HUNDRED: u64 = 100;
+
+ + + + + + + +
const THOUSAND: u64 = 1000;
+
+ + + + + + + +
const THOUSAND_U128: u128 = 1000;
+
+ + + + + +## Function `initialize` + +Initialization of the module. + + +
public fun initialize(account: &signer)
+
+ + + +
+Implementation + + +
public fun initialize(
+    account: &signer,
+) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+
+    let config = ConsensusConfig::get_config();
+    move_to<Epoch>(
+        account,
+        Epoch {
+            number: 0,
+            start_time: Timestamp::now_milliseconds(),
+            start_block_number: 0,
+            end_block_number: ConsensusConfig::epoch_block_count(&config),
+            block_time_target: ConsensusConfig::base_block_time_target(&config),
+            reward_per_block: ConsensusConfig::base_reward_per_block(&config),
+            reward_per_uncle_percent: ConsensusConfig::base_reward_per_uncle_percent(&config),
+            block_difficulty_window: ConsensusConfig::base_block_difficulty_window(&config),
+            max_uncles_per_block: ConsensusConfig::base_max_uncles_per_block(&config),
+            block_gas_limit: ConsensusConfig::base_block_gas_limit(&config),
+            strategy: ConsensusConfig::strategy(&config),
+            new_epoch_events: Event::new_event_handle<NewEpochEvent>(account),
+        },
+    );
+    move_to<EpochData>(account, EpochData { uncles: 0, total_reward: 0, total_gas: 0 });
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if !exists<Config::Config<ConsensusConfig>>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if exists<Epoch>(Signer::address_of(account));
+aborts_if exists<EpochData>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `compute_next_block_time_target` + +compute next block time_target. + + +
public fun compute_next_block_time_target(config: &ConsensusConfig::ConsensusConfig, last_epoch_time_target: u64, epoch_start_time: u64, now_milli_second: u64, start_block_number: u64, end_block_number: u64, total_uncles: u64): u64
+
+ + + +
+Implementation + + +
public fun compute_next_block_time_target(
+    config: &ConsensusConfig,
+    last_epoch_time_target: u64,
+    epoch_start_time: u64,
+    now_milli_second: u64,
+    start_block_number: u64,
+    end_block_number: u64,
+    total_uncles: u64
+): u64 {
+    let total_time = now_milli_second - epoch_start_time;
+    let blocks = end_block_number - start_block_number;
+    let avg_block_time = total_time / blocks;
+    let uncles_rate = total_uncles * THOUSAND / blocks;
+    let new_epoch_block_time_target = (THOUSAND + uncles_rate) * avg_block_time /
+        (ConsensusConfig::uncle_rate_target(config) + THOUSAND);
+    if (new_epoch_block_time_target > last_epoch_time_target * 2) {
+        new_epoch_block_time_target = last_epoch_time_target * 2;
+    };
+    if (new_epoch_block_time_target < last_epoch_time_target / 2) {
+        new_epoch_block_time_target = last_epoch_time_target / 2;
+    };
+    let min_block_time_target = ConsensusConfig::min_block_time_target(config);
+    let max_block_time_target = ConsensusConfig::max_block_time_target(config);
+    if (new_epoch_block_time_target < min_block_time_target) {
+        new_epoch_block_time_target = min_block_time_target;
+    };
+    if (new_epoch_block_time_target > max_block_time_target) {
+        new_epoch_block_time_target = max_block_time_target;
+    };
+    new_epoch_block_time_target
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `adjust_epoch` + +adjust_epoch try to advance to next epoch if current epoch ends. + + +
public fun adjust_epoch(account: &signer, block_number: u64, timestamp: u64, uncles: u64, parent_gas_used: u64): u128
+
+ + + +
+Implementation + + +
public fun adjust_epoch(
+    account: &signer,
+    block_number: u64,
+    timestamp: u64,
+    uncles: u64,
+    parent_gas_used: u64
+): u128
+acquires Epoch, EpochData {
+    CoreAddresses::assert_genesis_address(account);
+
+    let epoch_ref = borrow_global_mut<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+    // assert!(epoch_ref.max_uncles_per_block >= uncles, Errors::invalid_argument(EINVALID_UNCLES_COUNT));
+
+    let epoch_data = borrow_global_mut<EpochData>(CoreAddresses::GENESIS_ADDRESS());
+    let (new_epoch, reward_per_block) = if (block_number < epoch_ref.end_block_number) {
+        (false, epoch_ref.reward_per_block)
+    } else if (block_number == epoch_ref.end_block_number) {
+        //start a new epoch
+        // assert!(uncles == 0, Errors::invalid_argument(EINVALID_UNCLES_COUNT));
+        // block time target unit is milli_seconds.
+        let now_milli_seconds = timestamp;
+
+        let config = ConsensusConfig::get_config();
+        let last_epoch_time_target = epoch_ref.block_time_target;
+        let new_epoch_block_time_target = compute_next_block_time_target(
+            &config,
+            last_epoch_time_target,
+            epoch_ref.start_time,
+            now_milli_seconds,
+            epoch_ref.start_block_number,
+            epoch_ref.end_block_number,
+            epoch_data.uncles
+        );
+        let new_reward_per_block = ConsensusConfig::do_compute_reward_per_block(
+            &config,
+            new_epoch_block_time_target
+        );
+
+        //update epoch by adjust result or config, because ConsensusConfig may be updated.
+        epoch_ref.number = epoch_ref.number + 1;
+        epoch_ref.start_time = now_milli_seconds;
+        epoch_ref.start_block_number = block_number;
+        epoch_ref.end_block_number = block_number + ConsensusConfig::epoch_block_count(&config);
+        epoch_ref.block_time_target = new_epoch_block_time_target;
+        epoch_ref.reward_per_block = new_reward_per_block;
+        epoch_ref.reward_per_uncle_percent = ConsensusConfig::base_reward_per_uncle_percent(&config);
+        epoch_ref.block_difficulty_window = ConsensusConfig::base_block_difficulty_window(&config);
+        epoch_ref.max_uncles_per_block = ConsensusConfig::base_max_uncles_per_block(&config);
+        epoch_ref.strategy = ConsensusConfig::strategy(&config);
+
+        epoch_data.uncles = 0;
+        let last_epoch_total_gas = epoch_data.total_gas + (parent_gas_used as u128);
+        adjust_gas_limit(
+            &config,
+            epoch_ref,
+            last_epoch_time_target,
+            new_epoch_block_time_target,
+            last_epoch_total_gas
+        );
+        emit_epoch_event(epoch_ref, epoch_data.total_reward);
+        (true, new_reward_per_block)
+    } else {
+        //This should never happened.
+        abort EUNREACHABLE
+    };
+    let reward = reward_per_block +
+        reward_per_block * (epoch_ref.reward_per_uncle_percent as u128) * (uncles as u128) / (HUNDRED as u128);
+    update_epoch_data(epoch_data, new_epoch, reward, uncles, parent_gas_used);
+    reward
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<Epoch>(Signer::address_of(account));
+aborts_if global<Epoch>(Signer::address_of(account)).max_uncles_per_block < uncles;
+aborts_if exists<EpochData>(Signer::address_of(account));
+aborts_if block_number == global<Epoch>(Signer::address_of(account)).end_block_number && uncles != 0;
+
+ + + +
+ + + +## Function `adjust_gas_limit` + + + +
fun adjust_gas_limit(config: &ConsensusConfig::ConsensusConfig, epoch_ref: &mut Epoch::Epoch, last_epoch_time_target: u64, new_epoch_time_target: u64, last_epoch_total_gas: u128)
+
+ + + +
+Implementation + + +
fun adjust_gas_limit(
+    config: &ConsensusConfig,
+    epoch_ref: &mut Epoch,
+    last_epoch_time_target: u64,
+    new_epoch_time_target: u64,
+    last_epoch_total_gas: u128
+) {
+    let new_gas_limit = compute_gas_limit(
+        config,
+        last_epoch_time_target,
+        new_epoch_time_target,
+        epoch_ref.block_gas_limit,
+        last_epoch_total_gas
+    );
+    if (Option::is_some(&new_gas_limit)) {
+        epoch_ref.block_gas_limit = Option::destroy_some(new_gas_limit);
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `compute_gas_limit` + +Compute block's gas limit of next epoch. + + +
public fun compute_gas_limit(config: &ConsensusConfig::ConsensusConfig, last_epoch_time_target: u64, new_epoch_time_target: u64, last_epoch_block_gas_limit: u64, last_epoch_total_gas: u128): Option::Option<u64>
+
+ + + +
+Implementation + + +
public fun compute_gas_limit(
+    config: &ConsensusConfig,
+    last_epoch_time_target: u64,
+    new_epoch_time_target: u64,
+    last_epoch_block_gas_limit: u64,
+    last_epoch_total_gas: u128
+): Option::Option<u64> {
+    let epoch_block_count = (ConsensusConfig::epoch_block_count(config) as u128);
+    let gas_limit_threshold = (last_epoch_total_gas >= Math::mul_div(
+        (last_epoch_block_gas_limit as u128) * epoch_block_count,
+        (80 as u128),
+        (HUNDRED as u128)
+    ));
+    let new_gas_limit = Option::none<u64>();
+
+    let min_block_time_target = ConsensusConfig::min_block_time_target(config);
+    let max_block_time_target = ConsensusConfig::max_block_time_target(config);
+    let base_block_gas_limit = ConsensusConfig::base_block_gas_limit(config);
+    if (last_epoch_time_target == new_epoch_time_target) {
+        if (new_epoch_time_target == min_block_time_target && gas_limit_threshold) {
+            let increase_gas_limit = in_or_decrease_gas_limit(
+                last_epoch_block_gas_limit,
+                110,
+                base_block_gas_limit
+            );
+            new_gas_limit = Option::some(increase_gas_limit);
+        } else if (new_epoch_time_target == max_block_time_target && !gas_limit_threshold) {
+            let decrease_gas_limit = in_or_decrease_gas_limit(last_epoch_block_gas_limit, 90, base_block_gas_limit);
+            new_gas_limit = Option::some(decrease_gas_limit);
+        }
+    };
+
+    new_gas_limit
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `in_or_decrease_gas_limit` + + + +
fun in_or_decrease_gas_limit(last_epoch_block_gas_limit: u64, percent: u64, min_block_gas_limit: u64): u64
+
+ + + +
+Implementation + + +
fun in_or_decrease_gas_limit(last_epoch_block_gas_limit: u64, percent: u64, min_block_gas_limit: u64): u64 {
+    let tmp_gas_limit = Math::mul_div((last_epoch_block_gas_limit as u128), (percent as u128), (HUNDRED as u128));
+    let new_gas_limit = if (tmp_gas_limit > (min_block_gas_limit as u128)) {
+        (tmp_gas_limit as u64)
+    } else {
+        min_block_gas_limit
+    };
+
+    new_gas_limit
+}
+
+ + + +
+ +
+Specification + + + +
include Math::MulDivAbortsIf { x: last_epoch_block_gas_limit, y: percent, z: HUNDRED };
+aborts_if Math::spec_mul_div() > MAX_U64;
+
+ + + +
+ + + +## Function `update_epoch_data` + + + +
fun update_epoch_data(epoch_data: &mut Epoch::EpochData, new_epoch: bool, reward: u128, uncles: u64, parent_gas_used: u64)
+
+ + + +
+Implementation + + +
fun update_epoch_data(
+    epoch_data: &mut EpochData,
+    new_epoch: bool,
+    reward: u128,
+    uncles: u64,
+    parent_gas_used: u64
+) {
+    if (new_epoch) {
+        epoch_data.total_reward = reward;
+        epoch_data.uncles = uncles;
+        epoch_data.total_gas = 0;
+    } else {
+        epoch_data.total_reward = epoch_data.total_reward + reward;
+        epoch_data.uncles = epoch_data.uncles + uncles;
+        epoch_data.total_gas = epoch_data.total_gas + (parent_gas_used as u128);
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !new_epoch && epoch_data.total_reward + reward > MAX_U128;
+aborts_if !new_epoch && epoch_data.uncles + uncles > MAX_U64;
+aborts_if !new_epoch && epoch_data.total_gas + parent_gas_used > MAX_U128;
+
+ + + +
+ + + +## Function `emit_epoch_event` + + + +
fun emit_epoch_event(epoch_ref: &mut Epoch::Epoch, previous_epoch_total_reward: u128)
+
+ + + +
+Implementation + + +
fun emit_epoch_event(epoch_ref: &mut Epoch, previous_epoch_total_reward: u128) {
+    Event::emit_event(
+        &mut epoch_ref.new_epoch_events,
+        NewEpochEvent {
+            number: epoch_ref.number,
+            start_time: epoch_ref.start_time,
+            start_block_number: epoch_ref.start_block_number,
+            end_block_number: epoch_ref.end_block_number,
+            block_time_target: epoch_ref.block_time_target,
+            reward_per_block: epoch_ref.reward_per_block,
+            previous_epoch_total_reward,
+        },
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `start_time` + +Get start time of current epoch + + +
public fun start_time(): u64
+
+ + + +
+Implementation + + +
public fun start_time(): u64 acquires Epoch {
+    let epoch_ref = borrow_global<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_ref.start_time
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `uncles` + +Get uncles number of current epoch + + +
public fun uncles(): u64
+
+ + + +
+Implementation + + +
public fun uncles(): u64 acquires EpochData {
+    let epoch_data = borrow_global<EpochData>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_data.uncles
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<EpochData>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `total_gas` + +Get total gas of current epoch + + +
public fun total_gas(): u128
+
+ + + +
+Implementation + + +
public fun total_gas(): u128 acquires EpochData {
+    let epoch_data = borrow_global<EpochData>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_data.total_gas
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<EpochData>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `block_gas_limit` + +Get block's gas_limit of current epoch + + +
public fun block_gas_limit(): u64
+
+ + + +
+Implementation + + +
public fun block_gas_limit(): u64 acquires Epoch {
+    let epoch_ref = borrow_global<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_ref.block_gas_limit
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `start_block_number` + +Get start block's number of current epoch + + +
public fun start_block_number(): u64
+
+ + + +
+Implementation + + +
public fun start_block_number(): u64 acquires Epoch {
+    let epoch_ref = borrow_global<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_ref.start_block_number
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `end_block_number` + +Get end block's number of current epoch + + +
public fun end_block_number(): u64
+
+ + + +
+Implementation + + +
public fun end_block_number(): u64 acquires Epoch {
+    let epoch_ref = borrow_global<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_ref.end_block_number
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `number` + +Get current epoch number + + +
public fun number(): u64
+
+ + + +
+Implementation + + +
public fun number(): u64 acquires Epoch {
+    let epoch_ref = borrow_global<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_ref.number
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `block_time_target` + +Get current block time target + + +
public fun block_time_target(): u64
+
+ + + +
+Implementation + + +
public fun block_time_target(): u64 acquires Epoch {
+    let epoch_ref = borrow_global<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+    epoch_ref.block_time_target
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Epoch>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Errors.md b/release/v13/docs/Errors.md new file mode 100644 index 00000000..75b214eb --- /dev/null +++ b/release/v13/docs/Errors.md @@ -0,0 +1,623 @@ + + + +# Module `0x1::Errors` + +Module defining error codes used in Move aborts throughout the framework. + +A u64 error code is constructed from two values: + +1. The *error category* which is encoded in the lower 8 bits of the code. Error categories are +declared in this module and are globally unique across the Diem framework. There is a limited +fixed set of predefined categories, and the framework is guaranteed to use those consistently. + +2. The *error reason* which is encoded in the remaining 56 bits of the code. The reason is a unique +number relative to the module which raised the error and can be used to obtain more information about +the error at hand. It is mostly used for diagnosis purposes. Error reasons may change over time as the +framework evolves. + +Rules to declare or use *error reason*: +1. error reason is declared as const in the user module +2. error reason name must start with "E", for example, const EACCOUNT_DOES_NOT_EXIST = ... +3. value less than 100 is reserved for general purpose and shared by all modules +4. don't change general purpose error reason value, it's co-related with error code in starcoin vm +5. self-defined error reason value must be large than 100 +6. error reason must be used together with error category + + +- [Constants](#@Constants_0) +- [Function `make`](#0x1_Errors_make) +- [Function `invalid_state`](#0x1_Errors_invalid_state) +- [Function `requires_address`](#0x1_Errors_requires_address) +- [Function `requires_role`](#0x1_Errors_requires_role) +- [Function `requires_capability`](#0x1_Errors_requires_capability) +- [Function `not_published`](#0x1_Errors_not_published) +- [Function `already_published`](#0x1_Errors_already_published) +- [Function `invalid_argument`](#0x1_Errors_invalid_argument) +- [Function `limit_exceeded`](#0x1_Errors_limit_exceeded) +- [Function `internal`](#0x1_Errors_internal) +- [Function `deprecated`](#0x1_Errors_deprecated) +- [Function `custom`](#0x1_Errors_custom) +- [Module Specification](#@Module_Specification_1) + + +
+ + + + + +## Constants + + + + +Attempting to publish a resource that is already published. Example: calling an initialization function +twice. + + +
const ALREADY_PUBLISHED: u8 = 6;
+
+ + + + + +A custom error category for extension points. + + +
const CUSTOM: u8 = 255;
+
+ + + + + +deprecated code + + +
const DEPRECATED: u8 = 11;
+
+ + + + + +An internal error (bug) has occurred. + + +
const INTERNAL: u8 = 10;
+
+ + + + + +An argument provided to an operation is invalid. Example: a signing key has the wrong format. + + +
const INVALID_ARGUMENT: u8 = 7;
+
+ + + + + +The system is in a state where the performed operation is not allowed. Example: call to a function only allowed +in genesis + + +
const INVALID_STATE: u8 = 1;
+
+ + + + + +A limit on an amount, e.g. a currency, is exceeded. Example: withdrawal of money after account limits window +is exhausted. + + +
const LIMIT_EXCEEDED: u8 = 8;
+
+ + + + + +A resource is required but not published. Example: access to non-existing resource. + + +
const NOT_PUBLISHED: u8 = 5;
+
+ + + + + +The signer of a transaction does not have the expected address for this operation. Example: a call to a function +which publishes a resource under a particular address. + + +
const REQUIRES_ADDRESS: u8 = 2;
+
+ + + + + +The signer of a transaction does not have a required capability. + + +
const REQUIRES_CAPABILITY: u8 = 4;
+
+ + + + + +The signer of a transaction does not have the expected role for this operation. Example: a call to a function +which requires the signer to have the role of treasury compliance. + + +
const REQUIRES_ROLE: u8 = 3;
+
+ + + + + +## Function `make` + +A function to create an error from from a category and a reason. + + +
fun make(category: u8, reason: u64): u64
+
+ + + +
+Implementation + + +
fun make(category: u8, reason: u64): u64 {
+    (category as u64) + (reason << 8)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+pragma verify = false;
+aborts_if [abstract] false;
+ensures [abstract] result == category;
+
+ + + +
+ + + +## Function `invalid_state` + +Create an error of invalid_state + + +
public fun invalid_state(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun invalid_state(reason: u64): u64 { make(INVALID_STATE, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == INVALID_STATE;
+
+ + + +
+ + + +## Function `requires_address` + +Create an error of requires_address. + + +
public fun requires_address(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun requires_address(reason: u64): u64 { make(REQUIRES_ADDRESS, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == REQUIRES_ADDRESS;
+
+ + + +
+ + + +## Function `requires_role` + +Create an error of requires_role. + + +
public fun requires_role(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun requires_role(reason: u64): u64 { make(REQUIRES_ROLE, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == REQUIRES_ROLE;
+
+ + + +
+ + + +## Function `requires_capability` + +Create an error of requires_capability. + + +
public fun requires_capability(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun requires_capability(reason: u64): u64 { make(REQUIRES_CAPABILITY, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == REQUIRES_CAPABILITY;
+
+ + + +
+ + + +## Function `not_published` + +Create an error of not_published. + + +
public fun not_published(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun not_published(reason: u64): u64 { make(NOT_PUBLISHED, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == NOT_PUBLISHED;
+
+ + + +
+ + + +## Function `already_published` + +Create an error of already_published. + + +
public fun already_published(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun already_published(reason: u64): u64 { make(ALREADY_PUBLISHED, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == ALREADY_PUBLISHED;
+
+ + + +
+ + + +## Function `invalid_argument` + +Create an error of invalid_argument. + + +
public fun invalid_argument(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun invalid_argument(reason: u64): u64 { make(INVALID_ARGUMENT, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == INVALID_ARGUMENT;
+
+ + + +
+ + + +## Function `limit_exceeded` + +Create an error of limit_exceeded. + + +
public fun limit_exceeded(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun limit_exceeded(reason: u64): u64 { make(LIMIT_EXCEEDED, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == LIMIT_EXCEEDED;
+
+ + + +
+ + + +## Function `internal` + +Create an error of internal. + + +
public fun internal(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun internal(reason: u64): u64 { make(INTERNAL, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == INTERNAL;
+
+ + + +
+ + + +## Function `deprecated` + +Create an error of deprecated. + + +
public fun deprecated(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun deprecated(reason: u64): u64 { make(DEPRECATED, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == DEPRECATED;
+
+ + + +
+ + + +## Function `custom` + +Create an error of custom. + + +
public fun custom(reason: u64): u64
+
+ + + +
+Implementation + + +
public fun custom(reason: u64): u64 { make(CUSTOM, reason) }
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == CUSTOM;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Event.md b/release/v13/docs/Event.md new file mode 100644 index 00000000..aed6dda1 --- /dev/null +++ b/release/v13/docs/Event.md @@ -0,0 +1,300 @@ + + + +# Module `0x1::Event` + +The Event module defines an EventHandleGenerator that is used to create +EventHandles with unique GUIDs. It contains a counter for the number +of EventHandles it generates. An EventHandle is used to count the number of +events emitted to a handle and emit events to the event store. + + +- [Resource `EventHandleGenerator`](#0x1_Event_EventHandleGenerator) +- [Struct `EventHandle`](#0x1_Event_EventHandle) +- [Constants](#@Constants_0) +- [Function `publish_generator`](#0x1_Event_publish_generator) +- [Function `fresh_guid`](#0x1_Event_fresh_guid) +- [Function `new_event_handle`](#0x1_Event_new_event_handle) +- [Function `emit_event`](#0x1_Event_emit_event) +- [Function `write_to_event_store`](#0x1_Event_write_to_event_store) +- [Function `destroy_handle`](#0x1_Event_destroy_handle) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::BCS;
+use 0x1::Errors;
+use 0x1::Signer;
+use 0x1::Vector;
+
+ + + + + +## Resource `EventHandleGenerator` + +A resource representing the counter used to generate uniqueness under each account. There won't be destructor for +this resource to guarantee the uniqueness of the generated handle. + + +
struct EventHandleGenerator has key
+
+ + + +
+Fields + + +
+
+counter: u64 +
+
+ +
+
+addr: address +
+
+ +
+
+ + +
+ + + +## Struct `EventHandle` + +A handle for an event such that: +1. Other modules can emit events to this handle. +2. Storage can use this handle to prove the total number of events that happened in the past. + + +
struct EventHandle<T: drop, store> has store
+
+ + + +
+Fields + + +
+
+counter: u64 +
+
+ Total number of events emitted to this event stream. +
+
+guid: vector<u8> +
+
+ A globally unique ID for this event stream. +
+
+ + +
+ + + +## Constants + + + + +The event generator resource was in an invalid state + + +
const EEVENT_GENERATOR: u64 = 0;
+
+ + + + + +## Function `publish_generator` + +Publishs a new event handle generator. + + +
public fun publish_generator(account: &signer)
+
+ + + +
+Implementation + + +
public fun publish_generator(account: &signer) {
+    let addr = Signer::address_of(account);
+    assert!(!exists<EventHandleGenerator>(addr), Errors::already_published(EEVENT_GENERATOR));
+    move_to(account, EventHandleGenerator{ counter: 0, addr })
+}
+
+ + + +
+ + + +## Function `fresh_guid` + +Derive a fresh unique id by using sender's EventHandleGenerator. The generated vector is indeed unique because it +was derived from the hash(sender's EventHandleGenerator || sender_address). This module guarantees that the +EventHandleGenerator is only going to be monotonically increased and there's no way to revert it or destroy it. Thus +such counter is going to give distinct value for each of the new event stream under each sender. And since we +hash it with the sender's address, the result is guaranteed to be globally unique. + + +
fun fresh_guid(counter: &mut Event::EventHandleGenerator): vector<u8>
+
+ + + +
+Implementation + + +
fun fresh_guid(counter: &mut EventHandleGenerator): vector<u8> {
+    let sender_bytes = BCS::to_bytes(&counter.addr);
+    let count_bytes = BCS::to_bytes(&counter.counter);
+    counter.counter = counter.counter + 1;
+
+    // EventHandleGenerator goes first just in case we want to extend address in the future.
+    Vector::append(&mut count_bytes, sender_bytes);
+
+    count_bytes
+}
+
+ + + +
+ + + +## Function `new_event_handle` + +Use EventHandleGenerator to generate a unique event handle for sig + + +
public fun new_event_handle<T: drop, store>(account: &signer): Event::EventHandle<T>
+
+ + + +
+Implementation + + +
public fun new_event_handle<T: drop + store>(account: &signer): EventHandle<T>
+acquires EventHandleGenerator {
+    let addr = Signer::address_of(account);
+    assert!(exists<EventHandleGenerator>(addr), Errors::not_published(EEVENT_GENERATOR));
+    EventHandle<T> {
+        counter: 0,
+        guid: fresh_guid(borrow_global_mut<EventHandleGenerator>(addr))
+    }
+}
+
+ + + +
+ + + +## Function `emit_event` + +Emit an event with payload msg by using handle_ref's key and counter. + + +
public fun emit_event<T: drop, store>(handle_ref: &mut Event::EventHandle<T>, msg: T)
+
+ + + +
+Implementation + + +
public fun emit_event<T: drop + store>(handle_ref: &mut EventHandle<T>, msg: T) {
+    let guid = *&handle_ref.guid;
+
+    write_to_event_store<T>(guid, handle_ref.counter, msg);
+    handle_ref.counter = handle_ref.counter + 1;
+}
+
+ + + +
+ + + +## Function `write_to_event_store` + +Native procedure that writes to the actual event stream in Event store +This will replace the "native" portion of EmitEvent bytecode + + +
fun write_to_event_store<T: drop, store>(guid: vector<u8>, count: u64, msg: T)
+
+ + + +
+Implementation + + +
native fun write_to_event_store<T: drop + store>(guid: vector<u8>, count: u64, msg: T);
+
+ + + +
+ + + +## Function `destroy_handle` + +Destroy a unique handle. + + +
public fun destroy_handle<T: drop, store>(handle: Event::EventHandle<T>)
+
+ + + +
+Implementation + + +
public fun destroy_handle<T: drop + store>(handle: EventHandle<T>) {
+    EventHandle<T> { counter: _, guid: _ } = handle;
+}
+
+ + + +
+ + + +## Module Specification + + + +Functions of the event module are mocked out using the intrinsic +pragma. They are implemented in the prover's prelude. + + +
pragma intrinsic = true;
+
diff --git a/release/v13/docs/EventUtil.md b/release/v13/docs/EventUtil.md new file mode 100644 index 00000000..420e3c6f --- /dev/null +++ b/release/v13/docs/EventUtil.md @@ -0,0 +1,175 @@ + + + +# Module `0x1::EventUtil` + + + +- [Resource `EventHandleWrapper`](#0x1_EventUtil_EventHandleWrapper) +- [Constants](#@Constants_0) +- [Function `init_event`](#0x1_EventUtil_init_event) +- [Function `uninit_event`](#0x1_EventUtil_uninit_event) +- [Function `emit_event`](#0x1_EventUtil_emit_event) +- [Function `exist_event`](#0x1_EventUtil_exist_event) + + +
use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Signer;
+
+ + + + + +## Resource `EventHandleWrapper` + + + +
struct EventHandleWrapper<EventT: drop, store> has key
+
+ + + +
+Fields + + +
+
+handle: Event::EventHandle<EventT> +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_INIT_REPEATE: u64 = 101;
+
+ + + + + + + +
const ERR_RESOURCE_NOT_EXISTS: u64 = 102;
+
+ + + + + +## Function `init_event` + + + +
public fun init_event<EventT: drop, store>(sender: &signer)
+
+ + + +
+Implementation + + +
public fun init_event<EventT: store + drop>(sender: &signer) {
+    let broker = Signer::address_of(sender);
+    assert!(!exists<EventHandleWrapper<EventT>>(broker), Errors::invalid_state(ERR_INIT_REPEATE));
+    move_to(sender, EventHandleWrapper<EventT> {
+        handle: Event::new_event_handle<EventT>(sender)
+    });
+}
+
+ + + +
+ + + +## Function `uninit_event` + + + +
public fun uninit_event<EventT: drop, store>(sender: &signer)
+
+ + + +
+Implementation + + +
public fun uninit_event<EventT: store + drop>(sender: &signer) acquires EventHandleWrapper {
+    let broker = Signer::address_of(sender);
+    assert!(exists<EventHandleWrapper<EventT>>(broker), Errors::invalid_state(ERR_RESOURCE_NOT_EXISTS));
+    let EventHandleWrapper<EventT> { handle } = move_from<EventHandleWrapper<EventT>>(broker);
+    Event::destroy_handle<EventT>(handle);
+}
+
+ + + +
+ + + +## Function `emit_event` + + + +
public fun emit_event<EventT: drop, store>(broker: address, event: EventT)
+
+ + + +
+Implementation + + +
public fun emit_event<EventT: store + drop>(broker: address, event: EventT) acquires EventHandleWrapper {
+    let event_handle = borrow_global_mut<EventHandleWrapper<EventT>>(broker);
+    Event::emit_event(&mut event_handle.handle, event);
+}
+
+ + + +
+ + + +## Function `exist_event` + + + +
public fun exist_event<EventT: drop, store>(broker: address): bool
+
+ + + +
+Implementation + + +
public fun exist_event<EventT: store + drop>(broker: address): bool {
+    exists<EventHandleWrapper<EventT>>(broker)
+}
+
+ + + +
diff --git a/release/v13/docs/FixedPoint32.md b/release/v13/docs/FixedPoint32.md new file mode 100644 index 00000000..16bc13d8 --- /dev/null +++ b/release/v13/docs/FixedPoint32.md @@ -0,0 +1,439 @@ + + + +# Module `0x1::FixedPoint32` + +The module provide operations for FixedPoint32. + + +- [Struct `FixedPoint32`](#0x1_FixedPoint32_FixedPoint32) +- [Constants](#@Constants_0) +- [Function `multiply_u64`](#0x1_FixedPoint32_multiply_u64) +- [Function `divide_u64`](#0x1_FixedPoint32_divide_u64) +- [Function `create_from_rational`](#0x1_FixedPoint32_create_from_rational) +- [Function `create_from_raw_value`](#0x1_FixedPoint32_create_from_raw_value) +- [Function `get_raw_value`](#0x1_FixedPoint32_get_raw_value) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+
+ + + + + +## Struct `FixedPoint32` + +Define a fixed-point numeric type with 32 fractional bits. +This is just a u64 integer but it is wrapped in a struct to +make a unique type. + + +
struct FixedPoint32 has copy, drop, store
+
+ + + +
+Fields + + +
+
+value: u64 +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const MAX_U64: u128 = 18446744073709551615;
+
+ + + + + +The denominator provided was zero + + +
const EDENOMINATOR: u64 = 101;
+
+ + + + + +The quotient value would be too large to be held in a u64 + + +
const EDIVISION: u64 = 102;
+
+ + + + + +A division by zero was encountered + + +
const EDIVISION_BY_ZERO: u64 = 104;
+
+ + + + + +The multiplied value would be too large to be held in a u64 + + +
const EMULTIPLICATION: u64 = 103;
+
+ + + + + +The computed ratio when converting to a FixedPoint32 would be unrepresentable + + +
const ERATIO_OUT_OF_RANGE: u64 = 105;
+
+ + + + + +## Function `multiply_u64` + +Multiply a u64 integer by a fixed-point number, truncating any +fractional part of the product. This will abort if the product +overflows. + + +
public fun multiply_u64(val: u64, multiplier: FixedPoint32::FixedPoint32): u64
+
+ + + +
+Implementation + + +
public fun multiply_u64(val: u64, multiplier: FixedPoint32): u64 {
+    // The product of two 64 bit values has 128 bits, so perform the
+    // multiplication with u128 types and keep the full 128 bit product
+    // to avoid losing accuracy.
+    let unscaled_product = (val as u128) * (multiplier.value as u128);
+    // The unscaled product has 32 fractional bits (from the multiplier)
+    // so rescale it by shifting away the low bits.
+    let product = unscaled_product >> 32;
+    // Check whether the value is too large.
+    assert!(product <= MAX_U64, Errors::limit_exceeded(EMULTIPLICATION));
+    (product as u64)
+}
+
+ + + +
+ +
+Specification + + +Currently, we ignore the actual implementation of this function in verification +and treat it as uninterpreted, which simplifies the verification problem significantly. +This way we avoid the non-linear arithmetic problem presented by this function. + +Abstracting this and related functions is possible because the correctness of currency +conversion (where FixedPoint32 is used for) is not relevant for the rest of the contract +control flow, so we can assume some arbitrary (but fixed) behavior here. + + +
pragma opaque = true;
+include MultiplyAbortsIf;
+ensures result == spec_multiply_u64(val, multiplier);
+
+ + + + + + + +
schema MultiplyAbortsIf {
+    val: num;
+    multiplier: FixedPoint32;
+    aborts_if spec_multiply_u64(val, multiplier) > MAX_U64 with Errors::LIMIT_EXCEEDED;
+}
+
+ + + + + + + +
fun spec_multiply_u64(val: num, multiplier: FixedPoint32): num {
+   (val * multiplier.value) >> 32
+}
+
+ + + +
+ + + +## Function `divide_u64` + +Divide a u64 integer by a fixed-point number, truncating any +fractional part of the quotient. This will abort if the divisor +is zero or if the quotient overflows. + + +
public fun divide_u64(val: u64, divisor: FixedPoint32::FixedPoint32): u64
+
+ + + +
+Implementation + + +
public fun divide_u64(val: u64, divisor: FixedPoint32): u64 {
+    // Check for division by zero.
+    assert!(divisor.value != 0, Errors::invalid_argument(EDIVISION_BY_ZERO));
+    // First convert to 128 bits and then shift left to
+    // add 32 fractional zero bits to the dividend.
+    let scaled_value = (val as u128) << 32;
+    let quotient = scaled_value / (divisor.value as u128);
+    // Check whether the value is too large.
+    assert!(quotient <= MAX_U64, Errors::limit_exceeded(EDIVISION));
+    // the value may be too large, which will cause the cast to fail
+    // with an arithmetic error.
+    (quotient as u64)
+}
+
+ + + +
+ +
+Specification + + +See comment at Self::multiply_64. + + +
pragma opaque = true;
+include DivideAbortsIf;
+ensures result == spec_divide_u64(val, divisor);
+
+ + + + + + + +
schema DivideAbortsIf {
+    val: num;
+    divisor: FixedPoint32;
+    aborts_if divisor.value == 0 with Errors::INVALID_ARGUMENT;
+    aborts_if spec_divide_u64(val, divisor) > MAX_U64 with Errors::LIMIT_EXCEEDED;
+}
+
+ + + + + + + +
fun spec_divide_u64(val: num, divisor: FixedPoint32): num {
+   (val << 32) / divisor.value
+}
+
+ + + +
+ + + +## Function `create_from_rational` + +Create a fixed-point value from a rational number specified by its +numerator and denominator. This function is for convenience; it is also +perfectly fine to create a fixed-point value by directly specifying the +raw value. This will abort if the denominator is zero or if the ratio is +not in the range 2^-32 .. 2^32-1. + + +
public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32::FixedPoint32
+
+ + + +
+Implementation + + +
public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32 {
+    // If the denominator is zero, this will abort.
+    // Scale the numerator to have 64 fractional bits and the denominator
+    // to have 32 fractional bits, so that the quotient will have 32
+    // fractional bits.
+    let scaled_numerator = (numerator as u128) << 64;
+    let scaled_denominator = (denominator as u128) << 32;
+    assert!(scaled_denominator != 0, Errors::invalid_argument(EDENOMINATOR));
+    let quotient = scaled_numerator / scaled_denominator;
+    assert!(quotient != 0 || numerator == 0, Errors::invalid_argument(ERATIO_OUT_OF_RANGE));
+    // Return the quotient as a fixed-point number. We first need to check whether the cast
+    // can succeed.
+    assert!(quotient <= MAX_U64, Errors::limit_exceeded(ERATIO_OUT_OF_RANGE));
+    FixedPoint32 { value: (quotient as u64) }
+}
+
+ + + +
+ +
+Specification + + +See comment at Self::multiply_64. + + +
pragma verify = false;
+pragma opaque = true;
+include CreateFromRationalAbortsIf;
+ensures result == spec_create_from_rational(numerator, denominator);
+
+ + + + + + + +
schema CreateFromRationalAbortsIf {
+    numerator: u64;
+    denominator: u64;
+    let scaled_numerator = numerator << 64;
+    let scaled_denominator = denominator << 32;
+    let quotient = scaled_numerator / scaled_denominator;
+    aborts_if scaled_denominator == 0 with Errors::INVALID_ARGUMENT;
+    aborts_if quotient == 0 && scaled_numerator != 0 with Errors::INVALID_ARGUMENT;
+    aborts_if quotient > MAX_U64 with Errors::LIMIT_EXCEEDED;
+}
+
+ + + + + + + +
fun spec_create_from_rational(numerator: num, denominator: num): FixedPoint32 {
+   FixedPoint32{value: (numerator << 64) / (denominator << 32)}
+}
+
+ + + +
+ + + +## Function `create_from_raw_value` + +create a fixedpoint 32 from u64. + + +
public fun create_from_raw_value(value: u64): FixedPoint32::FixedPoint32
+
+ + + +
+Implementation + + +
public fun create_from_raw_value(value: u64): FixedPoint32 {
+    FixedPoint32 { value }
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result.value == value;
+
+ + + +
+ + + +## Function `get_raw_value` + +Accessor for the raw u64 value. Other less common operations, such as +adding or subtracting FixedPoint32 values, can be done using the raw +values directly. + + +
public fun get_raw_value(num: FixedPoint32::FixedPoint32): u64
+
+ + + +
+Implementation + + +
public fun get_raw_value(num: FixedPoint32): u64 {
+    num.value
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/FlexiDagConfig.md b/release/v13/docs/FlexiDagConfig.md new file mode 100644 index 00000000..c8090f6e --- /dev/null +++ b/release/v13/docs/FlexiDagConfig.md @@ -0,0 +1,167 @@ + + + +# Module `0x1::FlexiDagConfig` + + + +- [Struct `FlexiDagConfig`](#0x1_FlexiDagConfig_FlexiDagConfig) +- [Function `new_flexidag_config`](#0x1_FlexiDagConfig_new_flexidag_config) +- [Function `initialize`](#0x1_FlexiDagConfig_initialize) +- [Function `effective_height`](#0x1_FlexiDagConfig_effective_height) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::Block;
+use 0x1::Config;
+use 0x1::CoreAddresses;
+
+ + + + + +## Struct `FlexiDagConfig` + +The struct to hold all config data needed for Flexidag. + + +
struct FlexiDagConfig has copy, drop, store
+
+ + + +
+Fields + + +
+
+effective_height: u64 +
+
+ +
+
+ + +
+ + + +## Function `new_flexidag_config` + +Create a new configuration for flexidag, mainly used in DAO. + + +
public fun new_flexidag_config(effective_height: u64): FlexiDagConfig::FlexiDagConfig
+
+ + + +
+Implementation + + +
public fun new_flexidag_config(effective_height: u64): FlexiDagConfig {
+    FlexiDagConfig {
+        effective_height,
+    }
+}
+
+ + + +
+ + + +## Function `initialize` + + + +
public fun initialize(account: &signer, effective_height: u64)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, effective_height: u64) {
+    CoreAddresses::assert_genesis_address(account);
+    Config::publish_new_config<FlexiDagConfig>(account, new_flexidag_config(effective_height));
+    Block::initialize_blockmetadata_v2(account);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<Config::Config<FlexiDagConfig>>(Signer::address_of(account));
+aborts_if exists<Config::ModifyConfigCapabilityHolder<FlexiDagConfig>>(Signer::address_of(account));
+ensures exists<Config::Config<FlexiDagConfig>>(Signer::address_of(account));
+ensures
+    exists<Config::ModifyConfigCapabilityHolder<FlexiDagConfig>>(
+        Signer::address_of(account),
+    );
+
+ + + +
+ + + +## Function `effective_height` + + + +
public fun effective_height(account: address): u64
+
+ + + +
+Implementation + + +
public fun effective_height(account: address): u64 {
+    let flexi_dag_config = Config::get_by_address<FlexiDagConfig>(account);
+    flexi_dag_config.effective_height
+}
+
+ + + +
+ +
+Specification + + + +
include Config::AbortsIfConfigNotExist<FlexiDagConfig> { addr: account };
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/FromBCS.md b/release/v13/docs/FromBCS.md new file mode 100644 index 00000000..c9094f7c --- /dev/null +++ b/release/v13/docs/FromBCS.md @@ -0,0 +1,205 @@ + + + +# Module `0x1::FromBCS` + +This module provides a number of functions to convert _primitive_ types from their representation in std::bcs +to values. This is the opposite of bcs::to_bytes. Note that it is not safe to define a generic public from_bytes +function because this can violate implicit struct invariants, therefore only primitive types are offerred. If +a general conversion back-and-force is needed, consider the StarcoinFramework::Any type which preserves invariants. + +Example: +``` +use std::bcs; +use StarcoinFramework::from_bcs; + +assert!(from_bcs::to_address(bcs::to_bytes(&@0xabcdef)) == @0xabcdef, 0); +``` + + +- [Constants](#@Constants_0) +- [Function `to_bool`](#0x1_FromBCS_to_bool) +- [Function `to_u8`](#0x1_FromBCS_to_u8) +- [Function `to_u64`](#0x1_FromBCS_to_u64) +- [Function `to_u128`](#0x1_FromBCS_to_u128) +- [Function `to_address`](#0x1_FromBCS_to_address) +- [Function `from_bytes`](#0x1_FromBCS_from_bytes) + + +
+ + + + + +## Constants + + + + +UTF8 check failed in conversion from bytes to string + + +
const EINVALID_UTF8: u64 = 1;
+
+ + + + + +## Function `to_bool` + + + +
public fun to_bool(v: vector<u8>): bool
+
+ + + +
+Implementation + + +
public fun to_bool(v: vector<u8>): bool {
+    from_bytes<bool>(v)
+}
+
+ + + +
+ + + +## Function `to_u8` + + + +
public fun to_u8(v: vector<u8>): u8
+
+ + + +
+Implementation + + +
public fun to_u8(v: vector<u8>): u8 {
+    from_bytes<u8>(v)
+}
+
+ + + +
+ + + +## Function `to_u64` + + + +
public fun to_u64(v: vector<u8>): u64
+
+ + + +
+Implementation + + +
public fun to_u64(v: vector<u8>): u64 {
+    from_bytes<u64>(v)
+}
+
+ + + +
+ + + +## Function `to_u128` + + + +
public fun to_u128(v: vector<u8>): u128
+
+ + + +
+Implementation + + +
public fun to_u128(v: vector<u8>): u128 {
+    from_bytes<u128>(v)
+}
+
+ + + +
+ + + +## Function `to_address` + + + +
public fun to_address(v: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun to_address(v: vector<u8>): address {
+    from_bytes<address>(v)
+}
+
+ + + +
+ + + +## Function `from_bytes` + +Package private native function to deserialize a type T. + +Note that this function does not put any constraint on T. If code uses this function to +deserialize a linear value, its their responsibility that the data they deserialize is +owned. + + +
public(friend) fun from_bytes<T>(bytes: vector<u8>): T
+
+ + + +
+Implementation + + +
public(friend) native fun from_bytes<T>(bytes: vector<u8>): T;
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+
+ + + +
diff --git a/release/v13/docs/GasSchedule.md b/release/v13/docs/GasSchedule.md new file mode 100644 index 00000000..daab6ac6 --- /dev/null +++ b/release/v13/docs/GasSchedule.md @@ -0,0 +1,540 @@ + + + +# Module `0x1::GasSchedule` + +Gas schedule configuration. + + +- [Struct `GasEntry`](#0x1_GasSchedule_GasEntry) +- [Resource `GasSchedule`](#0x1_GasSchedule_GasSchedule) +- [Function `gas_schedule`](#0x1_GasSchedule_gas_schedule) +- [Function `new_gas_entry`](#0x1_GasSchedule_new_gas_entry) +- [Function `new_constant_entry`](#0x1_GasSchedule_new_constant_entry) +- [Function `initialize`](#0x1_GasSchedule_initialize) +- [Function `new_gas_schedule`](#0x1_GasSchedule_new_gas_schedule) +- [Function `new_gas_schedule_for_test`](#0x1_GasSchedule_new_gas_schedule_for_test) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::ChainId;
+use 0x1::Config;
+use 0x1::CoreAddresses;
+
+ + + + + +## Struct `GasEntry` + + + +
struct GasEntry has copy, drop, store
+
+ + + +
+Fields + + +
+
+key: vector<u8> +
+
+ +
+
+val: u64 +
+
+ +
+
+ + +
+ + + +## Resource `GasSchedule` + + + +
struct GasSchedule has copy, drop, store, key
+
+ + + +
+Fields + + +
+
+entries: vector<GasSchedule::GasEntry> +
+
+ +
+
+ + +
+ + + +## Function `gas_schedule` + +The GasCost tracks: +- instruction cost: how much time/computational power is needed to perform the instruction +- memory cost: how much memory is required for the instruction, and storage overhead + + +
public fun gas_schedule(): vector<GasSchedule::GasEntry>
+
+ + + +
+Implementation + + +
public fun gas_schedule(): vector<GasEntry> {
+    let table = Vector::empty();
+
+    // instruction_schedule
+    // POP
+    Vector::push_back(&mut table, new_gas_entry(b"instr.pop", 1, 1));
+    // RET
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ret", 638, 1));
+    // BR_TRUE
+    Vector::push_back(&mut table, new_gas_entry(b"instr.br_true", 1, 1));
+    // BR_FALSE
+    Vector::push_back(&mut table, new_gas_entry(b"instr.br_false", 1, 1));
+    // BRANCH
+    Vector::push_back(&mut table, new_gas_entry(b"instr.branch", 1, 1));
+    // LD_U64
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ld_u64", 1, 1));
+    // LD_CONST
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ld_const.per_byte", 1, 1));
+    // LD_TRUE
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ld_true", 1, 1));
+    // LD_FALSE
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ld_false", 1, 1));
+    // COPY_LOC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.copy_loc.per_abs_mem_unit", 1, 1));
+    // MOVE_LOC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.move_loc.per_abs_mem_unit", 1, 1));
+    // ST_LOC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.st_loc.per_abs_mem_unit", 1, 1));
+    // MUT_BORROW_LOC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_loc", 2, 1));
+    // IMM_BORROW_LOC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_loc", 1, 1));
+    // MUT_BORROW_FIELD
+    Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_field", 1, 1));
+    // IMM_BORROW_FIELD
+    Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_field", 1, 1));
+    // CALL
+    Vector::push_back(&mut table, new_gas_entry(b"instr.call.per_arg", 1132, 1));
+    // PACK
+    Vector::push_back(&mut table, new_gas_entry(b"instr.pack.per_abs_mem_unit", 2, 1));
+    // UNPACK
+    Vector::push_back(&mut table, new_gas_entry(b"instr.unpack.per_abs_mem_unit", 2, 1));
+    // READ_REF
+    Vector::push_back(&mut table, new_gas_entry(b"instr.read_ref.per_abs_mem_unit", 1, 1));
+    // WRITE_REF
+    Vector::push_back(&mut table, new_gas_entry(b"instr.write_ref.per_abs_mem_unit", 1, 1));
+    // ADD
+    Vector::push_back(&mut table, new_gas_entry(b"instr.add", 1, 1));
+    // SUB
+    Vector::push_back(&mut table, new_gas_entry(b"instr.sub", 1, 1));
+    // MUL
+    Vector::push_back(&mut table, new_gas_entry(b"instr.mul", 1, 1));
+    // MOD
+    Vector::push_back(&mut table, new_gas_entry(b"instr.mod", 1, 1));
+    // DIV
+    Vector::push_back(&mut table, new_gas_entry(b"instr.div", 3, 1));
+    // BIT_OR
+    Vector::push_back(&mut table, new_gas_entry(b"instr.bit_or", 2, 1));
+    // BIT_AND
+    Vector::push_back(&mut table, new_gas_entry(b"instr.bit_and", 2, 1));
+    // XOR
+    Vector::push_back(&mut table, new_gas_entry(b"instr.xor", 1, 1));
+    // OR
+    Vector::push_back(&mut table, new_gas_entry(b"instr.or", 2, 1));
+    // AND
+    Vector::push_back(&mut table, new_gas_entry(b"instr.and", 1, 1));
+    // NOT
+    Vector::push_back(&mut table, new_gas_entry(b"instr.not", 1, 1));
+    // EQ
+    Vector::push_back(&mut table, new_gas_entry(b"instr.eq.per_abs_mem_unit", 1, 1));
+    // NEQ
+    Vector::push_back(&mut table, new_gas_entry(b"instr.neq.per_abs_mem_unit", 1, 1));
+    // LT
+    Vector::push_back(&mut table, new_gas_entry(b"instr.lt", 1, 1));
+    // GT
+    Vector::push_back(&mut table, new_gas_entry(b"instr.gt", 1, 1));
+    // LE
+    Vector::push_back(&mut table, new_gas_entry(b"instr.le", 2, 1));
+    // GE
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ge", 1, 1));
+    // ABORT
+    Vector::push_back(&mut table, new_gas_entry(b"instr.abort", 1, 1));
+    // NOP
+    Vector::push_back(&mut table, new_gas_entry(b"instr.nop", 1, 1));
+    // EXISTS
+    Vector::push_back(&mut table, new_gas_entry(b"instr.exists.per_abs_mem_unit", 41, 1));
+    // MUT_BORROW_GLOBAL
+    Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_global.per_abs_mem_unit", 21, 1));
+    // IML_BORROW_GLOBAL
+    Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_global.per_abs_mem_unit", 23, 1));
+    // MOVE_FROM
+    Vector::push_back(&mut table, new_gas_entry(b"instr.move_from.per_abs_mem_unit", 459, 1));
+    // MOVE_TO
+    Vector::push_back(&mut table, new_gas_entry(b"instr.move_to.per_abs_mem_unit", 13, 1));
+    // FREEZE_REF
+    Vector::push_back(&mut table, new_gas_entry(b"instr.freeze_ref", 1, 1));
+    // SHL
+    Vector::push_back(&mut table, new_gas_entry(b"instr.shl", 2, 1));
+    // SHR
+    Vector::push_back(&mut table, new_gas_entry(b"instr.shr", 1, 1));
+    // LD_U8
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ld_u8", 1, 1));
+    // LD_U128
+    Vector::push_back(&mut table, new_gas_entry(b"instr.ld_u128", 1, 1));
+
+    // CAST_U8
+    Vector::push_back(&mut table, new_gas_entry(b"instr.cast_u8", 2, 1));
+    // CAST_U64
+    Vector::push_back(&mut table, new_gas_entry(b"instr.cast_u64", 1, 1));
+    // CAST_U128
+    Vector::push_back(&mut table, new_gas_entry(b"instr.cast_u128", 1, 1));
+    // MUT_BORORW_FIELD_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_field_generic.base", 1, 1));
+    // IMM_BORORW_FIELD_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_field_generic.base", 1, 1));
+    // CALL_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.call_generic.per_arg", 582, 1));
+    // PACK_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.pack_generic.per_abs_mem_unit", 2, 1));
+    // UNPACK_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.unpack_generic.per_abs_mem_unit", 2, 1));
+    // EXISTS_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.exists_generic.per_abs_mem_unit", 34, 1));
+    // MUT_BORROW_GLOBAL_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_global_generic.per_abs_mem_unit", 15, 1));
+    // IMM_BORROW_GLOBAL_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_global_generic.per_abs_mem_unit", 14, 1));
+    // MOVE_FROM_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.move_from_generic.per_abs_mem_unit", 13, 1));
+    // MOVE_TO_GENERIC
+    Vector::push_back(&mut table, new_gas_entry(b"instr.move_to_generic.per_abs_mem_unit", 27, 1));
+
+    // VEC_PACK
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_pack.per_elem", 84, 1));
+    // VEC_LEN
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_len.base", 98, 1));
+    // VEC_IMM_BORROW
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_imm_borrow.base", 1334, 1));
+    // VEC_MUT_BORROW
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_mut_borrow.base", 1902, 1));
+    // VEC_PUSH_BACK
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_push_back.per_abs_mem_unit", 53, 1));
+    // VEC_POP_BACK
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_pop_back.base", 227, 1));
+    // VEC_UNPACK
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_unpack.per_expected_elem", 572, 1));
+    // VEC_SWAP
+    Vector::push_back(&mut table, new_gas_entry(b"instr.vec_swap.base", 1436, 1));
+
+    Vector::push_back(&mut table, new_constant_entry(b"instr.ld_u16", 3));
+    Vector::push_back(&mut table, new_constant_entry(b"instr.ld_u32", 2));
+    Vector::push_back(&mut table, new_constant_entry(b"instr.ld_u256", 3));
+    Vector::push_back(&mut table, new_constant_entry(b"instr.cast_u16", 3));
+    Vector::push_back(&mut table, new_constant_entry(b"instr.cast_u32", 2));
+    Vector::push_back(&mut table, new_constant_entry(b"instr.cast_u256", 3));
+
+    // native_schedule
+    //Hash::sha2_256 0
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.hash.sha2_256.per_byte", 21, 1));
+    //Hash::sha3_256 1
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.hash.sha3_256.per_byte", 64, 1));
+    //Signature::ed25519_verify 2
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.signature.ed25519_verify.per_byte", 61, 1));
+    //ED25519_THRESHOLD_VERIFY 3 this native funciton is deprecated
+    //Vector::push_back(&mut table, new_gas_entry(b"", 3351, 1));
+    //BSC::to_bytes 4
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.bcs.to_bytes.per_byte_serialized", 181, 1));
+    //Vector::length 5
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.length.base", 98, 1));
+    //Vector::empty 6
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.empty.base", 84, 1));
+    //Vector::borrow 7
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.borrow.base", 1334, 1));
+    //Vector::borrow_mut 8
+    //Vector::push_back(&mut table, new_gas_entry(b"", 1902, 1));
+    //Vector::push_back 9
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.push_back.legacy_per_abstract_memory_unit", 53, 1));
+    //Vector::pop_back 10
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.pop_back.base", 227, 1));
+    //Vector::destory_empty 11
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.destroy_empty.base", 572, 1));
+    //Vector::swap 12
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.swap.base", 1436, 1));
+    //Signature::ed25519_validate_pubkey 13
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.signature.ed25519_validate_key.per_byte", 26, 1));
+    //Signer::borrow_address 14
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.signer.borrow_address.base", 353, 1));
+    //Account::creator_signer 15
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.account.create_signer.base", 24, 1));
+    //Account::destroy_signer 16
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.account.destroy_signer.base", 212, 1));
+    //Event::emit_event 17
+    Vector::push_back(&mut table, new_gas_entry(b"nursery.event.write_to_event_store.unit_cost", 52, 1));
+    //BCS::to_address 18
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.bcs.to_address.per_byte", 26, 1));
+    //Token::name_of 19
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.token.name_of.base", 2002, 1));
+    //Hash::keccak_256 20
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.hash.keccak256.per_byte", 64, 1));
+    //Hash::ripemd160 21
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.hash.ripemd160.per_byte", 64, 1));
+    //Signature::native_ecrecover 22
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.signature.ec_recover.per_byte", 128, 1));
+    //U256::from_bytes 23
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.from_bytes.per_byte", 2, 1));
+    //U256::add 24
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.add.base", 4, 1));
+    //U256::sub 25
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.sub.base", 4, 1));
+    //U256::mul 26
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.mul.base", 4, 1));
+    //U256::div 27
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.div.base", 10, 1));
+    // U256::rem 28
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.rem.base", 4, 1));
+    // U256::pow 29
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.pow.base", 8, 1));
+    // TODO: settle down the gas cost
+    // Vector::append 30
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.append.legacy_per_abstract_memory_unit", 40, 1));
+    // Vector::remove 31
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.remove.legacy_per_abstract_memory_unit", 20, 1));
+    // Vector::reverse 32
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.reverse.legacy_per_abstract_memory_unit", 10, 1));
+    // Table::new_table_handle 33
+    Vector::push_back(&mut table, new_gas_entry(b"table.new_table_handle.base", 4, 1));
+    // Table::add_box 34
+    Vector::push_back(&mut table, new_gas_entry(b"table.add_box.per_byte_serialized", 4, 1));
+    // Table::borrow_box 35
+    Vector::push_back(&mut table, new_gas_entry(b"table.borrow_box.per_byte_serialized", 10, 1));
+    // Table::remove_box 36
+    Vector::push_back(&mut table, new_gas_entry(b"table.remove_box.per_byte_serialized", 8, 1));
+    // Table::contains_box 37
+    Vector::push_back(&mut table, new_gas_entry(b"table.contains_box.per_byte_serialized", 40, 1));
+    // Table::destroy_empty_box 38
+    Vector::push_back(&mut table, new_gas_entry(b"table.destroy_empty_box.base", 20, 1));
+    // Table::drop_unchecked_box 39
+    Vector::push_back(&mut table, new_gas_entry(b"table.drop_unchecked_box.base", 73, 1));
+    // string.check_utf8 40
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.check_utf8.per_byte", 4, 1));
+    // string.sub_str 41
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.sub_string.per_byte", 4, 1));
+    // string.is_char_boundary 42
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.is_char_boundary.base", 4, 1));
+    // Table::string.index_of 43
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.index_of.per_byte_searched", 4, 1));
+    // Table::string.index_of 44
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.frombcs.base", 4, 1));
+    // Table::string.index_of 45
+    Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.secp256k1.base", 4, 1));
+    // Table::string.index_of 46
+    Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.spawn_from.legacy_per_abstract_memory_unit", 4, 1));
+
+    Vector::push_back(&mut table, new_constant_entry(b"nursery.debug.print.base_cost", 1));
+    Vector::push_back(&mut table, new_constant_entry(b"nursery.debug.print_stack_trace.base_cost", 1));
+    Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.hash.sha2_256.legacy_min_input_len", 1));
+    Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.hash.sha3_256.legacy_min_input_len", 1));
+    Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.bcs.to_bytes.failure", 182));
+    Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.bcs.to_bytes.legacy_min_output_size", 1));
+
+    // constant config values
+    Vector::push_back(&mut table, new_constant_entry(b"txn.global_memory_per_byte_cost", 4));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.global_memory_per_byte_write_cost", 9));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.min_transaction_gas_units", 600));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.large_transaction_cutoff", 600));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.intrinsic_gas_per_byte", 8));
+    let maximum_number_of_gas_units: u64 = 40000000;//must less than base_block_gas_limit
+    if (ChainId::is_test() || ChainId::is_dev() || ChainId::is_halley()) {
+        maximum_number_of_gas_units = maximum_number_of_gas_units * 10
+    };
+    Vector::push_back(&mut table, new_constant_entry(b"txn.maximum_number_of_gas_units", maximum_number_of_gas_units));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.min_price_per_gas_unit", if (ChainId::is_test()) { 0 }  else { 1 }));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.max_price_per_gas_unit", 10000));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.max_transaction_size_in_bytes", 1024 * 128));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.gas_unit_scaling_factor", 1));
+    Vector::push_back(&mut table, new_constant_entry(b"txn.default_account_size", 800));
+
+    table
+}
+
+ + + +
+ + + +## Function `new_gas_entry` + + + +
public fun new_gas_entry(key: vector<u8>, instr_gas: u64, mem_gas: u64): GasSchedule::GasEntry
+
+ + + +
+Implementation + + +
public fun new_gas_entry(key: vector<u8>, instr_gas: u64, mem_gas: u64): GasEntry {
+    GasEntry {
+        key,
+        val: instr_gas + mem_gas,
+    }
+}
+
+ + + +
+ + + +## Function `new_constant_entry` + + + +
fun new_constant_entry(key: vector<u8>, val: u64): GasSchedule::GasEntry
+
+ + + +
+Implementation + + +
fun new_constant_entry(key: vector<u8>, val: u64): GasEntry {
+    GasEntry {
+        key,
+        val,
+    }
+}
+
+ + + +
+ + + +## Function `initialize` + +Initialize the gas schedule under the genesis account + + +
public fun initialize(account: &signer, gas_schedule: GasSchedule::GasSchedule)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, gas_schedule: GasSchedule) {
+    CoreAddresses::assert_genesis_address(account);
+    Config::publish_new_config<GasSchedule>(
+        account,
+        gas_schedule,
+    );
+}
+
+ + + +
+ + + +## Function `new_gas_schedule` + + + +
public fun new_gas_schedule(): GasSchedule::GasSchedule
+
+ + + +
+Implementation + + +
public fun new_gas_schedule(): GasSchedule {
+    GasSchedule {
+        entries: gas_schedule(),
+    }
+}
+
+ + + +
+ + + +## Function `new_gas_schedule_for_test` + + + +
public fun new_gas_schedule_for_test(): GasSchedule::GasSchedule
+
+ + + +
+Implementation + + +
public fun new_gas_schedule_for_test(): GasSchedule {
+    let entry = GasEntry {
+        key: Vector::empty(),
+        val: 1,
+    };
+    let entries = Vector::empty();
+    Vector::push_back(&mut entries, entry);
+
+    GasSchedule {
+        entries,
+    }
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Genesis.md b/release/v13/docs/Genesis.md new file mode 100644 index 00000000..6d449dab --- /dev/null +++ b/release/v13/docs/Genesis.md @@ -0,0 +1,656 @@ + + + +# Module `0x1::Genesis` + +The module for init Genesis + + +- [Function `initialize`](#0x1_Genesis_initialize) +- [Function `initialize_v2`](#0x1_Genesis_initialize_v2) +- [Function `do_initialize`](#0x1_Genesis_do_initialize) +- [Function `initialize_for_unit_tests`](#0x1_Genesis_initialize_for_unit_tests) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::Account;
+use 0x1::Block;
+use 0x1::BlockReward;
+use 0x1::ChainId;
+use 0x1::Collection;
+use 0x1::Config;
+use 0x1::ConsensusConfig;
+use 0x1::ConsensusStrategy;
+use 0x1::CoreAddresses;
+use 0x1::DummyToken;
+use 0x1::Epoch;
+use 0x1::GenesisNFT;
+use 0x1::GenesisSignerCapability;
+use 0x1::Option;
+use 0x1::PackageTxnManager;
+use 0x1::STC;
+use 0x1::STCUSDOracle;
+use 0x1::Signer;
+use 0x1::StdlibUpgradeScripts;
+use 0x1::Timestamp;
+use 0x1::Token;
+use 0x1::TransactionFee;
+use 0x1::TransactionPublishOption;
+use 0x1::TransactionTimeoutConfig;
+use 0x1::Treasury;
+use 0x1::TreasuryWithdrawDaoProposal;
+use 0x1::VMConfig;
+use 0x1::Vector;
+use 0x1::Version;
+
+ + + + + +## Function `initialize` + + + +
public entry fun initialize(stdlib_version: u64, reward_delay: u64, pre_mine_stc_amount: u128, time_mint_stc_amount: u128, time_mint_stc_period: u64, parent_hash: vector<u8>, association_auth_key: vector<u8>, genesis_auth_key: vector<u8>, chain_id: u8, genesis_timestamp: u64, uncle_rate_target: u64, epoch_block_count: u64, base_block_time_target: u64, base_block_difficulty_window: u64, base_reward_per_block: u128, base_reward_per_uncle_percent: u64, min_block_time_target: u64, max_block_time_target: u64, base_max_uncles_per_block: u64, base_block_gas_limit: u64, strategy: u8, script_allowed: bool, module_publishing_allowed: bool, instruction_schedule: vector<u8>, native_schedule: vector<u8>, global_memory_per_byte_cost: u64, global_memory_per_byte_write_cost: u64, min_transaction_gas_units: u64, large_transaction_cutoff: u64, instrinsic_gas_per_byte: u64, maximum_number_of_gas_units: u64, min_price_per_gas_unit: u64, max_price_per_gas_unit: u64, max_transaction_size_in_bytes: u64, gas_unit_scaling_factor: u64, default_account_size: u64, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64, transaction_timeout: u64)
+
+ + + +
+Implementation + + +
public entry fun initialize(
+    stdlib_version: u64,
+
+    // block reward config
+    reward_delay: u64,
+
+    pre_mine_stc_amount: u128,
+    time_mint_stc_amount: u128,
+    time_mint_stc_period: u64,
+    parent_hash: vector<u8>,
+    association_auth_key: vector<u8>,
+    genesis_auth_key: vector<u8>,
+    chain_id: u8,
+    genesis_timestamp: u64,
+
+    //consensus config
+    uncle_rate_target: u64,
+    epoch_block_count: u64,
+    base_block_time_target: u64,
+    base_block_difficulty_window: u64,
+    base_reward_per_block: u128,
+    base_reward_per_uncle_percent: u64,
+    min_block_time_target: u64,
+    max_block_time_target: u64,
+    base_max_uncles_per_block: u64,
+    base_block_gas_limit: u64,
+    strategy: u8,
+
+    //vm config
+    script_allowed: bool,
+    module_publishing_allowed: bool,
+    instruction_schedule: vector<u8>,
+    native_schedule: vector<u8>,
+
+    //gas constants
+    global_memory_per_byte_cost: u64,
+    global_memory_per_byte_write_cost: u64,
+    min_transaction_gas_units: u64,
+    large_transaction_cutoff: u64,
+    instrinsic_gas_per_byte: u64,
+    maximum_number_of_gas_units: u64,
+    min_price_per_gas_unit: u64,
+    max_price_per_gas_unit: u64,
+    max_transaction_size_in_bytes: u64,
+    gas_unit_scaling_factor: u64,
+    default_account_size: u64,
+
+    // dao config
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+
+    // transaction timeout config
+    transaction_timeout: u64,
+) {
+    assert!(Timestamp::is_genesis(), 1);
+    // create genesis account
+    let genesis_account = Account::create_genesis_account(CoreAddresses::GENESIS_ADDRESS());
+    //Init global time
+    Timestamp::initialize(&genesis_account, genesis_timestamp);
+    ChainId::initialize(&genesis_account, chain_id);
+    ConsensusStrategy::initialize(&genesis_account, strategy);
+    Block::initialize(&genesis_account, parent_hash);
+    TransactionPublishOption::initialize(
+        &genesis_account,
+        script_allowed,
+        module_publishing_allowed,
+    );
+    // init config
+    VMConfig::initialize(
+        &genesis_account,
+        instruction_schedule,
+        native_schedule,
+        global_memory_per_byte_cost,
+        global_memory_per_byte_write_cost,
+        min_transaction_gas_units,
+        large_transaction_cutoff,
+        instrinsic_gas_per_byte,
+        maximum_number_of_gas_units,
+        min_price_per_gas_unit,
+        max_price_per_gas_unit,
+        max_transaction_size_in_bytes,
+        gas_unit_scaling_factor,
+        default_account_size,
+    );
+    TransactionTimeoutConfig::initialize(&genesis_account, transaction_timeout);
+    ConsensusConfig::initialize(
+        &genesis_account,
+        uncle_rate_target,
+        epoch_block_count,
+        base_block_time_target,
+        base_block_difficulty_window,
+        base_reward_per_block,
+        base_reward_per_uncle_percent,
+        min_block_time_target,
+        max_block_time_target,
+        base_max_uncles_per_block,
+        base_block_gas_limit,
+        strategy,
+    );
+    Epoch::initialize(&genesis_account);
+    BlockReward::initialize(&genesis_account, reward_delay);
+    TransactionFee::initialize(&genesis_account);
+    let association = Account::create_genesis_account(
+        CoreAddresses::ASSOCIATION_ROOT_ADDRESS(),
+    );
+    Config::publish_new_config<Version::Version>(&genesis_account, Version::new_version(stdlib_version));
+    // stdlib use two phase upgrade strategy.
+    PackageTxnManager::update_module_upgrade_strategy(
+        &genesis_account,
+        PackageTxnManager::get_strategy_two_phase(),
+        Option::some(0u64),
+    );
+    // stc should be initialized after genesis_account's module upgrade strategy set.
+    {
+        STC::initialize(&genesis_account, voting_delay, voting_period, voting_quorum_rate, min_action_delay);
+        Account::do_accept_token<STC>(&genesis_account);
+        DummyToken::initialize(&genesis_account);
+        Account::do_accept_token<STC>(&association);
+    };
+    if (pre_mine_stc_amount > 0) {
+        let stc = Token::mint<STC>(&genesis_account, pre_mine_stc_amount);
+        Account::deposit(Signer::address_of(&association), stc);
+    };
+    if (time_mint_stc_amount > 0) {
+        let cap = Token::remove_mint_capability<STC>(&genesis_account);
+        let key = Token::issue_linear_mint_key<STC>(&cap, time_mint_stc_amount, time_mint_stc_period);
+        Token::add_mint_capability(&genesis_account, cap);
+        Collection::put(&association, key);
+    };
+    // only dev network set genesis auth key.
+    if (!Vector::is_empty(&genesis_auth_key)) {
+        let genesis_rotate_key_cap = Account::extract_key_rotation_capability(&genesis_account);
+        Account::rotate_authentication_key_with_capability(&genesis_rotate_key_cap, genesis_auth_key);
+        Account::restore_key_rotation_capability(genesis_rotate_key_cap);
+    };
+    let assoc_rotate_key_cap = Account::extract_key_rotation_capability(&association);
+    Account::rotate_authentication_key_with_capability(&assoc_rotate_key_cap, association_auth_key);
+    Account::restore_key_rotation_capability(assoc_rotate_key_cap);
+    //Start time, Timestamp::is_genesis() will return false. this call should at the end of genesis init.
+    Timestamp::set_time_has_started(&genesis_account);
+    Account::release_genesis_signer(genesis_account);
+    Account::release_genesis_signer(association);
+}
+
+ + + +
+ + + +## Function `initialize_v2` + + + +
public entry fun initialize_v2(stdlib_version: u64, reward_delay: u64, total_stc_amount: u128, pre_mine_stc_amount: u128, time_mint_stc_amount: u128, time_mint_stc_period: u64, parent_hash: vector<u8>, association_auth_key: vector<u8>, genesis_auth_key: vector<u8>, chain_id: u8, genesis_timestamp: u64, uncle_rate_target: u64, epoch_block_count: u64, base_block_time_target: u64, base_block_difficulty_window: u64, base_reward_per_block: u128, base_reward_per_uncle_percent: u64, min_block_time_target: u64, max_block_time_target: u64, base_max_uncles_per_block: u64, base_block_gas_limit: u64, strategy: u8, script_allowed: bool, module_publishing_allowed: bool, instruction_schedule: vector<u8>, native_schedule: vector<u8>, global_memory_per_byte_cost: u64, global_memory_per_byte_write_cost: u64, min_transaction_gas_units: u64, large_transaction_cutoff: u64, instrinsic_gas_per_byte: u64, maximum_number_of_gas_units: u64, min_price_per_gas_unit: u64, max_price_per_gas_unit: u64, max_transaction_size_in_bytes: u64, gas_unit_scaling_factor: u64, default_account_size: u64, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64, transaction_timeout: u64)
+
+ + + +
+Implementation + + +
public entry fun initialize_v2(
+    stdlib_version: u64,
+
+    // block reward and stc config
+    reward_delay: u64,
+    total_stc_amount: u128,
+    pre_mine_stc_amount: u128,
+    time_mint_stc_amount: u128,
+    time_mint_stc_period: u64,
+
+    parent_hash: vector<u8>,
+    association_auth_key: vector<u8>,
+    genesis_auth_key: vector<u8>,
+    chain_id: u8,
+    genesis_timestamp: u64,
+
+    //consensus config
+    uncle_rate_target: u64,
+    epoch_block_count: u64,
+    base_block_time_target: u64,
+    base_block_difficulty_window: u64,
+    base_reward_per_block: u128,
+    base_reward_per_uncle_percent: u64,
+    min_block_time_target: u64,
+    max_block_time_target: u64,
+    base_max_uncles_per_block: u64,
+    base_block_gas_limit: u64,
+    strategy: u8,
+
+    //vm config
+    script_allowed: bool,
+    module_publishing_allowed: bool,
+    instruction_schedule: vector<u8>,
+    native_schedule: vector<u8>,
+
+    //gas constants
+    global_memory_per_byte_cost: u64,
+    global_memory_per_byte_write_cost: u64,
+    min_transaction_gas_units: u64,
+    large_transaction_cutoff: u64,
+    instrinsic_gas_per_byte: u64,
+    maximum_number_of_gas_units: u64,
+    min_price_per_gas_unit: u64,
+    max_price_per_gas_unit: u64,
+    max_transaction_size_in_bytes: u64,
+    gas_unit_scaling_factor: u64,
+    default_account_size: u64,
+
+    // dao config
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+
+    // transaction timeout config
+    transaction_timeout: u64,
+) {
+    Self::do_initialize(
+    stdlib_version,
+    reward_delay,
+    total_stc_amount,
+    pre_mine_stc_amount,
+    time_mint_stc_amount,
+    time_mint_stc_period,
+    parent_hash,
+    association_auth_key,
+    genesis_auth_key,
+    chain_id,
+    genesis_timestamp,
+    uncle_rate_target,
+    epoch_block_count,
+    base_block_time_target,
+    base_block_difficulty_window,
+    base_reward_per_block,
+    base_reward_per_uncle_percent,
+    min_block_time_target,
+    max_block_time_target,
+    base_max_uncles_per_block,
+    base_block_gas_limit,
+    strategy,
+    script_allowed,
+    module_publishing_allowed,
+    instruction_schedule,
+    native_schedule,
+    global_memory_per_byte_cost,
+    global_memory_per_byte_write_cost,
+    min_transaction_gas_units,
+    large_transaction_cutoff,
+    instrinsic_gas_per_byte,
+    maximum_number_of_gas_units,
+    min_price_per_gas_unit,
+    max_price_per_gas_unit,
+    max_transaction_size_in_bytes,
+    gas_unit_scaling_factor,
+    default_account_size,
+    voting_delay,
+    voting_period,
+    voting_quorum_rate,
+    min_action_delay,
+    transaction_timeout,
+    );
+}
+
+ + + +
+ + + +## Function `do_initialize` + + + +
fun do_initialize(stdlib_version: u64, reward_delay: u64, total_stc_amount: u128, pre_mine_stc_amount: u128, time_mint_stc_amount: u128, time_mint_stc_period: u64, parent_hash: vector<u8>, association_auth_key: vector<u8>, genesis_auth_key: vector<u8>, chain_id: u8, genesis_timestamp: u64, uncle_rate_target: u64, epoch_block_count: u64, base_block_time_target: u64, base_block_difficulty_window: u64, base_reward_per_block: u128, base_reward_per_uncle_percent: u64, min_block_time_target: u64, max_block_time_target: u64, base_max_uncles_per_block: u64, base_block_gas_limit: u64, strategy: u8, script_allowed: bool, module_publishing_allowed: bool, instruction_schedule: vector<u8>, native_schedule: vector<u8>, global_memory_per_byte_cost: u64, global_memory_per_byte_write_cost: u64, min_transaction_gas_units: u64, large_transaction_cutoff: u64, instrinsic_gas_per_byte: u64, maximum_number_of_gas_units: u64, min_price_per_gas_unit: u64, max_price_per_gas_unit: u64, max_transaction_size_in_bytes: u64, gas_unit_scaling_factor: u64, default_account_size: u64, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64, transaction_timeout: u64)
+
+ + + +
+Implementation + + +
fun do_initialize(
+    stdlib_version: u64,
+
+    // block reward and stc config
+    reward_delay: u64,
+    total_stc_amount: u128,
+    pre_mine_stc_amount: u128,
+    time_mint_stc_amount: u128,
+    time_mint_stc_period: u64,
+
+    parent_hash: vector<u8>,
+    association_auth_key: vector<u8>,
+    genesis_auth_key: vector<u8>,
+    chain_id: u8,
+    genesis_timestamp: u64,
+
+    //consensus config
+    uncle_rate_target: u64,
+    epoch_block_count: u64,
+    base_block_time_target: u64,
+    base_block_difficulty_window: u64,
+    base_reward_per_block: u128,
+    base_reward_per_uncle_percent: u64,
+    min_block_time_target: u64,
+    max_block_time_target: u64,
+    base_max_uncles_per_block: u64,
+    base_block_gas_limit: u64,
+    strategy: u8,
+
+    //vm config
+    script_allowed: bool,
+    module_publishing_allowed: bool,
+    instruction_schedule: vector<u8>,
+    native_schedule: vector<u8>,
+
+    //gas constants
+    global_memory_per_byte_cost: u64,
+    global_memory_per_byte_write_cost: u64,
+    min_transaction_gas_units: u64,
+    large_transaction_cutoff: u64,
+    instrinsic_gas_per_byte: u64,
+    maximum_number_of_gas_units: u64,
+    min_price_per_gas_unit: u64,
+    max_price_per_gas_unit: u64,
+    max_transaction_size_in_bytes: u64,
+    gas_unit_scaling_factor: u64,
+    default_account_size: u64,
+
+    // dao config
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+
+    // transaction timeout config
+    transaction_timeout: u64,
+){
+    Timestamp::assert_genesis();
+    // create genesis account
+    let genesis_account = Account::create_genesis_account(CoreAddresses::GENESIS_ADDRESS());
+    //Init global time
+    Timestamp::initialize(&genesis_account, genesis_timestamp);
+    ChainId::initialize(&genesis_account, chain_id);
+    ConsensusStrategy::initialize(&genesis_account, strategy);
+    Block::initialize(&genesis_account, parent_hash);
+    TransactionPublishOption::initialize(
+        &genesis_account,
+        script_allowed,
+        module_publishing_allowed,
+    );
+    // init config
+    VMConfig::initialize(
+        &genesis_account,
+        instruction_schedule,
+        native_schedule,
+        global_memory_per_byte_cost,
+        global_memory_per_byte_write_cost,
+        min_transaction_gas_units,
+        large_transaction_cutoff,
+        instrinsic_gas_per_byte,
+        maximum_number_of_gas_units,
+        min_price_per_gas_unit,
+        max_price_per_gas_unit,
+        max_transaction_size_in_bytes,
+        gas_unit_scaling_factor,
+        default_account_size,
+    );
+    TransactionTimeoutConfig::initialize(&genesis_account, transaction_timeout);
+    ConsensusConfig::initialize(
+        &genesis_account,
+        uncle_rate_target,
+        epoch_block_count,
+        base_block_time_target,
+        base_block_difficulty_window,
+        base_reward_per_block,
+        base_reward_per_uncle_percent,
+        min_block_time_target,
+        max_block_time_target,
+        base_max_uncles_per_block,
+        base_block_gas_limit,
+        strategy,
+    );
+    Epoch::initialize(&genesis_account);
+    let association = Account::create_genesis_account(
+        CoreAddresses::ASSOCIATION_ROOT_ADDRESS(),
+    );
+    Config::publish_new_config<Version::Version>(&genesis_account, Version::new_version(stdlib_version));
+    // stdlib use two phase upgrade strategy.
+    PackageTxnManager::update_module_upgrade_strategy(
+        &genesis_account,
+        PackageTxnManager::get_strategy_two_phase(),
+        Option::some(0u64),
+    );
+    BlockReward::initialize(&genesis_account, reward_delay);
+
+    // stc should be initialized after genesis_account's module upgrade strategy set and all on chain config init.
+    let withdraw_cap = STC::initialize_v2(&genesis_account, total_stc_amount, voting_delay, voting_period, voting_quorum_rate, min_action_delay);
+    Account::do_accept_token<STC>(&genesis_account);
+    Account::do_accept_token<STC>(&association);
+
+    DummyToken::initialize(&genesis_account);
+
+    if (pre_mine_stc_amount > 0) {
+        let stc = Treasury::withdraw_with_capability<STC>(&mut withdraw_cap, pre_mine_stc_amount);
+        Account::deposit(Signer::address_of(&association), stc);
+    };
+    if (time_mint_stc_amount > 0) {
+        let liner_withdraw_cap = Treasury::issue_linear_withdraw_capability<STC>(&mut withdraw_cap, time_mint_stc_amount, time_mint_stc_period);
+        Treasury::add_linear_withdraw_capability(&association, liner_withdraw_cap);
+    };
+
+    // Lock the TreasuryWithdrawCapability to Dao
+    TreasuryWithdrawDaoProposal::plugin(&genesis_account, withdraw_cap);
+
+    TransactionFee::initialize(&genesis_account);
+
+    // only test/dev network set genesis auth key.
+    if (!Vector::is_empty(&genesis_auth_key)) {
+        let genesis_rotate_key_cap = Account::extract_key_rotation_capability(&genesis_account);
+        Account::rotate_authentication_key_with_capability(&genesis_rotate_key_cap, genesis_auth_key);
+        Account::restore_key_rotation_capability(genesis_rotate_key_cap);
+    };
+
+    let assoc_rotate_key_cap = Account::extract_key_rotation_capability(&association);
+    Account::rotate_authentication_key_with_capability(&assoc_rotate_key_cap, association_auth_key);
+    Account::restore_key_rotation_capability(assoc_rotate_key_cap);
+
+    // v5 -> v6
+    {
+        let cap = Account::remove_signer_capability(&genesis_account);
+        GenesisSignerCapability::initialize(&genesis_account, cap);
+        //register oracle
+        STCUSDOracle::register(&genesis_account);
+        let merkle_root = x"5969f0e8e19f8769276fb638e6060d5c02e40088f5fde70a6778dd69d659ee6d";
+        let image = b"ipfs://QmSPcvcXgdtHHiVTAAarzTeubk5X3iWymPAoKBfiRFjPMY";
+        GenesisNFT::initialize(&genesis_account, merkle_root, 1639u64, image);
+    };
+    StdlibUpgradeScripts::do_upgrade_from_v6_to_v7_with_language_version(&genesis_account, 6);
+    StdlibUpgradeScripts::do_upgrade_from_v11_to_v12(&genesis_account);
+    StdlibUpgradeScripts::do_upgrade_from_v12_to_v13(&genesis_account);
+    //Start time, Timestamp::is_genesis() will return false. this call should at the end of genesis init.
+    Timestamp::set_time_has_started(&genesis_account);
+    Account::release_genesis_signer(genesis_account);
+    Account::release_genesis_signer(association);
+}
+
+ + + +
+ + + +## Function `initialize_for_unit_tests` + +Init the genesis for unit tests + + +
public fun initialize_for_unit_tests()
+
+ + + +
+Implementation + + +
public fun initialize_for_unit_tests(){
+    let stdlib_version: u64 = 6;
+    let reward_delay: u64 = 7;
+    let total_stc_amount: u128 = 3185136000000000000u128;
+    let pre_mine_stc_amount: u128 = 159256800000000000u128;
+    let time_mint_stc_amount: u128 = (85043130u128 * 3u128 + 74213670u128 * 3u128)*1000000000u128;
+    let time_mint_stc_period: u64 = 1000000000;
+
+    let parent_hash: vector<u8> = x"0000000000000000000000000000000000000000000000000000000000000000";
+    let association_auth_key: vector<u8> = x"0000000000000000000000000000000000000000000000000000000000000000";
+    let genesis_auth_key: vector<u8> = x"0000000000000000000000000000000000000000000000000000000000000000";
+    let chain_id: u8 = 255;
+    let genesis_timestamp: u64 =0;
+
+    //consensus config
+    let uncle_rate_target: u64 = 80;
+    let epoch_block_count: u64 = 240;
+    let base_block_time_target: u64 = 10000;
+    let base_block_difficulty_window: u64 = 24;
+    let base_reward_per_block: u128 = 1000000000;
+    let base_reward_per_uncle_percent: u64 = 10;
+    let min_block_time_target: u64 = 1000;
+    let max_block_time_target: u64 = 20000;
+    let base_max_uncles_per_block: u64 = 2;
+    let base_block_gas_limit: u64 = 500000000;
+    let strategy: u8 = 0;
+
+    //vm config
+    let script_allowed: bool = true;
+    let module_publishing_allowed: bool = true;
+    //TODO init the gas table.
+    let instruction_schedule: vector<u8> = Vector::empty();
+    let native_schedule: vector<u8> = Vector::empty();
+
+    //gas constants
+    let global_memory_per_byte_cost: u64 = 1;
+    let global_memory_per_byte_write_cost: u64 = 1;
+    let min_transaction_gas_units: u64 = 1;
+    let large_transaction_cutoff: u64 = 1;
+    let instrinsic_gas_per_byte: u64 = 1;
+    let maximum_number_of_gas_units: u64 = 1;
+    let min_price_per_gas_unit: u64 = 1;
+    let max_price_per_gas_unit: u64 = 10000;
+    let max_transaction_size_in_bytes: u64 = 1024*1024;
+    let gas_unit_scaling_factor: u64 = 1;
+    let default_account_size: u64 = 600;
+
+    // dao config
+    let voting_delay: u64 = 1000;
+    let voting_period: u64 =  6000;
+    let voting_quorum_rate: u8 = 4;
+    let min_action_delay: u64 = 1000;
+
+    // transaction timeout config
+    let transaction_timeout: u64 = 10000;
+
+    Self::do_initialize(
+        stdlib_version,
+        reward_delay,
+        total_stc_amount,
+        pre_mine_stc_amount,
+        time_mint_stc_amount,
+        time_mint_stc_period,
+        parent_hash,
+        association_auth_key,
+        genesis_auth_key,
+        chain_id,
+        genesis_timestamp,
+        uncle_rate_target,
+        epoch_block_count,
+        base_block_time_target,
+        base_block_difficulty_window,
+        base_reward_per_block,
+        base_reward_per_uncle_percent,
+        min_block_time_target,
+        max_block_time_target,
+        base_max_uncles_per_block,
+        base_block_gas_limit,
+        strategy,
+        script_allowed,
+        module_publishing_allowed,
+        instruction_schedule,
+        native_schedule,
+        global_memory_per_byte_cost,
+        global_memory_per_byte_write_cost,
+        min_transaction_gas_units,
+        large_transaction_cutoff,
+        instrinsic_gas_per_byte,
+        maximum_number_of_gas_units,
+        min_price_per_gas_unit,
+        max_price_per_gas_unit,
+        max_transaction_size_in_bytes,
+        gas_unit_scaling_factor,
+        default_account_size,
+        voting_delay,
+        voting_period,
+        voting_quorum_rate,
+        min_action_delay,
+        transaction_timeout,
+    );
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_partial = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/GenesisNFT.md b/release/v13/docs/GenesisNFT.md new file mode 100644 index 00000000..5f4ab2de --- /dev/null +++ b/release/v13/docs/GenesisNFT.md @@ -0,0 +1,49 @@ + + + +# Module `0x1::GenesisNFTScripts` + + + +- [Function `mint`](#0x1_GenesisNFTScripts_mint) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::GenesisNFT;
+
+ + + + + +## Function `mint` + +Mint a GenesisNFT + + +
public entry fun mint(sender: signer, index: u64, merkle_proof: vector<vector<u8>>)
+
+ + + +
+Implementation + + +
public entry fun mint(sender: signer, index: u64, merkle_proof:vector<vector<u8>>) {
+    GenesisNFT::mint_entry(sender, index, merkle_proof);
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+
diff --git a/release/v13/docs/GenesisSignerCapability.md b/release/v13/docs/GenesisSignerCapability.md new file mode 100644 index 00000000..5ffea8c8 --- /dev/null +++ b/release/v13/docs/GenesisSignerCapability.md @@ -0,0 +1,114 @@ + + + +# Module `0x1::GenesisSignerCapability` + + + +- [Resource `GenesisSignerCapability`](#0x1_GenesisSignerCapability_GenesisSignerCapability) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_GenesisSignerCapability_initialize) +- [Function `get_genesis_signer`](#0x1_GenesisSignerCapability_get_genesis_signer) + + +
use 0x1::Account;
+use 0x1::CoreAddresses;
+use 0x1::Errors;
+
+ + + + + +## Resource `GenesisSignerCapability` + + + +
struct GenesisSignerCapability has key
+
+ + + +
+Fields + + +
+
+cap: Account::SignerCapability +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ENOT_GENESIS_ACCOUNT: u64 = 11;
+
+ + + + + +## Function `initialize` + + + +
public(friend) fun initialize(signer: &signer, cap: Account::SignerCapability)
+
+ + + +
+Implementation + + +
public(friend) fun initialize(signer: &signer, cap: Account::SignerCapability) {
+    CoreAddresses::assert_genesis_address(signer);
+    assert!(
+        Account::signer_address(&cap) == CoreAddresses::GENESIS_ADDRESS(),
+        Errors::invalid_argument(ENOT_GENESIS_ACCOUNT)
+    );
+    move_to(signer, GenesisSignerCapability{cap});
+}
+
+ + + +
+ + + +## Function `get_genesis_signer` + + + +
public(friend) fun get_genesis_signer(): signer
+
+ + + +
+Implementation + + +
public(friend) fun get_genesis_signer(): signer acquires GenesisSignerCapability {
+    let cap = borrow_global<GenesisSignerCapability>(CoreAddresses::GENESIS_ADDRESS());
+    Account::create_signer_with_cap(&cap.cap)
+}
+
+ + + +
diff --git a/release/v13/docs/Hash.md b/release/v13/docs/Hash.md new file mode 100644 index 00000000..47f83022 --- /dev/null +++ b/release/v13/docs/Hash.md @@ -0,0 +1,116 @@ + + + +# Module `0x1::Hash` + +The module provide sha-hash functionality for Move. + + +- [Function `sha2_256`](#0x1_Hash_sha2_256) +- [Function `sha3_256`](#0x1_Hash_sha3_256) +- [Function `keccak_256`](#0x1_Hash_keccak_256) +- [Function `ripemd160`](#0x1_Hash_ripemd160) +- [Module Specification](#@Module_Specification_0) + + +
+ + + + + +## Function `sha2_256` + + + +
public fun sha2_256(data: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
native public fun sha2_256(data: vector<u8>): vector<u8>;
+
+ + + +
+ + + +## Function `sha3_256` + + + +
public fun sha3_256(data: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
native public fun sha3_256(data: vector<u8>): vector<u8>;
+
+ + + +
+ + + +## Function `keccak_256` + + + +
public fun keccak_256(data: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
native public fun keccak_256(data: vector<u8>): vector<u8>;
+
+ + + +
+ + + +## Function `ripemd160` + + + +
public fun ripemd160(data: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
native public fun ripemd160(data: vector<u8>): vector<u8>;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/LanguageVersion.md b/release/v13/docs/LanguageVersion.md new file mode 100644 index 00000000..1c8bb89d --- /dev/null +++ b/release/v13/docs/LanguageVersion.md @@ -0,0 +1,114 @@ + + + +# Module `0x1::LanguageVersion` + + + +- [Struct `LanguageVersion`](#0x1_LanguageVersion_LanguageVersion) +- [Function `new`](#0x1_LanguageVersion_new) +- [Function `version`](#0x1_LanguageVersion_version) + + +
+ + + + + +## Struct `LanguageVersion` + + + +
struct LanguageVersion has copy, drop, store
+
+ + + +
+Fields + + +
+
+major: u64 +
+
+ +
+
+ + +
+ + + +## Function `new` + + + +
public fun new(version: u64): LanguageVersion::LanguageVersion
+
+ + + +
+Implementation + + +
public fun new(version: u64): LanguageVersion {
+    LanguageVersion {major: version}
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `version` + + + +
public fun version(version: &LanguageVersion::LanguageVersion): u64
+
+ + + +
+Implementation + + +
public fun version(version: &LanguageVersion): u64 {
+    version.major
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
diff --git a/release/v13/docs/Math.md b/release/v13/docs/Math.md new file mode 100644 index 00000000..73b4eab0 --- /dev/null +++ b/release/v13/docs/Math.md @@ -0,0 +1,377 @@ + + + +# Module `0x1::Math` + +The module provide some improved math calculations. + + +- [Constants](#@Constants_0) +- [Function `u64_max`](#0x1_Math_u64_max) +- [Function `u128_max`](#0x1_Math_u128_max) +- [Function `sqrt`](#0x1_Math_sqrt) +- [Function `pow`](#0x1_Math_pow) +- [Function `mul_div`](#0x1_Math_mul_div) +- [Function `sum`](#0x1_Math_sum) +- [Function `avg`](#0x1_Math_avg) +- [Module Specification](#@Module_Specification_1) + + +
+ + + + + +## Constants + + + + + + +
const U128_MAX: u128 = 340282366920938463463374607431768211455;
+
+ + + + + + + +
const U64_MAX: u64 = 18446744073709551615;
+
+ + + + + +## Function `u64_max` + +u64::MAX + + +
public fun u64_max(): u64
+
+ + + +
+Implementation + + +
public fun u64_max(): u64 {
+    U64_MAX
+}
+
+ + + +
+ + + +## Function `u128_max` + +u128::MAX + + +
public fun u128_max(): u128
+
+ + + +
+Implementation + + +
public fun u128_max(): u128 {
+    U128_MAX
+}
+
+ + + +
+ + + +## Function `sqrt` + +babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) + + +
public fun sqrt(y: u128): u64
+
+ + + +
+Implementation + + +
public fun sqrt(y: u128): u64 {
+    if (y < 4) {
+        if (y == 0) {
+            0u64
+        } else {
+            1u64
+        }
+    } else {
+        let z = y;
+        let x = y / 2 + 1;
+        while (x < z) {
+            z = x;
+            x = (y / x + x) / 2;
+        };
+        (z as u64)
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+pragma verify = false;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_sqrt();
+
+ + +We use an uninterpreted function to represent the result of sqrt. The actual value +does not matter for the verification of callers. + + + + + +
fun spec_sqrt(): u128;
+
+ + + +
+ + + +## Function `pow` + +calculate the y pow of x. + + +
public fun pow(x: u64, y: u64): u128
+
+ + + +
+Implementation + + +
public fun pow(x: u64, y: u64): u128 {
+    let result = 1u128;
+    let z = y;
+    let u = (x as u128);
+    while (z > 0) {
+        if (z % 2 == 1) {
+            result = (u * result as u128);
+        };
+        u = (u * u as u128);
+        z = z / 2;
+    };
+    result
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+pragma verify = false;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_pow();
+
+ + +We use an uninterpreted function to represent the result of pow. The actual value +does not matter for the verification of callers. + + + + + +
fun spec_pow(): u128;
+
+ + + +
+ + + +## Function `mul_div` + +https://medium.com/coinmonks/math-in-solidity-part-3-percents-and-proportions-4db014e080b1 +calculate x * y /z with as little loss of precision as possible and avoid overflow + + +
public fun mul_div(x: u128, y: u128, z: u128): u128
+
+ + + +
+Implementation + + +
public fun mul_div(x: u128, y: u128, z: u128): u128 {
+    if (y == z) {
+        return x
+    };
+    if (x == z) {
+        return y
+    };
+    let a = x / z;
+    let b = x % z;
+    //x = a * z + b;
+    let c = y / z;
+    let d = y % z;
+    //y = c * z + d;
+    a * c * z + a * d + b * c + b * d / z
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+include MulDivAbortsIf;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_mul_div();
+
+ + + + + + + +
schema MulDivAbortsIf {
+    x: u128;
+    y: u128;
+    z: u128;
+    aborts_if y != z && x > z && z == 0;
+    aborts_if y != z && x > z && z!=0 && x/z*y > MAX_U128;
+    aborts_if y != z && x <= z && z == 0;
+    aborts_if y != z && x <= z && x / z * (x % z) > MAX_U128;
+    aborts_if y != z && x <= z && x / z * (x % z) * z > MAX_U128;
+    aborts_if y != z && x <= z && x / z * (y % z) > MAX_U128;
+    aborts_if y != z && x <= z && x / z * (x % z) * z + x / z * (y % z) > MAX_U128;
+    aborts_if y != z && x <= z && x % z * (y / z) > MAX_U128;
+    aborts_if y != z && x <= z && x % z * (y % z) > MAX_U128;
+    aborts_if y != z && x <= z && x % z * (y % z) / z > MAX_U128;
+    aborts_if y != z && x <= z && x / z * (x % z) * z + x / z * (y % z) + x % z * (y / z) > MAX_U128;
+    aborts_if y != z && x <= z && x / z * (x % z) * z + x / z * (y % z) + x % z * (y / z) + x % z * (y % z) / z > MAX_U128;
+}
+
+ + + + + + + +
fun spec_mul_div(): u128;
+
+ + + +
+ + + +## Function `sum` + +calculate sum of nums + + +
public fun sum(nums: &vector<u128>): u128
+
+ + + +
+Implementation + + +
public fun sum(nums: &vector<u128>): u128 {
+    let len = Vector::length(nums);
+    let i = 0;
+    let sum = 0;
+    while (i < len){
+        sum = sum + *Vector::borrow(nums, i);
+        i = i + 1;
+    };
+    sum
+}
+
+ + + +
+ + + +## Function `avg` + +calculate average of nums + + +
public fun avg(nums: &vector<u128>): u128
+
+ + + +
+Implementation + + +
public fun avg(nums: &vector<u128>): u128{
+    let len = Vector::length(nums);
+    let sum = sum(nums);
+    sum/(len as u128)
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/MerkleNFT.md b/release/v13/docs/MerkleNFT.md new file mode 100644 index 00000000..95bae1b9 --- /dev/null +++ b/release/v13/docs/MerkleNFT.md @@ -0,0 +1,330 @@ + + + +# Module `0x1::MerkleNFTDistributor` + + + +- [Resource `MerkleNFTDistribution`](#0x1_MerkleNFTDistributor_MerkleNFTDistribution) +- [Constants](#@Constants_0) +- [Function `register`](#0x1_MerkleNFTDistributor_register) +- [Function `mint_with_cap`](#0x1_MerkleNFTDistributor_mint_with_cap) +- [Function `encode_leaf`](#0x1_MerkleNFTDistributor_encode_leaf) +- [Function `set_minted_`](#0x1_MerkleNFTDistributor_set_minted_) +- [Function `verify_proof`](#0x1_MerkleNFTDistributor_verify_proof) +- [Function `is_minted`](#0x1_MerkleNFTDistributor_is_minted) +- [Function `is_minted_`](#0x1_MerkleNFTDistributor_is_minted_) + + +
use 0x1::BCS;
+use 0x1::Errors;
+use 0x1::Hash;
+use 0x1::MerkleProof;
+use 0x1::NFT;
+use 0x1::Signer;
+use 0x1::Vector;
+
+ + + + + +## Resource `MerkleNFTDistribution` + + + +
struct MerkleNFTDistribution<NFTMeta: copy, drop, store> has key
+
+ + + +
+Fields + + +
+
+merkle_root: vector<u8> +
+
+ +
+
+claimed_bitmap: vector<u128> +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_NO_MINT_CAPABILITY: u64 = 1002;
+
+ + + + + + + +
const ALREADY_MINTED: u64 = 1000;
+
+ + + + + + + +
const INVALID_PROOF: u64 = 1001;
+
+ + + + + +## Function `register` + + + +
public fun register<NFTMeta: copy, drop, store, Info: copy, drop, store>(signer: &signer, merkle_root: vector<u8>, leafs: u64, info: Info, meta: NFT::Metadata): NFT::MintCapability<NFTMeta>
+
+ + + +
+Implementation + + +
public fun register<NFTMeta: copy + store + drop, Info: copy + store + drop>(signer: &signer, merkle_root: vector<u8>, leafs: u64, info: Info, meta: Metadata): MintCapability<NFTMeta> {
+    let bitmap_count = leafs / 128;
+    if (bitmap_count * 128 < leafs) {
+        bitmap_count = bitmap_count + 1;
+    };
+    let claimed_bitmap = Vector::empty();
+    let j = 0;
+    while (j < bitmap_count) {
+        Vector::push_back( &mut claimed_bitmap, 0u128);
+        j = j + 1;
+    };
+    let distribution = MerkleNFTDistribution<NFTMeta>{
+        merkle_root,
+        claimed_bitmap
+    };
+    NFT::register<NFTMeta, Info>(signer, info, meta);
+    move_to(signer, distribution);
+    NFT::remove_mint_capability<NFTMeta>(signer)
+}
+
+ + + +
+ + + +## Function `mint_with_cap` + + + +
public fun mint_with_cap<NFTMeta: copy, drop, store, NFTBody: store, Info: copy, drop, store>(sender: &signer, cap: &mut NFT::MintCapability<NFTMeta>, creator: address, index: u64, base_meta: NFT::Metadata, type_meta: NFTMeta, body: NFTBody, merkle_proof: vector<vector<u8>>): NFT::NFT<NFTMeta, NFTBody>
+
+ + + +
+Implementation + + +
public fun mint_with_cap<NFTMeta: copy + store + drop, NFTBody: store, Info: copy + store + drop>(sender: &signer, cap:&mut MintCapability<NFTMeta>, creator: address, index: u64, base_meta: Metadata, type_meta: NFTMeta, body: NFTBody, merkle_proof:vector<vector<u8>>): NFT<NFTMeta, NFTBody>
+    acquires MerkleNFTDistribution {
+        let addr = Signer::address_of(sender);
+        let distribution = borrow_global_mut<MerkleNFTDistribution<NFTMeta>>(creator);
+        let minted = is_minted_<NFTMeta>(distribution, index);
+        assert!(!minted, Errors::custom(ALREADY_MINTED));
+        let leaf_data = encode_leaf(&index, &addr);
+        let verified = MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data));
+        assert!(verified, Errors::custom(INVALID_PROOF));
+        set_minted_(distribution, index);
+        let nft = NFT::mint_with_cap<NFTMeta, NFTBody, Info>(creator, cap, base_meta, type_meta, body);
+        return nft
+    }
+
+ + + +
+ + + +## Function `encode_leaf` + + + +
fun encode_leaf(index: &u64, account: &address): vector<u8>
+
+ + + +
+Implementation + + +
fun encode_leaf(index: &u64, account: &address): vector<u8> {
+    let leaf = Vector::empty();
+    Vector::append(&mut leaf, BCS::to_bytes(index));
+    Vector::append(&mut leaf, BCS::to_bytes(account));
+    leaf
+}
+
+ + + +
+ + + +## Function `set_minted_` + + + +
fun set_minted_<NFTMeta: copy, drop, store>(distribution: &mut MerkleNFTDistributor::MerkleNFTDistribution<NFTMeta>, index: u64)
+
+ + + +
+Implementation + + +
fun set_minted_<NFTMeta: copy + store + drop>(distribution: &mut MerkleNFTDistribution<NFTMeta>, index: u64) {
+    let claimed_word_index = index / 128;
+    let claimed_bit_index = ((index % 128) as u8);
+    let word = Vector::borrow_mut(&mut distribution.claimed_bitmap, claimed_word_index);
+    // word | (1 << bit_index)
+    let mask = 1u128 << claimed_bit_index;
+    *word = (*word | mask);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma opaque;
+
+ + + +
+ + + +## Function `verify_proof` + + + +
public fun verify_proof<NFTMeta: copy, drop, store>(account: address, creator: address, index: u64, merkle_proof: vector<vector<u8>>): bool
+
+ + + +
+Implementation + + +
public fun verify_proof<NFTMeta: copy + store + drop>(account: address, creator: address, index: u64, merkle_proof:vector<vector<u8>>): bool
+    acquires MerkleNFTDistribution {
+        let distribution = borrow_global_mut<MerkleNFTDistribution<NFTMeta>>(creator);
+        let leaf_data = encode_leaf(&index, &account);
+        MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data))
+    }
+
+ + + +
+ + + +## Function `is_minted` + + + +
public fun is_minted<NFTMeta: copy, drop, store>(creator: address, index: u64): bool
+
+ + + +
+Implementation + + +
public fun is_minted<NFTMeta: copy + store + drop>(creator: address, index: u64): bool
+    acquires MerkleNFTDistribution {
+        let distribution = borrow_global_mut<MerkleNFTDistribution<NFTMeta>>(creator);
+        is_minted_<NFTMeta>(distribution, index)
+    }
+
+ + + +
+ + + +## Function `is_minted_` + + + +
fun is_minted_<NFTMeta: copy, drop, store>(distribution: &MerkleNFTDistributor::MerkleNFTDistribution<NFTMeta>, index: u64): bool
+
+ + + +
+Implementation + + +
fun is_minted_<NFTMeta: copy + store + drop>(distribution: &MerkleNFTDistribution<NFTMeta>, index: u64): bool {
+    let claimed_word_index = index / 128;
+    let claimed_bit_index = ((index % 128) as u8);
+    let word = Vector::borrow( &distribution.claimed_bitmap, claimed_word_index);
+    let mask = 1u128 << claimed_bit_index;
+    (*word & mask) == mask
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma opaque;
+
+ + + +
diff --git a/release/v13/docs/MintDaoProposal.md b/release/v13/docs/MintDaoProposal.md new file mode 100644 index 00000000..7bf5a984 --- /dev/null +++ b/release/v13/docs/MintDaoProposal.md @@ -0,0 +1,258 @@ + + + +# Module `0x1::MintDaoProposal` + +MintDaoProposal is a dao proposal for mint extra tokens. + + +- [Resource `WrappedMintCapability`](#0x1_MintDaoProposal_WrappedMintCapability) +- [Struct `MintToken`](#0x1_MintDaoProposal_MintToken) +- [Constants](#@Constants_0) +- [Function `plugin`](#0x1_MintDaoProposal_plugin) +- [Function `propose_mint_to`](#0x1_MintDaoProposal_propose_mint_to) +- [Function `execute_mint_proposal`](#0x1_MintDaoProposal_execute_mint_proposal) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Account;
+use 0x1::Dao;
+use 0x1::Errors;
+use 0x1::Signer;
+use 0x1::Token;
+
+ + + + + +## Resource `WrappedMintCapability` + +A wrapper of Token MintCapability. + + +
struct WrappedMintCapability<TokenType> has key
+
+ + + +
+Fields + + +
+
+cap: Token::MintCapability<TokenType> +
+
+ +
+
+ + +
+ + + +## Struct `MintToken` + +MintToken request. + + +
struct MintToken has copy, drop, store
+
+ + + +
+Fields + + +
+
+receiver: address +
+
+ the receiver of minted tokens. +
+
+amount: u128 +
+
+ how many tokens to mint. +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_NOT_AUTHORIZED: u64 = 401;
+
+ + + + + +## Function `plugin` + +Plugin method of the module. +Should be called by token issuer. + + +
public fun plugin<TokenT: store>(signer: &signer)
+
+ + + +
+Implementation + + +
public fun plugin<TokenT: store>(signer: &signer) {
+    let token_issuer = Token::token_address<TokenT>();
+    assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    let mint_cap = Token::remove_mint_capability<TokenT>(signer);
+    move_to(signer, WrappedMintCapability { cap: mint_cap });
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+let sender = Signer::address_of(signer);
+aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS();
+aborts_if !exists<Token::MintCapability<TokenT>>(sender);
+aborts_if exists<WrappedMintCapability<TokenT>>(sender);
+ensures !exists<Token::MintCapability<TokenT>>(sender);
+ensures exists<WrappedMintCapability<TokenT>>(sender);
+
+ + + +
+ + + +## Function `propose_mint_to` + +Entrypoint for the proposal. + + +
public fun propose_mint_to<TokenT: copy, drop, store>(signer: &signer, receiver: address, amount: u128, exec_delay: u64)
+
+ + + +
+Implementation + + +
public fun propose_mint_to<TokenT: copy + drop + store>(signer: &signer, receiver: address, amount: u128, exec_delay: u64) {
+    Dao::propose<TokenT, MintToken>(
+        signer,
+        MintToken { receiver, amount },
+        exec_delay,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+include Dao::AbortIfDaoConfigNotExist<TokenT>;
+include Dao::AbortIfDaoInfoNotExist<TokenT>;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config<TokenT>().min_action_delay;
+include Dao::CheckQuorumVotes<TokenT>;
+let sender = Signer::address_of(signer);
+aborts_if exists<Dao::Proposal<TokenT, MintToken>>(sender);
+
+ + + +
+ + + +## Function `execute_mint_proposal` + +Once the proposal is agreed, anyone can call the method to make the proposal happen. + + +
public fun execute_mint_proposal<TokenT: copy, drop, store>(proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public fun execute_mint_proposal<TokenT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+) acquires WrappedMintCapability {
+    let MintToken { receiver, amount } = Dao::extract_proposal_action<TokenT, MintToken>(
+        proposer_address,
+        proposal_id,
+    );
+    let cap = borrow_global<WrappedMintCapability<TokenT>>(Token::token_address<TokenT>());
+    let tokens = Token::mint_with_capability<TokenT>(&cap.cap, amount);
+    Account::deposit(receiver, tokens);
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = true;
+let expected_states = vec<u8>(6);
+include Dao::CheckProposalStates<TokenT, MintToken>{expected_states};
+let proposal = global<Dao::Proposal<TokenT, MintToken>>(proposer_address);
+aborts_if Option::is_none(proposal.action);
+aborts_if !exists<WrappedMintCapability<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+pragma aborts_if_is_partial;
+
diff --git a/release/v13/docs/MintScripts.md b/release/v13/docs/MintScripts.md new file mode 100644 index 00000000..dbbdd845 --- /dev/null +++ b/release/v13/docs/MintScripts.md @@ -0,0 +1,10 @@ + + + +# Module `0x1::MintScripts` + + + + + +
diff --git a/release/v13/docs/ModifyDaoConfigProposal.md b/release/v13/docs/ModifyDaoConfigProposal.md new file mode 100644 index 00000000..1afc8c43 --- /dev/null +++ b/release/v13/docs/ModifyDaoConfigProposal.md @@ -0,0 +1,300 @@ + + + +# Module `0x1::ModifyDaoConfigProposal` + +A proposal module which is used to modify Token's DAO configuration. + + +- [Resource `DaoConfigModifyCapability`](#0x1_ModifyDaoConfigProposal_DaoConfigModifyCapability) +- [Struct `DaoConfigUpdate`](#0x1_ModifyDaoConfigProposal_DaoConfigUpdate) +- [Constants](#@Constants_0) +- [Function `plugin`](#0x1_ModifyDaoConfigProposal_plugin) +- [Function `propose`](#0x1_ModifyDaoConfigProposal_propose) +- [Function `execute`](#0x1_ModifyDaoConfigProposal_execute) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::Dao;
+use 0x1::Errors;
+use 0x1::Signer;
+use 0x1::Token;
+
+ + + + + +## Resource `DaoConfigModifyCapability` + +A wrapper of Config::ModifyConfigCapability<Dao::DaoConfig<TokenT>>. + + +
struct DaoConfigModifyCapability<TokenT: copy, drop, store> has key
+
+ + + +
+Fields + + +
+
+cap: Config::ModifyConfigCapability<Dao::DaoConfig<TokenT>> +
+
+ +
+
+ + +
+ + + +## Struct `DaoConfigUpdate` + +a proposal action to update dao config. +if any field is 0, that means the proposal want to update. + + +
struct DaoConfigUpdate has copy, drop, store
+
+ + + +
+Fields + + +
+
+voting_delay: u64 +
+
+ new voting delay setting. +
+
+voting_period: u64 +
+
+ new voting period setting. +
+
+voting_quorum_rate: u8 +
+
+ new voting quorum rate setting. +
+
+min_action_delay: u64 +
+
+ new min action delay setting. +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_NOT_AUTHORIZED: u64 = 401;
+
+ + + + + + + +
const ERR_QUORUM_RATE_INVALID: u64 = 402;
+
+ + + + + +## Function `plugin` + +Plugin method of the module. +Should be called by token issuer. + + +
public fun plugin<TokenT: copy, drop, store>(signer: &signer)
+
+ + + +
+Implementation + + +
public fun plugin<TokenT: copy + drop + store>(signer: &signer) {
+    let token_issuer = Token::token_address<TokenT>();
+    assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    let dao_config_modify_cap = Config::extract_modify_config_capability<
+        Dao::DaoConfig<TokenT>,
+    >(signer);
+    assert!(Config::account_address(&dao_config_modify_cap) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    let cap = DaoConfigModifyCapability { cap: dao_config_modify_cap };
+    move_to(signer, cap);
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+let sender = Signer::address_of(signer);
+aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS();
+include Config::AbortsIfCapNotExist<Dao::DaoConfig<TokenT>>{address: sender};
+let config_cap = Config::spec_cap<Dao::DaoConfig<TokenT>>(sender);
+aborts_if Option::is_none(config_cap);
+aborts_if Option::borrow(config_cap).account_address != sender;
+aborts_if exists<DaoConfigModifyCapability<TokenT>>(sender);
+ensures exists<DaoConfigModifyCapability<TokenT>>(sender);
+
+ + + +
+ + + +## Function `propose` + +Entrypoint for the proposal. + + +
public entry fun propose<TokenT: copy, drop, store>(signer: signer, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose<TokenT: copy + drop + store>(
+    signer: signer,
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+    exec_delay: u64,
+) {
+    assert!(voting_quorum_rate <= 100, Errors::invalid_argument(ERR_QUORUM_RATE_INVALID));
+    let action = DaoConfigUpdate {
+        voting_delay,
+        voting_period,
+        voting_quorum_rate,
+        min_action_delay,
+    };
+    Dao::propose<TokenT, DaoConfigUpdate>(&signer, action, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+aborts_if voting_quorum_rate > 100;
+include Dao::AbortIfDaoConfigNotExist<TokenT>;
+include Dao::AbortIfDaoInfoNotExist<TokenT>;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config<TokenT>().min_action_delay;
+include Dao::CheckQuorumVotes<TokenT>;
+let sender = Signer::address_of(signer);
+aborts_if exists<Dao::Proposal<TokenT, DaoConfigUpdate>>(sender);
+
+ + + +
+ + + +## Function `execute` + +Once the proposal is agreed, anyone can call the method to make the proposal happen. + + +
public entry fun execute<TokenT: copy, drop, store>(proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun execute<TokenT: copy + drop + store>(proposer_address: address, proposal_id: u64)
+acquires DaoConfigModifyCapability {
+    let DaoConfigUpdate {
+        voting_delay,
+        voting_period,
+        voting_quorum_rate,
+        min_action_delay,
+    } = Dao::extract_proposal_action<TokenT, DaoConfigUpdate>(proposer_address, proposal_id);
+    let cap = borrow_global_mut<DaoConfigModifyCapability<TokenT>>(
+        Token::token_address<TokenT>(),
+    );
+    Dao::modify_dao_config(
+        &mut cap.cap,
+        voting_delay,
+        voting_period,
+        voting_quorum_rate,
+        min_action_delay,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = true;
+aborts_if !exists<DaoConfigModifyCapability<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+pragma aborts_if_is_partial;
+
diff --git a/release/v13/docs/ModuleUpgradeScripts.md b/release/v13/docs/ModuleUpgradeScripts.md new file mode 100644 index 00000000..4ac2a7f2 --- /dev/null +++ b/release/v13/docs/ModuleUpgradeScripts.md @@ -0,0 +1,314 @@ + + + +# Module `0x1::ModuleUpgradeScripts` + + + +- [Constants](#@Constants_0) +- [Function `propose_module_upgrade_v2`](#0x1_ModuleUpgradeScripts_propose_module_upgrade_v2) +- [Function `update_module_upgrade_strategy`](#0x1_ModuleUpgradeScripts_update_module_upgrade_strategy) +- [Function `update_module_upgrade_strategy_with_min_time`](#0x1_ModuleUpgradeScripts_update_module_upgrade_strategy_with_min_time) +- [Function `submit_module_upgrade_plan`](#0x1_ModuleUpgradeScripts_submit_module_upgrade_plan) +- [Function `execute_module_upgrade_plan_propose`](#0x1_ModuleUpgradeScripts_execute_module_upgrade_plan_propose) +- [Function `submit_upgrade_plan`](#0x1_ModuleUpgradeScripts_submit_upgrade_plan) +- [Function `cancel_upgrade_plan`](#0x1_ModuleUpgradeScripts_cancel_upgrade_plan) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::Errors;
+use 0x1::Option;
+use 0x1::PackageTxnManager;
+use 0x1::Signer;
+use 0x1::UpgradeModuleDaoProposal;
+use 0x1::Version;
+
+ + + + + +## Constants + + + + + + +
const ERR_WRONG_UPGRADE_STRATEGY: u64 = 100;
+
+ + + + + +## Function `propose_module_upgrade_v2` + + + +
public entry fun propose_module_upgrade_v2<Token: copy, drop, store>(signer: signer, module_address: address, package_hash: vector<u8>, version: u64, exec_delay: u64, enforced: bool)
+
+ + + +
+Implementation + + +
public entry fun propose_module_upgrade_v2<Token: copy + drop + store>(
+    signer: signer,
+    module_address: address,
+    package_hash: vector<u8>,
+    version: u64,
+    exec_delay: u64,
+    enforced: bool,
+) {
+    UpgradeModuleDaoProposal::propose_module_upgrade_v2<Token>(
+        &signer,
+        module_address,
+        package_hash,
+        version,
+        exec_delay,
+        enforced
+    );
+}
+
+ + + +
+ + + +## Function `update_module_upgrade_strategy` + +Update sender's module upgrade strategy to strategy + + +
public entry fun update_module_upgrade_strategy(sender: signer, strategy: u8)
+
+ + + +
+Implementation + + +
public entry fun update_module_upgrade_strategy(
+    sender: signer,
+    strategy: u8,
+) {
+    // 1. check version
+    if (strategy == PackageTxnManager::get_strategy_two_phase()) {
+        if (!Config::config_exist_by_address<Version::Version>(Signer::address_of(&sender))) {
+            Config::publish_new_config<Version::Version>(&sender, Version::new_version(1));
+        }
+    };
+
+    // 2. update strategy
+    PackageTxnManager::update_module_upgrade_strategy(
+        &sender,
+        strategy,
+        Option::none<u64>(),
+    );
+}
+
+ + + +
+ + + +## Function `update_module_upgrade_strategy_with_min_time` + +Update sender's module upgrade strategy to strategy with min_time_limit. +This can only be invoked when strategy is STRATEGY_TWO_PHASE. + + +
public entry fun update_module_upgrade_strategy_with_min_time(sender: signer, strategy: u8, min_time_limit: u64)
+
+ + + +
+Implementation + + +
public entry fun update_module_upgrade_strategy_with_min_time(
+    sender: signer,
+    strategy: u8,
+    min_time_limit: u64,
+){
+    // 1. check version
+    assert!(strategy == PackageTxnManager::get_strategy_two_phase(), Errors::invalid_argument(ERR_WRONG_UPGRADE_STRATEGY));
+    // 2. update strategy
+    PackageTxnManager::update_module_upgrade_strategy(
+        &sender,
+        strategy,
+        Option::some<u64>(min_time_limit),
+    );
+}
+
+ + + +
+ + + +## Function `submit_module_upgrade_plan` + +a alias of execute_module_upgrade_plan_propose, will deprecated in the future. + + +
public entry fun submit_module_upgrade_plan<Token: copy, drop, store>(sender: signer, proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun submit_module_upgrade_plan<Token: copy + drop + store>(
+    sender: signer,
+    proposer_address: address,
+    proposal_id: u64,
+) {
+    Self::execute_module_upgrade_plan_propose<Token>(sender, proposer_address, proposal_id);
+}
+
+ + + +
+ + + +## Function `execute_module_upgrade_plan_propose` + +Execute module upgrade plan propose by submit module upgrade plan, the propose must been agreed, and anyone can execute this function. + + +
public entry fun execute_module_upgrade_plan_propose<Token: copy, drop, store>(_sender: signer, proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun execute_module_upgrade_plan_propose<Token: copy + drop + store>(
+    _sender: signer,
+    proposer_address: address,
+    proposal_id: u64,
+) {
+    UpgradeModuleDaoProposal::submit_module_upgrade_plan<Token>(proposer_address, proposal_id);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `submit_upgrade_plan` + +Directly submit a upgrade plan, the sender's module upgrade plan must been PackageTxnManager::STRATEGY_TWO_PHASE and have UpgradePlanCapability + + +
public entry fun submit_upgrade_plan(sender: signer, package_hash: vector<u8>, version: u64, enforced: bool)
+
+ + + +
+Implementation + + +
public entry fun submit_upgrade_plan(sender: signer, package_hash: vector<u8>, version:u64, enforced: bool) {
+    PackageTxnManager::submit_upgrade_plan_v2(&sender, package_hash, version, enforced);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `cancel_upgrade_plan` + +Cancel current upgrade plan, the sender must have UpgradePlanCapability. + + +
public entry fun cancel_upgrade_plan(signer: signer)
+
+ + + +
+Implementation + + +
public entry fun cancel_upgrade_plan(
+    signer: signer,
+) {
+    PackageTxnManager::cancel_upgrade_plan(&signer);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_partial = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/NFT.md b/release/v13/docs/NFT.md new file mode 100644 index 00000000..b257544e --- /dev/null +++ b/release/v13/docs/NFT.md @@ -0,0 +1,104 @@ + + + +# Module `0x1::NFTGalleryScripts` + + + +- [Function `accept`](#0x1_NFTGalleryScripts_accept) +- [Function `transfer`](#0x1_NFTGalleryScripts_transfer) +- [Function `remove_empty_gallery`](#0x1_NFTGalleryScripts_remove_empty_gallery) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::NFTGallery;
+
+ + + + + +## Function `accept` + +Init a NFTGallery for accept NFT + + +
public entry fun accept<NFTMeta: copy, drop, store, NFTBody: store>(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun accept<NFTMeta: copy + store + drop, NFTBody: store>(sender: signer) {
+    NFTGallery::accept_entry<NFTMeta, NFTBody>(sender);
+}
+
+ + + +
+ + + +## Function `transfer` + +Transfer NFT with id from sender to receiver + + +
public entry fun transfer<NFTMeta: copy, drop, store, NFTBody: store>(sender: signer, id: u64, receiver: address)
+
+ + + +
+Implementation + + +
public entry fun transfer<NFTMeta: copy + store + drop, NFTBody: store>(
+    sender: signer,
+    id: u64, receiver: address
+) {
+    NFTGallery::transfer_entry<NFTMeta, NFTBody>(sender, id, receiver);
+}
+
+ + + +
+ + + +## Function `remove_empty_gallery` + +Remove empty NFTGallery. + + +
public entry fun remove_empty_gallery<NFTMeta: copy, drop, store, NFTBody: store>(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun remove_empty_gallery<NFTMeta: copy + store + drop, NFTBody: store>(sender: signer) {
+    NFTGallery::remove_empty_gallery_entry<NFTMeta, NFTBody>(sender);
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+
diff --git a/release/v13/docs/Offer.md b/release/v13/docs/Offer.md new file mode 100644 index 00000000..59f75057 --- /dev/null +++ b/release/v13/docs/Offer.md @@ -0,0 +1,305 @@ + + + +# Module `0x1::Offer` + + + +- [Resource `Offer`](#0x1_Offer_Offer) +- [Constants](#@Constants_0) +- [Function `create`](#0x1_Offer_create) +- [Function `redeem`](#0x1_Offer_redeem) +- [Function `exists_at`](#0x1_Offer_exists_at) +- [Function `address_of`](#0x1_Offer_address_of) +- [Function `take_offer`](#0x1_Offer_take_offer) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Collection2;
+use 0x1::Errors;
+use 0x1::Signer;
+use 0x1::Timestamp;
+
+ + + + + +## Resource `Offer` + +A wrapper around value offered that can be claimed by the address stored in for when after lock time. + + +
struct Offer<Offered> has key
+
+ + + +
+Fields + + +
+
+offered: Offered +
+
+ +
+
+for: address +
+
+ +
+
+time_lock: u64 +
+
+ +
+
+ + +
+ + + +## Constants + + + + +An offer of the specified type for the account does not match + + +
const EOFFER_DNE_FOR_ACCOUNT: u64 = 101;
+
+ + + + + +Offer is not unlocked yet. + + +
const EOFFER_NOT_UNLOCKED: u64 = 102;
+
+ + + + + +## Function `create` + +Publish a value of type Offered under the sender's account. The value can be claimed by +either the for address or the transaction sender. + + +
public fun create<Offered: store>(account: &signer, offered: Offered, for: address, lock_period: u64)
+
+ + + +
+Implementation + + +
public fun create<Offered: store>(account: &signer, offered: Offered, for: address, lock_period: u64) {
+    let time_lock = Timestamp::now_seconds() + lock_period;
+    //TODO should support multi Offer?
+    move_to(account, Offer<Offered> { offered, for, time_lock });
+}
+
+ + + +
+ +
+Specification + + + +
include Timestamp::AbortsIfTimestampNotExists;
+aborts_if Timestamp::now_seconds() + lock_period > max_u64();
+aborts_if exists<Offer<Offered>>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `redeem` + +Claim the value of type Offered published at offer_address. +Only succeeds if the sender is the intended recipient stored in for or the original +publisher offer_address, and now >= time_lock +Also fails if no such value exists. + + +
public fun redeem<Offered: store>(account: &signer, offer_address: address): Offered
+
+ + + +
+Implementation + + +
public fun redeem<Offered: store>(account: &signer, offer_address: address): Offered acquires Offer {
+    let Offer<Offered> { offered, for, time_lock } = move_from<Offer<Offered>>(offer_address);
+    let sender = Signer::address_of(account);
+    let now = Timestamp::now_seconds();
+    assert!(sender == for || sender == offer_address, Errors::invalid_argument(EOFFER_DNE_FOR_ACCOUNT));
+    assert!(now >= time_lock, Errors::not_published(EOFFER_NOT_UNLOCKED));
+    offered
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Offer<Offered>>(offer_address);
+aborts_if Signer::address_of(account) != global<Offer<Offered>>(offer_address).for && Signer::address_of(account) != offer_address;
+aborts_if Timestamp::now_seconds() < global<Offer<Offered>>(offer_address).time_lock;
+include Timestamp::AbortsIfTimestampNotExists;
+
+ + + +
+ + + +## Function `exists_at` + +Returns true if an offer of type Offered exists at offer_address. + + +
public fun exists_at<Offered: store>(offer_address: address): bool
+
+ + + +
+Implementation + + +
public fun exists_at<Offered: store>(offer_address: address): bool {
+    exists<Offer<Offered>>(offer_address)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `address_of` + +Returns the address of the Offered type stored at offer_address. +Fails if no such Offer exists. + + +
public fun address_of<Offered: store>(offer_address: address): address
+
+ + + +
+Implementation + + +
public fun address_of<Offered: store>(offer_address: address): address acquires Offer {
+    borrow_global<Offer<Offered>>(offer_address).for
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Offer<Offered>>(offer_address);
+
+ + + +
+ + + +## Function `take_offer` + +Take Offer and put to signer's Collection. + + +
public entry fun take_offer<Offered: store>(signer: signer, offer_address: address)
+
+ + + +
+Implementation + + +
public entry fun take_offer<Offered: store>(
+    signer: signer,
+    offer_address: address,
+) acquires Offer {
+    let offered = redeem<Offered>(&signer, offer_address);
+    Collection2::put(&signer, Signer::address_of(&signer), offered);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = true;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/OnChainConfigDao.md b/release/v13/docs/OnChainConfigDao.md new file mode 100644 index 00000000..1fc075ec --- /dev/null +++ b/release/v13/docs/OnChainConfigDao.md @@ -0,0 +1,257 @@ + + + +# Module `0x1::OnChainConfigDao` + +OnChainConfigDao is a DAO proposal for modify onchain configuration. + + +- [Resource `WrappedConfigModifyCapability`](#0x1_OnChainConfigDao_WrappedConfigModifyCapability) +- [Struct `OnChainConfigUpdate`](#0x1_OnChainConfigDao_OnChainConfigUpdate) +- [Constants](#@Constants_0) +- [Function `plugin`](#0x1_OnChainConfigDao_plugin) +- [Function `propose_update`](#0x1_OnChainConfigDao_propose_update) +- [Function `execute`](#0x1_OnChainConfigDao_execute) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::Dao;
+use 0x1::Errors;
+use 0x1::Signer;
+use 0x1::Token;
+
+ + + + + +## Resource `WrappedConfigModifyCapability` + +A wrapper of Config::ModifyConfigCapability<ConfigT>. + + +
struct WrappedConfigModifyCapability<TokenT, ConfigT: copy, drop, store> has key
+
+ + + +
+Fields + + +
+
+cap: Config::ModifyConfigCapability<ConfigT> +
+
+ +
+
+ + +
+ + + +## Struct `OnChainConfigUpdate` + +request of updating configuration. + + +
struct OnChainConfigUpdate<ConfigT: copy, drop, store> has copy, drop, store
+
+ + + +
+Fields + + +
+
+value: ConfigT +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_NOT_AUTHORIZED: u64 = 401;
+
+ + + + + +## Function `plugin` + +Plugin method of the module. +Should be called by token issuer. + + +
public fun plugin<TokenT: copy, drop, store, ConfigT: copy, drop, store>(signer: &signer)
+
+ + + +
+Implementation + + +
public fun plugin<TokenT: copy + drop + store, ConfigT: copy + drop + store>(signer: &signer) {
+    let token_issuer = Token::token_address<TokenT>();
+    assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    let config_modify_cap = Config::extract_modify_config_capability<ConfigT>(signer);
+    let cap = WrappedConfigModifyCapability<TokenT, ConfigT> { cap: config_modify_cap };
+    move_to(signer, cap);
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+let sender = Signer::address_of(signer);
+aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS();
+include Config::AbortsIfCapNotExist<ConfigT>{address: sender};
+aborts_if exists<WrappedConfigModifyCapability<TokenT, ConfigT>>(sender);
+ensures exists<WrappedConfigModifyCapability<TokenT, ConfigT>>(sender);
+
+ + + +
+ + + +## Function `propose_update` + +issue a proposal to update config of ConfigT goved by TokenT + + +
public fun propose_update<TokenT: copy, drop, store, ConfigT: copy, drop, store>(signer: &signer, new_config: ConfigT, exec_delay: u64)
+
+ + + +
+Implementation + + +
public fun propose_update<TokenT: copy + drop + store, ConfigT: copy + drop + store>(
+    signer: &signer,
+    new_config: ConfigT,
+    exec_delay: u64,
+) {
+    Dao::propose<TokenT, OnChainConfigUpdate<ConfigT>>(
+        signer,
+        OnChainConfigUpdate { value: new_config },
+        exec_delay,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+include Dao::AbortIfDaoConfigNotExist<TokenT>;
+include Dao::AbortIfDaoInfoNotExist<TokenT>;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config<TokenT>().min_action_delay;
+include Dao::CheckQuorumVotes<TokenT>;
+let sender = Signer::address_of(signer);
+aborts_if exists<Dao::Proposal<TokenT, OnChainConfigUpdate<ConfigT>>>(sender);
+
+ + + +
+ + + +## Function `execute` + +Once the proposal is agreed, anyone can call the method to make the proposal happen. +Caller need to make sure that the proposal of proposal_id under proposal_address is +the kind of this proposal module. + + +
public fun execute<TokenT: copy, drop, store, ConfigT: copy, drop, store>(proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public fun execute<TokenT: copy + drop + store, ConfigT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+) acquires WrappedConfigModifyCapability {
+    let OnChainConfigUpdate { value } = Dao::extract_proposal_action<
+        TokenT,
+        OnChainConfigUpdate<ConfigT>,
+    >(proposer_address, proposal_id);
+    let cap = borrow_global_mut<WrappedConfigModifyCapability<TokenT, ConfigT>>(
+        Token::token_address<TokenT>(),
+    );
+    Config::set_with_capability(&mut cap.cap, value);
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = true;
+let expected_states = vec<u8>(6);
+include Dao::CheckProposalStates<TokenT, OnChainConfigUpdate<ConfigT>>{expected_states};
+aborts_if !exists<WrappedConfigModifyCapability<TokenT, ConfigT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+pragma aborts_if_is_partial;
+
diff --git a/release/v13/docs/OnChainConfigScripts.md b/release/v13/docs/OnChainConfigScripts.md new file mode 100644 index 00000000..42b2ea89 --- /dev/null +++ b/release/v13/docs/OnChainConfigScripts.md @@ -0,0 +1,417 @@ + + + +# Module `0x1::OnChainConfigScripts` + + + +- [Function `propose_update_consensus_config`](#0x1_OnChainConfigScripts_propose_update_consensus_config) +- [Function `propose_update_reward_config`](#0x1_OnChainConfigScripts_propose_update_reward_config) +- [Function `propose_update_txn_publish_option`](#0x1_OnChainConfigScripts_propose_update_txn_publish_option) +- [Function `propose_update_txn_timeout_config`](#0x1_OnChainConfigScripts_propose_update_txn_timeout_config) +- [Function `propose_update_vm_config`](#0x1_OnChainConfigScripts_propose_update_vm_config) +- [Function `propose_update_move_language_version`](#0x1_OnChainConfigScripts_propose_update_move_language_version) +- [Function `propose_update_flexi_dag_effective_height`](#0x1_OnChainConfigScripts_propose_update_flexi_dag_effective_height) +- [Function `execute_on_chain_config_proposal`](#0x1_OnChainConfigScripts_execute_on_chain_config_proposal) +- [Function `execute_on_chain_config_proposal_v2`](#0x1_OnChainConfigScripts_execute_on_chain_config_proposal_v2) + + +
use 0x1::ConsensusConfig;
+use 0x1::FlexiDagConfig;
+use 0x1::LanguageVersion;
+use 0x1::OnChainConfigDao;
+use 0x1::RewardConfig;
+use 0x1::STC;
+use 0x1::Signer;
+use 0x1::TransactionPublishOption;
+use 0x1::TransactionTimeoutConfig;
+use 0x1::VMConfig;
+
+ + + + + +## Function `propose_update_consensus_config` + + + +
public entry fun propose_update_consensus_config(account: signer, uncle_rate_target: u64, base_block_time_target: u64, base_reward_per_block: u128, base_reward_per_uncle_percent: u64, epoch_block_count: u64, base_block_difficulty_window: u64, min_block_time_target: u64, max_block_time_target: u64, base_max_uncles_per_block: u64, base_block_gas_limit: u64, strategy: u8, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_consensus_config(account: signer,
+                                                      uncle_rate_target: u64,
+                                                      base_block_time_target: u64,
+                                                      base_reward_per_block: u128,
+                                                      base_reward_per_uncle_percent: u64,
+                                                      epoch_block_count: u64,
+                                                      base_block_difficulty_window: u64,
+                                                      min_block_time_target: u64,
+                                                      max_block_time_target: u64,
+                                                      base_max_uncles_per_block: u64,
+                                                      base_block_gas_limit: u64,
+                                                      strategy: u8,
+                                                      exec_delay: u64) {
+    let consensus_config = ConsensusConfig::new_consensus_config(uncle_rate_target,
+        base_block_time_target,
+        base_reward_per_block,
+        base_reward_per_uncle_percent,
+        epoch_block_count,
+        base_block_difficulty_window,
+        min_block_time_target,
+        max_block_time_target,
+        base_max_uncles_per_block,
+        base_block_gas_limit,
+        strategy);
+    OnChainConfigDao::propose_update<STC::STC, ConsensusConfig::ConsensusConfig>(&account, consensus_config, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `propose_update_reward_config` + + + +
public entry fun propose_update_reward_config(account: signer, reward_delay: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_reward_config(account: signer,
+                                                   reward_delay: u64,
+                                                   exec_delay: u64) {
+    let reward_config = RewardConfig::new_reward_config(reward_delay);
+    OnChainConfigDao::propose_update<STC::STC, RewardConfig::RewardConfig>(&account, reward_config, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `propose_update_txn_publish_option` + + + +
public entry fun propose_update_txn_publish_option(account: signer, script_allowed: bool, module_publishing_allowed: bool, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_txn_publish_option(account: signer,
+                                                        script_allowed: bool,
+                                                        module_publishing_allowed: bool,
+                                                        exec_delay: u64) {
+    let txn_publish_option = TransactionPublishOption::new_transaction_publish_option(script_allowed, module_publishing_allowed);
+    OnChainConfigDao::propose_update<STC::STC, TransactionPublishOption::TransactionPublishOption>(&account, txn_publish_option, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `propose_update_txn_timeout_config` + + + +
public entry fun propose_update_txn_timeout_config(account: signer, duration_seconds: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_txn_timeout_config(account: signer,
+                                                        duration_seconds: u64,
+                                                        exec_delay: u64) {
+    let txn_timeout_config = TransactionTimeoutConfig::new_transaction_timeout_config(duration_seconds);
+    OnChainConfigDao::propose_update<STC::STC, TransactionTimeoutConfig::TransactionTimeoutConfig>(&account, txn_timeout_config, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `propose_update_vm_config` + + + +
public entry fun propose_update_vm_config(account: signer, instruction_schedule: vector<u8>, native_schedule: vector<u8>, global_memory_per_byte_cost: u64, global_memory_per_byte_write_cost: u64, min_transaction_gas_units: u64, large_transaction_cutoff: u64, instrinsic_gas_per_byte: u64, maximum_number_of_gas_units: u64, min_price_per_gas_unit: u64, max_price_per_gas_unit: u64, max_transaction_size_in_bytes: u64, gas_unit_scaling_factor: u64, default_account_size: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_vm_config(account: signer,
+                                               instruction_schedule: vector<u8>,
+                                               native_schedule: vector<u8>,
+                                               global_memory_per_byte_cost: u64,
+                                               global_memory_per_byte_write_cost: u64,
+                                               min_transaction_gas_units: u64,
+                                               large_transaction_cutoff: u64,
+                                               instrinsic_gas_per_byte: u64,
+                                               maximum_number_of_gas_units: u64,
+                                               min_price_per_gas_unit: u64,
+                                               max_price_per_gas_unit: u64,
+                                               max_transaction_size_in_bytes: u64,
+                                               gas_unit_scaling_factor: u64,
+                                               default_account_size: u64,
+                                               exec_delay: u64, ) {
+    let vm_config = VMConfig::new_vm_config(instruction_schedule,
+        native_schedule,
+        global_memory_per_byte_cost,
+        global_memory_per_byte_write_cost,
+        min_transaction_gas_units,
+        large_transaction_cutoff,
+        instrinsic_gas_per_byte,
+        maximum_number_of_gas_units,
+        min_price_per_gas_unit,
+        max_price_per_gas_unit,
+        max_transaction_size_in_bytes,
+        gas_unit_scaling_factor,
+        default_account_size);
+    OnChainConfigDao::propose_update<STC::STC, VMConfig::VMConfig>(&account, vm_config, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `propose_update_move_language_version` + + + +
public entry fun propose_update_move_language_version(account: signer, new_version: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_move_language_version(account: signer, new_version: u64, exec_delay: u64) {
+    let lang_version = LanguageVersion::new(new_version);
+    OnChainConfigDao::propose_update<STC::STC, LanguageVersion::LanguageVersion>(&account, lang_version, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `propose_update_flexi_dag_effective_height` + + + +
public entry fun propose_update_flexi_dag_effective_height(account: signer, new_height: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_update_flexi_dag_effective_height(account: signer, new_height: u64, exec_delay: u64) {
+    let config = FlexiDagConfig::new_flexidag_config(new_height);
+    OnChainConfigDao::propose_update<STC::STC, FlexiDagConfig::FlexiDagConfig>(&account, config, exec_delay);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `execute_on_chain_config_proposal` + + + +
public entry fun execute_on_chain_config_proposal<ConfigT: copy, drop, store>(account: signer, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun execute_on_chain_config_proposal<ConfigT: copy + drop + store>(account: signer, proposal_id: u64) {
+    OnChainConfigDao::execute<STC::STC, ConfigT>(Signer::address_of(&account), proposal_id);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `execute_on_chain_config_proposal_v2` + + + +
public entry fun execute_on_chain_config_proposal_v2<TokenType: copy, drop, store, ConfigT: copy, drop, store>(proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun execute_on_chain_config_proposal_v2<TokenType: copy + drop + store, ConfigT: copy + drop + store>(proposer_address: address, proposal_id: u64) {
+    OnChainConfigDao::execute<TokenType, ConfigT>(proposer_address, proposal_id);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
diff --git a/release/v13/docs/Option.md b/release/v13/docs/Option.md new file mode 100644 index 00000000..4f518c53 --- /dev/null +++ b/release/v13/docs/Option.md @@ -0,0 +1,787 @@ + + + +# Module `0x1::Option` + +This module defines the Option type and its methods to represent and handle an optional value. + + +- [Struct `Option`](#0x1_Option_Option) +- [Constants](#@Constants_0) +- [Function `none`](#0x1_Option_none) +- [Function `some`](#0x1_Option_some) +- [Function `is_none`](#0x1_Option_is_none) +- [Function `is_some`](#0x1_Option_is_some) +- [Function `contains`](#0x1_Option_contains) +- [Function `borrow`](#0x1_Option_borrow) +- [Function `borrow_with_default`](#0x1_Option_borrow_with_default) +- [Function `get_with_default`](#0x1_Option_get_with_default) +- [Function `fill`](#0x1_Option_fill) +- [Function `extract`](#0x1_Option_extract) +- [Function `borrow_mut`](#0x1_Option_borrow_mut) +- [Function `swap`](#0x1_Option_swap) +- [Function `destroy_with_default`](#0x1_Option_destroy_with_default) +- [Function `destroy_some`](#0x1_Option_destroy_some) +- [Function `destroy_none`](#0x1_Option_destroy_none) +- [Module Specification](#@Module_Specification_1) + - [Helper Schema](#@Helper_Schema_2) + + +
use 0x1::Errors;
+use 0x1::Vector;
+
+ + + + + +## Struct `Option` + +Abstraction of a value that may or may not be present. Implemented with a vector of size +zero or one because Move bytecode does not have ADTs. + + +
struct Option<Element> has copy, drop, store
+
+ + + +
+Fields + + +
+
+vec: vector<Element> +
+
+ +
+
+ + +
+ +
+Specification + + +The size of vector is always less than equal to 1 +because it's 0 for "none" or 1 for "some". + + +
invariant len(vec) <= 1;
+
+ + + +
+ + + +## Constants + + + + +The Option is in an invalid state for the operation attempted. +The Option is Some while it should be None. + + +
const EOPTION_IS_SET: u64 = 0;
+
+ + + + + +The Option is in an invalid state for the operation attempted. +The Option is None while it should be Some. + + +
const EOPTION_NOT_SET: u64 = 1;
+
+ + + + + +## Function `none` + +Return an empty Option + + +
public fun none<Element>(): Option::Option<Element>
+
+ + + +
+Implementation + + +
public fun none<Element>(): Option<Element> {
+    Option { vec: Vector::empty() }
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == spec_none<Element>();
+
+ + + + + + + +
fun spec_none<Element>(): Option<Element> {
+   Option{ vec: vec() }
+}
+
+ + + +
+ + + +## Function `some` + +Return an Option containing e + + +
public fun some<Element>(e: Element): Option::Option<Element>
+
+ + + +
+Implementation + + +
public fun some<Element>(e: Element): Option<Element> {
+    Option { vec: Vector::singleton(e) }
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == spec_some(e);
+
+ + + + + + + +
fun spec_some<Element>(e: Element): Option<Element> {
+   Option{ vec: vec(e) }
+}
+
+ + + +
+ + + +## Function `is_none` + +Return true if t does not hold a value + + +
public fun is_none<Element>(t: &Option::Option<Element>): bool
+
+ + + +
+Implementation + + +
public fun is_none<Element>(t: &Option<Element>): bool {
+    Vector::is_empty(&t.vec)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == is_none(t);
+
+ + + +
+ + + +## Function `is_some` + +Return true if t holds a value + + +
public fun is_some<Element>(t: &Option::Option<Element>): bool
+
+ + + +
+Implementation + + +
public fun is_some<Element>(t: &Option<Element>): bool {
+    !Vector::is_empty(&t.vec)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == is_some(t);
+
+ + + +
+ + + +## Function `contains` + +Return true if the value in t is equal to e_ref +Always returns false if t does not hold a value + + +
public fun contains<Element>(t: &Option::Option<Element>, e_ref: &Element): bool
+
+ + + +
+Implementation + + +
public fun contains<Element>(t: &Option<Element>, e_ref: &Element): bool {
+    Vector::contains(&t.vec, e_ref)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == spec_contains(t, e_ref);
+
+ + + + + + + +
fun spec_contains<Element>(t: Option<Element>, e: Element): bool {
+   is_some(t) && borrow(t) == e
+}
+
+ + + +
+ + + +## Function `borrow` + +Return an immutable reference to the value inside t +Aborts if t does not hold a value + + +
public fun borrow<Element>(t: &Option::Option<Element>): &Element
+
+ + + +
+Implementation + + +
public fun borrow<Element>(t: &Option<Element>): &Element {
+    assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET));
+    Vector::borrow(&t.vec, 0)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+include AbortsIfNone<Element>;
+ensures result == borrow(t);
+
+ + + +
+ + + +## Function `borrow_with_default` + +Return a reference to the value inside t if it holds one +Return default_ref if t does not hold a value + + +
public fun borrow_with_default<Element>(t: &Option::Option<Element>, default_ref: &Element): &Element
+
+ + + +
+Implementation + + +
public fun borrow_with_default<Element>(t: &Option<Element>, default_ref: &Element): &Element {
+    let vec_ref = &t.vec;
+    if (Vector::is_empty(vec_ref)) default_ref
+    else Vector::borrow(vec_ref, 0)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == (if (is_some(t)) borrow(t) else default_ref);
+
+ + + +
+ + + +## Function `get_with_default` + +Return the value inside t if it holds one +Return default if t does not hold a value + + +
public fun get_with_default<Element: copy, drop>(t: &Option::Option<Element>, default: Element): Element
+
+ + + +
+Implementation + + +
public fun get_with_default<Element: copy + drop>(
+    t: &Option<Element>,
+    default: Element,
+): Element {
+    let vec_ref = &t.vec;
+    if (Vector::is_empty(vec_ref)) default
+    else *Vector::borrow(vec_ref, 0)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == (if (is_some(t)) borrow(t) else default);
+
+ + + +
+ + + +## Function `fill` + +Convert the none option t to a some option by adding e. +Aborts if t already holds a value + + +
public fun fill<Element>(t: &mut Option::Option<Element>, e: Element)
+
+ + + +
+Implementation + + +
public fun fill<Element>(t: &mut Option<Element>, e: Element) {
+    let vec_ref = &mut t.vec;
+    if (Vector::is_empty(vec_ref)) Vector::push_back(vec_ref, e)
+    else abort Errors::invalid_argument(EOPTION_IS_SET)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if is_some(t) with Errors::INVALID_ARGUMENT;
+ensures is_some(t);
+ensures borrow(t) == e;
+
+ + + +
+ + + +## Function `extract` + +Convert a some option to a none by removing and returning the value stored inside t +Aborts if t does not hold a value + + +
public fun extract<Element>(t: &mut Option::Option<Element>): Element
+
+ + + +
+Implementation + + +
public fun extract<Element>(t: &mut Option<Element>): Element {
+    assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET));
+    Vector::pop_back(&mut t.vec)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+include AbortsIfNone<Element>;
+ensures result == borrow(old(t));
+ensures is_none(t);
+
+ + + +
+ + + +## Function `borrow_mut` + +Return a mutable reference to the value inside t +Aborts if t does not hold a value + + +
public fun borrow_mut<Element>(t: &mut Option::Option<Element>): &mut Element
+
+ + + +
+Implementation + + +
public fun borrow_mut<Element>(t: &mut Option<Element>): &mut Element {
+    assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET));
+    Vector::borrow_mut(&mut t.vec, 0)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+include AbortsIfNone<Element>;
+ensures result == borrow(t);
+
+ + + +
+ + + +## Function `swap` + +Swap the old value inside t with e and return the old value +Aborts if t does not hold a value + + +
public fun swap<Element>(t: &mut Option::Option<Element>, e: Element): Element
+
+ + + +
+Implementation + + +
public fun swap<Element>(t: &mut Option<Element>, e: Element): Element {
+    assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET));
+    let vec_ref = &mut t.vec;
+    let old_value = Vector::pop_back(vec_ref);
+    Vector::push_back(vec_ref, e);
+    old_value
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+include AbortsIfNone<Element>;
+ensures result == borrow(old(t));
+ensures is_some(t);
+ensures borrow(t) == e;
+
+ + + +
+ + + +## Function `destroy_with_default` + +Destroys t. If t holds a value, return it. Returns default otherwise + + +
public fun destroy_with_default<Element: drop>(t: Option::Option<Element>, default: Element): Element
+
+ + + +
+Implementation + + +
public fun destroy_with_default<Element: drop>(t: Option<Element>, default: Element): Element {
+    let Option { vec } = t;
+    if (Vector::is_empty(&mut vec)) default
+    else Vector::pop_back(&mut vec)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if false;
+ensures result == (if (is_some(t)) borrow(t) else default);
+
+ + + +
+ + + +## Function `destroy_some` + +Unpack t and return its contents +Aborts if t does not hold a value + + +
public fun destroy_some<Element>(t: Option::Option<Element>): Element
+
+ + + +
+Implementation + + +
public fun destroy_some<Element>(t: Option<Element>): Element {
+    assert!(is_some(&t), Errors::invalid_argument(EOPTION_NOT_SET));
+    let Option { vec } = t;
+    let elem = Vector::pop_back(&mut vec);
+    Vector::destroy_empty(vec);
+    elem
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+include AbortsIfNone<Element>;
+ensures result == borrow(t);
+
+ + + +
+ + + +## Function `destroy_none` + +Unpack t +Aborts if t holds a value + + +
public fun destroy_none<Element>(t: Option::Option<Element>)
+
+ + + +
+Implementation + + +
public fun destroy_none<Element>(t: Option<Element>) {
+    assert!(is_none(&t), Errors::invalid_argument(EOPTION_IS_SET));
+    let Option { vec } = t;
+    Vector::destroy_empty(vec)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if is_some(t) with Errors::INVALID_ARGUMENT;
+
+ + + +
+ + + +## Module Specification + + + + +
pragma aborts_if_is_strict;
+
+ + + + + +### Helper Schema + + + + + + +
schema AbortsIfNone<Element> {
+    t: Option<Element>;
+    aborts_if is_none(t) with Errors::INVALID_ARGUMENT;
+}
+
diff --git a/release/v13/docs/Oracle.md b/release/v13/docs/Oracle.md new file mode 100644 index 00000000..b3dcdb3b --- /dev/null +++ b/release/v13/docs/Oracle.md @@ -0,0 +1,88 @@ + + + +# Module `0x1::PriceOracleScripts` + + + +- [Function `register_oracle`](#0x1_PriceOracleScripts_register_oracle) +- [Function `init_data_source`](#0x1_PriceOracleScripts_init_data_source) +- [Function `update`](#0x1_PriceOracleScripts_update) + + +
use 0x1::PriceOracle;
+
+ + + + + +## Function `register_oracle` + + + +
public entry fun register_oracle<OracleT: copy, drop, store>(sender: signer, precision: u8)
+
+ + + +
+Implementation + + +
public entry fun register_oracle<OracleT: copy+store+drop>(sender: signer, precision: u8){
+    PriceOracle::register_oracle_entry<OracleT>(sender, precision);
+}
+
+ + + +
+ + + +## Function `init_data_source` + + + +
public entry fun init_data_source<OracleT: copy, drop, store>(sender: signer, init_value: u128)
+
+ + + +
+Implementation + + +
public entry fun init_data_source<OracleT: copy+store+drop>(sender: signer, init_value: u128){
+    PriceOracle::init_data_source_entry<OracleT>(sender, init_value);
+}
+
+ + + +
+ + + +## Function `update` + + + +
public entry fun update<OracleT: copy, drop, store>(sender: signer, value: u128)
+
+ + + +
+Implementation + + +
public entry fun update<OracleT: copy+store+drop>(sender: signer, value: u128){
+    PriceOracle::update_entry<OracleT>(sender, value);
+}
+
+ + + +
diff --git a/release/v13/docs/PackageTxnManager.md b/release/v13/docs/PackageTxnManager.md new file mode 100644 index 00000000..328505ed --- /dev/null +++ b/release/v13/docs/PackageTxnManager.md @@ -0,0 +1,1454 @@ + + + +# Module `0x1::PackageTxnManager` + +The module provides strategies for module upgrading. + + +- [Struct `UpgradePlan`](#0x1_PackageTxnManager_UpgradePlan) +- [Resource `UpgradePlanCapability`](#0x1_PackageTxnManager_UpgradePlanCapability) +- [Struct `UpgradePlanV2`](#0x1_PackageTxnManager_UpgradePlanV2) +- [Resource `ModuleUpgradeStrategy`](#0x1_PackageTxnManager_ModuleUpgradeStrategy) +- [Resource `TwoPhaseUpgrade`](#0x1_PackageTxnManager_TwoPhaseUpgrade) +- [Struct `TwoPhaseUpgradeConfig`](#0x1_PackageTxnManager_TwoPhaseUpgradeConfig) +- [Resource `TwoPhaseUpgradeV2`](#0x1_PackageTxnManager_TwoPhaseUpgradeV2) +- [Struct `UpgradeEvent`](#0x1_PackageTxnManager_UpgradeEvent) +- [Constants](#@Constants_0) +- [Function `get_strategy_arbitrary`](#0x1_PackageTxnManager_get_strategy_arbitrary) +- [Function `get_strategy_two_phase`](#0x1_PackageTxnManager_get_strategy_two_phase) +- [Function `get_strategy_new_module`](#0x1_PackageTxnManager_get_strategy_new_module) +- [Function `get_strategy_freeze`](#0x1_PackageTxnManager_get_strategy_freeze) +- [Function `get_default_min_time_limit`](#0x1_PackageTxnManager_get_default_min_time_limit) +- [Function `update_module_upgrade_strategy`](#0x1_PackageTxnManager_update_module_upgrade_strategy) +- [Function `account_address`](#0x1_PackageTxnManager_account_address) +- [Function `destroy_upgrade_plan_cap`](#0x1_PackageTxnManager_destroy_upgrade_plan_cap) +- [Function `extract_submit_upgrade_plan_cap`](#0x1_PackageTxnManager_extract_submit_upgrade_plan_cap) +- [Function `convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2`](#0x1_PackageTxnManager_convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2) +- [Function `submit_upgrade_plan_v2`](#0x1_PackageTxnManager_submit_upgrade_plan_v2) +- [Function `submit_upgrade_plan_with_cap_v2`](#0x1_PackageTxnManager_submit_upgrade_plan_with_cap_v2) +- [Function `cancel_upgrade_plan`](#0x1_PackageTxnManager_cancel_upgrade_plan) +- [Function `cancel_upgrade_plan_with_cap`](#0x1_PackageTxnManager_cancel_upgrade_plan_with_cap) +- [Function `get_module_upgrade_strategy`](#0x1_PackageTxnManager_get_module_upgrade_strategy) +- [Function `get_upgrade_plan`](#0x1_PackageTxnManager_get_upgrade_plan) +- [Function `get_upgrade_plan_v2`](#0x1_PackageTxnManager_get_upgrade_plan_v2) +- [Function `check_package_txn`](#0x1_PackageTxnManager_check_package_txn) +- [Function `check_package_txn_v2`](#0x1_PackageTxnManager_check_package_txn_v2) +- [Function `finish_upgrade_plan`](#0x1_PackageTxnManager_finish_upgrade_plan) +- [Function `package_txn_prologue`](#0x1_PackageTxnManager_package_txn_prologue) +- [Function `package_txn_prologue_v2`](#0x1_PackageTxnManager_package_txn_prologue_v2) +- [Function `package_txn_epilogue`](#0x1_PackageTxnManager_package_txn_epilogue) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::CoreAddresses;
+use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Option;
+use 0x1::Signer;
+use 0x1::Timestamp;
+use 0x1::Version;
+
+ + + + + +## Struct `UpgradePlan` + +module upgrade plan + + +
struct UpgradePlan has copy, drop, store
+
+ + + +
+Fields + + +
+
+package_hash: vector<u8> +
+
+ +
+
+active_after_time: u64 +
+
+ +
+
+version: u64 +
+
+ +
+
+ + +
+ + + +## Resource `UpgradePlanCapability` + +The holder of UpgradePlanCapability for account_address can submit UpgradePlan for account_address. + + +
struct UpgradePlanCapability has store, key
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+ + +
+ + + +## Struct `UpgradePlanV2` + + + +
struct UpgradePlanV2 has copy, drop, store
+
+ + + +
+Fields + + +
+
+package_hash: vector<u8> +
+
+ +
+
+active_after_time: u64 +
+
+ +
+
+version: u64 +
+
+ +
+
+enforced: bool +
+
+ +
+
+ + +
+ + + +## Resource `ModuleUpgradeStrategy` + +module upgrade strategy + + +
struct ModuleUpgradeStrategy has store, key
+
+ + + +
+Fields + + +
+
+strategy: u8 +
+
+ 0 arbitrary + 1 two phase upgrade + 2 only new module + 3 freeze +
+
+ + +
+ + + +## Resource `TwoPhaseUpgrade` + +data of two phase upgrade strategy. + + +
struct TwoPhaseUpgrade has key
+
+ + + +
+Fields + + +
+
+config: PackageTxnManager::TwoPhaseUpgradeConfig +
+
+ +
+
+plan: Option::Option<PackageTxnManager::UpgradePlan> +
+
+ +
+
+version_cap: Config::ModifyConfigCapability<Version::Version> +
+
+ +
+
+upgrade_event: Event::EventHandle<PackageTxnManager::UpgradeEvent> +
+
+ +
+
+ + +
+ + + +## Struct `TwoPhaseUpgradeConfig` + +config of two phase upgrade strategy. + + +
struct TwoPhaseUpgradeConfig has copy, drop, store
+
+ + + +
+Fields + + +
+
+min_time_limit: u64 +
+
+ +
+
+ + +
+ + + +## Resource `TwoPhaseUpgradeV2` + +data of two phase upgrade strategy. + + +
struct TwoPhaseUpgradeV2 has key
+
+ + + +
+Fields + + +
+
+config: PackageTxnManager::TwoPhaseUpgradeConfig +
+
+ +
+
+plan: Option::Option<PackageTxnManager::UpgradePlanV2> +
+
+ +
+
+version_cap: Config::ModifyConfigCapability<Version::Version> +
+
+ +
+
+upgrade_event: Event::EventHandle<PackageTxnManager::UpgradeEvent> +
+
+ +
+
+ + +
+ + + +## Struct `UpgradeEvent` + +module upgrade event. + + +
struct UpgradeEvent has drop, store
+
+ + + +
+Fields + + +
+
+package_address: address +
+
+ +
+
+package_hash: vector<u8> +
+
+ +
+
+version: u64 +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const DEFAULT_MIN_TIME_LIMIT: u64 = 86400000;
+
+ + + + + + + +
const EACTIVE_TIME_INCORRECT: u64 = 104;
+
+ + + + + + + +
const EPACKAGE_HASH_INCORRECT: u64 = 103;
+
+ + + + + + + +
const ESENDER_AND_PACKAGE_ADDRESS_MISMATCH: u64 = 109;
+
+ + + + + + + +
const ESTRATEGY_FREEZED: u64 = 105;
+
+ + + + + + + +
const ESTRATEGY_INCORRECT: u64 = 106;
+
+ + + + + + + +
const ESTRATEGY_NOT_TWO_PHASE: u64 = 107;
+
+ + + + + + + +
const EUNKNOWN_STRATEGY: u64 = 108;
+
+ + + + + + + +
const EUPGRADE_PLAN_IS_NONE: u64 = 102;
+
+ + + + + + + +
const STRATEGY_ARBITRARY: u8 = 0;
+
+ + + + + + + +
const STRATEGY_FREEZE: u8 = 3;
+
+ + + + + + + +
const STRATEGY_NEW_MODULE: u8 = 2;
+
+ + + + + + + +
const STRATEGY_TWO_PHASE: u8 = 1;
+
+ + + + + +## Function `get_strategy_arbitrary` + +arbitary stragegy + + +
public fun get_strategy_arbitrary(): u8
+
+ + + +
+Implementation + + +
public fun get_strategy_arbitrary(): u8 { STRATEGY_ARBITRARY }
+
+ + + +
+ + + +## Function `get_strategy_two_phase` + +two phase stragegy + + +
public fun get_strategy_two_phase(): u8
+
+ + + +
+Implementation + + +
public fun get_strategy_two_phase(): u8 { STRATEGY_TWO_PHASE }
+
+ + + +
+ + + +## Function `get_strategy_new_module` + +new module strategy + + +
public fun get_strategy_new_module(): u8
+
+ + + +
+Implementation + + +
public fun get_strategy_new_module(): u8 { STRATEGY_NEW_MODULE }
+
+ + + +
+ + + +## Function `get_strategy_freeze` + +freezed strategy + + +
public fun get_strategy_freeze(): u8
+
+ + + +
+Implementation + + +
public fun get_strategy_freeze(): u8 { STRATEGY_FREEZE }
+
+ + + +
+ + + +## Function `get_default_min_time_limit` + +default min time limit + + +
public fun get_default_min_time_limit(): u64
+
+ + + +
+Implementation + + +
public fun get_default_min_time_limit(): u64 { DEFAULT_MIN_TIME_LIMIT }
+
+ + + +
+ + + +## Function `update_module_upgrade_strategy` + +Update account's ModuleUpgradeStrategy + + +
public fun update_module_upgrade_strategy(account: &signer, strategy: u8, min_time: Option::Option<u64>)
+
+ + + +
+Implementation + + +
public fun update_module_upgrade_strategy(account: &signer, strategy: u8, min_time: Option<u64>) acquires ModuleUpgradeStrategy, TwoPhaseUpgrade, TwoPhaseUpgradeV2, UpgradePlanCapability{
+    assert!(strategy == STRATEGY_ARBITRARY || strategy == STRATEGY_TWO_PHASE || strategy == STRATEGY_NEW_MODULE || strategy == STRATEGY_FREEZE, Errors::invalid_argument(EUNKNOWN_STRATEGY));
+    let account_address = Signer::address_of(account);
+    let previous_strategy = get_module_upgrade_strategy(account_address);
+    assert!(strategy > previous_strategy, Errors::invalid_argument(ESTRATEGY_INCORRECT));
+    if (exists<ModuleUpgradeStrategy>(account_address)) {
+        borrow_global_mut<ModuleUpgradeStrategy>(account_address).strategy = strategy;
+    }else{
+        move_to(account, ModuleUpgradeStrategy{ strategy: strategy});
+    };
+    if (strategy == STRATEGY_TWO_PHASE){
+        let version_cap = Config::extract_modify_config_capability<Version::Version>(account);
+        let min_time_limit = Option::get_with_default(&min_time, DEFAULT_MIN_TIME_LIMIT);
+        move_to(account, UpgradePlanCapability{ account_address: account_address});
+        move_to(account, TwoPhaseUpgradeV2{
+            config: TwoPhaseUpgradeConfig{min_time_limit: min_time_limit},
+            plan: Option::none<UpgradePlanV2>(),
+            version_cap: version_cap,
+            upgrade_event: Event::new_event_handle<Self::UpgradeEvent>(account)}
+        );
+    };
+    //clean two phase upgrade resource
+    if (previous_strategy == STRATEGY_TWO_PHASE){
+        if (exists<TwoPhaseUpgrade>(account_address)) {
+            let tpu = move_from<TwoPhaseUpgrade>(account_address);
+            let TwoPhaseUpgrade{plan:_, version_cap, upgrade_event, config: _} = tpu;
+            Event::destroy_handle<Self::UpgradeEvent>(upgrade_event);
+            Config::destroy_modify_config_capability<Version::Version>(version_cap);
+        };
+        if (exists<TwoPhaseUpgradeV2>(account_address)) {
+            let tpu = move_from<TwoPhaseUpgradeV2>(account_address);
+            let TwoPhaseUpgradeV2{plan:_, version_cap, upgrade_event, config: _} = tpu;
+            Event::destroy_handle<Self::UpgradeEvent>(upgrade_event);
+            Config::destroy_modify_config_capability<Version::Version>(version_cap);
+        };
+        // UpgradePlanCapability may be extracted
+        if (exists<UpgradePlanCapability>(account_address)) {
+            let cap = move_from<UpgradePlanCapability>(account_address);
+            destroy_upgrade_plan_cap(cap);
+        };
+    };
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if strategy != 0 && strategy != 1 && strategy != 2 && strategy != 3;
+aborts_if exists<ModuleUpgradeStrategy>(Signer::address_of(account)) && strategy <= global<ModuleUpgradeStrategy>(Signer::address_of(account)).strategy;
+aborts_if !exists<ModuleUpgradeStrategy>(Signer::address_of(account)) && strategy == 0;
+aborts_if strategy == 1 && exists<UpgradePlanCapability>(Signer::address_of(account));
+aborts_if strategy == 1 && !exists<Config::ModifyConfigCapabilityHolder<Version::Version>>(Signer::address_of(account));
+let holder = global<Config::ModifyConfigCapabilityHolder<Version::Version>>(Signer::address_of(account));
+aborts_if strategy == 1 && Option::is_none<Config::ModifyConfigCapability<Version::Version>>(holder.cap);
+aborts_if strategy == 1 && exists<TwoPhaseUpgrade>(Signer::address_of(account));
+aborts_if exists<ModuleUpgradeStrategy>(Signer::address_of(account)) && global<ModuleUpgradeStrategy>(Signer::address_of(account)).strategy == 1
+    && !exists<TwoPhaseUpgrade>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `account_address` + +Get account address of UpgradePlanCapability + + +
public fun account_address(cap: &PackageTxnManager::UpgradePlanCapability): address
+
+ + + +
+Implementation + + +
public fun account_address(cap: &UpgradePlanCapability): address {
+    cap.account_address
+}
+
+ + + +
+ + + +## Function `destroy_upgrade_plan_cap` + +destroy the given UpgradePlanCapability + + +
public fun destroy_upgrade_plan_cap(cap: PackageTxnManager::UpgradePlanCapability)
+
+ + + +
+Implementation + + +
public fun destroy_upgrade_plan_cap(cap: UpgradePlanCapability){
+    let UpgradePlanCapability{account_address:_} = cap;
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `extract_submit_upgrade_plan_cap` + +extract out UpgradePlanCapability from signer. + + +
public fun extract_submit_upgrade_plan_cap(account: &signer): PackageTxnManager::UpgradePlanCapability
+
+ + + +
+Implementation + + +
public fun extract_submit_upgrade_plan_cap(account: &signer): UpgradePlanCapability acquires ModuleUpgradeStrategy, UpgradePlanCapability{
+    let account_address = Signer::address_of(account);
+    assert!(get_module_upgrade_strategy(account_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE));
+    move_from<UpgradePlanCapability>(account_address)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<ModuleUpgradeStrategy>(Signer::address_of(account));
+aborts_if global<ModuleUpgradeStrategy>(Signer::address_of(account)).strategy != 1;
+aborts_if !exists<UpgradePlanCapability>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2` + + + +
public entry fun convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2(account: signer, package_address: address)
+
+ + + +
+Implementation + + +
public entry fun convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2(account: signer, package_address: address) acquires TwoPhaseUpgrade {
+    let account_address = Signer::address_of(&account);
+    // sender should be package owner
+    assert!(account_address == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH));
+    let tpu = move_from<TwoPhaseUpgrade>(account_address);
+    let TwoPhaseUpgrade{config, plan, version_cap, upgrade_event} = tpu;
+    if (Option::is_some(&plan)) {
+        let old_plan = Option::borrow(&plan);
+        move_to(&account, TwoPhaseUpgradeV2{
+            config: config,
+            plan: Option::some(UpgradePlanV2 {
+                package_hash: *&old_plan.package_hash,
+                active_after_time: old_plan.active_after_time,
+                version: old_plan.version,
+                enforced: false }),
+            version_cap: version_cap,
+            upgrade_event: upgrade_event
+        });
+    } else {
+        move_to(&account, TwoPhaseUpgradeV2{
+            config: config,
+            plan: Option::none<UpgradePlanV2>(),
+            version_cap: version_cap,
+            upgrade_event: upgrade_event
+        });
+    };
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `submit_upgrade_plan_v2` + + + +
public fun submit_upgrade_plan_v2(account: &signer, package_hash: vector<u8>, version: u64, enforced: bool)
+
+ + + +
+Implementation + + +
public fun submit_upgrade_plan_v2(account: &signer, package_hash: vector<u8>, version:u64, enforced: bool) acquires TwoPhaseUpgradeV2,UpgradePlanCapability,ModuleUpgradeStrategy{
+    let account_address = Signer::address_of(account);
+    let cap = borrow_global<UpgradePlanCapability>(account_address);
+    submit_upgrade_plan_with_cap_v2(cap, package_hash, version, enforced);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if !exists<UpgradePlanCapability>(Signer::address_of(account));
+include SubmitUpgradePlanWithCapAbortsIf{account: global<UpgradePlanCapability>(Signer::address_of(account)).account_address};
+ensures Option::is_some(global<TwoPhaseUpgrade>(global<UpgradePlanCapability>(Signer::address_of(account)).account_address).plan);
+
+ + + +
+ + + +## Function `submit_upgrade_plan_with_cap_v2` + + + +
public fun submit_upgrade_plan_with_cap_v2(cap: &PackageTxnManager::UpgradePlanCapability, package_hash: vector<u8>, version: u64, enforced: bool)
+
+ + + +
+Implementation + + +
public fun submit_upgrade_plan_with_cap_v2(cap: &UpgradePlanCapability, package_hash: vector<u8>, version: u64, enforced: bool) acquires TwoPhaseUpgradeV2,ModuleUpgradeStrategy{
+    let package_address = cap.account_address;
+    assert!(get_module_upgrade_strategy(package_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE));
+    let tpu = borrow_global_mut<TwoPhaseUpgradeV2>(package_address);
+    let active_after_time = Timestamp::now_milliseconds() + tpu.config.min_time_limit;
+    tpu.plan = Option::some(UpgradePlanV2 { package_hash, active_after_time, version, enforced });
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+include SubmitUpgradePlanWithCapAbortsIf{account: cap.account_address};
+ensures Option::is_some(global<TwoPhaseUpgrade>(cap.account_address).plan);
+
+ + + + + + + +
schema SubmitUpgradePlanWithCapAbortsIf {
+    account: address;
+    aborts_if !exists<ModuleUpgradeStrategy>(account);
+    aborts_if global<ModuleUpgradeStrategy>(account).strategy != 1;
+    aborts_if !exists<TwoPhaseUpgrade>(account);
+    aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+    aborts_if Timestamp::now_milliseconds() + global<TwoPhaseUpgrade>(account).config.min_time_limit > max_u64();
+}
+
+ + + +
+ + + +## Function `cancel_upgrade_plan` + +Cancel a module upgrade plan. + + +
public fun cancel_upgrade_plan(account: &signer)
+
+ + + +
+Implementation + + +
public fun cancel_upgrade_plan(account: &signer) acquires TwoPhaseUpgradeV2,UpgradePlanCapability,ModuleUpgradeStrategy{
+    let account_address = Signer::address_of(account);
+    let cap = borrow_global<UpgradePlanCapability>(account_address);
+    cancel_upgrade_plan_with_cap(cap);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<UpgradePlanCapability>(Signer::address_of(account));
+include CancelUpgradePlanWithCapAbortsIf{account: global<UpgradePlanCapability>(Signer::address_of(account)).account_address};
+ensures Option::is_none(global<TwoPhaseUpgrade>(global<UpgradePlanCapability>(Signer::address_of(account)).account_address).plan);
+
+ + + +
+ + + +## Function `cancel_upgrade_plan_with_cap` + +Cancel a module upgrade plan with given cap. + + +
public fun cancel_upgrade_plan_with_cap(cap: &PackageTxnManager::UpgradePlanCapability)
+
+ + + +
+Implementation + + +
public fun cancel_upgrade_plan_with_cap(cap: &UpgradePlanCapability) acquires TwoPhaseUpgradeV2,ModuleUpgradeStrategy{
+    let package_address = cap.account_address;
+    assert!(get_module_upgrade_strategy(package_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE));
+    let tpu = borrow_global_mut<TwoPhaseUpgradeV2>(package_address);
+    assert!(Option::is_some(&tpu.plan), Errors::invalid_state(EUPGRADE_PLAN_IS_NONE));
+    tpu.plan = Option::none<UpgradePlanV2>();
+}
+
+ + + +
+ +
+Specification + + + +
include CancelUpgradePlanWithCapAbortsIf{account: cap.account_address};
+ensures Option::is_none(global<TwoPhaseUpgrade>(cap.account_address).plan);
+
+ + + + + + + +
schema CancelUpgradePlanWithCapAbortsIf {
+    account: address;
+    aborts_if !exists<ModuleUpgradeStrategy>(account);
+    aborts_if global<ModuleUpgradeStrategy>(account).strategy != 1;
+    aborts_if !exists<TwoPhaseUpgrade>(account);
+    aborts_if !Option::is_some(global<TwoPhaseUpgrade>(account).plan);
+}
+
+ + + +
+ + + +## Function `get_module_upgrade_strategy` + +Get module upgrade strategy of an module address. + + +
public fun get_module_upgrade_strategy(module_address: address): u8
+
+ + + +
+Implementation + + +
public fun get_module_upgrade_strategy(module_address: address): u8 acquires ModuleUpgradeStrategy {
+    if (exists<ModuleUpgradeStrategy>(module_address)) {
+        borrow_global<ModuleUpgradeStrategy>(module_address).strategy
+    }else{
+        0
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + + + + + +
fun spec_get_module_upgrade_strategy(module_address: address): u8 {
+   if (exists<ModuleUpgradeStrategy>(module_address)) {
+       global<ModuleUpgradeStrategy>(module_address).strategy
+   }else{
+       0
+   }
+}
+
+ + + +
+ + + +## Function `get_upgrade_plan` + +Get module upgrade plan of an address. + + +
public fun get_upgrade_plan(_module_address: address): Option::Option<PackageTxnManager::UpgradePlan>
+
+ + + +
+Implementation + + +
public fun get_upgrade_plan(_module_address: address): Option<UpgradePlan> {
+    // DEPRECATED_CODE
+    Option::none<UpgradePlan>()
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `get_upgrade_plan_v2` + +Get module upgrade plan of an address. + + +
public fun get_upgrade_plan_v2(module_address: address): Option::Option<PackageTxnManager::UpgradePlanV2>
+
+ + + +
+Implementation + + +
public fun get_upgrade_plan_v2(module_address: address): Option<UpgradePlanV2> acquires TwoPhaseUpgradeV2 {
+    if (exists<TwoPhaseUpgradeV2>(module_address)) {
+        *&borrow_global<TwoPhaseUpgradeV2>(module_address).plan
+    } else {
+        Option::none<UpgradePlanV2>()
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if false;
+
+ + + + + + + +
fun spec_get_upgrade_plan_v2(module_address: address): Option<UpgradePlan> {
+   if (exists<TwoPhaseUpgrade>(module_address)) {
+       global<TwoPhaseUpgrade>(module_address).plan
+   }else{
+       Option::spec_none<UpgradePlan>()
+   }
+}
+
+ + + +
+ + + +## Function `check_package_txn` + +Check againest on the given package data. + + +
public fun check_package_txn(package_address: address, package_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun check_package_txn(package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy{
+    let strategy = get_module_upgrade_strategy(package_address);
+    if (strategy == STRATEGY_ARBITRARY){
+        //do nothing
+    }else if(strategy == STRATEGY_TWO_PHASE){
+        let plan_opt = get_upgrade_plan_v2(package_address);
+        assert!(Option::is_some(&plan_opt), Errors::invalid_argument(EUPGRADE_PLAN_IS_NONE));
+        let plan = Option::borrow(&plan_opt);
+        assert!(*&plan.package_hash == package_hash, Errors::invalid_argument(EPACKAGE_HASH_INCORRECT));
+        assert!(plan.active_after_time <= Timestamp::now_milliseconds(), Errors::invalid_argument(EACTIVE_TIME_INCORRECT));
+    }else if(strategy == STRATEGY_NEW_MODULE){
+        //do check at VM runtime.
+    }else if(strategy == STRATEGY_FREEZE){
+        Errors::invalid_argument(ESTRATEGY_FREEZED);
+    };
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+include CheckPackageTxnAbortsIf;
+
+ + + +
+ + + +## Function `check_package_txn_v2` + + + +
public fun check_package_txn_v2(txn_sender: address, package_address: address, package_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun check_package_txn_v2(txn_sender: address, package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy{
+    let strategy = get_module_upgrade_strategy(package_address);
+    if (strategy == STRATEGY_ARBITRARY){
+        assert!(txn_sender == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH));
+    }else if(strategy == STRATEGY_TWO_PHASE){
+        let plan_opt = get_upgrade_plan_v2(package_address);
+        assert!(Option::is_some(&plan_opt), Errors::invalid_argument(EUPGRADE_PLAN_IS_NONE));
+        let plan = Option::borrow(&plan_opt);
+        assert!(*&plan.package_hash == package_hash, Errors::invalid_argument(EPACKAGE_HASH_INCORRECT));
+        assert!(plan.active_after_time <= Timestamp::now_milliseconds(), Errors::invalid_argument(EACTIVE_TIME_INCORRECT));
+    }else if(strategy == STRATEGY_NEW_MODULE){
+        //do check at VM runtime.
+        assert!(txn_sender == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH));
+    }else if(strategy == STRATEGY_FREEZE){
+        Errors::invalid_argument(ESTRATEGY_FREEZED);
+    };
+}
+
+ + + +
+ + + +## Function `finish_upgrade_plan` + + + +
fun finish_upgrade_plan(package_address: address)
+
+ + + +
+Implementation + + +
fun finish_upgrade_plan(package_address: address) acquires TwoPhaseUpgradeV2 {
+    let tpu = borrow_global_mut<TwoPhaseUpgradeV2>(package_address);
+    if (Option::is_some(&tpu.plan)) {
+        let plan = Option::borrow(&tpu.plan);
+        Config::set_with_capability<Version::Version>(&mut tpu.version_cap, Version::new_version(plan.version));
+        Event::emit_event<Self::UpgradeEvent>(&mut tpu.upgrade_event, UpgradeEvent {
+            package_address: package_address,
+            package_hash: *&plan.package_hash,
+            version: plan.version});
+    };
+    tpu.plan = Option::none<UpgradePlanV2>();
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if !exists<TwoPhaseUpgrade>(package_address);
+let tpu = global<TwoPhaseUpgrade>(package_address);
+aborts_if Option::is_some(tpu.plan) && !exists<Config::Config<Version::Version>>(tpu.version_cap.account_address);
+
+ + + +
+ + + +## Function `package_txn_prologue` + +Prologue of package transaction. + + +
public fun package_txn_prologue(account: &signer, package_address: address, package_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun package_txn_prologue(account: &signer, package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy {
+    // Can only be invoked by genesis account
+    CoreAddresses::assert_genesis_address(account);
+    check_package_txn(package_address, package_hash);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+include CheckPackageTxnAbortsIf{};
+
+ + + +
+ + + +## Function `package_txn_prologue_v2` + + + +
public fun package_txn_prologue_v2(account: &signer, txn_sender: address, package_address: address, package_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun package_txn_prologue_v2(account: &signer, txn_sender: address, package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy {
+    // Can only be invoked by genesis account
+    CoreAddresses::assert_genesis_address(account);
+    check_package_txn_v2(txn_sender, package_address, package_hash);
+}
+
+ + + +
+ + + +## Function `package_txn_epilogue` + +Package txn finished, and clean UpgradePlan + + +
public fun package_txn_epilogue(account: &signer, _txn_sender: address, package_address: address, success: bool)
+
+ + + +
+Implementation + + +
public fun package_txn_epilogue(account: &signer, _txn_sender: address, package_address: address, success: bool) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy {
+    // Can only be invoked by genesis account
+    CoreAddresses::assert_genesis_address(account);
+    let strategy = get_module_upgrade_strategy(package_address);
+    if(strategy == STRATEGY_TWO_PHASE){
+        if (success) {
+            finish_upgrade_plan(package_address);
+        };
+    };
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if spec_get_module_upgrade_strategy(package_address) == 1
+    && success && !exists<TwoPhaseUpgrade>(package_address);
+aborts_if spec_get_module_upgrade_strategy(package_address) == 1
+    && success && Option::is_some(global<TwoPhaseUpgrade>(package_address).plan)
+    && !exists<Config::Config<Version::Version>>(global<TwoPhaseUpgrade>(package_address).version_cap.account_address);
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/README.md b/release/v13/docs/README.md new file mode 100644 index 00000000..abbf5f43 --- /dev/null +++ b/release/v13/docs/README.md @@ -0,0 +1,111 @@ + + + +# Move StarcoinFramework Modules + + +This is the root document for the Move StarcoinFramework module documentation. The Move StarcoinFramework provides modules that can be used to access or interact with Starcoin blockchain. + + + + +## Index + + +- [`0x1::ACL`](ACL.md#0x1_ACL) +- [`0x1::Account`](Account.md#0x1_Account) +- [`0x1::AccountScripts`](AccountScripts.md#0x1_AccountScripts) +- [`0x1::Arith`](U256.md#0x1_Arith) +- [`0x1::Authenticator`](Authenticator.md#0x1_Authenticator) +- [`0x1::BCS`](BCS.md#0x1_BCS) +- [`0x1::BitOperators`](Bitwise.md#0x1_BitOperators) +- [`0x1::Block`](Block.md#0x1_Block) +- [`0x1::BlockReward`](BlockReward.md#0x1_BlockReward) +- [`0x1::ChainId`](ChainId.md#0x1_ChainId) +- [`0x1::Collection`](Collection.md#0x1_Collection) +- [`0x1::Collection2`](Collection2.md#0x1_Collection2) +- [`0x1::Compare`](Compare.md#0x1_Compare) +- [`0x1::Config`](Config.md#0x1_Config) +- [`0x1::ConsensusConfig`](ConsensusConfig.md#0x1_ConsensusConfig) +- [`0x1::ConsensusStrategy`](ConsensusStrategy.md#0x1_ConsensusStrategy) +- [`0x1::CoreAddresses`](CoreAddresses.md#0x1_CoreAddresses) +- [`0x1::Dao`](Dao.md#0x1_Dao) +- [`0x1::DaoVoteScripts`](DaoVoteScripts.md#0x1_DaoVoteScripts) +- [`0x1::Debug`](Debug.md#0x1_Debug) +- [`0x1::DummyToken`](DummyToken.md#0x1_DummyToken) +- [`0x1::DummyTokenScripts`](DummyToken.md#0x1_DummyTokenScripts) +- [`0x1::EVMAddress`](Signature.md#0x1_EVMAddress) +- [`0x1::EasyGas`](EasyGas.md#0x1_EasyGas) +- [`0x1::EasyGasScript`](EasyGas.md#0x1_EasyGasScript) +- [`0x1::EmptyScripts`](EmptyScripts.md#0x1_EmptyScripts) +- [`0x1::Epoch`](Epoch.md#0x1_Epoch) +- [`0x1::Errors`](Errors.md#0x1_Errors) +- [`0x1::Event`](Event.md#0x1_Event) +- [`0x1::EventUtil`](EventUtil.md#0x1_EventUtil) +- [`0x1::FixedPoint32`](FixedPoint32.md#0x1_FixedPoint32) +- [`0x1::FlexiDagConfig`](FlexiDagConfig.md#0x1_FlexiDagConfig) +- [`0x1::FromBCS`](FromBCS.md#0x1_FromBCS) +- [`0x1::GasSchedule`](GasSchedule.md#0x1_GasSchedule) +- [`0x1::Genesis`](Genesis.md#0x1_Genesis) +- [`0x1::GenesisNFT`](GenesisNFT.md#0x1_GenesisNFT) +- [`0x1::GenesisNFTScripts`](GenesisNFT.md#0x1_GenesisNFTScripts) +- [`0x1::GenesisSignerCapability`](GenesisSignerCapability.md#0x1_GenesisSignerCapability) +- [`0x1::Hash`](Hash.md#0x1_Hash) +- [`0x1::IdentifierNFT`](NFT.md#0x1_IdentifierNFT) +- [`0x1::IdentifierNFTScripts`](NFT.md#0x1_IdentifierNFTScripts) +- [`0x1::LanguageVersion`](LanguageVersion.md#0x1_LanguageVersion) +- [`0x1::Math`](Math.md#0x1_Math) +- [`0x1::MerkleNFTDistributor`](MerkleNFT.md#0x1_MerkleNFTDistributor) +- [`0x1::MerkleProof`](MerkleNFT.md#0x1_MerkleProof) +- [`0x1::MintDaoProposal`](MintDaoProposal.md#0x1_MintDaoProposal) +- [`0x1::MintScripts`](MintScripts.md#0x1_MintScripts) +- [`0x1::ModifyDaoConfigProposal`](ModifyDaoConfigProposal.md#0x1_ModifyDaoConfigProposal) +- [`0x1::ModuleUpgradeScripts`](ModuleUpgradeScripts.md#0x1_ModuleUpgradeScripts) +- [`0x1::NFT`](NFT.md#0x1_NFT) +- [`0x1::NFTGallery`](NFT.md#0x1_NFTGallery) +- [`0x1::NFTGalleryScripts`](NFT.md#0x1_NFTGalleryScripts) +- [`0x1::Offer`](Offer.md#0x1_Offer) +- [`0x1::OnChainConfigDao`](OnChainConfigDao.md#0x1_OnChainConfigDao) +- [`0x1::OnChainConfigScripts`](OnChainConfigScripts.md#0x1_OnChainConfigScripts) +- [`0x1::Option`](Option.md#0x1_Option) +- [`0x1::Oracle`](Oracle.md#0x1_Oracle) +- [`0x1::PackageTxnManager`](PackageTxnManager.md#0x1_PackageTxnManager) +- [`0x1::PriceOracle`](Oracle.md#0x1_PriceOracle) +- [`0x1::PriceOracleAggregator`](Oracle.md#0x1_PriceOracleAggregator) +- [`0x1::PriceOracleScripts`](Oracle.md#0x1_PriceOracleScripts) +- [`0x1::RewardConfig`](RewardConfig.md#0x1_RewardConfig) +- [`0x1::Ring`](Ring.md#0x1_Ring) +- [`0x1::SIP_2`](SIPs.md#0x1_SIP_2) +- [`0x1::SIP_3`](SIPs.md#0x1_SIP_3) +- [`0x1::STC`](STC.md#0x1_STC) +- [`0x1::STCUSDOracle`](Oracle.md#0x1_STCUSDOracle) +- [`0x1::Secp256k1`](Secp256k1.md#0x1_Secp256k1) +- [`0x1::SharedEd25519PublicKey`](SharedEd25519PublicKey.md#0x1_SharedEd25519PublicKey) +- [`0x1::Signature`](Signature.md#0x1_Signature) +- [`0x1::SignedInteger64`](SignedInteger64.md#0x1_SignedInteger64) +- [`0x1::Signer`](Signer.md#0x1_Signer) +- [`0x1::SimpleMap`](SimpleMap.md#0x1_SimpleMap) +- [`0x1::StarcoinVerifier`](StarcoinVerifier.md#0x1_StarcoinVerifier) +- [`0x1::StdlibUpgradeScripts`](StdlibUpgradeScripts.md#0x1_StdlibUpgradeScripts) +- [`0x1::String`](String.md#0x1_String) +- [`0x1::StructuredHash`](StarcoinVerifier.md#0x1_StructuredHash) +- [`0x1::Table`](Table.md#0x1_Table) +- [`0x1::Timestamp`](Timestamp.md#0x1_Timestamp) +- [`0x1::Token`](Token.md#0x1_Token) +- [`0x1::TransactionFee`](TransactionFee.md#0x1_TransactionFee) +- [`0x1::TransactionManager`](TransactionManager.md#0x1_TransactionManager) +- [`0x1::TransactionPublishOption`](TransactionPublishOption.md#0x1_TransactionPublishOption) +- [`0x1::TransactionTimeout`](TransactionTimeout.md#0x1_TransactionTimeout) +- [`0x1::TransactionTimeoutConfig`](TransactionTimeoutConfig.md#0x1_TransactionTimeoutConfig) +- [`0x1::TransferScripts`](TransferScripts.md#0x1_TransferScripts) +- [`0x1::Treasury`](Treasury.md#0x1_Treasury) +- [`0x1::TreasuryScripts`](TreasuryScripts.md#0x1_TreasuryScripts) +- [`0x1::TreasuryWithdrawDaoProposal`](TreasuryWithdrawDaoProposal.md#0x1_TreasuryWithdrawDaoProposal) +- [`0x1::TypeInfo`](TypeInfo.md#0x1_TypeInfo) +- [`0x1::U256`](U256.md#0x1_U256) +- [`0x1::UpgradeModuleDaoProposal`](UpgradeModuleDaoProposal.md#0x1_UpgradeModuleDaoProposal) +- [`0x1::VMConfig`](VMConfig.md#0x1_VMConfig) +- [`0x1::Vector`](Vector.md#0x1_Vector) +- [`0x1::Version`](Version.md#0x1_Version) +- [`0x1::YieldFarming`](YieldFarming.md#0x1_YieldFarming) +- [`0x1::YieldFarmingV2`](YieldFarmingV2.md#0x1_YieldFarmingV2) diff --git a/release/v13/docs/RewardConfig.md b/release/v13/docs/RewardConfig.md new file mode 100644 index 00000000..4c300f5d --- /dev/null +++ b/release/v13/docs/RewardConfig.md @@ -0,0 +1,240 @@ + + + +# Module `0x1::RewardConfig` + +The module provide configuration for block reward. + + +- [Struct `RewardConfig`](#0x1_RewardConfig_RewardConfig) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_RewardConfig_initialize) +- [Function `new_reward_config`](#0x1_RewardConfig_new_reward_config) +- [Function `get_reward_config`](#0x1_RewardConfig_get_reward_config) +- [Function `reward_delay`](#0x1_RewardConfig_reward_delay) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::CoreAddresses;
+use 0x1::Timestamp;
+
+ + + + + +## Struct `RewardConfig` + +Reward configuration + + +
struct RewardConfig has copy, drop, store
+
+ + + +
+Fields + + +
+
+reward_delay: u64 +
+
+ how many blocks delay reward distribution. +
+
+ + +
+ + + +## Constants + + + + + + +
const EINVALID_ARGUMENT: u64 = 18;
+
+ + + + + +## Function `initialize` + +Module initialization. + + +
public fun initialize(account: &signer, reward_delay: u64)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, reward_delay: u64) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+
+    Config::publish_new_config<Self::RewardConfig>(
+        account,
+        new_reward_config(reward_delay)
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<Config::Config<RewardConfig>>(Signer::address_of(account));
+include Config::PublishNewConfigAbortsIf<RewardConfig>;
+include Config::PublishNewConfigEnsures<RewardConfig>;
+
+ + + +
+ + + +## Function `new_reward_config` + +Create a new reward config mainly used in DAO. + + +
public fun new_reward_config(reward_delay: u64): RewardConfig::RewardConfig
+
+ + + +
+Implementation + + +
public fun new_reward_config(reward_delay: u64) : RewardConfig {
+    RewardConfig {reward_delay: reward_delay}
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Function `get_reward_config` + +Get reward configuration. + + +
public fun get_reward_config(): RewardConfig::RewardConfig
+
+ + + +
+Implementation + + +
public fun get_reward_config(): RewardConfig {
+    Config::get_by_address<RewardConfig>(CoreAddresses::GENESIS_ADDRESS())
+}
+
+ + + +
+ +
+Specification + + + +
include GetRewardConfigAbortsIf;
+
+ + + + + + + +
schema GetRewardConfigAbortsIf {
+    aborts_if !exists<Config::Config<RewardConfig>>(CoreAddresses::GENESIS_ADDRESS());
+}
+
+ + + +
+ + + +## Function `reward_delay` + +Get reward delay. + + +
public fun reward_delay(): u64
+
+ + + +
+Implementation + + +
public fun reward_delay() :u64 {
+    let reward_config = get_reward_config();
+    reward_config.reward_delay
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Config::Config<RewardConfig>>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/Ring.md b/release/v13/docs/Ring.md new file mode 100644 index 00000000..074de19a --- /dev/null +++ b/release/v13/docs/Ring.md @@ -0,0 +1,426 @@ + + + +# Module `0x1::Ring` + +A ring-shaped container that can hold any type, indexed from 0 +The capacity is fixed at creation time, and the accessible index is constantly growing + + +- [Struct `Ring`](#0x1_Ring_Ring) +- [Constants](#@Constants_0) +- [Function `create_with_capacity`](#0x1_Ring_create_with_capacity) +- [Function `is_full`](#0x1_Ring_is_full) +- [Function `capacity`](#0x1_Ring_capacity) +- [Function `push`](#0x1_Ring_push) +- [Function `borrow`](#0x1_Ring_borrow) +- [Function `borrow_mut`](#0x1_Ring_borrow_mut) +- [Function `index_of`](#0x1_Ring_index_of) +- [Function `destroy`](#0x1_Ring_destroy) + + +
use 0x1::Errors;
+use 0x1::Option;
+
+ + + + + +## Struct `Ring` + + + +
struct Ring<Element> has store
+
+ + + +
+Fields + + +
+
+data: vector<Option::Option<Element>> +
+
+ +
+
+insertion_index: u64 +
+
+ +
+
+external_index: u64 +
+
+ +
+
+ + +
+ + + +## Constants + + + + +The index into the vector is out of bounds + + +
const ERROR_RING_INDEX_OUT_OF_BOUNDS: u64 = 101;
+
+ + + + + +## Function `create_with_capacity` + +Create a Ring with capacity. + + +
public fun create_with_capacity<Element>(len: u64): Ring::Ring<Element>
+
+ + + +
+Implementation + + +
public fun create_with_capacity<Element>( len: u64 ):Ring<Element>{
+    let data = Vector::empty<Option::Option<Element>>();
+    let i = 0;
+    while(i < len){
+        Vector::push_back(&mut data , Option::none<Element>());
+        i = i + 1;
+    };
+    Ring {
+        data             : data,
+        insertion_index  : 0,
+        external_index   : 0,
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `is_full` + +is Ring full + + +
public fun is_full<Element>(r: &Ring::Ring<Element>): bool
+
+ + + +
+Implementation + + +
public fun is_full<Element>(r: &Ring<Element>):bool{
+    Option::is_some(Vector::borrow(&r.data, r.insertion_index))
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `capacity` + +Return the capacity of the Ring. + + +
public fun capacity<Element>(r: &Ring::Ring<Element>): u64
+
+ + + +
+Implementation + + +
public fun capacity<Element>(r: &Ring<Element>): u64{
+    Vector::length( &r.data )
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Function `push` + +Add element e to the insertion_index of the Ring r. + + +
public fun push<Element>(r: &mut Ring::Ring<Element>, e: Element): Option::Option<Element>
+
+ + + +
+Implementation + + +
public fun push<Element> (r: &mut Ring<Element> , e: Element):Option::Option<Element>{
+    let op_e = Vector::borrow_mut<Option::Option<Element>>(&mut r.data, r.insertion_index);
+    let res = if(  Option::is_none<Element>(op_e) ){
+        Option::fill( op_e, e);
+        Option::none<Element>()
+    }else{
+       Option::some<Element>( Option::swap( op_e, e) )
+    };
+    r.insertion_index = ( r.insertion_index + 1 ) % Vector::length(&r.data);
+    r.external_index = r.external_index + 1;
+    res
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `borrow` + +Return a reference to the ith element in the Ring r. + + +
public fun borrow<Element>(r: &Ring::Ring<Element>, i: u64): &Option::Option<Element>
+
+ + + +
+Implementation + + +
public fun borrow<Element>(r:& Ring<Element>, i: u64):&Option::Option<Element>{
+    let len = capacity<Element>(r);
+    if( r.external_index > len - 1) {
+        assert!( i >= r.external_index - len && i < r.external_index , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS));
+        Vector::borrow(&r.data, i % len)
+    }else {
+        assert!( i < len , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS));
+        Vector::borrow(&r.data, i )
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `borrow_mut` + +Return a mutable reference to the ith element in the Ring r. + + +
public fun borrow_mut<Element>(r: &mut Ring::Ring<Element>, i: u64): &mut Option::Option<Element>
+
+ + + +
+Implementation + + +
public fun borrow_mut<Element>(r: &mut Ring<Element>, i: u64):&mut Option::Option<Element>{
+    let len = capacity<Element>(r);
+    if( r.external_index > len - 1) {
+        assert!( i >= r.external_index - len && i < r.external_index , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS));
+        Vector::borrow_mut(&mut r.data, i % len)
+    }else {
+        assert!( i < len , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS));
+        Vector::borrow_mut(&mut r.data, i )
+    }
+
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Function `index_of` + +Return Option::Option<u64> if e is in the Ring r at index i. +Otherwise, returns Option::none<u64>. + + +
public fun index_of<Element>(r: &Ring::Ring<Element>, e: &Element): Option::Option<u64>
+
+ + + +
+Implementation + + +
public fun index_of<Element>(r: &Ring<Element>, e: &Element):Option::Option<u64>{
+    let i = 0;
+    let len = capacity<Element>(r);
+    while ( i < len ) {
+        if ( Option::borrow(Vector::borrow( &r.data, i )) == e) return Option::some(i + r.external_index - len);
+        i = i + 1;
+    };
+    Option::none<u64>()
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `destroy` + +Destroy the Ring r. +Returns the vector saved by ring + + +
public fun destroy<Element>(r: Ring::Ring<Element>): vector<Element>
+
+ + + +
+Implementation + + +
public fun destroy<Element>(r: Ring<Element>):vector<Element>{
+    let Ring {
+        data            : data ,
+        insertion_index : _,
+        external_index  : _,
+    } = r ;
+    let len = Vector::length(&data);
+    let i = 0;
+    let vec = Vector::empty<Element>();
+    while ( i < len ) {
+        let op_e = Vector::pop_back( &mut data );
+        if ( Option::is_some(&op_e) ) {
+            Vector::push_back(&mut vec, Option::destroy_some(op_e))
+        }else {
+           Option::destroy_none(op_e)
+        };
+        i = i + 1;
+    };
+    Vector::destroy_empty(data);
+    vec
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
diff --git a/release/v13/docs/SIPs.md b/release/v13/docs/SIPs.md new file mode 100644 index 00000000..b466bfe7 --- /dev/null +++ b/release/v13/docs/SIPs.md @@ -0,0 +1,11 @@ + + + +# Module `0x1::SIP_3` + +https://github.com/starcoinorg/SIPs/tree/master/sip-3 + + + + +
diff --git a/release/v13/docs/STC.md b/release/v13/docs/STC.md new file mode 100644 index 00000000..b22272d5 --- /dev/null +++ b/release/v13/docs/STC.md @@ -0,0 +1,403 @@ + + + +# Module `0x1::STC` + +STC is the token of Starcoin blockchain. +It uses apis defined in the Token module. + + +- [Struct `STC`](#0x1_STC_STC) +- [Resource `SharedBurnCapability`](#0x1_STC_SharedBurnCapability) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_STC_initialize) +- [Function `upgrade_from_v1_to_v2`](#0x1_STC_upgrade_from_v1_to_v2) +- [Function `initialize_v2`](#0x1_STC_initialize_v2) +- [Function `is_stc`](#0x1_STC_is_stc) +- [Function `burn`](#0x1_STC_burn) +- [Function `token_address`](#0x1_STC_token_address) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::ConsensusConfig;
+use 0x1::CoreAddresses;
+use 0x1::Dao;
+use 0x1::ModifyDaoConfigProposal;
+use 0x1::OnChainConfigDao;
+use 0x1::PackageTxnManager;
+use 0x1::RewardConfig;
+use 0x1::Token;
+use 0x1::TransactionPublishOption;
+use 0x1::TransactionTimeoutConfig;
+use 0x1::Treasury;
+use 0x1::UpgradeModuleDaoProposal;
+use 0x1::VMConfig;
+
+ + + + + +## Struct `STC` + +STC token marker. + + +
struct STC has copy, drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Resource `SharedBurnCapability` + +Burn capability of STC. + + +
struct SharedBurnCapability has store, key
+
+ + + +
+Fields + + +
+
+cap: Token::BurnCapability<STC::STC> +
+
+ +
+
+ + +
+ + + +## Constants + + + + +precision of STC token. + + +
const PRECISION: u8 = 9;
+
+ + + + + +## Function `initialize` + +STC initialization. + + +
public fun initialize(account: &signer, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64)
+
+ + + +
+Implementation + + +
public fun initialize(
+    account: &signer,
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+) {
+    Token::register_token<STC>(account, PRECISION);
+    let burn_cap = Token::remove_burn_capability<STC>(account);
+    move_to(account, SharedBurnCapability { cap: burn_cap });
+    Dao::plugin<STC>(
+        account,
+        voting_delay,
+        voting_period,
+        voting_quorum_rate,
+        min_action_delay,
+    );
+    ModifyDaoConfigProposal::plugin<STC>(account);
+    let upgrade_plan_cap = PackageTxnManager::extract_submit_upgrade_plan_cap(account);
+    UpgradeModuleDaoProposal::plugin<STC>(
+        account,
+        upgrade_plan_cap,
+    );
+    // the following configurations are gov-ed by Dao.
+    OnChainConfigDao::plugin<STC, TransactionPublishOption::TransactionPublishOption>(account);
+    OnChainConfigDao::plugin<STC, VMConfig::VMConfig>(account);
+    OnChainConfigDao::plugin<STC, ConsensusConfig::ConsensusConfig>(account);
+    OnChainConfigDao::plugin<STC, RewardConfig::RewardConfig>(account);
+    OnChainConfigDao::plugin<STC, TransactionTimeoutConfig::TransactionTimeoutConfig>(account);
+}
+
+ + + +
+ +
+Specification + + + +
include Token::RegisterTokenAbortsIf<STC>{precision: PRECISION};
+
+ + + +
+ + + +## Function `upgrade_from_v1_to_v2` + + + +
public fun upgrade_from_v1_to_v2(account: &signer, total_amount: u128): Treasury::WithdrawCapability<STC::STC>
+
+ + + +
+Implementation + + +
public fun upgrade_from_v1_to_v2(account: &signer,total_amount: u128,): Treasury::WithdrawCapability<STC> {
+    CoreAddresses::assert_genesis_address(account);
+
+    // Mint all stc, and destroy mint capability
+    let total_stc = Token::mint<STC>(account, total_amount-Token::market_cap<STC>());
+    let withdraw_cap = Treasury::initialize(account, total_stc);
+    let mint_cap = Token::remove_mint_capability<STC>(account);
+    Token::destroy_mint_capability(mint_cap);
+    withdraw_cap
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `initialize_v2` + +STC initialization. + + +
public fun initialize_v2(account: &signer, total_amount: u128, voting_delay: u64, voting_period: u64, voting_quorum_rate: u8, min_action_delay: u64): Treasury::WithdrawCapability<STC::STC>
+
+ + + +
+Implementation + + +
public fun initialize_v2(
+    account: &signer,
+    total_amount: u128,
+    voting_delay: u64,
+    voting_period: u64,
+    voting_quorum_rate: u8,
+    min_action_delay: u64,
+): Treasury::WithdrawCapability<STC> {
+    Token::register_token<STC>(account, PRECISION);
+
+    // Mint all stc, and destroy mint capability
+
+    let total_stc = Token::mint<STC>(account, total_amount);
+    let withdraw_cap = Treasury::initialize(account, total_stc);
+    let mint_cap = Token::remove_mint_capability<STC>(account);
+    Token::destroy_mint_capability(mint_cap);
+
+    let burn_cap = Token::remove_burn_capability<STC>(account);
+    move_to(account, SharedBurnCapability { cap: burn_cap });
+    Dao::plugin<STC>(
+        account,
+        voting_delay,
+        voting_period,
+        voting_quorum_rate,
+        min_action_delay,
+    );
+    ModifyDaoConfigProposal::plugin<STC>(account);
+    let upgrade_plan_cap = PackageTxnManager::extract_submit_upgrade_plan_cap(account);
+    UpgradeModuleDaoProposal::plugin<STC>(
+        account,
+        upgrade_plan_cap,
+    );
+    // the following configurations are gov-ed by Dao.
+    OnChainConfigDao::plugin<STC, TransactionPublishOption::TransactionPublishOption>(account);
+    OnChainConfigDao::plugin<STC, VMConfig::VMConfig>(account);
+    OnChainConfigDao::plugin<STC, ConsensusConfig::ConsensusConfig>(account);
+    OnChainConfigDao::plugin<STC, RewardConfig::RewardConfig>(account);
+    OnChainConfigDao::plugin<STC, TransactionTimeoutConfig::TransactionTimeoutConfig>(account);
+    withdraw_cap
+}
+
+ + + +
+ +
+Specification + + + +
include Token::RegisterTokenAbortsIf<STC>{precision: PRECISION};
+
+ + + +
+ + + +## Function `is_stc` + +Returns true if TokenType is STC::STC + + +
public fun is_stc<TokenType: store>(): bool
+
+ + + +
+Implementation + + +
public fun is_stc<TokenType: store>(): bool {
+    Token::is_same_token<STC, TokenType>()
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Function `burn` + +Burn STC tokens. +It can be called by anyone. + + +
public fun burn(token: Token::Token<STC::STC>)
+
+ + + +
+Implementation + + +
public fun burn(token: Token<STC>) acquires SharedBurnCapability {
+    let cap = borrow_global<SharedBurnCapability>(token_address());
+    Token::burn_with_capability(&cap.cap, token);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Token::spec_abstract_total_value<STC>() - token.value < 0;
+aborts_if !exists<SharedBurnCapability>(Token::SPEC_TOKEN_TEST_ADDRESS());
+
+ + + +
+ + + +## Function `token_address` + +Return STC token address. + + +
public fun token_address(): address
+
+ + + +
+Implementation + + +
public fun token_address(): address {
+    Token::token_address<STC>()
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/Secp256k1.md b/release/v13/docs/Secp256k1.md new file mode 100644 index 00000000..bb6b5bbb --- /dev/null +++ b/release/v13/docs/Secp256k1.md @@ -0,0 +1,296 @@ + + + +# Module `0x1::Secp256k1` + +This module implements ECDSA signatures based on the prime-order secp256k1 ellptic curve (i.e., cofactor is 1). + + +- [Struct `ECDSARawPublicKey`](#0x1_Secp256k1_ECDSARawPublicKey) +- [Struct `ECDSASignature`](#0x1_Secp256k1_ECDSASignature) +- [Constants](#@Constants_0) +- [Function `ecdsa_signature_from_bytes`](#0x1_Secp256k1_ecdsa_signature_from_bytes) +- [Function `ecdsa_raw_public_key_from_64_bytes`](#0x1_Secp256k1_ecdsa_raw_public_key_from_64_bytes) +- [Function `ecdsa_raw_public_key_to_bytes`](#0x1_Secp256k1_ecdsa_raw_public_key_to_bytes) +- [Function `ecdsa_signature_to_bytes`](#0x1_Secp256k1_ecdsa_signature_to_bytes) +- [Function `ecdsa_recover`](#0x1_Secp256k1_ecdsa_recover) +- [Function `ecdsa_recover_internal`](#0x1_Secp256k1_ecdsa_recover_internal) + + +
use 0x1::Errors;
+use 0x1::Option;
+
+ + + + + +## Struct `ECDSARawPublicKey` + +A 64-byte ECDSA public key. + + +
struct ECDSARawPublicKey has copy, drop, store
+
+ + + +
+Fields + + +
+
+bytes: vector<u8> +
+
+ +
+
+ + +
+ + + +## Struct `ECDSASignature` + +A 64-byte ECDSA signature. + + +
struct ECDSASignature has copy, drop, store
+
+ + + +
+Fields + + +
+
+bytes: vector<u8> +
+
+ +
+
+ + +
+ + + +## Constants + + + + +An error occurred while deserializing, for example due to wrong input size. + + +
const E_DESERIALIZE: u64 = 1;
+
+ + + + + +The size of a secp256k1-based ECDSA public key, in bytes. + + +
const RAW_PUBLIC_KEY_NUM_BYTES: u64 = 64;
+
+ + + + + +The size of a secp256k1-based ECDSA signature, in bytes. + + +
const SIGNATURE_NUM_BYTES: u64 = 64;
+
+ + + + + +## Function `ecdsa_signature_from_bytes` + +Constructs an ECDSASignature struct from the given 64 bytes. + + +
public fun ecdsa_signature_from_bytes(bytes: vector<u8>): Secp256k1::ECDSASignature
+
+ + + +
+Implementation + + +
public fun ecdsa_signature_from_bytes(bytes: vector<u8>): ECDSASignature {
+    assert!(Vector::length(&bytes) == SIGNATURE_NUM_BYTES, Errors::invalid_argument(E_DESERIALIZE));
+    ECDSASignature { bytes }
+}
+
+ + + +
+ + + +## Function `ecdsa_raw_public_key_from_64_bytes` + +Constructs an ECDSARawPublicKey struct, given a 64-byte raw representation. + + +
public fun ecdsa_raw_public_key_from_64_bytes(bytes: vector<u8>): Secp256k1::ECDSARawPublicKey
+
+ + + +
+Implementation + + +
public fun ecdsa_raw_public_key_from_64_bytes(bytes: vector<u8>): ECDSARawPublicKey {
+    assert!(Vector::length(&bytes) == RAW_PUBLIC_KEY_NUM_BYTES, Errors::invalid_argument(E_DESERIALIZE));
+    ECDSARawPublicKey { bytes }
+}
+
+ + + +
+ + + +## Function `ecdsa_raw_public_key_to_bytes` + +Serializes an ECDSARawPublicKey struct to 64-bytes. + + +
public fun ecdsa_raw_public_key_to_bytes(pk: &Secp256k1::ECDSARawPublicKey): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_raw_public_key_to_bytes(pk: &ECDSARawPublicKey): vector<u8> {
+    *&pk.bytes
+}
+
+ + + +
+ + + +## Function `ecdsa_signature_to_bytes` + +Serializes an ECDSASignature struct to 64-bytes. + + +
public fun ecdsa_signature_to_bytes(sig: &Secp256k1::ECDSASignature): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_signature_to_bytes(sig: &ECDSASignature): vector<u8> {
+    *&sig.bytes
+}
+
+ + + +
+ + + +## Function `ecdsa_recover` + +Recovers the signer's raw (64-byte) public key from a secp256k1 ECDSA signature given the recovery_id and the signed +message (32 byte digest). + +Note that an invalid signature, or a signature from a different message, will result in the recovery of an +incorrect public key. This recovery algorithm can only be used to check validity of a signature if the signer's +public key (or its hash) is known beforehand. + + +
public fun ecdsa_recover(message: vector<u8>, recovery_id: u8, signature: &Secp256k1::ECDSASignature): Option::Option<Secp256k1::ECDSARawPublicKey>
+
+ + + +
+Implementation + + +
public fun ecdsa_recover(
+    message: vector<u8>,
+    recovery_id: u8,
+    signature: &ECDSASignature,
+): Option<ECDSARawPublicKey> {
+    let (pk, success) = ecdsa_recover_internal(message, recovery_id, *&signature.bytes);
+    if (success) {
+        Option::some(ecdsa_raw_public_key_from_64_bytes(pk))
+    } else {
+        Option::none<ECDSARawPublicKey>()
+    }
+}
+
+ + + +
+ + + +## Function `ecdsa_recover_internal` + +Returns (public_key, true) if signature verifies on message under the recovered public_key +and returns ([], false) otherwise. + + +
fun ecdsa_recover_internal(message: vector<u8>, recovery_id: u8, signature: vector<u8>): (vector<u8>, bool)
+
+ + + +
+Implementation + + +
native fun ecdsa_recover_internal(
+    message: vector<u8>,
+    recovery_id: u8,
+    signature: vector<u8>
+): (vector<u8>, bool);
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+
+ + + +
diff --git a/release/v13/docs/SharedEd25519PublicKey.md b/release/v13/docs/SharedEd25519PublicKey.md new file mode 100644 index 00000000..f19c8fb2 --- /dev/null +++ b/release/v13/docs/SharedEd25519PublicKey.md @@ -0,0 +1,309 @@ + + + +# Module `0x1::SharedEd25519PublicKey` + +Each address that holds a SharedEd25519PublicKey resource can rotate the public key stored in +this resource, but the account's authentication key will be updated in lockstep. This ensures +that the two keys always stay in sync. + + +- [Resource `SharedEd25519PublicKey`](#0x1_SharedEd25519PublicKey_SharedEd25519PublicKey) +- [Constants](#@Constants_0) +- [Function `publish`](#0x1_SharedEd25519PublicKey_publish) +- [Function `rotate_key_`](#0x1_SharedEd25519PublicKey_rotate_key_) +- [Function `rotate_key`](#0x1_SharedEd25519PublicKey_rotate_key) +- [Function `key`](#0x1_SharedEd25519PublicKey_key) +- [Function `exists_at`](#0x1_SharedEd25519PublicKey_exists_at) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Account;
+use 0x1::Authenticator;
+use 0x1::Errors;
+use 0x1::Signature;
+use 0x1::Signer;
+
+ + + + + +## Resource `SharedEd25519PublicKey` + +A resource that forces the account associated with rotation_cap to use a ed25519 +authentication key derived from key + + +
struct SharedEd25519PublicKey has key
+
+ + + +
+Fields + + +
+
+key: vector<u8> +
+
+ 32 byte ed25519 public key +
+
+rotation_cap: Account::KeyRotationCapability +
+
+ rotation capability for an account whose authentication key is always derived from key +
+
+ + +
+ + + +## Constants + + + + + + +
const EMALFORMED_PUBLIC_KEY: u64 = 101;
+
+ + + + + +## Function `publish` + +(1) Rotate the authentication key of the sender to key +(2) Publish a resource containing a 32-byte ed25519 public key and the rotation capability +of the sender under the account's address. +Aborts if the sender already has a SharedEd25519PublicKey resource. +Aborts if the length of new_public_key is not 32. + + +
public fun publish(account: &signer, key: vector<u8>)
+
+ + + +
+Implementation + + +
public fun publish(account: &signer, key: vector<u8>) {
+    let t = SharedEd25519PublicKey {
+        key: x"",
+        rotation_cap: Account::extract_key_rotation_capability(account)
+    };
+    rotate_key_(&mut t, key);
+    move_to(account, t);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account::Account>(Signer::address_of(account));
+aborts_if StarcoinFramework::Option::is_none(global<Account::Account>(Signer::address_of(account)).key_rotation_capability);
+aborts_if !exists<Account::Account>(
+          StarcoinFramework::Option::borrow<Account::KeyRotationCapability>(
+              global<Account::Account>(Signer::address_of(account))
+              .key_rotation_capability
+          ).account_address);
+aborts_if !Signature::ed25519_validate_pubkey(key);
+aborts_if exists<SharedEd25519PublicKey>(Signer::address_of(account));
+aborts_if len(Authenticator::spec_ed25519_authentication_key(key)) != 32;
+
+ + + +
+ + + +## Function `rotate_key_` + + + +
fun rotate_key_(shared_key: &mut SharedEd25519PublicKey::SharedEd25519PublicKey, new_public_key: vector<u8>)
+
+ + + +
+Implementation + + +
fun rotate_key_(shared_key: &mut SharedEd25519PublicKey, new_public_key: vector<u8>) {
+    // Cryptographic check of public key validity
+    assert!(
+        Signature::ed25519_validate_pubkey(copy new_public_key),
+        Errors::invalid_argument(EMALFORMED_PUBLIC_KEY)
+    );
+    Account::rotate_authentication_key_with_capability(
+        &shared_key.rotation_cap,
+        Authenticator::ed25519_authentication_key(copy new_public_key)
+    );
+    shared_key.key = new_public_key;
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Account::Account>(shared_key.rotation_cap.account_address);
+aborts_if !Signature::ed25519_validate_pubkey(new_public_key);
+aborts_if len(Authenticator::spec_ed25519_authentication_key(new_public_key)) != 32;
+
+ + + +
+ + + +## Function `rotate_key` + +(1) rotate the public key stored account's SharedEd25519PublicKey resource to +new_public_key +(2) rotate the authentication key using the capability stored in the account's +SharedEd25519PublicKey to a new value derived from new_public_key +Aborts if the sender does not have a SharedEd25519PublicKey resource. +Aborts if the length of new_public_key is not 32. + + +
public fun rotate_key(account: &signer, new_public_key: vector<u8>)
+
+ + + +
+Implementation + + +
public fun rotate_key(account: &signer, new_public_key: vector<u8>) acquires SharedEd25519PublicKey {
+    rotate_key_(borrow_global_mut<SharedEd25519PublicKey>(Signer::address_of(account)), new_public_key);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<SharedEd25519PublicKey>(Signer::address_of(account));
+aborts_if !exists<Account::Account>(global<SharedEd25519PublicKey>(Signer::address_of(account)).rotation_cap.account_address);
+aborts_if !Signature::ed25519_validate_pubkey(new_public_key);
+aborts_if len(Authenticator::spec_ed25519_authentication_key(new_public_key)) != 32;
+
+ + + +
+ + + +## Function `key` + +Return the public key stored under addr. +Aborts if addr does not hold a SharedEd25519PublicKey resource. + + +
public fun key(addr: address): vector<u8>
+
+ + + +
+Implementation + + +
public fun key(addr: address): vector<u8> acquires SharedEd25519PublicKey {
+    *&borrow_global<SharedEd25519PublicKey>(addr).key
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<SharedEd25519PublicKey>(addr);
+
+ + + +
+ + + +## Function `exists_at` + +Returns true if addr holds a SharedEd25519PublicKey resource. + + +
public fun exists_at(addr: address): bool
+
+ + + +
+Implementation + + +
public fun exists_at(addr: address): bool {
+    exists<SharedEd25519PublicKey>(addr)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Signature.md b/release/v13/docs/Signature.md new file mode 100644 index 00000000..e819d8cf --- /dev/null +++ b/release/v13/docs/Signature.md @@ -0,0 +1,154 @@ + + + +# Module `0x1::Signature` + +Contains functions for [ed25519](https://en.wikipedia.org/wiki/EdDSA) digital signatures. + + +- [Function `ed25519_validate_pubkey`](#0x1_Signature_ed25519_validate_pubkey) +- [Function `ed25519_verify`](#0x1_Signature_ed25519_verify) +- [Function `native_ecrecover`](#0x1_Signature_native_ecrecover) +- [Function `ecrecover`](#0x1_Signature_ecrecover) +- [Function `secp256k1_verify`](#0x1_Signature_secp256k1_verify) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::EVMAddress;
+use 0x1::Option;
+use 0x1::Vector;
+
+ + + + + +## Function `ed25519_validate_pubkey` + + + +
public fun ed25519_validate_pubkey(public_key: vector<u8>): bool
+
+ + + +
+Implementation + + +
native public fun ed25519_validate_pubkey(public_key: vector<u8>): bool;
+
+ + + +
+ + + +## Function `ed25519_verify` + + + +
public fun ed25519_verify(signature: vector<u8>, public_key: vector<u8>, message: vector<u8>): bool
+
+ + + +
+Implementation + + +
native public fun ed25519_verify(signature: vector<u8>, public_key: vector<u8>, message: vector<u8>): bool;
+
+ + + +
+ + + +## Function `native_ecrecover` + +recover address from ECDSA signature, if recover fail, return an empty vector + + +
fun native_ecrecover(hash: vector<u8>, signature: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
native fun native_ecrecover(hash: vector<u8>, signature: vector<u8>): vector<u8>;
+
+ + + +
+ + + +## Function `ecrecover` + +recover address from ECDSA signature, if recover fail, return None + + +
public fun ecrecover(hash: vector<u8>, signature: vector<u8>): Option::Option<EVMAddress::EVMAddress>
+
+ + + +
+Implementation + + +
public fun ecrecover(hash: vector<u8>, signature: vector<u8>):Option<EVMAddress>{
+    let bytes = native_ecrecover(hash, signature);
+    if (Vector::is_empty(&bytes)){
+        Option::none<EVMAddress>()
+    }else{
+        Option::some(EVMAddress::new(bytes))
+    }
+}
+
+ + + +
+ + + +## Function `secp256k1_verify` + + + +
public fun secp256k1_verify(signature: vector<u8>, addr: vector<u8>, message: vector<u8>): bool
+
+ + + +
+Implementation + + +
public fun secp256k1_verify(signature: vector<u8>, addr: vector<u8>, message: vector<u8>) : bool{
+  let receover_address_opt:Option<EVMAddress>  = ecrecover(message, signature);
+  let expect_address =  EVMAddress::new(addr);
+  &Option::destroy_some<EVMAddress>(receover_address_opt) == &expect_address
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma intrinsic = true;
+
diff --git a/release/v13/docs/SignedInteger64.md b/release/v13/docs/SignedInteger64.md new file mode 100644 index 00000000..5d4c8fc2 --- /dev/null +++ b/release/v13/docs/SignedInteger64.md @@ -0,0 +1,352 @@ + + + +# Module `0x1::SignedInteger64` + +Implementation of i64. + + +- [Struct `SignedInteger64`](#0x1_SignedInteger64_SignedInteger64) +- [Function `multiply_u64`](#0x1_SignedInteger64_multiply_u64) +- [Function `divide_u64`](#0x1_SignedInteger64_divide_u64) +- [Function `sub_u64`](#0x1_SignedInteger64_sub_u64) +- [Function `add_u64`](#0x1_SignedInteger64_add_u64) +- [Function `create_from_raw_value`](#0x1_SignedInteger64_create_from_raw_value) +- [Function `get_value`](#0x1_SignedInteger64_get_value) +- [Function `is_negative`](#0x1_SignedInteger64_is_negative) +- [Module Specification](#@Module_Specification_0) + + +
+ + + + + +## Struct `SignedInteger64` + +Define a signed integer type with two 32 bits. + + +
struct SignedInteger64 has copy, drop, store
+
+ + + +
+Fields + + +
+
+value: u64 +
+
+ +
+
+is_negative: bool +
+
+ +
+
+ + +
+ + + +## Function `multiply_u64` + +Multiply a u64 integer by a signed integer number. + + +
public fun multiply_u64(num: u64, multiplier: SignedInteger64::SignedInteger64): SignedInteger64::SignedInteger64
+
+ + + +
+Implementation + + +
public fun multiply_u64(num: u64, multiplier: SignedInteger64): SignedInteger64 {
+    let product = multiplier.value * num;
+    SignedInteger64 { value: product, is_negative: multiplier.is_negative }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if multiplier.value * num > max_u64();
+
+ + + +
+ + + +## Function `divide_u64` + +Divide a u64 integer by a signed integer number. + + +
public fun divide_u64(num: u64, divisor: SignedInteger64::SignedInteger64): SignedInteger64::SignedInteger64
+
+ + + +
+Implementation + + +
public fun divide_u64(num: u64, divisor: SignedInteger64): SignedInteger64 {
+    let quotient = num / divisor.value;
+    SignedInteger64 { value: quotient, is_negative: divisor.is_negative }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if divisor.value == 0;
+
+ + + +
+ + + +## Function `sub_u64` + +Sub: num - minus + + +
public fun sub_u64(num: u64, minus: SignedInteger64::SignedInteger64): SignedInteger64::SignedInteger64
+
+ + + +
+Implementation + + +
public fun sub_u64(num: u64, minus: SignedInteger64): SignedInteger64 {
+    if (minus.is_negative) {
+        let result = num + minus.value;
+        SignedInteger64 { value: result, is_negative: false }
+    } else {
+        if (num >= minus.value) {
+            let result = num - minus.value;
+            SignedInteger64 { value: result, is_negative: false }
+        }else {
+            let result = minus.value - num;
+            SignedInteger64 { value: result, is_negative: true }
+        }
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if minus.is_negative && num + minus.value > max_u64();
+
+ + + +
+ + + +## Function `add_u64` + +Add: num + addend + + +
public fun add_u64(num: u64, addend: SignedInteger64::SignedInteger64): SignedInteger64::SignedInteger64
+
+ + + +
+Implementation + + +
public fun add_u64(num: u64, addend: SignedInteger64): SignedInteger64 {
+    if (addend.is_negative) {
+        if (num >= addend.value) {
+            let result = num - addend.value;
+            SignedInteger64 { value: result, is_negative: false }
+        }else {
+            let result = addend.value - num;
+            SignedInteger64 { value: result, is_negative: true }
+        }
+    } else {
+        let result = num + addend.value;
+        SignedInteger64 { value: result, is_negative: false }
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !addend.is_negative && num + addend.value > max_u64();
+
+ + + +
+ + + +## Function `create_from_raw_value` + +Create a signed integer value from a unsigned integer + + +
public fun create_from_raw_value(value: u64, is_negative: bool): SignedInteger64::SignedInteger64
+
+ + + +
+Implementation + + +
public fun create_from_raw_value(value: u64, is_negative: bool): SignedInteger64 {
+    SignedInteger64 { value, is_negative }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == SignedInteger64 { value, is_negative };
+
+ + + +
+ + + +## Function `get_value` + +Get value part of i64 ignore sign part. + + +
public fun get_value(num: SignedInteger64::SignedInteger64): u64
+
+ + + +
+Implementation + + +
public fun get_value(num: SignedInteger64): u64 {
+    num.value
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == num.value;
+
+ + + +
+ + + +## Function `is_negative` + +Check if the given num is negative. + + +
public fun is_negative(num: SignedInteger64::SignedInteger64): bool
+
+ + + +
+Implementation + + +
public fun is_negative(num: SignedInteger64): bool {
+    num.is_negative
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == num.is_negative;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Signer.md b/release/v13/docs/Signer.md new file mode 100644 index 00000000..8f0f1d8c --- /dev/null +++ b/release/v13/docs/Signer.md @@ -0,0 +1,94 @@ + + + +# Module `0x1::Signer` + +Provide access methods for Signer. + + +- [Function `borrow_address`](#0x1_Signer_borrow_address) +- [Function `address_of`](#0x1_Signer_address_of) +- [Module Specification](#@Module_Specification_0) + + +
+ + + + + +## Function `borrow_address` + +Borrows the address of the signer +Conceptually, you can think of the signer as being a resource struct wrapper around an +address +``` +resource struct Signer has key, store { addr: address } +``` +borrow_address borrows this inner field + + +
public fun borrow_address(s: &signer): &address
+
+ + + +
+Implementation + + +
native public fun borrow_address(s: &signer): &address;
+
+ + + +
+ + + +## Function `address_of` + +Copies the address of the signer + + +
public fun address_of(s: &signer): address
+
+ + + +
+Implementation + + +
public fun address_of(s: &signer): address {
+    *borrow_address(s)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures result == address_of(s);
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/SimpleMap.md b/release/v13/docs/SimpleMap.md new file mode 100644 index 00000000..7e47f32f --- /dev/null +++ b/release/v13/docs/SimpleMap.md @@ -0,0 +1,543 @@ + + + +# Module `0x1::SimpleMap` + +This module provides a solution for sorted maps, that is it has the properties that +1) Keys point to Values +2) Each Key must be unique +3) A Key can be found within O(N) time +4) The keys are unsorted. +5) Adds and removals take O(N) time + + +- [Struct `SimpleMap`](#0x1_SimpleMap_SimpleMap) +- [Struct `Element`](#0x1_SimpleMap_Element) +- [Constants](#@Constants_0) +- [Function `length`](#0x1_SimpleMap_length) +- [Function `create`](#0x1_SimpleMap_create) +- [Function `borrow`](#0x1_SimpleMap_borrow) +- [Function `borrow_mut`](#0x1_SimpleMap_borrow_mut) +- [Function `contains_key`](#0x1_SimpleMap_contains_key) +- [Function `destroy_empty`](#0x1_SimpleMap_destroy_empty) +- [Function `add`](#0x1_SimpleMap_add) +- [Function `upsert`](#0x1_SimpleMap_upsert) +- [Function `remove`](#0x1_SimpleMap_remove) +- [Function `find`](#0x1_SimpleMap_find) + + +
use 0x1::Errors;
+use 0x1::Option;
+use 0x1::Vector;
+
+ + + + + +## Struct `SimpleMap` + + + +
struct SimpleMap<Key, Value> has copy, drop, store
+
+ + + +
+Fields + + +
+
+data: vector<SimpleMap::Element<Key, Value>> +
+
+ +
+
+ + +
+ + + +## Struct `Element` + + + +
struct Element<Key, Value> has copy, drop, store
+
+ + + +
+Fields + + +
+
+key: Key +
+
+ +
+
+value: Value +
+
+ +
+
+ + +
+ + + +## Constants + + + + +Map key already exists + + +
const EKEY_ALREADY_EXISTS: u64 = 1;
+
+ + + + + +Map key is not found + + +
const EKEY_NOT_FOUND: u64 = 2;
+
+ + + + + +## Function `length` + + + +
public fun length<Key: store, Value: store>(map: &SimpleMap::SimpleMap<Key, Value>): u64
+
+ + + +
+Implementation + + +
public fun length<Key: store, Value: store>(map: &SimpleMap<Key, Value>): u64 {
+    Vector::length(&map.data)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `create` + + + +
public fun create<Key: store, Value: store>(): SimpleMap::SimpleMap<Key, Value>
+
+ + + +
+Implementation + + +
public fun create<Key: store, Value: store>(): SimpleMap<Key, Value> {
+    SimpleMap {
+        data: Vector::empty(),
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `borrow` + + + +
public fun borrow<Key: store, Value: store>(map: &SimpleMap::SimpleMap<Key, Value>, key: &Key): &Value
+
+ + + +
+Implementation + + +
public fun borrow<Key: store, Value: store>(
+    map: &SimpleMap<Key, Value>,
+    key: &Key,
+): &Value {
+    let maybe_idx = find(map, key);
+    assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND));
+    let idx = Option::extract(&mut maybe_idx);
+    &Vector::borrow(&map.data, idx).value
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `borrow_mut` + + + +
public fun borrow_mut<Key: store, Value: store>(map: &mut SimpleMap::SimpleMap<Key, Value>, key: &Key): &mut Value
+
+ + + +
+Implementation + + +
public fun borrow_mut<Key: store, Value: store>(
+    map: &mut SimpleMap<Key, Value>,
+    key: &Key,
+): &mut Value {
+    let maybe_idx = find(map, key);
+    assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND));
+    let idx = Option::extract(&mut maybe_idx);
+    &mut Vector::borrow_mut(&mut map.data, idx).value
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `contains_key` + + + +
public fun contains_key<Key: store, Value: store>(map: &SimpleMap::SimpleMap<Key, Value>, key: &Key): bool
+
+ + + +
+Implementation + + +
public fun contains_key<Key: store, Value: store>(
+    map: &SimpleMap<Key, Value>,
+    key: &Key,
+): bool {
+    let maybe_idx = find(map, key);
+    Option::is_some(&maybe_idx)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `destroy_empty` + + + +
public fun destroy_empty<Key: store, Value: store>(map: SimpleMap::SimpleMap<Key, Value>)
+
+ + + +
+Implementation + + +
public fun destroy_empty<Key: store, Value: store>(map: SimpleMap<Key, Value>) {
+    let SimpleMap { data } = map;
+    Vector::destroy_empty(data);
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `add` + + + +
public fun add<Key: store, Value: store>(map: &mut SimpleMap::SimpleMap<Key, Value>, key: Key, value: Value)
+
+ + + +
+Implementation + + +
public fun add<Key: store, Value: store>(
+    map: &mut SimpleMap<Key, Value>,
+    key: Key,
+    value: Value,
+) {
+    let maybe_idx = find(map, &key);
+    assert!(Option::is_none(&maybe_idx), Errors::invalid_argument(EKEY_ALREADY_EXISTS));
+
+    Vector::push_back(&mut map.data, Element { key, value });
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `upsert` + +Insert key/value pair or update an existing key to a new value + + +
public fun upsert<Key: store, Value: store>(map: &mut SimpleMap::SimpleMap<Key, Value>, key: Key, value: Value): (Option::Option<Key>, Option::Option<Value>)
+
+ + + +
+Implementation + + +
public fun upsert<Key: store, Value: store>(
+    map: &mut SimpleMap<Key, Value>,
+    key: Key,
+    value: Value
+): (Option::Option<Key>, Option::Option<Value>) {
+    let data = &mut map.data;
+    let len = Vector::length(data);
+    let i = 0;
+    while (i < len) {
+        let element = Vector::borrow(data, i);
+        if (&element.key == &key) {
+            Vector::push_back(data, Element { key, value });
+            Vector::swap(data, i, len);
+            let Element { key, value } = Vector::pop_back(data);
+            return (Option::some(key), Option::some(value))
+        };
+        i = i + 1;
+    };
+    Vector::push_back(&mut map.data, Element { key, value });
+    (Option::none(), Option::none())
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify=false;
+
+ + + +
+ + + +## Function `remove` + + + +
public fun remove<Key: store, Value: store>(map: &mut SimpleMap::SimpleMap<Key, Value>, key: &Key): (Key, Value)
+
+ + + +
+Implementation + + +
public fun remove<Key: store, Value: store>(
+    map: &mut SimpleMap<Key, Value>,
+    key: &Key,
+): (Key, Value) {
+    let maybe_idx = find(map, key);
+    assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND));
+    let placement = Option::extract(&mut maybe_idx);
+    let Element { key, value } = Vector::swap_remove(&mut map.data, placement);
+    (key, value)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `find` + + + +
fun find<Key: store, Value: store>(map: &SimpleMap::SimpleMap<Key, Value>, key: &Key): Option::Option<u64>
+
+ + + +
+Implementation + + +
fun find<Key: store, Value: store>(
+    map: &SimpleMap<Key, Value>,
+    key: &Key,
+): Option::Option<u64> {
+    let leng = Vector::length(&map.data);
+    let i = 0;
+    while (i < leng) {
+        let element = Vector::borrow(&map.data, i);
+        if (&element.key == key) {
+            return Option::some(i)
+        };
+        i = i + 1;
+    };
+    Option::none<u64>()
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify=false;
+
+ + + +
diff --git a/release/v13/docs/StarcoinVerifier.md b/release/v13/docs/StarcoinVerifier.md new file mode 100644 index 00000000..cb7c6a28 --- /dev/null +++ b/release/v13/docs/StarcoinVerifier.md @@ -0,0 +1,715 @@ + + + +# Module `0x1::StarcoinVerifier` + + + +- [Struct `AccountState`](#0x1_StarcoinVerifier_AccountState) +- [Struct `StateProof`](#0x1_StarcoinVerifier_StateProof) +- [Struct `SparseMerkleProof`](#0x1_StarcoinVerifier_SparseMerkleProof) +- [Struct `SMTNode`](#0x1_StarcoinVerifier_SMTNode) +- [Constants](#@Constants_0) +- [Function `bcs_deserialize_account_state`](#0x1_StarcoinVerifier_bcs_deserialize_account_state) +- [Function `new_state_proof`](#0x1_StarcoinVerifier_new_state_proof) +- [Function `new_sparse_merkle_proof`](#0x1_StarcoinVerifier_new_sparse_merkle_proof) +- [Function `new_smt_node`](#0x1_StarcoinVerifier_new_smt_node) +- [Function `empty_smt_node`](#0x1_StarcoinVerifier_empty_smt_node) +- [Function `verify_state_proof`](#0x1_StarcoinVerifier_verify_state_proof) +- [Function `verify_smp`](#0x1_StarcoinVerifier_verify_smp) +- [Function `compute_smp_root_by_path_and_node_hash`](#0x1_StarcoinVerifier_compute_smp_root_by_path_and_node_hash) +- [Function `placeholder`](#0x1_StarcoinVerifier_placeholder) +- [Function `create_literal_hash`](#0x1_StarcoinVerifier_create_literal_hash) +- [Function `hash_key`](#0x1_StarcoinVerifier_hash_key) +- [Function `hash_value`](#0x1_StarcoinVerifier_hash_value) +- [Function `count_common_prefix`](#0x1_StarcoinVerifier_count_common_prefix) +- [Function `get_bit_at_from_msb`](#0x1_StarcoinVerifier_get_bit_at_from_msb) + + +
use 0x1::BCS;
+use 0x1::Hash;
+use 0x1::Option;
+use 0x1::StructuredHash;
+
+ + + + + +## Struct `AccountState` + + + +
struct AccountState has copy, drop, store
+
+ + + +
+Fields + + +
+
+storage_roots: vector<Option::Option<vector<u8>>> +
+
+ +
+
+ + +
+ + + +## Struct `StateProof` + + + +
struct StateProof has copy, drop, store
+
+ + + +
+Fields + + +
+
+account_proof: StarcoinVerifier::SparseMerkleProof +
+
+ + * Account state's proof for global state root. + +
+
+account_state: vector<u8> +
+
+ + * Account state including storage roots. + +
+
+proof: StarcoinVerifier::SparseMerkleProof +
+
+ + * State's proof for account storage root. + +
+
+ + +
+ + + +## Struct `SparseMerkleProof` + + + +
struct SparseMerkleProof has copy, drop, store
+
+ + + +
+Fields + + +
+
+siblings: vector<vector<u8>> +
+
+ +
+
+leaf: StarcoinVerifier::SMTNode +
+
+ +
+
+ + +
+ + + +## Struct `SMTNode` + + + +
struct SMTNode has copy, drop, store
+
+ + + +
+Fields + + +
+
+hash1: vector<u8> +
+
+ +
+
+hash2: vector<u8> +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ACCOUNT_STORAGE_INDEX_RESOURCE: u64 = 1;
+
+ + + + + + + +
const BLOB_HASH_PREFIX: vector<u8> = [66, 108, 111, 98];
+
+ + + + + + + +
const DEFAULT_VALUE: vector<u8> = [];
+
+ + + + + + + +
const ERROR_ACCOUNT_STORAGE_ROOTS: u64 = 101;
+
+ + + + + + + +
const ERROR_LITERAL_HASH_WRONG_LENGTH: u64 = 102;
+
+ + + + + + + +
const HASH_LEN_IN_BITS: u64 = 256;
+
+ + + + + + + +
const SPARSE_MERKLE_INTERNAL_NODE: vector<u8> = [83, 112, 97, 114, 115, 101, 77, 101, 114, 107, 108, 101, 73, 110, 116, 101, 114, 110, 97, 108, 78, 111, 100, 101];
+
+ + + + + + + +
const SPARSE_MERKLE_LEAF_NODE: vector<u8> = [83, 112, 97, 114, 115, 101, 77, 101, 114, 107, 108, 101, 76, 101, 97, 102, 78, 111, 100, 101];
+
+ + + + + + + +
const SPARSE_MERKLE_PLACEHOLDER_HASH_LITERAL: vector<u8> = [83, 80, 65, 82, 83, 69, 95, 77, 69, 82, 75, 76, 69, 95, 80, 76, 65, 67, 69, 72, 79, 76, 68, 69, 82, 95, 72, 65, 83, 72];
+
+ + + + + +## Function `bcs_deserialize_account_state` + + + +
public fun bcs_deserialize_account_state(data: &vector<u8>): StarcoinVerifier::AccountState
+
+ + + +
+Implementation + + +
public fun bcs_deserialize_account_state(data: &vector<u8>): AccountState {
+    let (vec, _) = BCS::deserialize_option_bytes_vector(data, 0);
+    AccountState{
+        storage_roots: vec
+    }
+}
+
+ + + +
+ + + +## Function `new_state_proof` + + + +
public fun new_state_proof(account_proof: StarcoinVerifier::SparseMerkleProof, account_state: vector<u8>, proof: StarcoinVerifier::SparseMerkleProof): StarcoinVerifier::StateProof
+
+ + + +
+Implementation + + +
public fun new_state_proof(account_proof: SparseMerkleProof, account_state: vector<u8>, proof: SparseMerkleProof): StateProof {
+    StateProof{
+        account_proof,
+        account_state,
+        proof,
+    }
+}
+
+ + + +
+ + + +## Function `new_sparse_merkle_proof` + + + +
public fun new_sparse_merkle_proof(siblings: vector<vector<u8>>, leaf: StarcoinVerifier::SMTNode): StarcoinVerifier::SparseMerkleProof
+
+ + + +
+Implementation + + +
public fun new_sparse_merkle_proof(siblings: vector<vector<u8>>, leaf: SMTNode): SparseMerkleProof {
+    SparseMerkleProof{
+        siblings,
+        leaf,
+    }
+}
+
+ + + +
+ + + +## Function `new_smt_node` + + + +
public fun new_smt_node(hash1: vector<u8>, hash2: vector<u8>): StarcoinVerifier::SMTNode
+
+ + + +
+Implementation + + +
public fun new_smt_node(hash1: vector<u8>, hash2: vector<u8>): SMTNode {
+    SMTNode{
+        hash1,
+        hash2,
+    }
+}
+
+ + + +
+ + + +## Function `empty_smt_node` + + + +
public fun empty_smt_node(): StarcoinVerifier::SMTNode
+
+ + + +
+Implementation + + +
public fun empty_smt_node(): SMTNode {
+    SMTNode{
+        hash1: Vector::empty(),
+        hash2: Vector::empty(),
+    }
+}
+
+ + + +
+ + + +## Function `verify_state_proof` + + + +
public fun verify_state_proof(state_proof: &StarcoinVerifier::StateProof, state_root: &vector<u8>, account_address: address, resource_struct_tag: &vector<u8>, state: &vector<u8>): bool
+
+ + + +
+Implementation + + +
public fun verify_state_proof(state_proof: &StateProof, state_root: &vector<u8>,
+                                       account_address: address, resource_struct_tag: &vector<u8>,
+                                       state: &vector<u8>): bool {
+    let accountState: AccountState = bcs_deserialize_account_state(&state_proof.account_state);
+    assert!(Vector::length(&accountState.storage_roots) > ACCOUNT_STORAGE_INDEX_RESOURCE, ERROR_ACCOUNT_STORAGE_ROOTS);
+
+    // First, verify state for storage root.
+    let storageRoot = Option::borrow(Vector::borrow(&accountState.storage_roots, ACCOUNT_STORAGE_INDEX_RESOURCE));
+    let ok: bool = verify_smp(&state_proof.proof.siblings,
+        &state_proof.proof.leaf,
+        storageRoot,
+        resource_struct_tag, // resource struct tag BCS serialized as key
+        state);
+    if (!ok) {
+        return false
+    };
+
+    // Then, verify account state for global state root.
+    ok = verify_smp(&state_proof.account_proof.siblings,
+        &state_proof.account_proof.leaf,
+        state_root,
+        &BCS::to_bytes<address>(&account_address), // account address as key
+        &state_proof.account_state,
+    );
+    ok
+}
+
+ + + +
+ + + +## Function `verify_smp` + +Verify sparse merkle proof by key and value. + + +
public fun verify_smp(sibling_nodes: &vector<vector<u8>>, leaf_data: &StarcoinVerifier::SMTNode, expected_root: &vector<u8>, key: &vector<u8>, value: &vector<u8>): bool
+
+ + + +
+Implementation + + +
public fun verify_smp(sibling_nodes: &vector<vector<u8>>, leaf_data: &SMTNode, expected_root: &vector<u8>, key: &vector<u8>, value: &vector<u8>): bool {
+    let path = hash_key(key);
+    let current_hash: vector<u8>;
+    if (*value == DEFAULT_VALUE) {
+        // Non-membership proof.
+        if (empty_smt_node() == *leaf_data) {
+            current_hash = placeholder();
+        } else {
+            if (*&leaf_data.hash1 == *&path) {
+                return false
+            };
+            if (!(count_common_prefix(&leaf_data.hash1, &path) >= Vector::length(sibling_nodes))) {
+                return false
+            };
+            current_hash = StructuredHash::hash(SPARSE_MERKLE_LEAF_NODE, leaf_data);
+        };
+    } else {
+        // Membership proof.
+        if (empty_smt_node() == *leaf_data) {
+            return false
+        };
+        if (*&leaf_data.hash1 != *&path) {
+            return false
+        };
+        let value_hash = hash_value(value);
+        if (*&leaf_data.hash2 != value_hash) {
+            return false
+        };
+        current_hash = StructuredHash::hash(SPARSE_MERKLE_LEAF_NODE, leaf_data);
+    };
+
+    current_hash = compute_smp_root_by_path_and_node_hash(sibling_nodes, &path, ¤t_hash);
+    current_hash == *expected_root
+}
+
+ + + +
+ + + +## Function `compute_smp_root_by_path_and_node_hash` + + + +
public fun compute_smp_root_by_path_and_node_hash(sibling_nodes: &vector<vector<u8>>, path: &vector<u8>, node_hash: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun compute_smp_root_by_path_and_node_hash(sibling_nodes: &vector<vector<u8>>, path: &vector<u8>, node_hash: &vector<u8>): vector<u8> {
+    let current_hash = *node_hash;
+    let i = 0;
+    let proof_length = Vector::length(sibling_nodes);
+    while (i < proof_length) {
+        let sibling = *Vector::borrow(sibling_nodes, i);
+        let bit = get_bit_at_from_msb(path, proof_length - i - 1);
+        let internal_node = if (bit) {
+            SMTNode{ hash1: sibling, hash2: current_hash }
+        } else {
+            SMTNode{ hash1: current_hash, hash2: sibling }
+        };
+        current_hash = StructuredHash::hash(SPARSE_MERKLE_INTERNAL_NODE, &internal_node);
+        i = i + 1;
+    };
+    current_hash
+}
+
+ + + +
+ + + +## Function `placeholder` + + + +
public fun placeholder(): vector<u8>
+
+ + + +
+Implementation + + +
public fun placeholder(): vector<u8> {
+    create_literal_hash(&SPARSE_MERKLE_PLACEHOLDER_HASH_LITERAL)
+}
+
+ + + +
+ + + +## Function `create_literal_hash` + + + +
public fun create_literal_hash(word: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun create_literal_hash(word: &vector<u8>): vector<u8> {
+    if (Vector::length(word)  <= 32) {
+        let lenZero = 32 - Vector::length(word);
+        let i = 0;
+        let r = *word;
+        while (i < lenZero) {
+            Vector::push_back(&mut r, 0);
+            i = i + 1;
+        };
+        return r
+    };
+    abort ERROR_LITERAL_HASH_WRONG_LENGTH
+}
+
+ + + +
+ + + +## Function `hash_key` + + + +
fun hash_key(key: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
fun hash_key(key: &vector<u8>): vector<u8> {
+    Hash::sha3_256(*key)
+}
+
+ + + +
+ + + +## Function `hash_value` + + + +
fun hash_value(value: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
fun hash_value(value: &vector<u8>): vector<u8> {
+    StructuredHash::hash(BLOB_HASH_PREFIX, value)
+}
+
+ + + +
+ + + +## Function `count_common_prefix` + + + +
fun count_common_prefix(data1: &vector<u8>, data2: &vector<u8>): u64
+
+ + + +
+Implementation + + +
fun count_common_prefix(data1: &vector<u8>, data2: &vector<u8>): u64 {
+    let count = 0;
+    let i = 0;
+    while ( i < Vector::length(data1) * 8) {
+        if (get_bit_at_from_msb(data1, i) == get_bit_at_from_msb(data2, i)) {
+            count = count + 1;
+        } else {
+            break
+        };
+        i = i + 1;
+    };
+    count
+}
+
+ + + +
+ + + +## Function `get_bit_at_from_msb` + + + +
fun get_bit_at_from_msb(data: &vector<u8>, index: u64): bool
+
+ + + +
+Implementation + + +
fun get_bit_at_from_msb(data: &vector<u8>, index: u64): bool {
+    let pos = index / 8;
+    let bit = (7 - index % 8);
+    (*Vector::borrow(data, pos) >> (bit as u8)) & 1u8 != 0
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma opaque;
+
+ + + +
diff --git a/release/v13/docs/StdlibUpgradeScripts.md b/release/v13/docs/StdlibUpgradeScripts.md new file mode 100644 index 00000000..0a8950dd --- /dev/null +++ b/release/v13/docs/StdlibUpgradeScripts.md @@ -0,0 +1,422 @@ + + + +# Module `0x1::StdlibUpgradeScripts` + +The module for StdlibUpgrade init scripts + + +- [Function `upgrade_from_v2_to_v3`](#0x1_StdlibUpgradeScripts_upgrade_from_v2_to_v3) +- [Function `take_linear_withdraw_capability`](#0x1_StdlibUpgradeScripts_take_linear_withdraw_capability) +- [Function `do_upgrade_from_v5_to_v6`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v5_to_v6) +- [Function `upgrade_from_v5_to_v6`](#0x1_StdlibUpgradeScripts_upgrade_from_v5_to_v6) +- [Function `upgrade_from_v6_to_v7`](#0x1_StdlibUpgradeScripts_upgrade_from_v6_to_v7) +- [Function `do_upgrade_from_v6_to_v7`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v6_to_v7) +- [Function `do_upgrade_from_v6_to_v7_with_language_version`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v6_to_v7_with_language_version) +- [Function `upgrade_from_v7_to_v8`](#0x1_StdlibUpgradeScripts_upgrade_from_v7_to_v8) +- [Function `do_upgrade_from_v7_to_v8`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v7_to_v8) +- [Function `upgrade_from_v11_to_v12`](#0x1_StdlibUpgradeScripts_upgrade_from_v11_to_v12) +- [Function `do_upgrade_from_v11_to_v12`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v11_to_v12) +- [Function `upgrade_from_v12_to_v13`](#0x1_StdlibUpgradeScripts_upgrade_from_v12_to_v13) +- [Function `do_upgrade_from_v12_to_v13`](#0x1_StdlibUpgradeScripts_do_upgrade_from_v12_to_v13) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::Account;
+use 0x1::Block;
+use 0x1::Collection;
+use 0x1::Config;
+use 0x1::CoreAddresses;
+use 0x1::EasyGas;
+use 0x1::FlexiDagConfig;
+use 0x1::GasSchedule;
+use 0x1::GenesisNFT;
+use 0x1::GenesisSignerCapability;
+use 0x1::LanguageVersion;
+use 0x1::Math;
+use 0x1::NFT;
+use 0x1::Offer;
+use 0x1::OnChainConfigDao;
+use 0x1::Oracle;
+use 0x1::STC;
+use 0x1::STCUSDOracle;
+use 0x1::Timestamp;
+use 0x1::Token;
+use 0x1::Treasury;
+use 0x1::TreasuryWithdrawDaoProposal;
+
+ + + + + +## Function `upgrade_from_v2_to_v3` + +Stdlib upgrade script from v2 to v3 + + +
public entry fun upgrade_from_v2_to_v3(account: signer, total_stc_amount: u128)
+
+ + + +
+Implementation + + +
public entry fun upgrade_from_v2_to_v3(account: signer, total_stc_amount: u128 ) {
+    CoreAddresses::assert_genesis_address(&account);
+
+    let withdraw_cap = STC::upgrade_from_v1_to_v2(&account, total_stc_amount);
+
+    let mint_keys = Collection::borrow_collection<LinearTimeMintKey<STC>>(CoreAddresses::ASSOCIATION_ROOT_ADDRESS());
+    let mint_key = Collection::borrow(&mint_keys, 0);
+    let (total, minted, start_time, period) = Token::read_linear_time_key(mint_key);
+    Collection::return_collection(mint_keys);
+
+    let now = Timestamp::now_seconds();
+    let linear_withdraw_cap = Treasury::issue_linear_withdraw_capability(&mut withdraw_cap, total-minted, period - (now - start_time));
+    // Lock the TreasuryWithdrawCapability to Dao
+    TreasuryWithdrawDaoProposal::plugin(&account, withdraw_cap);
+    // Give a LinearWithdrawCapability Offer to association, association need to take the offer, and destroy old LinearTimeMintKey.
+    Offer::create(&account, linear_withdraw_cap, CoreAddresses::ASSOCIATION_ROOT_ADDRESS(), 0);
+}
+
+ + + +
+ + + +## Function `take_linear_withdraw_capability` + +association account should call this script after upgrade from v2 to v3. + + +
public entry fun take_linear_withdraw_capability(signer: signer)
+
+ + + +
+Implementation + + +
public entry fun take_linear_withdraw_capability(signer: signer){
+    let offered = Offer::redeem<LinearWithdrawCapability<STC>>(&signer, CoreAddresses::GENESIS_ADDRESS());
+    Treasury::add_linear_withdraw_capability(&signer, offered);
+    let mint_key = Collection::take<LinearTimeMintKey<STC>>(&signer);
+    Token::destroy_linear_time_key(mint_key);
+}
+
+ + + +
+ + + +## Function `do_upgrade_from_v5_to_v6` + + + +
public fun do_upgrade_from_v5_to_v6(sender: &signer)
+
+ + + +
+Implementation + + +
public fun do_upgrade_from_v5_to_v6(sender: &signer) {
+    CoreAddresses::assert_genesis_address(sender);
+    Oracle::initialize(sender);
+    //register oracle
+    STCUSDOracle::register(sender);
+    NFT::initialize(sender);
+    let merkle_root = x"5969f0e8e19f8769276fb638e6060d5c02e40088f5fde70a6778dd69d659ee6d";
+    let image = b"ipfs://QmSPcvcXgdtHHiVTAAarzTeubk5X3iWymPAoKBfiRFjPMY";
+    GenesisNFT::initialize(sender, merkle_root, 1639u64, image);
+}
+
+ + + +
+ + + +## Function `upgrade_from_v5_to_v6` + + + +
public entry fun upgrade_from_v5_to_v6(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun upgrade_from_v5_to_v6(sender: signer) {
+   Self::do_upgrade_from_v5_to_v6(&sender)
+}
+
+ + + +
+ + + +## Function `upgrade_from_v6_to_v7` + + + +
public entry fun upgrade_from_v6_to_v7(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun upgrade_from_v6_to_v7(sender: signer) {
+    Self::do_upgrade_from_v6_to_v7_with_language_version(&sender, 2);
+}
+
+ + + +
+ + + +## Function `do_upgrade_from_v6_to_v7` + +deprecated, use do_upgrade_from_v6_to_v7_with_language_version. + + +
public fun do_upgrade_from_v6_to_v7(sender: &signer)
+
+ + + +
+Implementation + + +
public fun do_upgrade_from_v6_to_v7(sender: &signer) {
+   do_upgrade_from_v6_to_v7_with_language_version(sender, 2);
+}
+
+ + + +
+ + + +## Function `do_upgrade_from_v6_to_v7_with_language_version` + + + +
public fun do_upgrade_from_v6_to_v7_with_language_version(sender: &signer, language_version: u64)
+
+ + + +
+Implementation + + +
public fun do_upgrade_from_v6_to_v7_with_language_version(sender: &signer, language_version: u64) {
+    // initialize the language version config.
+    Config::publish_new_config(sender, LanguageVersion::new(language_version));
+    // use STC Dao to upgrade onchain's move-language-version configuration.
+    OnChainConfigDao::plugin<STC, LanguageVersion::LanguageVersion>(sender);
+    // upgrade genesis NFT
+    GenesisNFT::upgrade_to_nft_type_info_v2(sender);
+}
+
+ + + +
+ + + +## Function `upgrade_from_v7_to_v8` + + + +
public entry fun upgrade_from_v7_to_v8(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun upgrade_from_v7_to_v8(sender: signer) {
+    do_upgrade_from_v7_to_v8(&sender);
+}
+
+ + + +
+ + + +## Function `do_upgrade_from_v7_to_v8` + + + +
public fun do_upgrade_from_v7_to_v8(sender: &signer)
+
+ + + +
+Implementation + + +
public fun do_upgrade_from_v7_to_v8(sender: &signer) {
+    {
+        let cap = Oracle::extract_signer_cap(sender);
+        GenesisSignerCapability::initialize(sender, cap);
+    };
+
+    {
+        let cap = NFT::extract_signer_cap(sender);
+        Account::destroy_signer_cap(cap);
+    };
+}
+
+ + + +
+ + + +## Function `upgrade_from_v11_to_v12` + + + +
public entry fun upgrade_from_v11_to_v12(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun upgrade_from_v11_to_v12(sender: signer) {
+    do_upgrade_from_v11_to_v12(&sender);
+}
+
+ + + +
+ + + +## Function `do_upgrade_from_v11_to_v12` + + + +
public fun do_upgrade_from_v11_to_v12(sender: &signer)
+
+ + + +
+Implementation + + +
public fun do_upgrade_from_v11_to_v12(sender: &signer) {
+    {
+        GasSchedule::initialize(sender,GasSchedule::new_gas_schedule());
+        let address = @0x8c109349c6bd91411d6bc962e080c4a3;
+        EasyGas::initialize(sender,
+            address,
+            b"STAR",b"STAR",
+            address);
+        Block::checkpoints_init(sender);
+    };
+}
+
+ + + +
+ + + +## Function `upgrade_from_v12_to_v13` + + + +
public entry fun upgrade_from_v12_to_v13(sender: signer)
+
+ + + +
+Implementation + + +
public entry fun upgrade_from_v12_to_v13(sender: signer) {
+    do_upgrade_from_v12_to_v13(&sender);
+}
+
+ + + +
+ + + +## Function `do_upgrade_from_v12_to_v13` + + + +
public fun do_upgrade_from_v12_to_v13(sender: &signer)
+
+ + + +
+Implementation + + +
public fun do_upgrade_from_v12_to_v13(sender: &signer) {
+    {
+        FlexiDagConfig::initialize(sender, u64_max());
+        OnChainConfigDao::plugin<STC, FlexiDagConfig::FlexiDagConfig>(sender);
+    };
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/String.md b/release/v13/docs/String.md new file mode 100644 index 00000000..856a29b2 --- /dev/null +++ b/release/v13/docs/String.md @@ -0,0 +1,530 @@ + + + +# Module `0x1::String` + +The string module defines the String type which represents UTF8 encoded strings. + + +- [Struct `String`](#0x1_String_String) +- [Constants](#@Constants_0) +- [Function `utf8`](#0x1_String_utf8) +- [Function `try_utf8`](#0x1_String_try_utf8) +- [Function `bytes`](#0x1_String_bytes) +- [Function `is_empty`](#0x1_String_is_empty) +- [Function `length`](#0x1_String_length) +- [Function `append`](#0x1_String_append) +- [Function `append_utf8`](#0x1_String_append_utf8) +- [Function `insert`](#0x1_String_insert) +- [Function `sub_string`](#0x1_String_sub_string) +- [Function `index_of`](#0x1_String_index_of) +- [Function `internal_check_utf8`](#0x1_String_internal_check_utf8) +- [Function `internal_is_char_boundary`](#0x1_String_internal_is_char_boundary) +- [Function `internal_sub_string`](#0x1_String_internal_sub_string) +- [Function `internal_index_of`](#0x1_String_internal_index_of) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Option;
+use 0x1::Vector;
+
+ + + + + +## Struct `String` + +A String holds a sequence of bytes which is guaranteed to be in utf8 format. + + +
struct String has copy, drop, store
+
+ + + +
+Fields + + +
+
+bytes: vector<u8> +
+
+ +
+
+ + +
+ + + +## Constants + + + + +An invalid UTF8 encoding. + + +
const EINVALID_UTF8: u64 = 1;
+
+ + + + + +Index out of range. + + +
const EINVALID_INDEX: u64 = 2;
+
+ + + + + +## Function `utf8` + +Creates a new string from a sequence of bytes. Aborts if the bytes do not represent valid utf8. + + +
public fun utf8(bytes: vector<u8>): String::String
+
+ + + +
+Implementation + + +
public fun utf8(bytes: vector<u8>): String {
+    assert!(internal_check_utf8(&bytes), Errors::invalid_state(EINVALID_UTF8));
+    String{bytes}
+}
+
+ + + +
+ + + +## Function `try_utf8` + +Tries to create a new string from a sequence of bytes. + + +
public fun try_utf8(bytes: vector<u8>): Option::Option<String::String>
+
+ + + +
+Implementation + + +
public fun try_utf8(bytes: vector<u8>): Option<String> {
+    if (internal_check_utf8(&bytes)) {
+        Option::some(String{bytes})
+    } else {
+        Option::none()
+    }
+}
+
+ + + +
+ + + +## Function `bytes` + +Returns a reference to the underlying byte vector. + + +
public fun bytes(s: &String::String): &vector<u8>
+
+ + + +
+Implementation + + +
public fun bytes(s: &String): &vector<u8> {
+    &s.bytes
+}
+
+ + + +
+ + + +## Function `is_empty` + +Checks whether this string is empty. + + +
public fun is_empty(s: &String::String): bool
+
+ + + +
+Implementation + + +
public fun is_empty(s: &String): bool {
+    Vector::is_empty(&s.bytes)
+}
+
+ + + +
+ + + +## Function `length` + +Returns the length of this string, in bytes. + + +
public fun length(s: &String::String): u64
+
+ + + +
+Implementation + + +
public fun length(s: &String): u64 {
+    Vector::length(&s.bytes)
+}
+
+ + + +
+ + + +## Function `append` + +Appends a string. + + +
public fun append(s: &mut String::String, r: String::String)
+
+ + + +
+Implementation + + +
public fun append(s: &mut String, r: String) {
+    Vector::append(&mut s.bytes, *&r.bytes)
+}
+
+ + + +
+ + + +## Function `append_utf8` + +Appends bytes which must be in valid utf8 format. + + +
public fun append_utf8(s: &mut String::String, bytes: vector<u8>)
+
+ + + +
+Implementation + + +
public fun append_utf8(s: &mut String, bytes: vector<u8>) {
+    append(s, utf8(bytes))
+}
+
+ + + +
+ + + +## Function `insert` + +Insert the other string at the byte index in given string. The index must be at a valid utf8 char +boundary. + + +
public fun insert(s: &mut String::String, at: u64, o: String::String)
+
+ + + +
+Implementation + + +
public fun insert(s: &mut String, at: u64, o: String) {
+    let bytes = &s.bytes;
+    assert!(at <= Vector::length(bytes) && internal_is_char_boundary(bytes, at), Errors::invalid_state(EINVALID_INDEX));
+    let l = length(s);
+    let front = sub_string(s, 0, at);
+    let end = sub_string(s, at, l);
+    append(&mut front, o);
+    append(&mut front, end);
+    *s = front;
+}
+
+ + + +
+ + + +## Function `sub_string` + +Returns a sub-string using the given byte indices, where i is the first byte position and j is the start +of the first byte not included (or the length of the string). The indices must be at valid utf8 char boundaries, +guaranteeing that the result is valid utf8. + + +
public fun sub_string(s: &String::String, i: u64, j: u64): String::String
+
+ + + +
+Implementation + + +
public fun sub_string(s: &String, i: u64, j: u64): String {
+    let bytes = &s.bytes;
+    let l = Vector::length(bytes);
+    assert!(
+        j <= l && i <= j && internal_is_char_boundary(bytes, i) && internal_is_char_boundary(bytes, j),
+        Errors::invalid_state(EINVALID_INDEX)
+    );
+    String{bytes: internal_sub_string(bytes, i, j)}
+}
+
+ + + +
+ + + +## Function `index_of` + +Computes the index of the first occurrence of a string. Returns length(s) if no occurrence found. + + +
public fun index_of(s: &String::String, r: &String::String): u64
+
+ + + +
+Implementation + + +
public fun index_of(s: &String, r: &String): u64 {
+    internal_index_of(&s.bytes, &r.bytes)
+}
+
+ + + +
+ + + +## Function `internal_check_utf8` + + + +
fun internal_check_utf8(v: &vector<u8>): bool
+
+ + + +
+Implementation + + +
native fun internal_check_utf8(v: &vector<u8>): bool;
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_internal_check_utf8(v);
+
+ + + +
+ + + +## Function `internal_is_char_boundary` + + + +
fun internal_is_char_boundary(v: &vector<u8>, i: u64): bool
+
+ + + +
+Implementation + + +
native fun internal_is_char_boundary(v: &vector<u8>, i: u64): bool;
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_internal_is_char_boundary(v, i);
+
+ + + +
+ + + +## Function `internal_sub_string` + + + +
fun internal_sub_string(v: &vector<u8>, i: u64, j: u64): vector<u8>
+
+ + + +
+Implementation + + +
native fun internal_sub_string(v: &vector<u8>, i: u64, j: u64): vector<u8>;
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_internal_sub_string(v, i, j);
+
+ + + +
+ + + +## Function `internal_index_of` + + + +
fun internal_index_of(v: &vector<u8>, r: &vector<u8>): u64
+
+ + + +
+Implementation + + +
native fun internal_index_of(v: &vector<u8>, r: &vector<u8>): u64;
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_internal_index_of(v, r);
+
+ + + + + + + +
fun spec_internal_check_utf8(v: vector<u8>): bool;
+
+fun spec_internal_is_char_boundary(v: vector<u8>, i: u64): bool;
+
+fun spec_internal_sub_string(v: vector<u8>, i: u64, j: u64): vector<u8>;
+
+fun spec_internal_index_of(v: vector<u8>, r: vector<u8>): u64;
+
+ + + +
+ + + +## Module Specification + + + + + + +
fun spec_utf8(bytes: vector<u8>): String {
+   String{bytes}
+}
+
diff --git a/release/v13/docs/Table.md b/release/v13/docs/Table.md new file mode 100644 index 00000000..f7c4a3f5 --- /dev/null +++ b/release/v13/docs/Table.md @@ -0,0 +1,831 @@ + + + +# Module `0x1::Table` + +Type of large-scale storage tables. + + +- [Struct `Table`](#0x1_Table_Table) +- [Resource `Box`](#0x1_Table_Box) +- [Constants](#@Constants_0) +- [Function `new`](#0x1_Table_new) +- [Function `destroy_empty`](#0x1_Table_destroy_empty) +- [Function `add`](#0x1_Table_add) +- [Function `borrow`](#0x1_Table_borrow) +- [Function `borrow_with_default`](#0x1_Table_borrow_with_default) +- [Function `borrow_mut`](#0x1_Table_borrow_mut) +- [Function `length`](#0x1_Table_length) +- [Function `empty`](#0x1_Table_empty) +- [Function `borrow_mut_with_default`](#0x1_Table_borrow_mut_with_default) +- [Function `upsert`](#0x1_Table_upsert) +- [Function `remove`](#0x1_Table_remove) +- [Function `contains`](#0x1_Table_contains) +- [Function `new_table_handle`](#0x1_Table_new_table_handle) +- [Function `add_box`](#0x1_Table_add_box) +- [Function `borrow_box`](#0x1_Table_borrow_box) +- [Function `borrow_box_mut`](#0x1_Table_borrow_box_mut) +- [Function `contains_box`](#0x1_Table_contains_box) +- [Function `remove_box`](#0x1_Table_remove_box) +- [Function `destroy_empty_box`](#0x1_Table_destroy_empty_box) +- [Function `drop_unchecked_box`](#0x1_Table_drop_unchecked_box) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+
+ + + + + +## Struct `Table` + +Type of tables + + +
struct Table<K: copy, drop, V> has store
+
+ + + +
+Fields + + +
+
+handle: address +
+
+ +
+
+length: u64 +
+
+ +
+
+ + +
+ +
+Specification + + + +
pragma intrinsic = map,
+map_new = new,
+map_destroy_empty = destroy_empty,
+map_len = length,
+map_is_empty = empty,
+map_has_key = contains,
+map_add_no_override = add,
+map_del_must_exist = remove,
+map_borrow = borrow,
+map_borrow_mut = borrow_mut,
+map_spec_get = spec_get,
+map_spec_set = spec_set,
+map_spec_del = spec_remove,
+map_spec_len = spec_len,
+map_spec_has_key = spec_contains;
+
+ + + +
+ + + +## Resource `Box` + +Wrapper for values. Required for making values appear as resources in the implementation. + + +
struct Box<V> has drop, store, key
+
+ + + +
+Fields + + +
+
+val: V +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const EALREADY_EXISTS: u64 = 100;
+
+ + + + + + + +
const ENOT_EMPTY: u64 = 102;
+
+ + + + + + + +
const ENOT_FOUND: u64 = 101;
+
+ + + + + +## Function `new` + +Create a new Table. + + +
public fun new<K: copy, drop, V: store>(): Table::Table<K, V>
+
+ + + +
+Implementation + + +
public fun new<K: copy + drop, V: store>(): Table<K, V> {
+    Table{
+        handle: new_table_handle<K, V>(),
+        length: 0,
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `destroy_empty` + +Destroy a table. The table must be empty to succeed. + + +
public fun destroy_empty<K: copy, drop, V>(table: Table::Table<K, V>)
+
+ + + +
+Implementation + + +
public fun destroy_empty<K: copy + drop, V>(table: Table<K, V>) {
+    assert!(table.length == 0, Errors::invalid_state(ENOT_EMPTY));
+    destroy_empty_box<K, V, Box<V>>(&table);
+    drop_unchecked_box<K, V, Box<V>>(table)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `add` + +Add a new entry to the table. Aborts if an entry for this +key already exists. The entry itself is not stored in the +table, and cannot be discovered from it. + + +
public fun add<K: copy, drop, V>(table: &mut Table::Table<K, V>, key: K, val: V)
+
+ + + +
+Implementation + + +
public fun add<K: copy + drop, V>(table: &mut Table<K, V>, key: K, val: V) {
+    add_box<K, V, Box<V>>(table, key, Box{val});
+    table.length = table.length + 1
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `borrow` + +Acquire an immutable reference to the value which key maps to. +Aborts if there is no entry for key. + + +
public fun borrow<K: copy, drop, V>(table: &Table::Table<K, V>, key: K): &V
+
+ + + +
+Implementation + + +
public fun borrow<K: copy + drop, V>(table: &Table<K, V>, key: K): &V {
+    &borrow_box<K, V, Box<V>>(table, key).val
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `borrow_with_default` + +Acquire an immutable reference to the value which key maps to. +Returns specified default value if there is no entry for key. + + +
public fun borrow_with_default<K: copy, drop, V>(table: &Table::Table<K, V>, key: K, default: &V): &V
+
+ + + +
+Implementation + + +
public fun borrow_with_default<K: copy + drop, V>(table: &Table<K, V>, key: K, default: &V): &V {
+    if (!contains(table, copy key)) {
+        default
+    } else {
+        borrow(table, copy key)
+    }
+}
+
+ + + +
+ + + +## Function `borrow_mut` + +Acquire a mutable reference to the value which key maps to. +Aborts if there is no entry for key. + + +
public fun borrow_mut<K: copy, drop, V>(table: &mut Table::Table<K, V>, key: K): &mut V
+
+ + + +
+Implementation + + +
public fun borrow_mut<K: copy + drop, V>(table: &mut Table<K, V>, key: K): &mut V {
+    &mut borrow_box_mut<K, V, Box<V>>(table, key).val
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `length` + +Returns the length of the table, i.e. the number of entries. + + +
public fun length<K: copy, drop, V>(table: &Table::Table<K, V>): u64
+
+ + + +
+Implementation + + +
public fun length<K: copy + drop, V>(table: &Table<K, V>): u64 {
+    table.length
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `empty` + +Returns true if this table is empty. + + +
public fun empty<K: copy, drop, V>(table: &Table::Table<K, V>): bool
+
+ + + +
+Implementation + + +
public fun empty<K: copy + drop, V>(table: &Table<K, V>): bool {
+    table.length == 0
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `borrow_mut_with_default` + +Acquire a mutable reference to the value which key maps to. +Insert the pair (key, default) first if there is no entry for key. + + +
public fun borrow_mut_with_default<K: copy, drop, V: drop>(table: &mut Table::Table<K, V>, key: K, default: V): &mut V
+
+ + + +
+Implementation + + +
public fun borrow_mut_with_default<K: copy + drop, V: drop>(table: &mut Table<K, V>, key: K, default: V): &mut V {
+    if (!contains(table, copy key)) {
+        add(table, copy key, default)
+    };
+    borrow_mut(table, key)
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque, verify=false;
+aborts_if false;
+
+ + + +
+ + + +## Function `upsert` + +Insert the pair (key, value) if there is no entry for key. +update the value of the entry for key to value otherwise + + +
public fun upsert<K: copy, drop, V: drop>(table: &mut Table::Table<K, V>, key: K, value: V)
+
+ + + +
+Implementation + + +
public fun upsert<K: copy + drop, V: drop>(table: &mut Table<K, V>, key: K, value: V) {
+    if (!contains(table, copy key)) {
+        add(table, copy key, value)
+    } else {
+        let ref = borrow_mut(table, key);
+        *ref = value;
+    };
+}
+
+ + + +
+ + + +## Function `remove` + +Remove from table and return the value which key maps to. +Aborts if there is no entry for key. + + +
public fun remove<K: copy, drop, V>(table: &mut Table::Table<K, V>, key: K): V
+
+ + + +
+Implementation + + +
public fun remove<K: copy + drop, V>(table: &mut Table<K, V>, key: K): V {
+    let Box{val} = remove_box<K, V, Box<V>>(table, key);
+    table.length = table.length - 1;
+    val
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `contains` + +Returns true iff table contains an entry for key. + + +
public fun contains<K: copy, drop, V>(table: &Table::Table<K, V>, key: K): bool
+
+ + + +
+Implementation + + +
public fun contains<K: copy + drop, V>(table: &Table<K, V>, key: K): bool {
+    contains_box<K, V, Box<V>>(table, key)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic;
+
+ + + +
+ + + +## Function `new_table_handle` + + + +
fun new_table_handle<K, V>(): address
+
+ + + +
+Implementation + + +
native fun new_table_handle<K, V>(): address;
+
+ + + +
+ + + +## Function `add_box` + + + +
fun add_box<K: copy, drop, V, B>(table: &mut Table::Table<K, V>, key: K, val: Table::Box<V>)
+
+ + + +
+Implementation + + +
native fun add_box<K: copy + drop, V, B>(table: &mut Table<K, V>, key: K, val: Box<V>);
+
+ + + +
+ + + +## Function `borrow_box` + + + +
fun borrow_box<K: copy, drop, V, B>(table: &Table::Table<K, V>, key: K): &Table::Box<V>
+
+ + + +
+Implementation + + +
native fun borrow_box<K: copy + drop, V, B>(table: &Table<K, V>, key: K): &Box<V>;
+
+ + + +
+ + + +## Function `borrow_box_mut` + + + +
fun borrow_box_mut<K: copy, drop, V, B>(table: &mut Table::Table<K, V>, key: K): &mut Table::Box<V>
+
+ + + +
+Implementation + + +
native fun borrow_box_mut<K: copy + drop, V, B>(table: &mut Table<K, V>, key: K): &mut Box<V>;
+
+ + + +
+ + + +## Function `contains_box` + + + +
fun contains_box<K: copy, drop, V, B>(table: &Table::Table<K, V>, key: K): bool
+
+ + + +
+Implementation + + +
native fun contains_box<K: copy + drop, V, B>(table: &Table<K, V>, key: K): bool;
+
+ + + +
+ + + +## Function `remove_box` + + + +
fun remove_box<K: copy, drop, V, B>(table: &mut Table::Table<K, V>, key: K): Table::Box<V>
+
+ + + +
+Implementation + + +
native fun remove_box<K: copy + drop, V, B>(table: &mut Table<K, V>, key: K): Box<V>;
+
+ + + +
+ + + +## Function `destroy_empty_box` + + + +
fun destroy_empty_box<K: copy, drop, V, B>(table: &Table::Table<K, V>)
+
+ + + +
+Implementation + + +
native fun destroy_empty_box<K: copy + drop, V, B>(table: &Table<K, V>);
+
+ + + +
+ + + +## Function `drop_unchecked_box` + + + +
fun drop_unchecked_box<K: copy, drop, V, B>(table: Table::Table<K, V>)
+
+ + + +
+Implementation + + +
native fun drop_unchecked_box<K: copy + drop, V, B>(table: Table<K, V>);
+
+ + + +
+ + + +## Module Specification + + + + + + +
native fun spec_len<K, V>(t: Table<K, V>): num;
+
+ + + + + + + +
native fun spec_contains<K, V>(t: Table<K, V>, k: K): bool;
+
+ + + + + + + +
native fun spec_set<K, V>(t: Table<K, V>, k: K, v: V): Table<K, V>;
+
+ + + + + + + +
native fun spec_remove<K, V>(t: Table<K, V>, k: K): Table<K, V>;
+
+ + + + + + + +
native fun spec_get<K, V>(t: Table<K, V>, k: K): V;
+
diff --git a/release/v13/docs/Timestamp.md b/release/v13/docs/Timestamp.md new file mode 100644 index 00000000..a4af9aaa --- /dev/null +++ b/release/v13/docs/Timestamp.md @@ -0,0 +1,461 @@ + + + +# Module `0x1::Timestamp` + +The module implements onchain timestamp oracle. +Timestamp is updated on each block. It always steps forward, and never come backward. + + +- [Resource `CurrentTimeMilliseconds`](#0x1_Timestamp_CurrentTimeMilliseconds) +- [Resource `TimeHasStarted`](#0x1_Timestamp_TimeHasStarted) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_Timestamp_initialize) +- [Function `update_global_time`](#0x1_Timestamp_update_global_time) +- [Function `now_seconds`](#0x1_Timestamp_now_seconds) +- [Function `now_milliseconds`](#0x1_Timestamp_now_milliseconds) +- [Function `set_time_has_started`](#0x1_Timestamp_set_time_has_started) +- [Function `is_genesis`](#0x1_Timestamp_is_genesis) +- [Function `assert_genesis`](#0x1_Timestamp_assert_genesis) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::CoreAddresses;
+use 0x1::Errors;
+
+ + + + + +## Resource `CurrentTimeMilliseconds` + + + +
struct CurrentTimeMilliseconds has key
+
+ + + +
+Fields + + +
+
+milliseconds: u64 +
+
+ +
+
+ + +
+ + + +## Resource `TimeHasStarted` + +A singleton resource used to determine whether time has started. This +is called at the end of genesis. + + +
struct TimeHasStarted has key
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const EINVALID_TIMESTAMP: u64 = 14;
+
+ + + + + + + +
const ENOT_GENESIS: u64 = 12;
+
+ + + + + + + +
const ENOT_INITIALIZED: u64 = 101;
+
+ + + + + +Conversion factor between seconds and milliseconds + + +
const MILLI_CONVERSION_FACTOR: u64 = 1000;
+
+ + + + + +## Function `initialize` + + + +
public fun initialize(account: &signer, genesis_timestamp: u64)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, genesis_timestamp: u64) {
+    // Only callable by the Genesis address
+    CoreAddresses::assert_genesis_address(account);
+    let milli_timer = CurrentTimeMilliseconds {milliseconds: genesis_timestamp};
+    move_to<CurrentTimeMilliseconds>(account, milli_timer);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<CurrentTimeMilliseconds>(Signer::address_of(account));
+ensures exists<CurrentTimeMilliseconds>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `update_global_time` + + + +
public fun update_global_time(account: &signer, timestamp: u64)
+
+ + + +
+Implementation + + +
public fun update_global_time(account: &signer, timestamp: u64) acquires CurrentTimeMilliseconds {
+    CoreAddresses::assert_genesis_address(account);
+    //Do not update time before time start.
+    let global_milli_timer = borrow_global_mut<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+    assert!(timestamp > global_milli_timer.milliseconds, Errors::invalid_argument(EINVALID_TIMESTAMP));
+    global_milli_timer.milliseconds = timestamp;
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if timestamp <= global<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS()).milliseconds;
+ensures global<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS()).milliseconds == timestamp;
+
+ + + +
+ + + +## Function `now_seconds` + + + +
public fun now_seconds(): u64
+
+ + + +
+Implementation + + +
public fun now_seconds(): u64 acquires CurrentTimeMilliseconds {
+    now_milliseconds() / MILLI_CONVERSION_FACTOR
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+ensures result == now_milliseconds() / MILLI_CONVERSION_FACTOR;
+
+ + + + + + + +
fun spec_now_seconds(): u64 {
+   global<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS()).milliseconds / MILLI_CONVERSION_FACTOR
+}
+
+ + + +
+ + + +## Function `now_milliseconds` + + + +
public fun now_milliseconds(): u64
+
+ + + +
+Implementation + + +
public fun now_milliseconds(): u64 acquires CurrentTimeMilliseconds {
+    borrow_global<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS()).milliseconds
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+ensures result == global<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS()).milliseconds;
+
+ + + + + + + +
fun spec_now_millseconds(): u64 {
+   global<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS()).milliseconds
+}
+
+ + + +
+ + + +## Function `set_time_has_started` + +Marks that time has started and genesis has finished. This can only be called from genesis. + + +
public fun set_time_has_started(account: &signer)
+
+ + + +
+Implementation + + +
public fun set_time_has_started(account: &signer) {
+    CoreAddresses::assert_genesis_address(account);
+
+    // Current time must have been initialized.
+    assert!(
+        exists<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS()),
+        Errors::invalid_state(ENOT_INITIALIZED)
+    );
+    move_to(account, TimeHasStarted{});
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<CurrentTimeMilliseconds>(Signer::address_of(account));
+aborts_if exists<TimeHasStarted>(Signer::address_of(account));
+ensures exists<TimeHasStarted>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `is_genesis` + +Helper function to determine if the blockchain is in genesis state. + + +
public fun is_genesis(): bool
+
+ + + +
+Implementation + + +
public fun is_genesis(): bool {
+    !exists<TimeHasStarted>(CoreAddresses::GENESIS_ADDRESS())
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == !exists<TimeHasStarted>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `assert_genesis` + +Helper function to assert genesis state. + + +
public fun assert_genesis()
+
+ + + +
+Implementation + + +
public fun assert_genesis() {
+    assert!(is_genesis(), Errors::invalid_state(ENOT_GENESIS));
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+include AbortsIfNotGenesis;
+
+ + +Helper schema to specify that a function aborts if not in genesis. + + + + + +
schema AbortsIfNotGenesis {
+    aborts_if !is_genesis();
+}
+
+ + + + + + + +
schema AbortsIfTimestampNotExists {
+    aborts_if !exists<CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Token.md b/release/v13/docs/Token.md new file mode 100644 index 00000000..a32c5fb7 --- /dev/null +++ b/release/v13/docs/Token.md @@ -0,0 +1,1845 @@ + + + +# Module `0x1::Token` + +Token implementation of Starcoin. + + +- [Struct `Token`](#0x1_Token_Token) +- [Struct `TokenCode`](#0x1_Token_TokenCode) +- [Resource `MintCapability`](#0x1_Token_MintCapability) +- [Resource `FixedTimeMintKey`](#0x1_Token_FixedTimeMintKey) +- [Resource `LinearTimeMintKey`](#0x1_Token_LinearTimeMintKey) +- [Resource `BurnCapability`](#0x1_Token_BurnCapability) +- [Struct `MintEvent`](#0x1_Token_MintEvent) +- [Struct `BurnEvent`](#0x1_Token_BurnEvent) +- [Resource `TokenInfo`](#0x1_Token_TokenInfo) +- [Constants](#@Constants_0) +- [Function `register_token`](#0x1_Token_register_token) +- [Function `remove_mint_capability`](#0x1_Token_remove_mint_capability) +- [Function `add_mint_capability`](#0x1_Token_add_mint_capability) +- [Function `destroy_mint_capability`](#0x1_Token_destroy_mint_capability) +- [Function `remove_burn_capability`](#0x1_Token_remove_burn_capability) +- [Function `add_burn_capability`](#0x1_Token_add_burn_capability) +- [Function `destroy_burn_capability`](#0x1_Token_destroy_burn_capability) +- [Function `mint`](#0x1_Token_mint) +- [Function `mint_with_capability`](#0x1_Token_mint_with_capability) +- [Function `do_mint`](#0x1_Token_do_mint) +- [Function `issue_fixed_mint_key`](#0x1_Token_issue_fixed_mint_key) +- [Function `issue_linear_mint_key`](#0x1_Token_issue_linear_mint_key) +- [Function `destroy_linear_time_key`](#0x1_Token_destroy_linear_time_key) +- [Function `read_linear_time_key`](#0x1_Token_read_linear_time_key) +- [Function `burn`](#0x1_Token_burn) +- [Function `burn_with_capability`](#0x1_Token_burn_with_capability) +- [Function `zero`](#0x1_Token_zero) +- [Function `value`](#0x1_Token_value) +- [Function `split`](#0x1_Token_split) +- [Function `withdraw`](#0x1_Token_withdraw) +- [Function `join`](#0x1_Token_join) +- [Function `deposit`](#0x1_Token_deposit) +- [Function `destroy_zero`](#0x1_Token_destroy_zero) +- [Function `scaling_factor`](#0x1_Token_scaling_factor) +- [Function `market_cap`](#0x1_Token_market_cap) +- [Function `is_registered_in`](#0x1_Token_is_registered_in) +- [Function `is_same_token`](#0x1_Token_is_same_token) +- [Function `token_address`](#0x1_Token_token_address) +- [Function `token_code`](#0x1_Token_token_code) +- [Function `type_of`](#0x1_Token_type_of) +- [Function `name_of`](#0x1_Token_name_of) +- [Function `name_of_token`](#0x1_Token_name_of_token) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Math;
+use 0x1::Signer;
+
+ + + + + +## Struct `Token` + +The token has a TokenType color that tells us what token the +value inside represents. + + +
struct Token<TokenType> has store
+
+ + + +
+Fields + + +
+
+value: u128 +
+
+ +
+
+ + +
+ + + +## Struct `TokenCode` + +Token Code which identify a unique Token. + + +
struct TokenCode has copy, drop, store
+
+ + + +
+Fields + + +
+
+addr: address +
+
+ address who define the module contains the Token Type. +
+
+module_name: vector<u8> +
+
+ module which contains the Token Type. +
+
+name: vector<u8> +
+
+ name of the token. may nested if the token is an instantiated generic token type. +
+
+ + +
+ + + +## Resource `MintCapability` + +A minting capability allows tokens of type TokenType to be minted + + +
struct MintCapability<TokenType> has store, key
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Resource `FixedTimeMintKey` + +A fixed time mint key which can mint token until global time > end_time + + +
struct FixedTimeMintKey<TokenType> has store, key
+
+ + + +
+Fields + + +
+
+total: u128 +
+
+ +
+
+end_time: u64 +
+
+ +
+
+ + +
+ + + +## Resource `LinearTimeMintKey` + +A linear time mint key which can mint token in a period by time-based linear release. + + +
struct LinearTimeMintKey<TokenType> has store, key
+
+ + + +
+Fields + + +
+
+total: u128 +
+
+ +
+
+minted: u128 +
+
+ +
+
+start_time: u64 +
+
+ +
+
+period: u64 +
+
+ +
+
+ + +
+ + + +## Resource `BurnCapability` + +A burn capability allows tokens of type TokenType to be burned. + + +
struct BurnCapability<TokenType> has store, key
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Struct `MintEvent` + +Event emitted when token minted. + + +
struct MintEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u128 +
+
+ funds added to the system +
+
+token_code: Token::TokenCode +
+
+ full info of Token. +
+
+ + +
+ + + +## Struct `BurnEvent` + +Event emitted when token burned. + + +
struct BurnEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u128 +
+
+ funds removed from the system +
+
+token_code: Token::TokenCode +
+
+ full info of Token +
+
+ + +
+ + + +## Resource `TokenInfo` + +Token information. + + +
struct TokenInfo<TokenType> has key
+
+ + + +
+Fields + + +
+
+total_value: u128 +
+
+ The total value for the token represented by + TokenType. Mutable. +
+
+scaling_factor: u128 +
+
+ The scaling factor for the coin (i.e. the amount to divide by + to get to the human-readable representation for this currency). + e.g. 10^6 for Coin1 +
+
+mint_events: Event::EventHandle<Token::MintEvent> +
+
+ event stream for minting +
+
+burn_events: Event::EventHandle<Token::BurnEvent> +
+
+ event stream for burning +
+
+ + +
+ + + +## Constants + + + + + + +
const EAMOUNT_EXCEEDS_COIN_VALUE: u64 = 102;
+
+ + + + + + + +
const EDEPRECATED_FUNCTION: u64 = 19;
+
+ + + + + + + +
const EDESTROY_KEY_NOT_EMPTY: u64 = 104;
+
+ + + + + + + +
const EDESTROY_TOKEN_NON_ZERO: u64 = 16;
+
+ + + + + + + +
const EEMPTY_KEY: u64 = 106;
+
+ + + + + + + +
const EINVALID_ARGUMENT: u64 = 18;
+
+ + + + + + + +
const EMINT_AMOUNT_EQUAL_ZERO: u64 = 109;
+
+ + + + + + + +
const EMINT_KEY_TIME_LIMIT: u64 = 103;
+
+ + + + + + + +
const EPERIOD_NEW: u64 = 108;
+
+ + + + + + + +
const EPRECISION_TOO_LARGE: u64 = 105;
+
+ + + + + + + +
const ESPLIT: u64 = 107;
+
+ + + + + +Token register's address should same as TokenType's address. + + +
const ETOKEN_REGISTER: u64 = 101;
+
+ + + + + +2^128 < 10**39 + + +
const MAX_PRECISION: u8 = 38;
+
+ + + + + +## Function `register_token` + +Register the type TokenType as a Token and got MintCapability and BurnCapability. + + +
public fun register_token<TokenType: store>(account: &signer, precision: u8)
+
+ + + +
+Implementation + + +
public fun register_token<TokenType: store>(
+    account: &signer,
+    precision: u8,
+) {
+    assert!(precision <= MAX_PRECISION, Errors::invalid_argument(EPRECISION_TOO_LARGE));
+    let scaling_factor = Math::pow(10, (precision as u64));
+    let token_address = token_address<TokenType>();
+    assert!(Signer::address_of(account) == token_address, Errors::requires_address(ETOKEN_REGISTER));
+    move_to(account, MintCapability<TokenType> {});
+    move_to(account, BurnCapability<TokenType> {});
+    move_to(
+        account,
+        TokenInfo<TokenType> {
+            total_value: 0,
+            scaling_factor,
+            mint_events: Event::new_event_handle<MintEvent>(account),
+            burn_events: Event::new_event_handle<BurnEvent>(account),
+        },
+    );
+}
+
+ + + +
+ +
+Specification + + + +
include RegisterTokenAbortsIf<TokenType>;
+include RegisterTokenEnsures<TokenType>;
+
+ + + + + + + +
schema RegisterTokenAbortsIf<TokenType> {
+    precision: u8;
+    account: signer;
+    aborts_if precision > MAX_PRECISION;
+    aborts_if Signer::address_of(account) != SPEC_TOKEN_TEST_ADDRESS();
+    aborts_if exists<MintCapability<TokenType>>(Signer::address_of(account));
+    aborts_if exists<BurnCapability<TokenType>>(Signer::address_of(account));
+    aborts_if exists<TokenInfo<TokenType>>(Signer::address_of(account));
+}
+
+ + + + + + + +
schema RegisterTokenEnsures<TokenType> {
+    account: signer;
+    ensures exists<MintCapability<TokenType>>(Signer::address_of(account));
+    ensures exists<BurnCapability<TokenType>>(Signer::address_of(account));
+    ensures exists<TokenInfo<TokenType>>(Signer::address_of(account));
+}
+
+ + + +
+ + + +## Function `remove_mint_capability` + +Remove mint capability from signer. + + +
public fun remove_mint_capability<TokenType: store>(signer: &signer): Token::MintCapability<TokenType>
+
+ + + +
+Implementation + + +
public fun remove_mint_capability<TokenType: store>(signer: &signer): MintCapability<TokenType>
+acquires MintCapability {
+    move_from<MintCapability<TokenType>>(Signer::address_of(signer))
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<MintCapability<TokenType>>(Signer::address_of(signer));
+ensures !exists<MintCapability<TokenType>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `add_mint_capability` + +Add mint capability to signer. + + +
public fun add_mint_capability<TokenType: store>(signer: &signer, cap: Token::MintCapability<TokenType>)
+
+ + + +
+Implementation + + +
public fun add_mint_capability<TokenType: store>(signer: &signer, cap: MintCapability<TokenType>) {
+    move_to(signer, cap)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if exists<MintCapability<TokenType>>(Signer::address_of(signer));
+ensures exists<MintCapability<TokenType>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `destroy_mint_capability` + +Destroy the given mint capability. + + +
public fun destroy_mint_capability<TokenType: store>(cap: Token::MintCapability<TokenType>)
+
+ + + +
+Implementation + + +
public fun destroy_mint_capability<TokenType: store>(cap: MintCapability<TokenType>) {
+    let MintCapability<TokenType> {} = cap;
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Function `remove_burn_capability` + +remove the token burn capability from signer. + + +
public fun remove_burn_capability<TokenType: store>(signer: &signer): Token::BurnCapability<TokenType>
+
+ + + +
+Implementation + + +
public fun remove_burn_capability<TokenType: store>(signer: &signer): BurnCapability<TokenType>
+acquires BurnCapability {
+    move_from<BurnCapability<TokenType>>(Signer::address_of(signer))
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<BurnCapability<TokenType>>(Signer::address_of(signer));
+ensures !exists<BurnCapability<TokenType>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `add_burn_capability` + +Add token burn capability to signer. + + +
public fun add_burn_capability<TokenType: store>(signer: &signer, cap: Token::BurnCapability<TokenType>)
+
+ + + +
+Implementation + + +
public fun add_burn_capability<TokenType: store>(signer: &signer, cap: BurnCapability<TokenType>) {
+    move_to(signer, cap)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if exists<BurnCapability<TokenType>>(Signer::address_of(signer));
+ensures exists<BurnCapability<TokenType>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `destroy_burn_capability` + +Destroy the given burn capability. + + +
public fun destroy_burn_capability<TokenType: store>(cap: Token::BurnCapability<TokenType>)
+
+ + + +
+Implementation + + +
public fun destroy_burn_capability<TokenType: store>(cap: BurnCapability<TokenType>) {
+    let BurnCapability<TokenType> {} = cap;
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Function `mint` + +Return amount tokens. +Fails if the sender does not have a published MintCapability. + + +
public fun mint<TokenType: store>(account: &signer, amount: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun mint<TokenType: store>(account: &signer, amount: u128): Token<TokenType>
+acquires TokenInfo, MintCapability {
+    mint_with_capability(
+        borrow_global<MintCapability<TokenType>>(Signer::address_of(account)),
+        amount,
+    )
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if spec_abstract_total_value<TokenType>() + amount > MAX_U128;
+aborts_if !exists<MintCapability<TokenType>>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `mint_with_capability` + +Mint a new Token::Token worth amount. +The caller must have a reference to a MintCapability. +Only the Association account can acquire such a reference, and it can do so only via +borrow_sender_mint_capability + + +
public fun mint_with_capability<TokenType: store>(_capability: &Token::MintCapability<TokenType>, amount: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun mint_with_capability<TokenType: store>(
+    _capability: &MintCapability<TokenType>,
+    amount: u128,
+): Token<TokenType> acquires TokenInfo {
+    do_mint(amount)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if spec_abstract_total_value<TokenType>() + amount > MAX_U128;
+ensures spec_abstract_total_value<TokenType>() ==
+        old(global<TokenInfo<TokenType>>(SPEC_TOKEN_TEST_ADDRESS()).total_value) + amount;
+
+ + + +
+ + + +## Function `do_mint` + + + +
fun do_mint<TokenType: store>(amount: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
fun do_mint<TokenType: store>(amount: u128): Token<TokenType> acquires TokenInfo {
+    // update market cap resource to reflect minting
+    let (token_address, module_name, token_name) = name_of_token<TokenType>();
+    let info = borrow_global_mut<TokenInfo<TokenType>>(token_address);
+    info.total_value = info.total_value + amount;
+    Event::emit_event(
+        &mut info.mint_events,
+        MintEvent {
+            amount,
+            token_code: TokenCode { addr: token_address, module_name, name: token_name },
+        },
+    );
+    Token<TokenType> { value: amount }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<TokenInfo<TokenType>>(SPEC_TOKEN_TEST_ADDRESS());
+aborts_if spec_abstract_total_value<TokenType>() + amount > MAX_U128;
+
+ + + +
+ + + +## Function `issue_fixed_mint_key` + +Deprecated since @v3 +Issue a FixedTimeMintKey with given MintCapability. + + +
public fun issue_fixed_mint_key<TokenType: store>(_capability: &Token::MintCapability<TokenType>, _amount: u128, _period: u64): Token::FixedTimeMintKey<TokenType>
+
+ + + +
+Implementation + + +
public fun issue_fixed_mint_key<TokenType: store>(
+    _capability: &MintCapability<TokenType>,
+    _amount: u128,
+    _period: u64,
+): FixedTimeMintKey<TokenType> {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if true;
+
+ + + +
+ + + +## Function `issue_linear_mint_key` + +Deprecated since @v3 +Issue a LinearTimeMintKey with given MintCapability. + + +
public fun issue_linear_mint_key<TokenType: store>(_capability: &Token::MintCapability<TokenType>, _amount: u128, _period: u64): Token::LinearTimeMintKey<TokenType>
+
+ + + +
+Implementation + + +
public fun issue_linear_mint_key<TokenType: store>(
+    _capability: &MintCapability<TokenType>,
+    _amount: u128,
+    _period: u64,
+): LinearTimeMintKey<TokenType> {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if true;
+
+ + + +
+ + + +## Function `destroy_linear_time_key` + +Destroy LinearTimeMintKey, for deprecated + + +
public fun destroy_linear_time_key<TokenType: store>(key: Token::LinearTimeMintKey<TokenType>): (u128, u128, u64, u64)
+
+ + + +
+Implementation + + +
public fun destroy_linear_time_key<TokenType: store>(key: LinearTimeMintKey<TokenType>): (u128, u128, u64, u64) {
+    let LinearTimeMintKey<TokenType> { total, minted, start_time, period } = key;
+    (total, minted, start_time, period)
+}
+
+ + + +
+ + + +## Function `read_linear_time_key` + + + +
public fun read_linear_time_key<TokenType: store>(key: &Token::LinearTimeMintKey<TokenType>): (u128, u128, u64, u64)
+
+ + + +
+Implementation + + +
public fun read_linear_time_key<TokenType: store>(key: &LinearTimeMintKey<TokenType>): (u128, u128, u64, u64) {
+    (key.total, key.minted, key.start_time, key.period)
+}
+
+ + + +
+ + + +## Function `burn` + +Burn some tokens of signer. + + +
public fun burn<TokenType: store>(account: &signer, tokens: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun burn<TokenType: store>(account: &signer, tokens: Token<TokenType>)
+acquires TokenInfo, BurnCapability {
+    burn_with_capability(
+        borrow_global<BurnCapability<TokenType>>(Signer::address_of(account)),
+        tokens,
+    )
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if spec_abstract_total_value<TokenType>() - tokens.value < 0;
+aborts_if !exists<BurnCapability<TokenType>>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `burn_with_capability` + +Burn tokens with the given BurnCapability. + + +
public fun burn_with_capability<TokenType: store>(_capability: &Token::BurnCapability<TokenType>, tokens: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun burn_with_capability<TokenType: store>(
+    _capability: &BurnCapability<TokenType>,
+    tokens: Token<TokenType>,
+) acquires TokenInfo {
+    let (token_address, module_name, token_name) = name_of_token<TokenType>();
+    let info = borrow_global_mut<TokenInfo<TokenType>>(token_address);
+    let Token { value } = tokens;
+    info.total_value = info.total_value - value;
+    Event::emit_event(
+        &mut info.burn_events,
+        BurnEvent {
+            amount: value,
+            token_code: TokenCode { addr: token_address, module_name, name: token_name },
+        },
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if spec_abstract_total_value<TokenType>() - tokens.value < 0;
+ensures spec_abstract_total_value<TokenType>() ==
+        old(global<TokenInfo<TokenType>>(SPEC_TOKEN_TEST_ADDRESS()).total_value) - tokens.value;
+
+ + + +
+ + + +## Function `zero` + +Create a new Token::Token with a value of 0 + + +
public fun zero<TokenType: store>(): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun zero<TokenType: store>(): Token<TokenType> {
+    Token<TokenType> { value: 0 }
+}
+
+ + + +
+ +
+Specification + + + +
ensures result.value == 0;
+
+ + + +
+ + + +## Function `value` + +Public accessor for the value of a token + + +
public fun value<TokenType: store>(token: &Token::Token<TokenType>): u128
+
+ + + +
+Implementation + + +
public fun value<TokenType: store>(token: &Token<TokenType>): u128 {
+    token.value
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == token.value;
+
+ + + +
+ + + +## Function `split` + +Splits the given token into two and returns them both + + +
public fun split<TokenType: store>(token: Token::Token<TokenType>, value: u128): (Token::Token<TokenType>, Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun split<TokenType: store>(
+    token: Token<TokenType>,
+    value: u128,
+): (Token<TokenType>, Token<TokenType>) {
+    let rest = withdraw(&mut token, value);
+    (token, rest)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if token.value < value;
+ensures token.value == result_1.value + result_2.value;
+
+ + + +
+ + + +## Function `withdraw` + +"Divides" the given token into two, where the original token is modified in place. +The original token will have value = original value - value +The new token will have a value = value +Fails if the tokens value is less than value + + +
public fun withdraw<TokenType: store>(token: &mut Token::Token<TokenType>, value: u128): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun withdraw<TokenType: store>(
+    token: &mut Token<TokenType>,
+    value: u128,
+): Token<TokenType> {
+    // Check that `value` is less than the token's value
+    assert!(token.value >= value, Errors::limit_exceeded(EAMOUNT_EXCEEDS_COIN_VALUE));
+    token.value = token.value - value;
+    Token { value: value }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if token.value < value;
+ensures result.value == value;
+ensures token.value == old(token).value - value;
+
+ + + +
+ + + +## Function `join` + +Merges two tokens of the same token and returns a new token whose +value is equal to the sum of the two inputs + + +
public fun join<TokenType: store>(token1: Token::Token<TokenType>, token2: Token::Token<TokenType>): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun join<TokenType: store>(
+    token1: Token<TokenType>,
+    token2: Token<TokenType>,
+): Token<TokenType> {
+    deposit(&mut token1, token2);
+    token1
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if token1.value + token2.value > max_u128();
+ensures token1.value + token2.value == result.value;
+
+ + + +
+ + + +## Function `deposit` + +"Merges" the two tokens +The token passed in by reference will have a value equal to the sum of the two tokens +The check token is consumed in the process + + +
public fun deposit<TokenType: store>(token: &mut Token::Token<TokenType>, check: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun deposit<TokenType: store>(token: &mut Token<TokenType>, check: Token<TokenType>) {
+    let Token { value } = check;
+    token.value = token.value + value;
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if token.value + check.value > max_u128();
+ensures old(token).value + check.value == token.value;
+
+ + + +
+ + + +## Function `destroy_zero` + +Destroy a token +Fails if the value is non-zero +The amount of Token in the system is a tightly controlled property, +so you cannot "burn" any non-zero amount of Token + + +
public fun destroy_zero<TokenType: store>(token: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun destroy_zero<TokenType: store>(token: Token<TokenType>) {
+    let Token { value } = token;
+    assert!(value == 0, Errors::invalid_state(EDESTROY_TOKEN_NON_ZERO))
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if token.value > 0;
+
+ + + +
+ + + +## Function `scaling_factor` + +Returns the scaling_factor for the TokenType token. + + +
public fun scaling_factor<TokenType: store>(): u128
+
+ + + +
+Implementation + + +
public fun scaling_factor<TokenType: store>(): u128 acquires TokenInfo {
+    let token_address = token_address<TokenType>();
+    borrow_global<TokenInfo<TokenType>>(token_address).scaling_factor
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == global<TokenInfo<TokenType>>(SPEC_TOKEN_TEST_ADDRESS()).scaling_factor;
+
+ + + +
+ + + +## Function `market_cap` + +Return the total amount of token of type TokenType. + + +
public fun market_cap<TokenType: store>(): u128
+
+ + + +
+Implementation + + +
public fun market_cap<TokenType: store>(): u128 acquires TokenInfo {
+    let token_address = token_address<TokenType>();
+    borrow_global<TokenInfo<TokenType>>(token_address).total_value
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == global<TokenInfo<TokenType>>(SPEC_TOKEN_TEST_ADDRESS()).total_value;
+
+ + + +
+ + + +## Function `is_registered_in` + +Return true if the type TokenType is a registered in token_address. + + +
public fun is_registered_in<TokenType: store>(token_address: address): bool
+
+ + + +
+Implementation + + +
public fun is_registered_in<TokenType: store>(token_address: address): bool {
+    exists<TokenInfo<TokenType>>(token_address)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == exists<TokenInfo<TokenType>>(token_address);
+
+ + + +
+ + + +## Function `is_same_token` + +Return true if the type TokenType1 is same with TokenType2 + + +
public fun is_same_token<TokenType1: store, TokenType2: store>(): bool
+
+ + + +
+Implementation + + +
public fun is_same_token<TokenType1: store, TokenType2: store>(): bool {
+    return token_code<TokenType1>() == token_code<TokenType2>()
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `token_address` + +Return the TokenType's address + + +
public fun token_address<TokenType: store>(): address
+
+ + + +
+Implementation + + +
public fun token_address<TokenType: store>(): address {
+    let (addr, _, _) = name_of<TokenType>();
+    addr
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures [abstract] exists<TokenInfo<TokenType>>(result);
+ensures [abstract] result == SPEC_TOKEN_TEST_ADDRESS();
+ensures [abstract] global<TokenInfo<TokenType>>(result).total_value == 100000000u128;
+
+ + + +
+ + + +## Function `token_code` + +Return the token code for the registered token. + + +
public fun token_code<TokenType: store>(): Token::TokenCode
+
+ + + +
+Implementation + + +
public fun token_code<TokenType: store>(): TokenCode {
+    let (addr, module_name, name) = name_of<TokenType>();
+    TokenCode {
+        addr,
+        module_name,
+        name
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+
+ + +We use an uninterpreted function to represent the result of derived address. The actual value +does not matter for the verification of callers. + + + + + +
fun spec_token_code<TokenType>(): TokenCode;
+
+ + + +
+ + + +## Function `type_of` + + + +
public(friend) fun type_of<T>(): (address, vector<u8>, vector<u8>)
+
+ + + +
+Implementation + + +
public (friend) fun type_of<T>(): (address, vector<u8>, vector<u8>){
+    name_of<T>()
+}
+
+ + + +
+ + + +## Function `name_of` + +Return Token's module address, module name, and type name of TokenType. + + +
fun name_of<TokenType>(): (address, vector<u8>, vector<u8>)
+
+ + + +
+Implementation + + +
native fun name_of<TokenType>(): (address, vector<u8>, vector<u8>);
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+
+ + + +
+ + + +## Function `name_of_token` + + + +
fun name_of_token<TokenType: store>(): (address, vector<u8>, vector<u8>)
+
+ + + +
+Implementation + + +
fun name_of_token<TokenType: store>(): (address, vector<u8>, vector<u8>) {
+    name_of<TokenType>()
+}
+
+ + + +
+ +
+Specification + + + +
pragma opaque = true;
+aborts_if false;
+ensures [abstract] exists<TokenInfo<TokenType>>(result_1);
+ensures [abstract] result_1 == SPEC_TOKEN_TEST_ADDRESS();
+ensures [abstract] global<TokenInfo<TokenType>>(result_1).total_value == 100000000u128;
+
+ + + + + + + +
fun SPEC_TOKEN_TEST_ADDRESS(): address {
+   @0x2
+}
+
+ + + + + + + +
fun spec_abstract_total_value<TokenType>(): num {
+   global<TokenInfo<TokenType>>(SPEC_TOKEN_TEST_ADDRESS()).total_value
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/TransactionFee.md b/release/v13/docs/TransactionFee.md new file mode 100644 index 00000000..b6dcc89b --- /dev/null +++ b/release/v13/docs/TransactionFee.md @@ -0,0 +1,245 @@ + + + +# Module `0x1::TransactionFee` + +TransactionFee collect gas fees used by transactions in blocks temporarily. +Then they are distributed in TransactionManager. + + +- [Resource `TransactionFee`](#0x1_TransactionFee_TransactionFee) +- [Function `initialize`](#0x1_TransactionFee_initialize) +- [Function `add_txn_fee_token`](#0x1_TransactionFee_add_txn_fee_token) +- [Function `pay_fee`](#0x1_TransactionFee_pay_fee) +- [Function `distribute_transaction_fees`](#0x1_TransactionFee_distribute_transaction_fees) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::CoreAddresses;
+use 0x1::STC;
+use 0x1::Timestamp;
+use 0x1::Token;
+
+ + + + + +## Resource `TransactionFee` + +The TransactionFee resource holds a preburn resource for each +fiat TokenType that can be collected as a transaction fee. + + +
struct TransactionFee<TokenType> has key
+
+ + + +
+Fields + + +
+
+fee: Token::Token<TokenType> +
+
+ +
+
+ + +
+ + + +## Function `initialize` + +Called in genesis. Sets up the needed resources to collect transaction fees from the +TransactionFee resource with the TreasuryCompliance account. + + +
public fun initialize(account: &signer)
+
+ + + +
+Implementation + + +
public fun initialize(
+    account: &signer,
+) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+
+    // accept fees in all the currencies
+    add_txn_fee_token<STC>(account);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<TransactionFee<STC>>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `add_txn_fee_token` + +publishing a wrapper of the Preburn<TokenType> resource under fee_account + + +
fun add_txn_fee_token<TokenType: store>(account: &signer)
+
+ + + +
+Implementation + + +
fun add_txn_fee_token<TokenType: store>(
+    account: &signer,
+) {
+    move_to(
+        account,
+        TransactionFee<TokenType> {
+            fee: Token::zero(),
+        }
+    )
+ }
+
+ + + +
+ +
+Specification + + + +
aborts_if exists<TransactionFee<TokenType>>(Signer::address_of(account));
+
+ + + +
+ + + +## Function `pay_fee` + +Deposit token into the transaction fees bucket + + +
public fun pay_fee<TokenType: store>(token: Token::Token<TokenType>)
+
+ + + +
+Implementation + + +
public fun pay_fee<TokenType: store>(token: Token<TokenType>) acquires TransactionFee {
+    let txn_fees = borrow_global_mut<TransactionFee<TokenType>>(
+        CoreAddresses::GENESIS_ADDRESS()
+    );
+    Token::deposit(&mut txn_fees.fee, token)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<TransactionFee<TokenType>>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if global<TransactionFee<TokenType>>(CoreAddresses::GENESIS_ADDRESS()).fee.value + token.value > max_u128();
+
+ + + +
+ + + +## Function `distribute_transaction_fees` + +Distribute the transaction fees collected in the TokenType token. +If the TokenType is STC, it unpacks the token and preburns the +underlying fiat. + + +
public fun distribute_transaction_fees<TokenType: store>(account: &signer): Token::Token<TokenType>
+
+ + + +
+Implementation + + +
public fun distribute_transaction_fees<TokenType: store>(
+    account: &signer,
+): Token<TokenType> acquires TransactionFee {
+    let fee_address =  CoreAddresses::GENESIS_ADDRESS();
+    CoreAddresses::assert_genesis_address(account);
+
+    // extract fees
+    let txn_fees = borrow_global_mut<TransactionFee<TokenType>>(fee_address);
+    let value = Token::value<TokenType>(&txn_fees.fee);
+    if (value > 0) {
+        Token::withdraw(&mut txn_fees.fee, value)
+    }else {
+        Token::zero<TokenType>()
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/TransactionManager.md b/release/v13/docs/TransactionManager.md new file mode 100644 index 00000000..33f76b55 --- /dev/null +++ b/release/v13/docs/TransactionManager.md @@ -0,0 +1,759 @@ + + + +# Module `0x1::TransactionManager` + +TransactionManager manages: +1. prologue and epilogue of transactions. +2. prologue of blocks. + + +- [Constants](#@Constants_0) +- [Function `prologue`](#0x1_TransactionManager_prologue) +- [Function `epilogue`](#0x1_TransactionManager_epilogue) +- [Function `epilogue_v2`](#0x1_TransactionManager_epilogue_v2) +- [Function `block_prologue`](#0x1_TransactionManager_block_prologue) +- [Function `block_prologue_v2`](#0x1_TransactionManager_block_prologue_v2) +- [Function `txn_prologue_v2`](#0x1_TransactionManager_txn_prologue_v2) +- [Function `txn_epilogue_v3`](#0x1_TransactionManager_txn_epilogue_v3) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Account;
+use 0x1::Authenticator;
+use 0x1::Block;
+use 0x1::BlockReward;
+use 0x1::ChainId;
+use 0x1::CoreAddresses;
+use 0x1::EasyGas;
+use 0x1::Epoch;
+use 0x1::Errors;
+use 0x1::Hash;
+use 0x1::PackageTxnManager;
+use 0x1::STC;
+use 0x1::Signer;
+use 0x1::Timestamp;
+use 0x1::Token;
+use 0x1::TransactionFee;
+use 0x1::TransactionPublishOption;
+use 0x1::TransactionTimeout;
+use 0x1::Vector;
+
+ + + + + +## Constants + + + + + + +
const MAX_U64: u128 = 18446744073709551615;
+
+ + + + + + + +
const EDEPRECATED_FUNCTION: u64 = 19;
+
+ + + + + + + +
const EPROLOGUE_ACCOUNT_DOES_NOT_EXIST: u64 = 0;
+
+ + + + + + + +
const ECOIN_DEPOSIT_IS_ZERO: u64 = 15;
+
+ + + + + + + +
const EINSUFFICIENT_BALANCE: u64 = 10;
+
+ + + + + + + +
const EPROLOGUE_CANT_PAY_GAS_DEPOSIT: u64 = 4;
+
+ + + + + + + +
const EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY: u64 = 1;
+
+ + + + + + + +
const EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG: u64 = 9;
+
+ + + + + + + +
const EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW: u64 = 3;
+
+ + + + + + + +
const EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD: u64 = 2;
+
+ + + + + + + +
const EPROLOGUE_SIGNER_ALREADY_DELEGATED: u64 = 200;
+
+ + + + + + + +
const EPROLOGUE_BAD_CHAIN_ID: u64 = 6;
+
+ + + + + + + +
const EPROLOGUE_MODULE_NOT_ALLOWED: u64 = 7;
+
+ + + + + + + +
const EPROLOGUE_SCRIPT_NOT_ALLOWED: u64 = 8;
+
+ + + + + + + +
const EPROLOGUE_TRANSACTION_EXPIRED: u64 = 5;
+
+ + + + + + + +
const TXN_PAYLOAD_TYPE_PACKAGE: u8 = 1;
+
+ + + + + + + +
const TXN_PAYLOAD_TYPE_SCRIPT: u8 = 0;
+
+ + + + + + + +
const TXN_PAYLOAD_TYPE_SCRIPT_FUNCTION: u8 = 2;
+
+ + + + + +## Function `prologue` + +The prologue is invoked at the beginning of every transaction +It verifies: +- The account's auth key matches the transaction's public key +- That the account has enough balance to pay for all of the gas +- That the sequence number matches the transaction's sequence key + + +
public fun prologue<TokenType: store>(account: signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, txn_expiration_time: u64, chain_id: u8, txn_payload_type: u8, txn_script_or_package_hash: vector<u8>, txn_package_address: address)
+
+ + + +
+Implementation + + +
public fun prologue<TokenType: store>(
+    account: signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    txn_expiration_time: u64,
+    chain_id: u8,
+    txn_payload_type: u8,
+    txn_script_or_package_hash: vector<u8>,
+    txn_package_address: address,
+) {
+    // Can only be invoked by genesis account
+    assert!(
+        Signer::address_of(&account) == CoreAddresses::GENESIS_ADDRESS(),
+        Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST),
+    );
+    // Check that the chain ID stored on-chain matches the chain ID
+    // specified by the transaction
+    assert!(ChainId::get() == chain_id, Errors::invalid_argument(EPROLOGUE_BAD_CHAIN_ID));
+    let (stc_price,scaling_factor)= if (!STC::is_stc<TokenType>()){
+        (EasyGas::gas_oracle_read<TokenType>(), EasyGas::get_scaling_factor<TokenType>())
+    }else{
+        (1,1)
+    };
+
+    txn_prologue_v2<TokenType>(
+        &account,
+        txn_sender,
+        txn_sequence_number,
+        txn_authentication_key_preimage,
+        txn_gas_price,
+        txn_max_gas_units,
+        stc_price,
+        scaling_factor,
+    );
+    assert!(
+        TransactionTimeout::is_valid_transaction_timestamp(txn_expiration_time),
+        Errors::invalid_argument(EPROLOGUE_TRANSACTION_EXPIRED),
+    );
+    if (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE) {
+        // stdlib upgrade is not affected by PublishOption
+        if (txn_package_address != CoreAddresses::GENESIS_ADDRESS()) {
+            assert!(
+                TransactionPublishOption::is_module_allowed(Signer::address_of(&account)),
+                Errors::invalid_argument(EPROLOGUE_MODULE_NOT_ALLOWED),
+            );
+        };
+        PackageTxnManager::package_txn_prologue_v2(
+            &account,
+            txn_sender,
+            txn_package_address,
+            txn_script_or_package_hash,
+        );
+    } else if (txn_payload_type == TXN_PAYLOAD_TYPE_SCRIPT) {
+        assert!(
+            TransactionPublishOption::is_script_allowed(
+                Signer::address_of(&account),
+            ),
+            Errors::invalid_argument(EPROLOGUE_SCRIPT_NOT_ALLOWED),
+        );
+    };
+    // do nothing for TXN_PAYLOAD_TYPE_SCRIPT_FUNCTION
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<ChainId::ChainId>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if ChainId::get() != chain_id;
+aborts_if !exists<Account::Account>(txn_sender);
+aborts_if Hash::sha3_256(txn_authentication_key_preimage) != global<Account::Account>(txn_sender).authentication_key;
+aborts_if txn_gas_price * txn_max_gas_units > max_u64();
+include Timestamp::AbortsIfTimestampNotExists;
+include Block::AbortsIfBlockMetadataNotExist;
+aborts_if txn_gas_price * txn_max_gas_units > 0 && !exists<Account::Balance<TokenType>>(txn_sender);
+aborts_if txn_gas_price * txn_max_gas_units > 0 && txn_sequence_number >= max_u64();
+aborts_if txn_sequence_number < global<Account::Account>(txn_sender).sequence_number;
+aborts_if txn_sequence_number != global<Account::Account>(txn_sender).sequence_number;
+include TransactionTimeout::AbortsIfTimestampNotValid;
+aborts_if !TransactionTimeout::spec_is_valid_transaction_timestamp(txn_expiration_time);
+include TransactionPublishOption::AbortsIfTxnPublishOptionNotExistWithBool {
+    is_script_or_package: (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE || txn_payload_type == TXN_PAYLOAD_TYPE_SCRIPT),
+};
+aborts_if txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE && txn_package_address != CoreAddresses::GENESIS_ADDRESS() && !TransactionPublishOption::spec_is_module_allowed(Signer::address_of(account));
+aborts_if txn_payload_type == TXN_PAYLOAD_TYPE_SCRIPT && !TransactionPublishOption::spec_is_script_allowed(Signer::address_of(account));
+include PackageTxnManager::CheckPackageTxnAbortsIfWithType{is_package: (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE), sender:txn_sender, package_address: txn_package_address, package_hash: txn_script_or_package_hash};
+
+ + + +
+ + + +## Function `epilogue` + +The epilogue is invoked at the end of transactions. +It collects gas and bumps the sequence number + + +
public fun epilogue<TokenType: store>(account: signer, txn_sender: address, txn_sequence_number: u64, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, txn_payload_type: u8, _txn_script_or_package_hash: vector<u8>, txn_package_address: address, success: bool)
+
+ + + +
+Implementation + + +
public fun epilogue<TokenType: store>(
+    account: signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    gas_units_remaining: u64,
+    txn_payload_type: u8,
+    _txn_script_or_package_hash: vector<u8>,
+    txn_package_address: address,
+    // txn execute success or fail.
+    success: bool,
+) {
+    epilogue_v2<TokenType>(account, txn_sender, txn_sequence_number, Vector::empty(), txn_gas_price, txn_max_gas_units, gas_units_remaining, txn_payload_type, _txn_script_or_package_hash, txn_package_address, success)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+include CoreAddresses::AbortsIfNotGenesisAddress;
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<Account::Account>(txn_sender);
+aborts_if !exists<Account::Balance<TokenType>>(txn_sender);
+aborts_if txn_max_gas_units < gas_units_remaining;
+aborts_if txn_sequence_number + 1 > max_u64();
+aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > max_u64();
+include PackageTxnManager::AbortsIfPackageTxnEpilogue {
+    is_package: (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE),
+    package_address: txn_package_address,
+    success: success,
+};
+
+ + + +
+ + + +## Function `epilogue_v2` + +The epilogue is invoked at the end of transactions. +It collects gas and bumps the sequence number + + +
public fun epilogue_v2<TokenType: store>(account: signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, txn_payload_type: u8, _txn_script_or_package_hash: vector<u8>, txn_package_address: address, success: bool)
+
+ + + +
+Implementation + + +
public fun epilogue_v2<TokenType: store>(
+    account: signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    gas_units_remaining: u64,
+    txn_payload_type: u8,
+    _txn_script_or_package_hash: vector<u8>,
+    txn_package_address: address,
+    // txn execute success or fail.
+    success: bool,
+) {
+    CoreAddresses::assert_genesis_address(&account);
+    let (stc_price,scaling_factor) =
+    if (!STC::is_stc<TokenType>()){
+        (EasyGas::gas_oracle_read<TokenType>(), EasyGas::get_scaling_factor<TokenType>())
+    }else{
+        (1,1)
+    };
+    txn_epilogue_v3<TokenType>(
+        &account,
+        txn_sender,
+        txn_sequence_number,
+        txn_authentication_key_preimage,
+        txn_gas_price,
+        txn_max_gas_units,
+        gas_units_remaining,
+        stc_price,
+        scaling_factor
+    );
+    if (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE) {
+        PackageTxnManager::package_txn_epilogue(
+            &account,
+            txn_sender,
+            txn_package_address,
+            success,
+        );
+    }
+}
+
+ + + +
+ + + +## Function `block_prologue` + +Set the metadata for the current block and distribute transaction fees and block rewards. +The runtime always runs this before executing the transactions in a block. + + +
public fun block_prologue(account: signer, parent_hash: vector<u8>, timestamp: u64, author: address, auth_key_vec: vector<u8>, uncles: u64, number: u64, chain_id: u8, parent_gas_used: u64)
+
+ + + +
+Implementation + + +
public fun block_prologue(
+    account: signer,
+    parent_hash: vector<u8>,
+    timestamp: u64,
+    author: address,
+    auth_key_vec: vector<u8>,
+    uncles: u64,
+    number: u64,
+    chain_id: u8,
+    parent_gas_used: u64,
+) {
+    Self::block_prologue_v2(account, parent_hash, timestamp, author, auth_key_vec, uncles, number, chain_id, parent_gas_used, Vector::empty<u8>())
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `block_prologue_v2` + +Set the metadata for the current block and distribute transaction fees and block rewards. +The runtime always runs this before executing the transactions in a block. +For Flexidag block + + +
public fun block_prologue_v2(account: signer, parent_hash: vector<u8>, timestamp: u64, author: address, auth_key_vec: vector<u8>, uncles: u64, number: u64, chain_id: u8, parent_gas_used: u64, parents_hash: vector<u8>)
+
+ + + +
+Implementation + + +
public fun block_prologue_v2(
+    account: signer,
+    parent_hash: vector<u8>,
+    timestamp: u64,
+    author: address,
+    auth_key_vec: vector<u8>,
+    uncles: u64,
+    number: u64,
+    chain_id: u8,
+    parent_gas_used: u64,
+    parents_hash: vector<u8>,
+) {
+    // Can only be invoked by genesis account
+    CoreAddresses::assert_genesis_address(&account);
+    // Check that the chain ID stored on-chain matches the chain ID
+    // specified by the transaction
+    assert!(ChainId::get() == chain_id, Errors::invalid_argument(EPROLOGUE_BAD_CHAIN_ID));
+
+    // deal with previous block first.
+    let txn_fee = TransactionFee::distribute_transaction_fees<STC>(&account);
+
+    // then deal with current block.
+    Timestamp::update_global_time(&account, timestamp);
+    Block::process_block_metadata_v2(
+        &account,
+        parent_hash,
+        author,
+        timestamp,
+        uncles,
+        number,
+        parents_hash,
+    );
+    let reward = Epoch::adjust_epoch(&account, number, timestamp, uncles, parent_gas_used);
+    // pass in previous block gas fees.
+    BlockReward::process_block_reward(&account, number, reward, author, auth_key_vec, txn_fee);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `txn_prologue_v2` + + + +
public fun txn_prologue_v2<TokenType: store>(account: &signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, stc_price: u128, stc_price_scaling: u128)
+
+ + + +
+Implementation + + +
public fun txn_prologue_v2<TokenType: store>(
+    account: &signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    stc_price: u128,
+    stc_price_scaling: u128
+)  {
+    CoreAddresses::assert_genesis_address(account);
+
+    // Verify that the transaction sender's account exists
+    assert!(exists_at(txn_sender), Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST));
+    // Verify the account has not delegate its signer cap.
+    assert!(!is_signer_delegated(txn_sender), Errors::invalid_state(EPROLOGUE_SIGNER_ALREADY_DELEGATED));
+
+    // Load the transaction sender's account
+    //let sender_account = borrow_global_mut<Account>(txn_sender);
+    if (Account::is_dummy_auth_key_v2(txn_sender)){
+        // if sender's auth key is empty, use address as auth key for check transaction.
+        assert!(
+            Authenticator::derived_address(Hash::sha3_256(txn_authentication_key_preimage)) == txn_sender,
+            Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY)
+        );
+    }else{
+        // Check that the hash of the transaction's public key matches the account's auth key
+        assert!(
+            Hash::sha3_256(txn_authentication_key_preimage) == Account::authentication_key(txn_sender),
+            Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY)
+        );
+    };
+    // Check that the account has enough balance for all of the gas
+    let (max_transaction_fee_stc,max_transaction_fee_token) = transaction_fee_simulate(txn_gas_price,txn_max_gas_units,0, stc_price, stc_price_scaling);
+    assert!(
+        max_transaction_fee_stc <= MAX_U64,
+        Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT),
+    );
+    if (max_transaction_fee_stc > 0) {
+        assert!(
+            (txn_sequence_number as u128) < MAX_U64,
+            Errors::limit_exceeded(EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG)
+        );
+        let balance_amount_token = balance<TokenType>(txn_sender);
+        assert!(balance_amount_token >= max_transaction_fee_token, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT));
+        if (!is_stc<TokenType>()){
+            let gas_fee_address = EasyGas::get_gas_fee_address();
+            let balance_amount_stc= balance<STC>(gas_fee_address);
+            assert!(balance_amount_stc >= max_transaction_fee_stc, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT));
+        }
+    };
+    // Check that the transaction sequence number matches the sequence number of the account
+    assert!(txn_sequence_number >= Account::sequence_number(txn_sender), Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD));
+    assert!(txn_sequence_number == Account::sequence_number(txn_sender), Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW));
+
+}
+
+ + + +
+ + + +## Function `txn_epilogue_v3` + +The epilogue is invoked at the end of transactions. +It collects gas and bumps the sequence number + + +
public fun txn_epilogue_v3<TokenType: store>(account: &signer, txn_sender: address, txn_sequence_number: u64, txn_authentication_key_preimage: vector<u8>, txn_gas_price: u64, txn_max_gas_units: u64, gas_units_remaining: u64, stc_price: u128, stc_price_scaling: u128)
+
+ + + +
+Implementation + + +
public fun txn_epilogue_v3<TokenType: store>(
+    account: &signer,
+    txn_sender: address,
+    txn_sequence_number: u64,
+    txn_authentication_key_preimage: vector<u8>,
+    txn_gas_price: u64,
+    txn_max_gas_units: u64,
+    gas_units_remaining: u64,
+    stc_price: u128,
+    stc_price_scaling: u128,
+) {
+    CoreAddresses::assert_genesis_address(account);
+    // Charge for gas
+    let (transaction_fee_amount_stc,transaction_fee_amount_token) = transaction_fee_simulate(
+        txn_gas_price,
+        txn_max_gas_units,
+        gas_units_remaining,
+        stc_price,
+        stc_price_scaling);
+    assert!(
+        balance<TokenType>(txn_sender) >= transaction_fee_amount_token,
+        Errors::limit_exceeded(EINSUFFICIENT_BALANCE)
+    );
+
+    if (!is_stc<TokenType>()){
+        let gas_fee_address = EasyGas::get_gas_fee_address();
+        let genesis_balance_amount_stc=balance<STC>(gas_fee_address);
+        assert!(genesis_balance_amount_stc >= transaction_fee_amount_stc,
+            Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT)
+        );
+    };
+    // Bump the sequence number
+    Account::set_sequence_number(txn_sender,txn_sequence_number+1);
+    // Set auth key when user send transaction first.
+    if (Account::is_dummy_auth_key_v2(txn_sender) && !Vector::is_empty(&txn_authentication_key_preimage)){
+        Account::set_authentication_key(txn_sender, Hash::sha3_256(txn_authentication_key_preimage));
+    };
+
+    if (transaction_fee_amount_stc > 0) {
+        let transaction_fee_token = Account::withdraw_from_balance_v2<TokenType>(
+            txn_sender,
+            transaction_fee_amount_token
+        );
+        if(!is_stc<TokenType>()) {
+            let gas_fee_address = EasyGas::get_gas_fee_address();
+            Account::deposit<TokenType>(gas_fee_address, transaction_fee_token);
+            let stc_fee_token = Account::withdraw_from_balance_v2<STC>(gas_fee_address, transaction_fee_amount_stc);
+            TransactionFee::pay_fee(stc_fee_token);
+        }else{
+            TransactionFee::pay_fee(transaction_fee_token);
+        }
+    };
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if !exists<Account>(txn_sender);
+aborts_if !exists<Balance<TokenType>>(txn_sender);
+aborts_if txn_sequence_number + 1 > max_u64();
+aborts_if !exists<Balance<TokenType>>(txn_sender);
+aborts_if txn_max_gas_units < gas_units_remaining;
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/TransactionPublishOption.md b/release/v13/docs/TransactionPublishOption.md new file mode 100644 index 00000000..9507c62a --- /dev/null +++ b/release/v13/docs/TransactionPublishOption.md @@ -0,0 +1,348 @@ + + + +# Module `0x1::TransactionPublishOption` + +TransactionPublishOption provide an option to limit: +- whether user can use script or publish custom modules on chain. + + +- [Struct `TransactionPublishOption`](#0x1_TransactionPublishOption_TransactionPublishOption) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_TransactionPublishOption_initialize) +- [Function `new_transaction_publish_option`](#0x1_TransactionPublishOption_new_transaction_publish_option) +- [Function `is_script_allowed`](#0x1_TransactionPublishOption_is_script_allowed) +- [Function `is_module_allowed`](#0x1_TransactionPublishOption_is_module_allowed) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+use 0x1::CoreAddresses;
+use 0x1::Errors;
+use 0x1::Signer;
+use 0x1::Timestamp;
+
+ + + + + +## Struct `TransactionPublishOption` + +Defines and holds the publishing policies for the VM. There are three possible configurations: +1. !script_allowed && !module_publishing_allowed No module publishing, only script function in module are allowed. +2. script_allowed && !module_publishing_allowed No module publishing, custom scripts are allowed. +3. script_allowed && module_publishing_allowed Both module publishing and custom scripts are allowed. +We represent these as the following resource. + + +
struct TransactionPublishOption has copy, drop, store
+
+ + + +
+Fields + + +
+
+script_allowed: bool +
+
+ +
+
+module_publishing_allowed: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const EINVALID_ARGUMENT: u64 = 18;
+
+ + + + + +The script hash already exists in the allowlist + + +
const EALLOWLIST_ALREADY_CONTAINS_SCRIPT: u64 = 1002;
+
+ + + + + +The script hash has an invalid length + + +
const EINVALID_SCRIPT_HASH: u64 = 1001;
+
+ + + + + + + +
const EPROLOGUE_ACCOUNT_DOES_NOT_EXIST: u64 = 0;
+
+ + + + + + + +
const SCRIPT_HASH_LENGTH: u64 = 32;
+
+ + + + + +## Function `initialize` + +Module initialization. + + +
public fun initialize(account: &signer, script_allowed: bool, module_publishing_allowed: bool)
+
+ + + +
+Implementation + + +
public fun initialize(
+    account: &signer,
+    script_allowed: bool,
+    module_publishing_allowed: bool,
+) {
+    Timestamp::assert_genesis();
+    assert!(
+        Signer::address_of(account) == CoreAddresses::GENESIS_ADDRESS(),
+        Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST),
+    );
+    let transaction_publish_option = Self::new_transaction_publish_option(script_allowed, module_publishing_allowed);
+    Config::publish_new_config(
+        account,
+        transaction_publish_option,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+include Config::PublishNewConfigAbortsIf<TransactionPublishOption>;
+include Config::PublishNewConfigEnsures<TransactionPublishOption>;
+
+ + + +
+ + + +## Function `new_transaction_publish_option` + +Create a new option. Mainly used in DAO. + + +
public fun new_transaction_publish_option(script_allowed: bool, module_publishing_allowed: bool): TransactionPublishOption::TransactionPublishOption
+
+ + + +
+Implementation + + +
public fun new_transaction_publish_option(
+    script_allowed: bool,
+    module_publishing_allowed: bool,
+): TransactionPublishOption {
+    TransactionPublishOption { script_allowed, module_publishing_allowed }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `is_script_allowed` + +Check if sender can execute script with + + +
public fun is_script_allowed(account: address): bool
+
+ + + +
+Implementation + + +
public fun is_script_allowed(account: address): bool {
+    let publish_option = Config::get_by_address<TransactionPublishOption>(account);
+    publish_option.script_allowed
+}
+
+ + + +
+ +
+Specification + + + +
include Config::AbortsIfConfigNotExist<TransactionPublishOption>{
+    addr: account
+};
+
+ + + +
+ + + +## Function `is_module_allowed` + +Check if a sender can publish a module + + +
public fun is_module_allowed(account: address): bool
+
+ + + +
+Implementation + + +
public fun is_module_allowed(account: address): bool {
+    let publish_option = Config::get_by_address<TransactionPublishOption>(account);
+    publish_option.module_publishing_allowed
+}
+
+ + + +
+ +
+Specification + + + +
include Config::AbortsIfConfigNotExist<TransactionPublishOption>{
+    addr: account
+};
+
+ + + + + + + +
schema AbortsIfTxnPublishOptionNotExist {
+    include Config::AbortsIfConfigNotExist<TransactionPublishOption>{
+        addr: CoreAddresses::GENESIS_ADDRESS()
+    };
+}
+
+ + + + + + + +
schema AbortsIfTxnPublishOptionNotExistWithBool {
+    is_script_or_package : bool;
+    aborts_if is_script_or_package && !exists<Config::Config<TransactionPublishOption>>(CoreAddresses::GENESIS_ADDRESS());
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
+ + + + + + + +
fun spec_is_script_allowed(addr: address) : bool{
+   let publish_option = Config::get_by_address<TransactionPublishOption>(addr);
+   publish_option.script_allowed
+}
+
+ + + + + + + +
fun spec_is_module_allowed(addr: address) : bool{
+   let publish_option = Config::get_by_address<TransactionPublishOption>(addr);
+   publish_option.module_publishing_allowed
+}
+
diff --git a/release/v13/docs/TransactionTimeout.md b/release/v13/docs/TransactionTimeout.md new file mode 100644 index 00000000..01d2b3f2 --- /dev/null +++ b/release/v13/docs/TransactionTimeout.md @@ -0,0 +1,108 @@ + + + +# Module `0x1::TransactionTimeout` + +A module used to check expiration time of transactions. + + +- [Function `is_valid_transaction_timestamp`](#0x1_TransactionTimeout_is_valid_transaction_timestamp) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::Block;
+use 0x1::Timestamp;
+use 0x1::TransactionTimeoutConfig;
+
+ + + + + +## Function `is_valid_transaction_timestamp` + +Check whether the given timestamp is valid for transactions. + + +
public fun is_valid_transaction_timestamp(txn_timestamp: u64): bool
+
+ + + +
+Implementation + + +
public fun is_valid_transaction_timestamp(txn_timestamp: u64): bool {
+  let current_block_time = Timestamp::now_seconds();
+  let block_number = Block::get_current_block_number();
+  // before first block, just require txn_timestamp > genesis timestamp.
+  if (block_number == 0) {
+    return txn_timestamp > current_block_time
+  };
+  let timeout = TransactionTimeoutConfig::duration_seconds();
+  let max_txn_time = current_block_time + timeout;
+  current_block_time < txn_timestamp && txn_timestamp < max_txn_time
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if !exists<Block::BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+include Timestamp::AbortsIfTimestampNotExists;
+aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64();
+aborts_if Block::get_current_block_number() != 0 && !exists<Config::Config<TransactionTimeoutConfig::TransactionTimeoutConfig>>(CoreAddresses::GENESIS_ADDRESS());
+
+ + + + + + + +
schema AbortsIfTimestampNotValid {
+    aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+    aborts_if !exists<Block::BlockMetadataV2>(CoreAddresses::GENESIS_ADDRESS());
+    include Timestamp::AbortsIfTimestampNotExists;
+    aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64();
+    aborts_if Block::get_current_block_number() != 0 && !exists<Config::Config<TransactionTimeoutConfig::TransactionTimeoutConfig>>(CoreAddresses::GENESIS_ADDRESS());
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
+ + + + + + + +
fun spec_is_valid_transaction_timestamp(txn_timestamp: u64):bool {
+ if (Block::get_current_block_number() == 0) {
+   txn_timestamp > Timestamp::now_seconds()
+ } else {
+     Timestamp::now_seconds() < txn_timestamp && txn_timestamp <
+     (Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds())
+ }
+}
+
diff --git a/release/v13/docs/TransactionTimeoutConfig.md b/release/v13/docs/TransactionTimeoutConfig.md new file mode 100644 index 00000000..e0488861 --- /dev/null +++ b/release/v13/docs/TransactionTimeoutConfig.md @@ -0,0 +1,235 @@ + + + +# Module `0x1::TransactionTimeoutConfig` + +Onchain configuration for timeout setting of transaction. + + +- [Struct `TransactionTimeoutConfig`](#0x1_TransactionTimeoutConfig_TransactionTimeoutConfig) +- [Function `initialize`](#0x1_TransactionTimeoutConfig_initialize) +- [Function `new_transaction_timeout_config`](#0x1_TransactionTimeoutConfig_new_transaction_timeout_config) +- [Function `get_transaction_timeout_config`](#0x1_TransactionTimeoutConfig_get_transaction_timeout_config) +- [Function `duration_seconds`](#0x1_TransactionTimeoutConfig_duration_seconds) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::Config;
+use 0x1::CoreAddresses;
+use 0x1::Timestamp;
+
+ + + + + +## Struct `TransactionTimeoutConfig` + +config structs. + + +
struct TransactionTimeoutConfig has copy, drop, store
+
+ + + +
+Fields + + +
+
+duration_seconds: u64 +
+
+ timeout in second. +
+
+ + +
+ + + +## Function `initialize` + +Initialize function. Should only be called in genesis. + + +
public fun initialize(account: &signer, duration_seconds: u64)
+
+ + + +
+Implementation + + +
public fun initialize(account: &signer, duration_seconds: u64) {
+    Timestamp::assert_genesis();
+    CoreAddresses::assert_genesis_address(account);
+
+    Config::publish_new_config<Self::TransactionTimeoutConfig>(
+        account,
+        new_transaction_timeout_config(duration_seconds)
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !Timestamp::is_genesis();
+aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+include Config::PublishNewConfigAbortsIf<TransactionTimeoutConfig>;
+include Config::PublishNewConfigEnsures<TransactionTimeoutConfig>;
+
+ + + +
+ + + +## Function `new_transaction_timeout_config` + +Create a new timeout config used in dao proposal. + + +
public fun new_transaction_timeout_config(duration_seconds: u64): TransactionTimeoutConfig::TransactionTimeoutConfig
+
+ + + +
+Implementation + + +
public fun new_transaction_timeout_config(duration_seconds: u64) : TransactionTimeoutConfig {
+    TransactionTimeoutConfig {duration_seconds: duration_seconds}
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `get_transaction_timeout_config` + +Get current timeout config. + + +
public fun get_transaction_timeout_config(): TransactionTimeoutConfig::TransactionTimeoutConfig
+
+ + + +
+Implementation + + +
public fun get_transaction_timeout_config(): TransactionTimeoutConfig {
+    Config::get_by_address<TransactionTimeoutConfig>(CoreAddresses::GENESIS_ADDRESS())
+}
+
+ + + +
+ +
+Specification + + + +
include Config::AbortsIfConfigNotExist<TransactionTimeoutConfig>{
+    addr: CoreAddresses::GENESIS_ADDRESS()
+};
+
+ + + +
+ + + +## Function `duration_seconds` + +Get current txn timeout in seconds. + + +
public fun duration_seconds(): u64
+
+ + + +
+Implementation + + +
public fun duration_seconds() :u64 {
+    let config = get_transaction_timeout_config();
+    config.duration_seconds
+}
+
+ + + +
+ +
+Specification + + + +
include Config::AbortsIfConfigNotExist<TransactionTimeoutConfig>{
+    addr: CoreAddresses::GENESIS_ADDRESS()
+};
+
+ + + + + + + +
schema AbortsIfTxnTimeoutConfigNotExist {
+    include Config::AbortsIfConfigNotExist<TransactionTimeoutConfig>{
+        addr: CoreAddresses::GENESIS_ADDRESS()
+    };
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict = true;
+
diff --git a/release/v13/docs/TransferScripts.md b/release/v13/docs/TransferScripts.md new file mode 100644 index 00000000..f9227cb6 --- /dev/null +++ b/release/v13/docs/TransferScripts.md @@ -0,0 +1,252 @@ + + + +# Module `0x1::TransferScripts` + + + +- [Constants](#@Constants_0) +- [Function `peer_to_peer`](#0x1_TransferScripts_peer_to_peer) +- [Function `peer_to_peer_v2`](#0x1_TransferScripts_peer_to_peer_v2) +- [Function `batch_peer_to_peer`](#0x1_TransferScripts_batch_peer_to_peer) +- [Function `batch_peer_to_peer_v2`](#0x1_TransferScripts_batch_peer_to_peer_v2) +- [Function `peer_to_peer_batch`](#0x1_TransferScripts_peer_to_peer_batch) +- [Function `peer_to_peer_with_metadata`](#0x1_TransferScripts_peer_to_peer_with_metadata) +- [Function `peer_to_peer_with_metadata_v2`](#0x1_TransferScripts_peer_to_peer_with_metadata_v2) + + +
use 0x1::Account;
+use 0x1::Errors;
+
+ + + + + +## Constants + + + + + + +
const EDEPRECATED_FUNCTION: u64 = 19;
+
+ + + + + + + +
const EADDRESS_AND_AUTH_KEY_MISMATCH: u64 = 101;
+
+ + + + + + + +
const ELENGTH_MISMATCH: u64 = 102;
+
+ + + + + +## Function `peer_to_peer` + + + +
public entry fun peer_to_peer<TokenType: store>(account: signer, payee: address, _payee_auth_key: vector<u8>, amount: u128)
+
+ + + +
+Implementation + + +
public entry fun peer_to_peer<TokenType: store>(account: signer, payee: address, _payee_auth_key: vector<u8>, amount: u128) {
+     peer_to_peer_v2<TokenType>(account, payee, amount)
+}
+
+ + + +
+ + + +## Function `peer_to_peer_v2` + + + +
public entry fun peer_to_peer_v2<TokenType: store>(account: signer, payee: address, amount: u128)
+
+ + + +
+Implementation + + +
public entry fun peer_to_peer_v2<TokenType: store>(account: signer, payee: address, amount: u128) {
+    if (!Account::exists_at(payee)) {
+        Account::create_account_with_address<TokenType>(payee);
+    };
+    Account::pay_from<TokenType>(&account, payee, amount)
+}
+
+ + + +
+ + + +## Function `batch_peer_to_peer` + +Batch transfer token to others. + + +
public entry fun batch_peer_to_peer<TokenType: store>(account: signer, payeees: vector<address>, _payee_auth_keys: vector<vector<u8>>, amounts: vector<u128>)
+
+ + + +
+Implementation + + +
public entry fun batch_peer_to_peer<TokenType: store>(account: signer, payeees: vector<address>, _payee_auth_keys: vector<vector<u8>>, amounts: vector<u128>) {
+     batch_peer_to_peer_v2<TokenType>(account, payeees, amounts)
+}
+
+ + + +
+ + + +## Function `batch_peer_to_peer_v2` + +Batch transfer token to others. + + +
public entry fun batch_peer_to_peer_v2<TokenType: store>(account: signer, payeees: vector<address>, amounts: vector<u128>)
+
+ + + +
+Implementation + + +
public entry fun batch_peer_to_peer_v2<TokenType: store>(account: signer, payeees: vector<address>, amounts: vector<u128>) {
+    let len = Vector::length(&payeees);
+    assert!(len == Vector::length(&amounts), ELENGTH_MISMATCH);
+    let i = 0;
+    while (i < len){
+        let payee = *Vector::borrow(&payeees, i);
+        if (!Account::exists_at(payee)) {
+            Account::create_account_with_address<TokenType>(payee);
+        };
+        let amount = *Vector::borrow(&amounts, i);
+        Account::pay_from<TokenType>(&account, payee, amount);
+        i = i + 1;
+    }
+}
+
+ + + +
+ + + +## Function `peer_to_peer_batch` + + + +
public entry fun peer_to_peer_batch<TokenType: store>(_account: signer, _payeees: vector<u8>, _payee_auth_keys: vector<u8>, _amount: u128)
+
+ + + +
+Implementation + + +
public entry fun peer_to_peer_batch<TokenType: store>(_account: signer, _payeees: vector<u8>, _payee_auth_keys: vector<u8>, _amount: u128) {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ + + +## Function `peer_to_peer_with_metadata` + + + +
public entry fun peer_to_peer_with_metadata<TokenType: store>(account: signer, payee: address, _payee_auth_key: vector<u8>, amount: u128, metadata: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun peer_to_peer_with_metadata<TokenType: store>(
+    account: signer,
+    payee: address,
+    _payee_auth_key: vector<u8>,
+    amount: u128,
+    metadata: vector<u8>,
+) {
+     peer_to_peer_with_metadata_v2<TokenType>(account, payee, amount, metadata)
+}
+
+ + + +
+ + + +## Function `peer_to_peer_with_metadata_v2` + + + +
public entry fun peer_to_peer_with_metadata_v2<TokenType: store>(account: signer, payee: address, amount: u128, metadata: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun peer_to_peer_with_metadata_v2<TokenType: store>(
+        account: signer,
+        payee: address,
+        amount: u128,
+        metadata: vector<u8>,
+) {
+    if (!Account::exists_at(payee)) {
+        Account::create_account_with_address<TokenType>(payee);
+    };
+    Account::pay_from_with_metadata<TokenType>(&account,payee, amount, metadata)
+}
+
+ + + +
diff --git a/release/v13/docs/Treasury.md b/release/v13/docs/Treasury.md new file mode 100644 index 00000000..09400578 --- /dev/null +++ b/release/v13/docs/Treasury.md @@ -0,0 +1,1230 @@ + + + +# Module `0x1::Treasury` + +The module for the Treasury of DAO, which can hold the token of DAO. + + +- [Resource `Treasury`](#0x1_Treasury_Treasury) +- [Resource `WithdrawCapability`](#0x1_Treasury_WithdrawCapability) +- [Resource `LinearWithdrawCapability`](#0x1_Treasury_LinearWithdrawCapability) +- [Struct `WithdrawEvent`](#0x1_Treasury_WithdrawEvent) +- [Struct `DepositEvent`](#0x1_Treasury_DepositEvent) +- [Constants](#@Constants_0) +- [Function `initialize`](#0x1_Treasury_initialize) +- [Function `exists_at`](#0x1_Treasury_exists_at) +- [Function `balance`](#0x1_Treasury_balance) +- [Function `deposit`](#0x1_Treasury_deposit) +- [Function `do_withdraw`](#0x1_Treasury_do_withdraw) +- [Function `withdraw_with_capability`](#0x1_Treasury_withdraw_with_capability) +- [Function `withdraw`](#0x1_Treasury_withdraw) +- [Function `issue_linear_withdraw_capability`](#0x1_Treasury_issue_linear_withdraw_capability) +- [Function `withdraw_with_linear_capability`](#0x1_Treasury_withdraw_with_linear_capability) +- [Function `withdraw_by_linear`](#0x1_Treasury_withdraw_by_linear) +- [Function `split_linear_withdraw_cap`](#0x1_Treasury_split_linear_withdraw_cap) +- [Function `withdraw_amount_of_linear_cap`](#0x1_Treasury_withdraw_amount_of_linear_cap) +- [Function `is_empty_linear_withdraw_cap`](#0x1_Treasury_is_empty_linear_withdraw_cap) +- [Function `remove_withdraw_capability`](#0x1_Treasury_remove_withdraw_capability) +- [Function `add_withdraw_capability`](#0x1_Treasury_add_withdraw_capability) +- [Function `destroy_withdraw_capability`](#0x1_Treasury_destroy_withdraw_capability) +- [Function `add_linear_withdraw_capability`](#0x1_Treasury_add_linear_withdraw_capability) +- [Function `remove_linear_withdraw_capability`](#0x1_Treasury_remove_linear_withdraw_capability) +- [Function `destroy_linear_withdraw_capability`](#0x1_Treasury_destroy_linear_withdraw_capability) +- [Function `is_empty_linear_withdraw_capability`](#0x1_Treasury_is_empty_linear_withdraw_capability) +- [Function `get_linear_withdraw_capability_total`](#0x1_Treasury_get_linear_withdraw_capability_total) +- [Function `get_linear_withdraw_capability_withdraw`](#0x1_Treasury_get_linear_withdraw_capability_withdraw) +- [Function `get_linear_withdraw_capability_period`](#0x1_Treasury_get_linear_withdraw_capability_period) +- [Function `get_linear_withdraw_capability_start_time`](#0x1_Treasury_get_linear_withdraw_capability_start_time) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Event;
+use 0x1::Math;
+use 0x1::Signer;
+use 0x1::Timestamp;
+use 0x1::Token;
+
+ + + + + +## Resource `Treasury` + + + +
struct Treasury<TokenT> has store, key
+
+ + + +
+Fields + + +
+
+balance: Token::Token<TokenT> +
+
+ +
+
+withdraw_events: Event::EventHandle<Treasury::WithdrawEvent> +
+
+ event handle for treasury withdraw event +
+
+deposit_events: Event::EventHandle<Treasury::DepositEvent> +
+
+ event handle for treasury deposit event +
+
+ + +
+ +
+Specification + + + +
+ + + +## Resource `WithdrawCapability` + +A withdraw capability allows tokens of type TokenT to be withdraw from Treasury. + + +
struct WithdrawCapability<TokenT> has store, key
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Resource `LinearWithdrawCapability` + +A linear time withdraw capability which can withdraw token from Treasury in a period by time-based linear release. + + +
struct LinearWithdrawCapability<TokenT> has store, key
+
+ + + +
+Fields + + +
+
+total: u128 +
+
+ The total amount of tokens that can be withdrawn by this capability +
+
+withdraw: u128 +
+
+ The amount of tokens that have been withdrawn by this capability +
+
+start_time: u64 +
+
+ The time-based linear release start time, timestamp in seconds. +
+
+period: u64 +
+
+ The time-based linear release period in seconds +
+
+ + +
+ + + +## Struct `WithdrawEvent` + +Message for treasury withdraw event. + + +
struct WithdrawEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u128 +
+
+ +
+
+ + +
+ + + +## Struct `DepositEvent` + +Message for treasury deposit event. + + +
struct DepositEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u128 +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_INVALID_PERIOD: u64 = 101;
+
+ + + + + + + +
const ERR_NOT_AUTHORIZED: u64 = 104;
+
+ + + + + + + +
const ERR_TOO_BIG_AMOUNT: u64 = 103;
+
+ + + + + + + +
const ERR_TREASURY_NOT_EXIST: u64 = 105;
+
+ + + + + + + +
const ERR_ZERO_AMOUNT: u64 = 102;
+
+ + + + + +## Function `initialize` + +Init a Treasury for TokenT. Can only be called by token issuer. + + +
public fun initialize<TokenT: store>(signer: &signer, init_token: Token::Token<TokenT>): Treasury::WithdrawCapability<TokenT>
+
+ + + +
+Implementation + + +
public fun initialize<TokenT: store>(signer: &signer, init_token: Token<TokenT>): WithdrawCapability<TokenT> {
+    let token_issuer = Token::token_address<TokenT>();
+    assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    let treasure = Treasury {
+        balance: init_token,
+        withdraw_events: Event::new_event_handle<WithdrawEvent>(signer),
+        deposit_events: Event::new_event_handle<DepositEvent>(signer),
+    };
+    move_to(signer, treasure);
+    WithdrawCapability<TokenT>{}
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(signer) != Token::SPEC_TOKEN_TEST_ADDRESS();
+aborts_if exists<Treasury<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+ensures exists<Treasury<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+ensures result == WithdrawCapability<TokenT>{};
+
+ + + +
+ + + +## Function `exists_at` + +Check the Treasury of TokenT is exists. + + +
public fun exists_at<TokenT: store>(): bool
+
+ + + +
+Implementation + + +
public fun exists_at<TokenT: store>(): bool {
+    let token_issuer = Token::token_address<TokenT>();
+    exists<Treasury<TokenT>>(token_issuer)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == exists<Treasury<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+
+ + + +
+ + + +## Function `balance` + +Get the balance of TokenT's Treasury +if the Treasury do not exists, return 0. + + +
public fun balance<TokenT: store>(): u128
+
+ + + +
+Implementation + + +
public fun balance<TokenT:store>(): u128 acquires Treasury {
+    let token_issuer = Token::token_address<TokenT>();
+    if (!exists<Treasury<TokenT>>(token_issuer)) {
+        return 0
+    };
+    let treasury = borrow_global<Treasury<TokenT>>(token_issuer);
+    Token::value(&treasury.balance)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures if (exists<Treasury<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS()))
+            result == spec_balance<TokenT>()
+        else
+            result == 0;
+
+ + + +
+ + + +## Function `deposit` + + + +
public fun deposit<TokenT: store>(token: Token::Token<TokenT>)
+
+ + + +
+Implementation + + +
public fun deposit<TokenT: store>(token: Token<TokenT>) acquires Treasury {
+    assert!(exists_at<TokenT>(), Errors::not_published(ERR_TREASURY_NOT_EXIST));
+    let token_address = Token::token_address<TokenT>();
+    let treasury = borrow_global_mut<Treasury<TokenT>>(token_address);
+    let amount = Token::value(&token);
+    Event::emit_event(
+        &mut treasury.deposit_events,
+        DepositEvent { amount },
+    );
+    Token::deposit(&mut treasury.balance, token);
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Treasury<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+aborts_if spec_balance<TokenT>() + token.value > MAX_U128;
+ensures spec_balance<TokenT>() == old(spec_balance<TokenT>()) + token.value;
+
+ + + +
+ + + +## Function `do_withdraw` + + + +
fun do_withdraw<TokenT: store>(amount: u128): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
fun do_withdraw<TokenT: store>(amount: u128): Token<TokenT> acquires Treasury {
+    assert!(amount > 0, Errors::invalid_argument(ERR_ZERO_AMOUNT));
+    assert!(exists_at<TokenT>(), Errors::not_published(ERR_TREASURY_NOT_EXIST));
+    let token_address = Token::token_address<TokenT>();
+    let treasury = borrow_global_mut<Treasury<TokenT>>(token_address);
+    assert!(amount <= Token::value(&treasury.balance) , Errors::invalid_argument(ERR_TOO_BIG_AMOUNT));
+    Event::emit_event(
+        &mut treasury.withdraw_events,
+        WithdrawEvent { amount },
+    );
+    Token::withdraw(&mut treasury.balance, amount)
+}
+
+ + + +
+ +
+Specification + + + +
include WithdrawSchema<TokenT>;
+
+ + + + + + + +
schema WithdrawSchema<TokenT> {
+    amount: u64;
+    aborts_if amount <= 0;
+    aborts_if !exists<Treasury<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+    aborts_if spec_balance<TokenT>() < amount;
+    ensures spec_balance<TokenT>() ==
+        old(spec_balance<TokenT>()) - amount;
+}
+
+ + + +
+ + + +## Function `withdraw_with_capability` + +Withdraw tokens with given LinearWithdrawCapability. + + +
public fun withdraw_with_capability<TokenT: store>(_cap: &mut Treasury::WithdrawCapability<TokenT>, amount: u128): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
public fun withdraw_with_capability<TokenT: store>(
+    _cap: &mut WithdrawCapability<TokenT>,
+    amount: u128,
+): Token<TokenT> acquires Treasury {
+    do_withdraw(amount)
+}
+
+ + + +
+ +
+Specification + + + +
include WithdrawSchema<TokenT>;
+
+ + + +
+ + + +## Function `withdraw` + +Withdraw from TokenT's treasury, the signer must have WithdrawCapability + + +
public fun withdraw<TokenT: store>(signer: &signer, amount: u128): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
public fun withdraw<TokenT: store>(
+    signer: &signer,
+    amount: u128
+): Token<TokenT> acquires Treasury, WithdrawCapability {
+    let cap = borrow_global_mut<WithdrawCapability<TokenT>>(Signer::address_of(signer));
+    Self::withdraw_with_capability(cap, amount)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<WithdrawCapability<TokenT>>(Signer::address_of(signer));
+include WithdrawSchema<TokenT>;
+
+ + + +
+ + + +## Function `issue_linear_withdraw_capability` + +Issue a LinearWithdrawCapability with given WithdrawCapability. + + +
public fun issue_linear_withdraw_capability<TokenT: store>(_capability: &mut Treasury::WithdrawCapability<TokenT>, amount: u128, period: u64): Treasury::LinearWithdrawCapability<TokenT>
+
+ + + +
+Implementation + + +
public fun issue_linear_withdraw_capability<TokenT: store>(
+    _capability: &mut WithdrawCapability<TokenT>,
+    amount: u128,
+    period: u64
+): LinearWithdrawCapability<TokenT> {
+    assert!(period > 0, Errors::invalid_argument(ERR_INVALID_PERIOD));
+    assert!(amount > 0, Errors::invalid_argument(ERR_ZERO_AMOUNT));
+    let start_time = Timestamp::now_seconds();
+    LinearWithdrawCapability<TokenT> {
+        total: amount,
+        withdraw: 0,
+        start_time,
+        period,
+    }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if period == 0;
+aborts_if amount == 0;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(StarcoinFramework::CoreAddresses::GENESIS_ADDRESS());
+
+ + + +
+ + + +## Function `withdraw_with_linear_capability` + +Withdraw tokens with given LinearWithdrawCapability. + + +
public fun withdraw_with_linear_capability<TokenT: store>(cap: &mut Treasury::LinearWithdrawCapability<TokenT>): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
public fun withdraw_with_linear_capability<TokenT: store>(
+    cap: &mut LinearWithdrawCapability<TokenT>,
+): Token<TokenT> acquires Treasury {
+    let amount = withdraw_amount_of_linear_cap(cap);
+    let token = do_withdraw(amount);
+    cap.withdraw = cap.withdraw + amount;
+    token
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial;
+
+ + + +
+ + + +## Function `withdraw_by_linear` + +Withdraw from TokenT's treasury, the signer must have LinearWithdrawCapability + + +
public fun withdraw_by_linear<TokenT: store>(signer: &signer): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
public fun withdraw_by_linear<TokenT:store>(
+    signer: &signer,
+): Token<TokenT> acquires Treasury, LinearWithdrawCapability {
+    let cap = borrow_global_mut<LinearWithdrawCapability<TokenT>>(Signer::address_of(signer));
+    Self::withdraw_with_linear_capability(cap)
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial;
+aborts_if !exists<LinearWithdrawCapability<TokenT>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `split_linear_withdraw_cap` + +Split the given LinearWithdrawCapability. + + +
public fun split_linear_withdraw_cap<TokenT: store>(cap: &mut Treasury::LinearWithdrawCapability<TokenT>, amount: u128): (Token::Token<TokenT>, Treasury::LinearWithdrawCapability<TokenT>)
+
+ + + +
+Implementation + + +
public fun split_linear_withdraw_cap<TokenT: store>(
+    cap: &mut LinearWithdrawCapability<TokenT>,
+    amount: u128,
+): (Token<TokenT>, LinearWithdrawCapability<TokenT>) acquires Treasury {
+    assert!(amount > 0, Errors::invalid_argument(ERR_ZERO_AMOUNT));
+    let token = Self::withdraw_with_linear_capability(cap);
+    assert!((cap.withdraw + amount) <= cap.total, Errors::invalid_argument(ERR_TOO_BIG_AMOUNT));
+    cap.total = cap.total - amount;
+    let start_time = Timestamp::now_seconds();
+    let new_period = cap.start_time + cap.period - start_time;
+    let new_key = LinearWithdrawCapability<TokenT> {
+        total: amount,
+        withdraw: 0,
+        start_time,
+        period: new_period
+    };
+    (token, new_key)
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial;
+ensures old(cap.total - cap.withdraw) ==
+    result_1.value + (result_2.total - result_2.withdraw) + (cap.total - cap.withdraw);
+
+ + + +
+ + + +## Function `withdraw_amount_of_linear_cap` + +Returns the amount of the LinearWithdrawCapability can mint now. + + +
public fun withdraw_amount_of_linear_cap<TokenT: store>(cap: &Treasury::LinearWithdrawCapability<TokenT>): u128
+
+ + + +
+Implementation + + +
public fun withdraw_amount_of_linear_cap<TokenT: store>(cap: &LinearWithdrawCapability<TokenT>): u128 {
+    let now = Timestamp::now_seconds();
+    let elapsed_time = now - cap.start_time;
+    if (elapsed_time >= cap.period) {
+        cap.total - cap.withdraw
+    } else {
+        Math::mul_div(cap.total, (elapsed_time as u128), (cap.period as u128)) - cap.withdraw
+    }
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(StarcoinFramework::CoreAddresses::GENESIS_ADDRESS());
+aborts_if Timestamp::spec_now_seconds() < cap.start_time;
+aborts_if Timestamp::spec_now_seconds() - cap.start_time >= cap.period && cap.total < cap.withdraw;
+aborts_if [abstract]
+    Timestamp::spec_now_seconds() - cap.start_time < cap.period && Math::spec_mul_div() < cap.withdraw;
+ensures [abstract] result <= cap.total - cap.withdraw;
+
+ + + +
+ + + +## Function `is_empty_linear_withdraw_cap` + +Check if the given LinearWithdrawCapability is empty. + + +
public fun is_empty_linear_withdraw_cap<TokenT: store>(key: &Treasury::LinearWithdrawCapability<TokenT>): bool
+
+ + + +
+Implementation + + +
public fun is_empty_linear_withdraw_cap<TokenT:store>(key: &LinearWithdrawCapability<TokenT>) : bool {
+    key.total == key.withdraw
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == (key.total == key.withdraw);
+
+ + + +
+ + + +## Function `remove_withdraw_capability` + +Remove mint capability from signer. + + +
public fun remove_withdraw_capability<TokenT: store>(signer: &signer): Treasury::WithdrawCapability<TokenT>
+
+ + + +
+Implementation + + +
public fun remove_withdraw_capability<TokenT: store>(signer: &signer): WithdrawCapability<TokenT>
+acquires WithdrawCapability {
+    move_from<WithdrawCapability<TokenT>>(Signer::address_of(signer))
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<WithdrawCapability<TokenT>>(Signer::address_of(signer));
+ensures !exists<WithdrawCapability<TokenT>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `add_withdraw_capability` + +Save mint capability to signer. + + +
public fun add_withdraw_capability<TokenT: store>(signer: &signer, cap: Treasury::WithdrawCapability<TokenT>)
+
+ + + +
+Implementation + + +
public fun add_withdraw_capability<TokenT: store>(signer: &signer, cap: WithdrawCapability<TokenT>) {
+    move_to(signer, cap)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if exists<WithdrawCapability<TokenT>>(Signer::address_of(signer));
+ensures exists<WithdrawCapability<TokenT>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `destroy_withdraw_capability` + +Destroy the given mint capability. + + +
public fun destroy_withdraw_capability<TokenT: store>(cap: Treasury::WithdrawCapability<TokenT>)
+
+ + + +
+Implementation + + +
public fun destroy_withdraw_capability<TokenT: store>(cap: WithdrawCapability<TokenT>) {
+    let WithdrawCapability<TokenT> {} = cap;
+}
+
+ + + +
+ +
+Specification + + + +
+ + + +## Function `add_linear_withdraw_capability` + +Add LinearWithdrawCapability to signer, a address only can have one LinearWithdrawCapability + + +
public fun add_linear_withdraw_capability<TokenT: store>(signer: &signer, cap: Treasury::LinearWithdrawCapability<TokenT>)
+
+ + + +
+Implementation + + +
public fun add_linear_withdraw_capability<TokenT: store>(signer: &signer, cap: LinearWithdrawCapability<TokenT>) {
+    move_to(signer, cap)
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if exists<LinearWithdrawCapability<TokenT>>(Signer::address_of(signer));
+ensures exists<LinearWithdrawCapability<TokenT>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `remove_linear_withdraw_capability` + +Remove LinearWithdrawCapability from signer. + + +
public fun remove_linear_withdraw_capability<TokenT: store>(signer: &signer): Treasury::LinearWithdrawCapability<TokenT>
+
+ + + +
+Implementation + + +
public fun remove_linear_withdraw_capability<TokenT: store>(signer: &signer): LinearWithdrawCapability<TokenT>
+acquires LinearWithdrawCapability {
+    move_from<LinearWithdrawCapability<TokenT>>(Signer::address_of(signer))
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<LinearWithdrawCapability<TokenT>>(Signer::address_of(signer));
+ensures !exists<LinearWithdrawCapability<TokenT>>(Signer::address_of(signer));
+
+ + + +
+ + + +## Function `destroy_linear_withdraw_capability` + +Destroy LinearWithdrawCapability. + + +
public fun destroy_linear_withdraw_capability<TokenT: store>(cap: Treasury::LinearWithdrawCapability<TokenT>)
+
+ + + +
+Implementation + + +
public fun destroy_linear_withdraw_capability<TokenT: store>(cap: LinearWithdrawCapability<TokenT>) {
+    let LinearWithdrawCapability{ total: _, withdraw: _, start_time: _, period: _ } = cap;
+}
+
+ + + +
+ + + +## Function `is_empty_linear_withdraw_capability` + + + +
public fun is_empty_linear_withdraw_capability<TokenT: store>(cap: &Treasury::LinearWithdrawCapability<TokenT>): bool
+
+ + + +
+Implementation + + +
public fun is_empty_linear_withdraw_capability<TokenT: store>(cap: &LinearWithdrawCapability<TokenT>): bool {
+    cap.total == cap.withdraw
+}
+
+ + + +
+ + + +## Function `get_linear_withdraw_capability_total` + +Get LinearWithdrawCapability total amount + + +
public fun get_linear_withdraw_capability_total<TokenT: store>(cap: &Treasury::LinearWithdrawCapability<TokenT>): u128
+
+ + + +
+Implementation + + +
public fun get_linear_withdraw_capability_total<TokenT: store>(cap: &LinearWithdrawCapability<TokenT>): u128 {
+    cap.total
+}
+
+ + + +
+ + + +## Function `get_linear_withdraw_capability_withdraw` + +Get LinearWithdrawCapability withdraw amount + + +
public fun get_linear_withdraw_capability_withdraw<TokenT: store>(cap: &Treasury::LinearWithdrawCapability<TokenT>): u128
+
+ + + +
+Implementation + + +
public fun get_linear_withdraw_capability_withdraw<TokenT: store>(cap: &LinearWithdrawCapability<TokenT>): u128 {
+    cap.withdraw
+}
+
+ + + +
+ + + +## Function `get_linear_withdraw_capability_period` + +Get LinearWithdrawCapability period in seconds + + +
public fun get_linear_withdraw_capability_period<TokenT: store>(cap: &Treasury::LinearWithdrawCapability<TokenT>): u64
+
+ + + +
+Implementation + + +
public fun get_linear_withdraw_capability_period<TokenT: store>(cap: &LinearWithdrawCapability<TokenT>): u64 {
+    cap.period
+}
+
+ + + +
+ + + +## Function `get_linear_withdraw_capability_start_time` + +Get LinearWithdrawCapability start_time in seconds + + +
public fun get_linear_withdraw_capability_start_time<TokenT: store>(cap: &Treasury::LinearWithdrawCapability<TokenT>): u64
+
+ + + +
+Implementation + + +
public fun get_linear_withdraw_capability_start_time<TokenT: store>(cap: &LinearWithdrawCapability<TokenT>): u64 {
+    cap.start_time
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
+ + + + + + + +
fun spec_balance<TokenType>(): num {
+   global<Treasury<TokenType>>(Token::SPEC_TOKEN_TEST_ADDRESS()).balance.value
+}
+
diff --git a/release/v13/docs/TreasuryScripts.md b/release/v13/docs/TreasuryScripts.md new file mode 100644 index 00000000..68c60d2f --- /dev/null +++ b/release/v13/docs/TreasuryScripts.md @@ -0,0 +1,204 @@ + + + +# Module `0x1::TreasuryScripts` + + + +- [Function `withdraw_and_split_lt_withdraw_cap`](#0x1_TreasuryScripts_withdraw_and_split_lt_withdraw_cap) +- [Function `withdraw_token_with_linear_withdraw_capability`](#0x1_TreasuryScripts_withdraw_token_with_linear_withdraw_capability) +- [Function `propose_withdraw`](#0x1_TreasuryScripts_propose_withdraw) +- [Function `execute_withdraw_proposal`](#0x1_TreasuryScripts_execute_withdraw_proposal) + + +
use 0x1::Account;
+use 0x1::Offer;
+use 0x1::Token;
+use 0x1::Treasury;
+use 0x1::TreasuryWithdrawDaoProposal;
+
+ + + + + +## Function `withdraw_and_split_lt_withdraw_cap` + + + +
public entry fun withdraw_and_split_lt_withdraw_cap<TokenT: store>(signer: signer, for_address: address, amount: u128, lock_period: u64)
+
+ + + +
+Implementation + + +
public entry fun withdraw_and_split_lt_withdraw_cap<TokenT: store>(
+    signer: signer,
+    for_address: address,
+    amount: u128,
+    lock_period: u64,
+) {
+    // 1. take cap: LinearWithdrawCapability<TokenT>
+    let cap = Treasury::remove_linear_withdraw_capability<TokenT>(&signer);
+
+    // 2. withdraw token and split
+    let (tokens, new_cap) = Treasury::split_linear_withdraw_cap(&mut cap, amount);
+
+    // 3. deposit
+    Account::deposit_to_self(&signer, tokens);
+
+    // 4. put or destroy key
+    if (Treasury::is_empty_linear_withdraw_capability(&cap)) {
+        Treasury::destroy_linear_withdraw_capability(cap);
+    } else {
+        Treasury::add_linear_withdraw_capability(&signer, cap);
+    };
+
+    // 5. offer
+    Offer::create(&signer, new_cap, for_address, lock_period);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `withdraw_token_with_linear_withdraw_capability` + + + +
public entry fun withdraw_token_with_linear_withdraw_capability<TokenT: store>(signer: signer)
+
+ + + +
+Implementation + + +
public entry fun withdraw_token_with_linear_withdraw_capability<TokenT: store>(
+    signer: signer,
+) {
+    // 1. take cap
+    let cap = Treasury::remove_linear_withdraw_capability<TokenT>(&signer);
+
+    // 2. withdraw token
+    let tokens = Treasury::withdraw_with_linear_capability(&mut cap);
+
+    // 3. deposit
+    Account::deposit_to_self(&signer, tokens);
+
+    // 4. put or destroy key
+    if (Treasury::is_empty_linear_withdraw_capability(&cap)) {
+        Treasury::destroy_linear_withdraw_capability(cap);
+    } else {
+        Treasury::add_linear_withdraw_capability(&signer, cap);
+    };
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `propose_withdraw` + + + +
public entry fun propose_withdraw<TokenT: copy, drop, store>(signer: signer, receiver: address, amount: u128, period: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public entry fun propose_withdraw<TokenT: copy + drop + store>(signer: signer, receiver: address, amount: u128, period: u64, exec_delay: u64){
+    TreasuryWithdrawDaoProposal::propose_withdraw<TokenT>(&signer, receiver, amount, period, exec_delay)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `execute_withdraw_proposal` + + + +
public entry fun execute_withdraw_proposal<TokenT: copy, drop, store>(signer: signer, proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public entry fun execute_withdraw_proposal<TokenT:copy + drop + store>(signer: signer, proposer_address: address,
+                                                                   proposal_id: u64,){
+    TreasuryWithdrawDaoProposal::execute_withdraw_proposal<TokenT>(&signer, proposer_address, proposal_id);
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
diff --git a/release/v13/docs/TreasuryWithdrawDaoProposal.md b/release/v13/docs/TreasuryWithdrawDaoProposal.md new file mode 100644 index 00000000..0e13d8cc --- /dev/null +++ b/release/v13/docs/TreasuryWithdrawDaoProposal.md @@ -0,0 +1,320 @@ + + + +# Module `0x1::TreasuryWithdrawDaoProposal` + +TreasuryWithdrawDaoProposal is a dao proposal for withdraw Token from Treasury. + + +- [Resource `WrappedWithdrawCapability`](#0x1_TreasuryWithdrawDaoProposal_WrappedWithdrawCapability) +- [Struct `WithdrawToken`](#0x1_TreasuryWithdrawDaoProposal_WithdrawToken) +- [Constants](#@Constants_0) +- [Function `plugin`](#0x1_TreasuryWithdrawDaoProposal_plugin) +- [Function `propose_withdraw`](#0x1_TreasuryWithdrawDaoProposal_propose_withdraw) +- [Function `execute_withdraw_proposal`](#0x1_TreasuryWithdrawDaoProposal_execute_withdraw_proposal) +- [Function `withdraw_for_block_reward`](#0x1_TreasuryWithdrawDaoProposal_withdraw_for_block_reward) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::CoreAddresses;
+use 0x1::Dao;
+use 0x1::Errors;
+use 0x1::Signer;
+use 0x1::Token;
+use 0x1::Treasury;
+
+ + + + + +## Resource `WrappedWithdrawCapability` + +A wrapper of Token MintCapability. + + +
struct WrappedWithdrawCapability<TokenT> has key
+
+ + + +
+Fields + + +
+
+cap: Treasury::WithdrawCapability<TokenT> +
+
+ +
+
+ + +
+ + + +## Struct `WithdrawToken` + +WithdrawToken request. + + +
struct WithdrawToken has copy, drop, store
+
+ + + +
+Fields + + +
+
+receiver: address +
+
+ the receiver of withdraw tokens. +
+
+amount: u128 +
+
+ how many tokens to mint. +
+
+period: u64 +
+
+ How long in milliseconds does it take for the token to be released +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_NOT_AUTHORIZED: u64 = 101;
+
+ + + + + +Only receiver can execute TreasuryWithdrawDaoProposal + + +
const ERR_NEED_RECEIVER_TO_EXECUTE: u64 = 102;
+
+ + + + + +The withdraw amount of propose is too many. + + +
const ERR_TOO_MANY_WITHDRAW_AMOUNT: u64 = 103;
+
+ + + + + +## Function `plugin` + +Plugin method of the module. +Should be called by token issuer. + + +
public fun plugin<TokenT: store>(signer: &signer, cap: Treasury::WithdrawCapability<TokenT>)
+
+ + + +
+Implementation + + +
public fun plugin<TokenT: store>(signer: &signer, cap: Treasury::WithdrawCapability<TokenT>) {
+    let token_issuer = Token::token_address<TokenT>();
+    assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    move_to(signer, WrappedWithdrawCapability<TokenT> { cap: cap });
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+let sender = Signer::address_of(signer);
+aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS();
+aborts_if !exists<Treasury::WithdrawCapability<TokenT>>(sender);
+aborts_if exists<WrappedWithdrawCapability<TokenT>>(sender);
+ensures !exists<Treasury::WithdrawCapability<TokenT>>(sender);
+ensures exists<WrappedWithdrawCapability<TokenT>>(sender);
+
+ + + +
+ + + +## Function `propose_withdraw` + +Entrypoint for the proposal. + + +
public fun propose_withdraw<TokenT: copy, drop, store>(signer: &signer, receiver: address, amount: u128, period: u64, exec_delay: u64)
+
+ + + +
+Implementation + + +
public fun propose_withdraw<TokenT: copy + drop + store>(signer: &signer, receiver: address, amount: u128, period: u64, exec_delay: u64) {
+    let quorum_votes = Dao::quorum_votes<TokenT>();
+    assert!(amount <= quorum_votes,  Errors::invalid_argument(ERR_TOO_MANY_WITHDRAW_AMOUNT));
+    Dao::propose<TokenT, WithdrawToken>(
+        signer,
+        WithdrawToken { receiver, amount, period },
+        exec_delay,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+let quorum_votes = Dao::spec_quorum_votes<TokenT>();
+aborts_if amount > quorum_votes;
+include Dao::AbortIfDaoConfigNotExist<TokenT>;
+include Dao::AbortIfDaoInfoNotExist<TokenT>;
+aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
+aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config<TokenT>().min_action_delay;
+include Dao::CheckQuorumVotes<TokenT>;
+let sender = Signer::address_of(signer);
+aborts_if exists<Dao::Proposal<TokenT, WithdrawToken>>(sender);
+
+ + + +
+ + + +## Function `execute_withdraw_proposal` + +Once the proposal is agreed, anyone can call the method to make the proposal happen. + + +
public fun execute_withdraw_proposal<TokenT: copy, drop, store>(signer: &signer, proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public fun execute_withdraw_proposal<TokenT: copy + drop + store>(
+    signer: &signer,
+    proposer_address: address,
+    proposal_id: u64,
+) acquires WrappedWithdrawCapability {
+    let WithdrawToken { receiver, amount, period } = Dao::extract_proposal_action<TokenT, WithdrawToken>(
+        proposer_address,
+        proposal_id,
+    );
+    assert!(receiver == Signer::address_of(signer), Errors::requires_address(ERR_NEED_RECEIVER_TO_EXECUTE));
+    let cap = borrow_global_mut<WrappedWithdrawCapability<TokenT>>(Token::token_address<TokenT>());
+    let linear_cap = Treasury::issue_linear_withdraw_capability<TokenT>(&mut cap.cap, amount, period);
+    Treasury::add_linear_withdraw_capability(signer, linear_cap);
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = true;
+let expected_states = vec<u8>(6);
+include Dao::CheckProposalStates<TokenT, WithdrawToken>{expected_states};
+let proposal = global<Dao::Proposal<TokenT, WithdrawToken>>(proposer_address);
+aborts_if Option::is_none(proposal.action);
+aborts_if !exists<WrappedWithdrawCapability<TokenT>>(Token::SPEC_TOKEN_TEST_ADDRESS());
+
+ + + +
+ + + +## Function `withdraw_for_block_reward` + +Provider a port for get block reward STC from Treasury, only genesis account can invoke this function. +The TreasuryWithdrawCapability is locked in TreasuryWithdrawDaoProposal, and only can withdraw by DAO proposal. +This approach is not graceful, but restricts the operation to genesis accounts only, so there are no security issues either. + + +
public fun withdraw_for_block_reward<TokenT: store>(signer: &signer, reward: u128): Token::Token<TokenT>
+
+ + + +
+Implementation + + +
public fun withdraw_for_block_reward<TokenT: store>(signer: &signer, reward: u128):Token<TokenT> acquires WrappedWithdrawCapability  {
+    CoreAddresses::assert_genesis_address(signer);
+    let cap = borrow_global_mut<WrappedWithdrawCapability<TokenT>>(Signer::address_of(signer));
+    Treasury::withdraw_with_capability(&mut cap.cap, reward)
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+pragma aborts_if_is_partial;
+
diff --git a/release/v13/docs/TypeInfo.md b/release/v13/docs/TypeInfo.md new file mode 100644 index 00000000..be768667 --- /dev/null +++ b/release/v13/docs/TypeInfo.md @@ -0,0 +1,192 @@ + + + +# Module `0x1::TypeInfo` + + + +- [Struct `TypeInfo`](#0x1_TypeInfo_TypeInfo) +- [Function `account_address`](#0x1_TypeInfo_account_address) +- [Function `module_name`](#0x1_TypeInfo_module_name) +- [Function `struct_name`](#0x1_TypeInfo_struct_name) +- [Function `type_of`](#0x1_TypeInfo_type_of) +- [Function `size_of_val`](#0x1_TypeInfo_size_of_val) + + +
use 0x1::BCS;
+use 0x1::Token;
+
+ + + + + +## Struct `TypeInfo` + + + +
struct TypeInfo has copy, drop, store
+
+ + + +
+Fields + + +
+
+account_address: address +
+
+ +
+
+module_name: vector<u8> +
+
+ +
+
+struct_name: vector<u8> +
+
+ +
+
+ + +
+ + + +## Function `account_address` + + + +
public fun account_address(type_info: &TypeInfo::TypeInfo): address
+
+ + + +
+Implementation + + +
public fun account_address(type_info: &TypeInfo): address {
+    type_info.account_address
+}
+
+ + + +
+ + + +## Function `module_name` + + + +
public fun module_name(type_info: &TypeInfo::TypeInfo): vector<u8>
+
+ + + +
+Implementation + + +
public fun module_name(type_info: &TypeInfo): vector<u8> {
+    *&type_info.module_name
+}
+
+ + + +
+ + + +## Function `struct_name` + + + +
public fun struct_name(type_info: &TypeInfo::TypeInfo): vector<u8>
+
+ + + +
+Implementation + + +
public fun struct_name(type_info: &TypeInfo): vector<u8> {
+    *&type_info.struct_name
+}
+
+ + + +
+ + + +## Function `type_of` + + + +
public fun type_of<T>(): TypeInfo::TypeInfo
+
+ + + +
+Implementation + + +
public fun type_of<T>(): TypeInfo {
+    let (account_address, module_name, struct_name) = Token::type_of<T>();
+    TypeInfo {
+        account_address,
+        module_name,
+        struct_name
+    }
+}
+
+ + + +
+ + + +## Function `size_of_val` + +Return the BCS size, in bytes, of value at val_ref. + +See the [BCS spec](https://github.com/diem/bcs) + +See test_size_of_val() for an analysis of common types and +nesting patterns, as well as test_size_of_val_vectors() for an +analysis of vector size dynamism. + + +
public fun size_of_val<T: store>(val_ref: &T): u64
+
+ + + +
+Implementation + + +
public fun size_of_val<T: store>(val_ref: &T): u64 {
+    // Return vector length of vectorized BCS representation.
+    Vector::length(&BCS::to_bytes<T>(val_ref))
+}
+
+ + + +
diff --git a/release/v13/docs/U256.md b/release/v13/docs/U256.md new file mode 100644 index 00000000..7b3f3368 --- /dev/null +++ b/release/v13/docs/U256.md @@ -0,0 +1,946 @@ + + + +# Module `0x1::U256` + +Implementation u256. + + +- [Struct `U256`](#0x1_U256_U256) +- [Constants](#@Constants_0) +- [Function `zero`](#0x1_U256_zero) +- [Function `one`](#0x1_U256_one) +- [Function `from_u64`](#0x1_U256_from_u64) +- [Function `from_u128`](#0x1_U256_from_u128) +- [Function `from_big_endian`](#0x1_U256_from_big_endian) +- [Function `from_little_endian`](#0x1_U256_from_little_endian) +- [Function `to_u128`](#0x1_U256_to_u128) +- [Function `compare`](#0x1_U256_compare) +- [Function `add`](#0x1_U256_add) +- [Function `sub`](#0x1_U256_sub) +- [Function `mul`](#0x1_U256_mul) +- [Function `div`](#0x1_U256_div) +- [Function `rem`](#0x1_U256_rem) +- [Function `pow`](#0x1_U256_pow) +- [Function `from_bytes`](#0x1_U256_from_bytes) +- [Function `native_add`](#0x1_U256_native_add) +- [Function `native_sub`](#0x1_U256_native_sub) +- [Function `native_mul`](#0x1_U256_native_mul) +- [Function `native_div`](#0x1_U256_native_div) +- [Function `native_rem`](#0x1_U256_native_rem) +- [Function `native_pow`](#0x1_U256_native_pow) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Vector;
+
+ + + + + +## Struct `U256` + +use vector to represent data. +so that we can use buildin vector ops later to construct U256. +vector should always has two elements. + + +
struct U256 has copy, drop, store
+
+ + + +
+Fields + + +
+
+bits: vector<u64> +
+
+ little endian representation +
+
+ + +
+ +
+Specification + + + +
invariant len(bits) == 4;
+
+ + + + + + + +
fun value_of_U256(a: U256): num {
+   a.bits[0] +
+   a.bits[1] * P64 +
+   a.bits[2] * P64 * P64 +
+   a.bits[3] * P64 * P64 * P64
+}
+
+ + + +
+ + + +## Constants + + + + + + +
const P32: u64 = 4294967296;
+
+ + + + + + + +
const P64: u128 = 18446744073709551616;
+
+ + + + + + + +
const EQUAL: u8 = 0;
+
+ + + + + + + +
const GREATER_THAN: u8 = 2;
+
+ + + + + + + +
const LESS_THAN: u8 = 1;
+
+ + + + + + + +
const ERR_INVALID_LENGTH: u64 = 100;
+
+ + + + + + + +
const ERR_OVERFLOW: u64 = 200;
+
+ + + + + + + +
const WORD: u8 = 4;
+
+ + + + + +## Function `zero` + + + +
public fun zero(): U256::U256
+
+ + + +
+Implementation + + +
public fun zero(): U256 {
+    from_u128(0u128)
+}
+
+ + + +
+ + + +## Function `one` + + + +
public fun one(): U256::U256
+
+ + + +
+Implementation + + +
public fun one(): U256 {
+    from_u128(1u128)
+}
+
+ + + +
+ + + +## Function `from_u64` + + + +
public fun from_u64(v: u64): U256::U256
+
+ + + +
+Implementation + + +
public fun from_u64(v: u64): U256 {
+    from_u128((v as u128))
+}
+
+ + + +
+ + + +## Function `from_u128` + + + +
public fun from_u128(v: u128): U256::U256
+
+ + + +
+Implementation + + +
public fun from_u128(v: u128): U256 {
+    let low = ((v & 0xffffffffffffffff) as u64);
+    let high = ((v >> 64) as u64);
+    let bits = Vector::singleton(low);
+    Vector::push_back(&mut bits, high);
+    Vector::push_back(&mut bits, 0u64);
+    Vector::push_back(&mut bits, 0u64);
+    U256 { bits }
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma opaque;
+ensures value_of_U256(result) == v;
+
+ + + +
+ + + +## Function `from_big_endian` + + + +
public fun from_big_endian(data: vector<u8>): U256::U256
+
+ + + +
+Implementation + + +
public fun from_big_endian(data: vector<u8>): U256 {
+    // TODO: define error code.
+    assert!(Vector::length(&data) <= 32, Errors::invalid_argument(ERR_INVALID_LENGTH));
+    from_bytes(&data, true)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `from_little_endian` + + + +
public fun from_little_endian(data: vector<u8>): U256::U256
+
+ + + +
+Implementation + + +
public fun from_little_endian(data: vector<u8>): U256 {
+    // TODO: define error code.
+    assert!(Vector::length(&data) <= 32, Errors::invalid_argument(ERR_INVALID_LENGTH));
+    from_bytes(&data, false)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+
+ + + +
+ + + +## Function `to_u128` + + + +
public fun to_u128(v: &U256::U256): u128
+
+ + + +
+Implementation + + +
public fun to_u128(v: &U256): u128 {
+    assert!(*Vector::borrow(&v.bits, 3) == 0, Errors::invalid_state(ERR_OVERFLOW));
+    assert!(*Vector::borrow(&v.bits, 2) == 0, Errors::invalid_state(ERR_OVERFLOW));
+    ((*Vector::borrow(&v.bits, 1) as u128) << 64) | (*Vector::borrow(&v.bits, 0) as u128)
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma opaque;
+aborts_if value_of_U256(v) >= P64 * P64;
+ensures value_of_U256(v) == result;
+
+ + + +
+ + + +## Function `compare` + + + +
public fun compare(a: &U256::U256, b: &U256::U256): u8
+
+ + + +
+Implementation + + +
public fun compare(a: &U256, b: &U256): u8 {
+    let i = (WORD as u64);
+    while (i > 0) {
+        i = i - 1;
+        let a_bits = *Vector::borrow(&a.bits, i);
+        let b_bits = *Vector::borrow(&b.bits, i);
+        if (a_bits != b_bits) {
+            if (a_bits < b_bits) {
+                return LESS_THAN
+            } else {
+                return GREATER_THAN
+            }
+        }
+    };
+    return EQUAL
+}
+
+ + + +
+ + + +## Function `add` + + + +
public fun add(a: U256::U256, b: U256::U256): U256::U256
+
+ + + +
+Implementation + + +
public fun add(a: U256, b: U256): U256 {
+    native_add(&mut a, &b);
+    a
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if value_of_U256(a) + value_of_U256(b) >= P64 * P64 * P64 * P64;
+ensures value_of_U256(result) == value_of_U256(a) + value_of_U256(b);
+
+ + + +
+ + + +## Function `sub` + + + +
public fun sub(a: U256::U256, b: U256::U256): U256::U256
+
+ + + +
+Implementation + + +
public fun sub(a: U256, b: U256): U256 {
+    native_sub(&mut a, &b);
+    a
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if value_of_U256(a) < value_of_U256(b);
+ensures value_of_U256(result) == value_of_U256(a) - value_of_U256(b);
+
+ + + +
+ + + +## Function `mul` + + + +
public fun mul(a: U256::U256, b: U256::U256): U256::U256
+
+ + + +
+Implementation + + +
public fun mul(a: U256, b: U256): U256 {
+    native_mul(&mut a, &b);
+    a
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma timeout = 200;
+aborts_if value_of_U256(a) * value_of_U256(b) >= P64 * P64 * P64 * P64;
+ensures value_of_U256(result) == value_of_U256(a) * value_of_U256(b);
+
+ + + +
+ + + +## Function `div` + + + +
public fun div(a: U256::U256, b: U256::U256): U256::U256
+
+ + + +
+Implementation + + +
public fun div(a: U256, b: U256): U256 {
+    native_div(&mut a, &b);
+    a
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma timeout = 160;
+aborts_if value_of_U256(b) == 0;
+ensures value_of_U256(result) == value_of_U256(a) / value_of_U256(b);
+
+ + + +
+ + + +## Function `rem` + + + +
public fun rem(a: U256::U256, b: U256::U256): U256::U256
+
+ + + +
+Implementation + + +
public fun rem(a: U256, b: U256): U256 {
+    native_rem(&mut a, &b);
+    a
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma timeout = 160;
+aborts_if value_of_U256(b) == 0;
+ensures value_of_U256(result) == value_of_U256(a) % value_of_U256(b);
+
+ + + +
+ + + +## Function `pow` + + + +
public fun pow(a: U256::U256, b: U256::U256): U256::U256
+
+ + + +
+Implementation + + +
public fun pow(a: U256, b: U256): U256 {
+    native_pow(&mut a, &b);
+    a
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+pragma opaque;
+pragma timeout = 600;
+let p = pow_spec(value_of_U256(a), value_of_U256(b));
+aborts_if p >= P64 * P64 * P64 * P64;
+ensures value_of_U256(result) == p;
+
+ + + +
+ + + +## Function `from_bytes` + + + +
fun from_bytes(data: &vector<u8>, be: bool): U256::U256
+
+ + + +
+Implementation + + +
native fun from_bytes(data: &vector<u8>, be: bool): U256;
+
+ + + +
+ + + +## Function `native_add` + + + +
fun native_add(a: &mut U256::U256, b: &U256::U256)
+
+ + + +
+Implementation + + +
native fun native_add(a: &mut U256, b: &U256);
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if value_of_U256(a) + value_of_U256(b) >= P64 * P64 * P64 * P64;
+ensures value_of_U256(a) == value_of_U256(old(a)) + value_of_U256(b);
+
+ + + +
+ + + +## Function `native_sub` + + + +
fun native_sub(a: &mut U256::U256, b: &U256::U256)
+
+ + + +
+Implementation + + +
native fun native_sub(a: &mut U256, b: &U256);
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if value_of_U256(a) - value_of_U256(b) < 0;
+ensures value_of_U256(a) == value_of_U256(old(a)) - value_of_U256(b);
+
+ + + +
+ + + +## Function `native_mul` + + + +
fun native_mul(a: &mut U256::U256, b: &U256::U256)
+
+ + + +
+Implementation + + +
native fun native_mul(a: &mut U256, b: &U256);
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if value_of_U256(a) * value_of_U256(b) >= P64 * P64 * P64 * P64;
+ensures value_of_U256(a) == value_of_U256(old(a)) * value_of_U256(b);
+
+ + + +
+ + + +## Function `native_div` + + + +
fun native_div(a: &mut U256::U256, b: &U256::U256)
+
+ + + +
+Implementation + + +
native fun native_div(a: &mut U256, b: &U256);
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if value_of_U256(b) == 0;
+ensures value_of_U256(a) == value_of_U256(old(a)) / value_of_U256(b);
+
+ + + +
+ + + +## Function `native_rem` + + + +
fun native_rem(a: &mut U256::U256, b: &U256::U256)
+
+ + + +
+Implementation + + +
native fun native_rem(a: &mut U256, b: &U256);
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if value_of_U256(b) == 0;
+ensures value_of_U256(a) == value_of_U256(old(a)) % value_of_U256(b);
+
+ + + +
+ + + +## Function `native_pow` + + + +
fun native_pow(a: &mut U256::U256, b: &U256::U256)
+
+ + + +
+Implementation + + +
native fun native_pow(a: &mut U256, b: &U256);
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+aborts_if pow_spec(value_of_U256(a), value_of_U256(b)) >= P64 * P64 * P64 * P64;
+ensures value_of_U256(a) == pow_spec(value_of_U256(old(a)), value_of_U256(b));
+
+ + + + + + + +
fun pow_spec(base: num, expon: num): num {
+   // This actually doesn't follow a strict definition as 0^0 is undefined
+   // mathematically. But the U256::pow of Rust is defined to be like this:
+   // Link: https://docs.rs/uint/0.9.3/src/uint/uint.rs.html#1000-1003
+   if (expon > 0) {
+       let x = pow_spec(base, expon / 2);
+       if (expon % 2 == 0) { x * x } else { x * x * base }
+   } else {
+       1
+   }
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = true;
+
diff --git a/release/v13/docs/UpgradeModuleDaoProposal.md b/release/v13/docs/UpgradeModuleDaoProposal.md new file mode 100644 index 00000000..add8f904 --- /dev/null +++ b/release/v13/docs/UpgradeModuleDaoProposal.md @@ -0,0 +1,350 @@ + + + +# Module `0x1::UpgradeModuleDaoProposal` + +UpgradeModuleDaoProposal is a proposal moudle used to upgrade contract codes under a token. + + +- [Resource `UpgradeModuleCapability`](#0x1_UpgradeModuleDaoProposal_UpgradeModuleCapability) +- [Struct `UpgradeModule`](#0x1_UpgradeModuleDaoProposal_UpgradeModule) +- [Struct `UpgradeModuleV2`](#0x1_UpgradeModuleDaoProposal_UpgradeModuleV2) +- [Constants](#@Constants_0) +- [Function `plugin`](#0x1_UpgradeModuleDaoProposal_plugin) +- [Function `propose_module_upgrade_v2`](#0x1_UpgradeModuleDaoProposal_propose_module_upgrade_v2) +- [Function `submit_module_upgrade_plan`](#0x1_UpgradeModuleDaoProposal_submit_module_upgrade_plan) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Dao;
+use 0x1::Errors;
+use 0x1::PackageTxnManager;
+use 0x1::Signer;
+use 0x1::Token;
+
+ + + + + +## Resource `UpgradeModuleCapability` + +A wrapper of PackageTxnManager::UpgradePlanCapability. + + +
struct UpgradeModuleCapability<TokenT> has key
+
+ + + +
+Fields + + +
+
+cap: PackageTxnManager::UpgradePlanCapability +
+
+ +
+
+ + +
+ + + +## Struct `UpgradeModule` + +request of upgrading module contract code. + + +
struct UpgradeModule has copy, drop, store
+
+ + + +
+Fields + + +
+
+module_address: address +
+
+ +
+
+package_hash: vector<u8> +
+
+ +
+
+version: u64 +
+
+ +
+
+ + +
+ + + +## Struct `UpgradeModuleV2` + + + +
struct UpgradeModuleV2 has copy, drop, store
+
+ + + +
+Fields + + +
+
+module_address: address +
+
+ +
+
+package_hash: vector<u8> +
+
+ +
+
+version: u64 +
+
+ +
+
+enforced: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_NOT_AUTHORIZED: u64 = 401;
+
+ + + + + + + +
const ERR_ADDRESS_MISSMATCH: u64 = 402;
+
+ + + + + + + +
const ERR_UNABLE_TO_UPGRADE: u64 = 400;
+
+ + + + + +## Function `plugin` + +If this goverment can upgrade module, call this to register capability. + + +
public fun plugin<TokenT: store>(signer: &signer, cap: PackageTxnManager::UpgradePlanCapability)
+
+ + + +
+Implementation + + +
public fun plugin<TokenT: store>(
+    signer: &signer,
+    cap: PackageTxnManager::UpgradePlanCapability,
+) {
+    let token_issuer = Token::token_address<TokenT>();
+    assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED));
+    move_to(signer, UpgradeModuleCapability<TokenT> { cap })
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = false;
+let sender = Signer::address_of(signer);
+aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS();
+aborts_if exists<UpgradeModuleCapability<TokenT>>(sender);
+
+ + + + + + + +
schema AbortIfUnableUpgrade<TokenT> {
+    module_address: address;
+    let token_issuer = Token::SPEC_TOKEN_TEST_ADDRESS();
+    aborts_if !exists<UpgradeModuleCapability<TokenT>>(token_issuer);
+    let cap = global<UpgradeModuleCapability<TokenT>>(token_issuer).cap;
+    aborts_if PackageTxnManager::account_address(cap) != module_address;
+}
+
+ + + +
+ + + +## Function `propose_module_upgrade_v2` + + + +
public fun propose_module_upgrade_v2<TokenT: copy, drop, store>(signer: &signer, module_address: address, package_hash: vector<u8>, version: u64, exec_delay: u64, enforced: bool)
+
+ + + +
+Implementation + + +
public fun propose_module_upgrade_v2<TokenT: copy + drop + store>(
+    signer: &signer,
+    module_address: address,
+    package_hash: vector<u8>,
+    version: u64,
+    exec_delay: u64,
+    enforced: bool,
+) acquires UpgradeModuleCapability {
+    let cap = borrow_global<UpgradeModuleCapability<TokenT>>(Token::token_address<TokenT>());
+    let account_address = PackageTxnManager::account_address(&cap.cap);
+    assert!(account_address == module_address, Errors::requires_capability(ERR_ADDRESS_MISSMATCH));
+    Dao::propose<TokenT, UpgradeModuleV2>(
+        signer,
+        UpgradeModuleV2 { module_address, package_hash, version, enforced },
+        exec_delay,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
pragma aborts_if_is_partial = true;
+include AbortIfUnableUpgrade<TokenT>;
+
+ + + +
+ + + +## Function `submit_module_upgrade_plan` + +Once the proposal is agreed, anyone can call this method to generate the upgrading plan. + + +
public fun submit_module_upgrade_plan<TokenT: copy, drop, store>(proposer_address: address, proposal_id: u64)
+
+ + + +
+Implementation + + +
public fun submit_module_upgrade_plan<TokenT: copy + drop + store>(
+    proposer_address: address,
+    proposal_id: u64,
+) acquires UpgradeModuleCapability {
+    let UpgradeModuleV2 { module_address, package_hash, version, enforced } = Dao::extract_proposal_action<
+        TokenT,
+        UpgradeModuleV2,
+    >(proposer_address, proposal_id);
+    let cap = borrow_global<UpgradeModuleCapability<TokenT>>(Token::token_address<TokenT>());
+    let account_address = PackageTxnManager::account_address(&cap.cap);
+    assert!(account_address == module_address, Errors::requires_capability(ERR_ADDRESS_MISSMATCH));
+    PackageTxnManager::submit_upgrade_plan_with_cap_v2(
+        &cap.cap,
+        package_hash,
+        version,
+        enforced,
+    );
+}
+
+ + + +
+ +
+Specification + + + +
let expected_states = vec<u8>(6);
+include Dao::CheckProposalStates<TokenT, UpgradeModule>{expected_states};
+let proposal = global<Dao::Proposal<TokenT, UpgradeModule>>(proposer_address);
+aborts_if Option::is_none(proposal.action);
+let action = proposal.action.vec[0];
+include AbortIfUnableUpgrade<TokenT>{module_address: action.module_address};
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+pragma aborts_if_is_partial;
+
diff --git a/release/v13/docs/VMConfig.md b/release/v13/docs/VMConfig.md new file mode 100644 index 00000000..973bf229 --- /dev/null +++ b/release/v13/docs/VMConfig.md @@ -0,0 +1,738 @@ + + + +# Module `0x1::VMConfig` + +VMConfig keep track of VM related configuration, like gas schedule. + + +- [Struct `VMConfig`](#0x1_VMConfig_VMConfig) +- [Struct `GasSchedule`](#0x1_VMConfig_GasSchedule) +- [Struct `GasConstants`](#0x1_VMConfig_GasConstants) +- [Struct `GasCost`](#0x1_VMConfig_GasCost) +- [Function `instruction_schedule`](#0x1_VMConfig_instruction_schedule) +- [Function `native_schedule`](#0x1_VMConfig_native_schedule) +- [Function `gas_constants`](#0x1_VMConfig_gas_constants) +- [Function `new_gas_cost`](#0x1_VMConfig_new_gas_cost) +- [Function `new_vm_config`](#0x1_VMConfig_new_vm_config) +- [Function `initialize`](#0x1_VMConfig_initialize) +- [Module Specification](#@Module_Specification_0) + + +
use 0x1::ChainId;
+use 0x1::Config;
+use 0x1::CoreAddresses;
+
+ + + + + +## Struct `VMConfig` + +The struct to hold all config data needed to operate the VM. +* gas_schedule: Cost of running the VM. + + +
struct VMConfig has copy, drop, store
+
+ + + +
+Fields + + +
+
+gas_schedule: VMConfig::GasSchedule +
+
+ +
+
+ + +
+ + + +## Struct `GasSchedule` + +The gas schedule keeps two separate schedules for the gas: +* The instruction_schedule: This holds the gas for each bytecode instruction. +* The native_schedule: This holds the gas for used (per-byte operated over) for each native +function. +A couple notes: +1. In the case that an instruction is deleted from the bytecode, that part of the cost schedule +still needs to remain the same; once a slot in the table is taken by an instruction, that is its +slot for the rest of time (since that instruction could already exist in a module on-chain). +2. The initialization of the module will publish the instruction table to the genesis +address, and will preload the vector with the gas schedule for instructions. The VM will then +load this into memory at the startup of each block. + + +
struct GasSchedule has copy, drop, store
+
+ + + +
+Fields + + +
+
+instruction_schedule: vector<u8> +
+
+ +
+
+native_schedule: vector<u8> +
+
+ +
+
+gas_constants: VMConfig::GasConstants +
+
+ +
+
+ + +
+ + + +## Struct `GasConstants` + +The gas constants contains all kind of constants used in gas calculation. + + +
struct GasConstants has copy, drop, store
+
+ + + +
+Fields + + +
+
+global_memory_per_byte_cost: u64 +
+
+ The cost per-byte written to global storage. +
+
+global_memory_per_byte_write_cost: u64 +
+
+ The cost per-byte written to storage. +
+
+min_transaction_gas_units: u64 +
+
+ We charge one unit of gas per-byte for the first 600 bytes +
+
+large_transaction_cutoff: u64 +
+
+ Any transaction over this size will be charged INTRINSIC_GAS_PER_BYTE per byte +
+
+instrinsic_gas_per_byte: u64 +
+
+ The units of gas that should be charged per byte for every transaction. +
+
+maximum_number_of_gas_units: u64 +
+
+ 1 nanosecond should equal one unit of computational gas. We bound the maximum + computational time of any given transaction at 10 milliseconds. We want this number and + MAX_PRICE_PER_GAS_UNIT to always satisfy the inequality that + MAXIMUM_NUMBER_OF_GAS_UNITS * MAX_PRICE_PER_GAS_UNIT < min(u64::MAX, GasUnits::MAX) +
+
+min_price_per_gas_unit: u64 +
+
+ The minimum gas price that a transaction can be submitted with. +
+
+max_price_per_gas_unit: u64 +
+
+ The maximum gas unit price that a transaction can be submitted with. +
+
+max_transaction_size_in_bytes: u64 +
+
+ The max transaction size in bytes that a transaction can have. +
+
+gas_unit_scaling_factor: u64 +
+
+ gas unit scaling factor. +
+
+default_account_size: u64 +
+
+ default account size. +
+
+ + +
+ + + +## Struct `GasCost` + +The GasCost tracks: +- instruction cost: how much time/computational power is needed to perform the instruction +- memory cost: how much memory is required for the instruction, and storage overhead + + +
struct GasCost has copy, drop, store
+
+ + + +
+Fields + + +
+
+instruction_gas: u64 +
+
+ +
+
+memory_gas: u64 +
+
+ +
+
+ + +
+ + + +## Function `instruction_schedule` + + + +
public fun instruction_schedule(): vector<VMConfig::GasCost>
+
+ + + +
+Implementation + + +
public fun instruction_schedule(): vector<GasCost> {
+    let table = Vector::empty();
+
+    // POP
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // RET
+    Vector::push_back(&mut table, new_gas_cost(638, 1));
+    // BR_TRUE
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // BR_FALSE
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // BRANCH
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LD_U64
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LD_CONST
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LD_TRUE
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LD_FALSE
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // COPY_LOC
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // MOVE_LOC
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // ST_LOC
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // MUT_BORROW_LOC
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // IMM_BORROW_LOC
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // MUT_BORROW_FIELD
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // IMM_BORROW_FIELD
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // CALL
+    Vector::push_back(&mut table, new_gas_cost(1132, 1));
+    // PACK
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // UNPACK
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // READ_REF
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // WRITE_REF
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // ADD
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // SUB
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // MUL
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // MOD
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // DIV
+    Vector::push_back(&mut table, new_gas_cost(3, 1));
+    // BIT_OR
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // BIT_AND
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // XOR
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // OR
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // AND
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // NOT
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // EQ
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // NEQ
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LT
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // GT
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LE
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // GE
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // ABORT
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // NOP
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // EXISTS
+    Vector::push_back(&mut table, new_gas_cost(41, 1));
+    // MUT_BORROW_GLOBAL
+    Vector::push_back(&mut table, new_gas_cost(21, 1));
+    // IML_BORROW_GLOBAL
+    Vector::push_back(&mut table, new_gas_cost(23, 1));
+    // MOVE_FROM
+    Vector::push_back(&mut table, new_gas_cost(459, 1));
+    // MOVE_TO
+    Vector::push_back(&mut table, new_gas_cost(13, 1));
+    // FREEZE_REF
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // SHL
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // SHR
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LD_U8
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // LD_U128
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+
+    // CAST_U8
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // CAST_U64
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // CAST_U128
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // MUT_BORORW_FIELD_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // IMM_BORORW_FIELD_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(1, 1));
+    // CALL_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(582, 1));
+    // PACK_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // UNPACK_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    // EXISTS_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(34, 1));
+    // MUT_BORROW_GLOBAL_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(15, 1));
+    // IMM_BORROW_GLOBAL_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(14, 1));
+    // MOVE_FROM_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(13, 1));
+    // MOVE_TO_GENERIC
+    Vector::push_back(&mut table, new_gas_cost(27, 1));
+
+    // VEC_PACK
+    Vector::push_back(&mut table, new_gas_cost(84, 1));
+    // VEC_LEN
+    Vector::push_back(&mut table, new_gas_cost(98, 1));
+    // VEC_IMM_BORROW
+    Vector::push_back(&mut table, new_gas_cost(1334, 1));
+    // VEC_MUT_BORROW
+    Vector::push_back(&mut table, new_gas_cost(1902, 1));
+    // VEC_PUSH_BACK
+    Vector::push_back(&mut table, new_gas_cost(53, 1));
+    // VEC_POP_BACK
+    Vector::push_back(&mut table, new_gas_cost(227, 1));
+    // VEC_UNPACK
+    Vector::push_back(&mut table, new_gas_cost(572, 1));
+    // VEC_SWAP
+    Vector::push_back(&mut table, new_gas_cost(1436, 1));
+    table
+}
+
+ + + +
+ + + +## Function `native_schedule` + + + +
public fun native_schedule(): vector<VMConfig::GasCost>
+
+ + + +
+Implementation + + +
public fun native_schedule(): vector<GasCost> {
+    let table = Vector::empty();
+    //Hash::sha2_256 0
+    Vector::push_back(&mut table, new_gas_cost(21, 1));
+    //Hash::sha3_256 1
+    Vector::push_back(&mut table, new_gas_cost(64, 1));
+    //Signature::ed25519_verify 2
+    Vector::push_back(&mut table, new_gas_cost(61, 1));
+    //ED25519_THRESHOLD_VERIFY 3 this native funciton is deprecated
+    Vector::push_back(&mut table, new_gas_cost(3351, 1));
+    //BSC::to_bytes 4
+    Vector::push_back(&mut table, new_gas_cost(181, 1));
+    //Vector::length 5
+    Vector::push_back(&mut table, new_gas_cost(98, 1));
+    //Vector::empty 6
+    Vector::push_back(&mut table, new_gas_cost(84, 1));
+    //Vector::borrow 7
+    Vector::push_back(&mut table, new_gas_cost(1334, 1));
+    //Vector::borrow_mut 8
+    Vector::push_back(&mut table, new_gas_cost(1902, 1));
+    //Vector::push_back 9
+    Vector::push_back(&mut table, new_gas_cost(53, 1));
+    //Vector::pop_back 10
+    Vector::push_back(&mut table, new_gas_cost(227, 1));
+    //Vector::destory_empty 11
+    Vector::push_back(&mut table, new_gas_cost(572, 1));
+    //Vector::swap 12
+    Vector::push_back(&mut table, new_gas_cost(1436, 1));
+    //Signature::ed25519_validate_pubkey 13
+    Vector::push_back(&mut table, new_gas_cost(26, 1));
+    //Signer::borrow_address 14
+    Vector::push_back(&mut table, new_gas_cost(353, 1));
+    //Account::creator_signer 15
+    Vector::push_back(&mut table, new_gas_cost(24, 1));
+    //Account::destroy_signer 16
+    Vector::push_back(&mut table, new_gas_cost(212, 1));
+    //Event::emit_event 17
+    Vector::push_back(&mut table, new_gas_cost(52, 1));
+    //BCS::to_address 18
+    Vector::push_back(&mut table, new_gas_cost(26, 1));
+    //Token::name_of 19
+    Vector::push_back(&mut table, new_gas_cost(2002, 1));
+    //Hash::keccak_256 20
+    Vector::push_back(&mut table, new_gas_cost(64, 1));
+    //Hash::ripemd160 21
+    Vector::push_back(&mut table, new_gas_cost(64, 1));
+    //Signature::native_ecrecover 22
+    Vector::push_back(&mut table, new_gas_cost(128, 1));
+    //U256::from_bytes 23
+    Vector::push_back(&mut table, new_gas_cost(2, 1));
+    //U256::add 24
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    //U256::sub 25
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    //U256::mul 26
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    //U256::div 27
+    Vector::push_back(&mut table, new_gas_cost(10, 1));
+    // U256::rem 28
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // U256::pow 29
+    Vector::push_back(&mut table, new_gas_cost(8, 1));
+    // TODO: settle down the gas cost
+    // Vector::append 30
+    Vector::push_back(&mut table, new_gas_cost(40, 1));
+    // Vector::remove 31
+    Vector::push_back(&mut table, new_gas_cost(20, 1));
+    // Vector::reverse 32
+    Vector::push_back(&mut table, new_gas_cost(10, 1));
+
+    // Table::new_table_handle 33
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // Table::add_box 34
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // Table::borrow_box 35
+    Vector::push_back(&mut table, new_gas_cost(10, 1));
+    // Table::remove_box 36
+    Vector::push_back(&mut table, new_gas_cost(8, 1));
+    // Table::contains_box 37
+    Vector::push_back(&mut table, new_gas_cost(40, 1));
+    // Table::destroy_empty_box 38
+    Vector::push_back(&mut table, new_gas_cost(20, 1));
+    // Table::drop_unchecked_box 39
+    Vector::push_back(&mut table, new_gas_cost(73, 1));
+    // string.check_utf8 40
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // string.sub_str 41
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // string.is_char_boundary 42
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // Table::string.index_of 43
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // FromBCS::from_bytes 44
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // Secp256k1::ecdsa_recover_internal 45
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+    // Vector::spawn_from 46
+    Vector::push_back(&mut table, new_gas_cost(4, 1));
+
+    table
+}
+
+ + + +
+ + + +## Function `gas_constants` + + + +
public fun gas_constants(): VMConfig::GasConstants
+
+ + + +
+Implementation + + +
public fun gas_constants(): GasConstants {
+    let min_price_per_gas_unit: u64 = if (ChainId::is_test()) { 0 }  else { 1 };
+    let maximum_number_of_gas_units: u64 = 40000000;//must less than base_block_gas_limit
+
+    if (ChainId::is_test() || ChainId::is_dev() || ChainId::is_halley()) {
+        maximum_number_of_gas_units = maximum_number_of_gas_units * 10
+    };
+    GasConstants {
+        global_memory_per_byte_cost: 4,
+        global_memory_per_byte_write_cost: 9,
+        min_transaction_gas_units: 600,
+        large_transaction_cutoff: 600,
+        instrinsic_gas_per_byte: 8,
+        maximum_number_of_gas_units,
+        min_price_per_gas_unit,
+        max_price_per_gas_unit: 10000,
+        max_transaction_size_in_bytes: 1024 * 128,
+        gas_unit_scaling_factor: 1,
+        default_account_size: 800,
+    }
+}
+
+ + + +
+ + + +## Function `new_gas_cost` + + + +
fun new_gas_cost(instr_gas: u64, mem_gas: u64): VMConfig::GasCost
+
+ + + +
+Implementation + + +
fun new_gas_cost(instr_gas: u64, mem_gas: u64): GasCost {
+    GasCost {
+        instruction_gas: instr_gas,
+        memory_gas: mem_gas,
+    }
+}
+
+ + + +
+ + + +## Function `new_vm_config` + +Create a new vm config, mainly used in DAO. + + +
public fun new_vm_config(instruction_schedule: vector<u8>, native_schedule: vector<u8>, global_memory_per_byte_cost: u64, global_memory_per_byte_write_cost: u64, min_transaction_gas_units: u64, large_transaction_cutoff: u64, instrinsic_gas_per_byte: u64, maximum_number_of_gas_units: u64, min_price_per_gas_unit: u64, max_price_per_gas_unit: u64, max_transaction_size_in_bytes: u64, gas_unit_scaling_factor: u64, default_account_size: u64): VMConfig::VMConfig
+
+ + + +
+Implementation + + +
public fun new_vm_config(
+    instruction_schedule: vector<u8>,
+    native_schedule: vector<u8>,
+    global_memory_per_byte_cost: u64,
+    global_memory_per_byte_write_cost: u64,
+    min_transaction_gas_units: u64,
+    large_transaction_cutoff: u64,
+    instrinsic_gas_per_byte: u64,
+    maximum_number_of_gas_units: u64,
+    min_price_per_gas_unit: u64,
+    max_price_per_gas_unit: u64,
+    max_transaction_size_in_bytes: u64,
+    gas_unit_scaling_factor: u64,
+    default_account_size: u64,
+): VMConfig {
+    let gas_constants = GasConstants {
+        global_memory_per_byte_cost,
+        global_memory_per_byte_write_cost,
+        min_transaction_gas_units,
+        large_transaction_cutoff,
+        instrinsic_gas_per_byte,
+        maximum_number_of_gas_units,
+        min_price_per_gas_unit,
+        max_price_per_gas_unit,
+        max_transaction_size_in_bytes,
+        gas_unit_scaling_factor,
+        default_account_size,
+    };
+    VMConfig {
+        gas_schedule: GasSchedule { instruction_schedule, native_schedule, gas_constants },
+    }
+}
+
+ + + +
+ + + +## Function `initialize` + +Initialize the table under the genesis account + + +
public fun initialize(account: &signer, instruction_schedule: vector<u8>, native_schedule: vector<u8>, global_memory_per_byte_cost: u64, global_memory_per_byte_write_cost: u64, min_transaction_gas_units: u64, large_transaction_cutoff: u64, instrinsic_gas_per_byte: u64, maximum_number_of_gas_units: u64, min_price_per_gas_unit: u64, max_price_per_gas_unit: u64, max_transaction_size_in_bytes: u64, gas_unit_scaling_factor: u64, default_account_size: u64)
+
+ + + +
+Implementation + + +
public fun initialize(
+    account: &signer,
+    instruction_schedule: vector<u8>,
+    native_schedule: vector<u8>,
+    global_memory_per_byte_cost: u64,
+    global_memory_per_byte_write_cost: u64,
+    min_transaction_gas_units: u64,
+    large_transaction_cutoff: u64,
+    instrinsic_gas_per_byte: u64,
+    maximum_number_of_gas_units: u64,
+    min_price_per_gas_unit: u64,
+    max_price_per_gas_unit: u64,
+    max_transaction_size_in_bytes: u64,
+    gas_unit_scaling_factor: u64,
+    default_account_size: u64,
+) {
+    CoreAddresses::assert_genesis_address(account);
+    Config::publish_new_config<VMConfig>(
+        account,
+        new_vm_config(
+            instruction_schedule,
+            native_schedule,
+            global_memory_per_byte_cost,
+            global_memory_per_byte_write_cost,
+            min_transaction_gas_units,
+            large_transaction_cutoff,
+            instrinsic_gas_per_byte,
+            maximum_number_of_gas_units,
+            min_price_per_gas_unit,
+            max_price_per_gas_unit,
+            max_transaction_size_in_bytes,
+            gas_unit_scaling_factor,
+            default_account_size,
+        ),
+    );
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
+aborts_if exists<Config::Config<VMConfig>>(Signer::address_of(account));
+aborts_if
+    exists<Config::ModifyConfigCapabilityHolder<VMConfig>>(
+        Signer::address_of(account),
+    );
+ensures exists<Config::Config<VMConfig>>(Signer::address_of(account));
+ensures
+    exists<Config::ModifyConfigCapabilityHolder<VMConfig>>(
+        Signer::address_of(account),
+    );
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/Vector.md b/release/v13/docs/Vector.md new file mode 100644 index 00000000..5ad6187f --- /dev/null +++ b/release/v13/docs/Vector.md @@ -0,0 +1,861 @@ + + + +# Module `0x1::Vector` + +A variable-sized container that can hold any type. Indexing is 0-based, and +vectors are growable. This module has many native functions. +Verification of modules that use this one uses model functions that are implemented +directly in Boogie. The specification language has built-in functions operations such +as vec. There are some helper functions defined here for specifications in other +modules as well. + +>Note: We did not verify most of the +Move functions here because many have loops, requiring loop invariants to prove, and +the return on investment didn't seem worth it for these simple functions. + + +- [Constants](#@Constants_0) +- [Function `empty`](#0x1_Vector_empty) +- [Function `length`](#0x1_Vector_length) +- [Function `borrow`](#0x1_Vector_borrow) +- [Function `push_back`](#0x1_Vector_push_back) +- [Function `borrow_mut`](#0x1_Vector_borrow_mut) +- [Function `pop_back`](#0x1_Vector_pop_back) +- [Function `destroy_empty`](#0x1_Vector_destroy_empty) +- [Function `spawn_from`](#0x1_Vector_spawn_from) +- [Function `spawn_from_vec`](#0x1_Vector_spawn_from_vec) +- [Function `swap`](#0x1_Vector_swap) +- [Function `singleton`](#0x1_Vector_singleton) +- [Function `reverse`](#0x1_Vector_reverse) +- [Function `native_reverse`](#0x1_Vector_native_reverse) +- [Function `append`](#0x1_Vector_append) +- [Function `native_append`](#0x1_Vector_native_append) +- [Function `is_empty`](#0x1_Vector_is_empty) +- [Function `contains`](#0x1_Vector_contains) +- [Function `index_of`](#0x1_Vector_index_of) +- [Function `remove`](#0x1_Vector_remove) +- [Function `native_remove`](#0x1_Vector_native_remove) +- [Function `swap_remove`](#0x1_Vector_swap_remove) +- [Function `split`](#0x1_Vector_split) +- [Module Specification](#@Module_Specification_1) + - [Helper Functions](#@Helper_Functions_2) + + +
+ + + + + +## Constants + + + + +The index into the vector is out of bounds + + +
const EINDEX_OUT_OF_BOUNDS: u64 = 0;
+
+ + + + + +## Function `empty` + +Create an empty vector. + + +
public fun empty<Element>(): vector<Element>
+
+ + + +
+Implementation + + +
native public fun empty<Element>(): vector<Element>;
+
+ + + +
+ + + +## Function `length` + +Return the length of the vector. + + +
public fun length<Element>(v: &vector<Element>): u64
+
+ + + +
+Implementation + + +
native public fun length<Element>(v: &vector<Element>): u64;
+
+ + + +
+ + + +## Function `borrow` + +Acquire an immutable reference to the ith element of the vector v. +Aborts if i is out of bounds. + + +
public fun borrow<Element>(v: &vector<Element>, i: u64): &Element
+
+ + + +
+Implementation + + +
native public fun borrow<Element>(v: &vector<Element>, i: u64): ∈
+
+ + + +
+ + + +## Function `push_back` + +Add element e to the end of the vector v. + + +
public fun push_back<Element>(v: &mut vector<Element>, e: Element)
+
+ + + +
+Implementation + + +
native public fun push_back<Element>(v: &mut vector<Element>, e: Element);
+
+ + + +
+ + + +## Function `borrow_mut` + +Return a mutable reference to the ith element in the vector v. +Aborts if i is out of bounds. + + +
public fun borrow_mut<Element>(v: &mut vector<Element>, i: u64): &mut Element
+
+ + + +
+Implementation + + +
native public fun borrow_mut<Element>(v: &mut vector<Element>, i: u64): &mut Element;
+
+ + + +
+ + + +## Function `pop_back` + +Pop an element from the end of vector v. +Aborts if v is empty. + + +
public fun pop_back<Element>(v: &mut vector<Element>): Element
+
+ + + +
+Implementation + + +
native public fun pop_back<Element>(v: &mut vector<Element>): Element;
+
+ + + +
+ + + +## Function `destroy_empty` + +Destroy the vector v. +Aborts if v is not empty. + + +
public fun destroy_empty<Element>(v: vector<Element>)
+
+ + + +
+Implementation + + +
native public fun destroy_empty<Element>(v: vector<Element>);
+
+ + + +
+ + + +## Function `spawn_from` + +Spawn a sub vector from a vector + + +
fun spawn_from<Element>(v: &vector<Element>, offset: u64, size: u64): vector<Element>
+
+ + + +
+Implementation + + +
native fun spawn_from<Element>(v: &vector<Element>, offset: u64, size: u64): vector<Element>;
+
+ + + +
+ +
+Specification + + + +
pragma opaque;
+ensures [abstract] result == v[offset..offset+size];
+
+ + + +
+ + + +## Function `spawn_from_vec` + + + +
public fun spawn_from_vec<Element: copy>(v: &vector<Element>, offset: u64, size: u64): vector<Element>
+
+ + + +
+Implementation + + +
public fun spawn_from_vec<Element: copy>(v: &vector<Element>, offset: u64, size: u64): vector<Element> {
+    let len = length(v);
+    let end_idx = (offset + size);
+    assert!(end_idx <= len, EINDEX_OUT_OF_BOUNDS);
+    assert!(size > 0, EINDEX_OUT_OF_BOUNDS);
+    if (offset == 0 && end_idx == len) {
+        return *v
+    };
+    spawn_from(v, offset, size)
+}
+
+ + + +
+ + + +## Function `swap` + +Swaps the elements at the ith and jth indices in the vector v. +Aborts if ior j is out of bounds. + + +
public fun swap<Element>(v: &mut vector<Element>, i: u64, j: u64)
+
+ + + +
+Implementation + + +
native public fun swap<Element>(v: &mut vector<Element>, i: u64, j: u64);
+
+ + + +
+ + + +## Function `singleton` + +Return an vector of size one containing element e. + + +
public fun singleton<Element>(e: Element): vector<Element>
+
+ + + +
+Implementation + + +
public fun singleton<Element>(e: Element): vector<Element> {
+    let v = empty();
+    push_back(&mut v, e);
+    v
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+ensures result == vec(e);
+
+ + + + + + + +
fun spec_singleton<Element>(e: Element): vector<Element> {
+   vec(e)
+}
+
+ + + +
+ + + +## Function `reverse` + +Reverses the order of the elements in the vector v in place. + + +
public fun reverse<Element>(v: &mut vector<Element>)
+
+ + + +
+Implementation + + +
public fun reverse<Element>(v: &mut vector<Element>) {
+    native_reverse(v)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `native_reverse` + + + +
fun native_reverse<Element>(this: &mut vector<Element>)
+
+ + + +
+Implementation + + +
native fun native_reverse<Element>(this: &mut vector<Element>);
+
+ + + +
+ + + +## Function `append` + +Pushes all of the elements of the other vector into the lhs vector. + + +
public fun append<Element>(lhs: &mut vector<Element>, other: vector<Element>)
+
+ + + +
+Implementation + + +
public fun append<Element>(lhs: &mut vector<Element>, other: vector<Element>) {
+    native_append(lhs, other);
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `native_append` + + + +
fun native_append<Element>(lhs: &mut vector<Element>, other: vector<Element>)
+
+ + + +
+Implementation + + +
native fun native_append<Element>(lhs: &mut vector<Element>, other: vector<Element>);
+
+ + + +
+ + + +## Function `is_empty` + +Return true if the vector v has no elements and false otherwise. + + +
public fun is_empty<Element>(v: &vector<Element>): bool
+
+ + + +
+Implementation + + +
public fun is_empty<Element>(v: &vector<Element>): bool {
+    length(v) == 0
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `contains` + +Return true if e is in the vector v. + + +
public fun contains<Element>(v: &vector<Element>, e: &Element): bool
+
+ + + +
+Implementation + + +
public fun contains<Element>(v: &vector<Element>, e: &Element): bool {
+    let i = 0;
+    let len = length(v);
+    while (i < len) {
+        if (borrow(v, i) == e) return true;
+        i = i + 1;
+    };
+    false
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `index_of` + +Return (true, i) if e is in the vector v at index i. +Otherwise, returns (false, 0). + + +
public fun index_of<Element>(v: &vector<Element>, e: &Element): (bool, u64)
+
+ + + +
+Implementation + + +
public fun index_of<Element>(v: &vector<Element>, e: &Element): (bool, u64) {
+    let i = 0;
+    let len = length(v);
+    while (i < len) {
+        if (borrow(v, i) == e) return (true, i);
+        i = i + 1;
+    };
+    (false, 0)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `remove` + +Remove the ith element of the vector v, shifting all subsequent elements. +This is O(n) and preserves ordering of elements in the vector. +Aborts if i is out of bounds. + + +
public fun remove<Element>(v: &mut vector<Element>, i: u64): Element
+
+ + + +
+Implementation + + +
public fun remove<Element>(v: &mut vector<Element>, i: u64): Element {
+    let len = length(v);
+    // i out of bounds; abort
+    if (i >= len) abort EINDEX_OUT_OF_BOUNDS;
+
+    native_remove(v, i)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `native_remove` + + + +
fun native_remove<Element>(this: &mut vector<Element>, i: u64): Element
+
+ + + +
+Implementation + + +
native fun native_remove<Element>(this: &mut vector<Element>, i: u64): Element;
+
+ + + +
+ + + +## Function `swap_remove` + +Swap the ith element of the vector v with the last element and then pop the vector. +This is O(1), but does not preserve ordering of elements in the vector. +Aborts if i is out of bounds. + + +
public fun swap_remove<Element>(v: &mut vector<Element>, i: u64): Element
+
+ + + +
+Implementation + + +
public fun swap_remove<Element>(v: &mut vector<Element>, i: u64): Element {
+    let last_idx = length(v) - 1;
+    swap(v, i, last_idx);
+    pop_back(v)
+}
+
+ + + +
+ +
+Specification + + + +
pragma intrinsic = true;
+
+ + + +
+ + + +## Function `split` + +Split a vector into sub-vectors of size sub_len, + + +
public fun split<Element: copy, drop, store>(v: &vector<Element>, sub_len: u64): vector<vector<Element>>
+
+ + + +
+Implementation + + +
public fun split<Element: copy + drop + store>(v: &vector<Element>, sub_len: u64): vector<vector<Element>> {
+    let result = empty<vector<Element>>();
+    let len = length(v) / sub_len;
+
+    let rem = 0;
+    if (len * sub_len < length(v)) {
+        rem = length(v) - len * sub_len;
+    };
+
+    let i = 0;
+    while (i < len) {
+        let sub = empty<Element>();
+        let j = 0;
+        while (j < sub_len) {
+            let index = sub_len * i + j;
+            push_back(&mut sub, *borrow(v, index));
+            j = j + 1;
+        };
+        push_back<vector<Element>>(&mut result, sub);
+        i = i + 1;
+    };
+
+    if (rem > 0) {
+        let sub = empty<Element>();
+        let index = length(v) - rem;
+        while (index < length(v)) {
+            push_back(&mut sub, *borrow(v, index));
+            index = index + 1;
+        };
+        push_back<vector<Element>>(&mut result, sub);
+    };
+    result
+}
+
+ + + +
+ +
+Specification + + + +
pragma verify = false;
+aborts_if sub_len == 0;
+
+ + + +
+ + + +## Module Specification + + + + + +### Helper Functions + +Check whether a vector contains an element. + + + + + +
fun spec_contains<Element>(v: vector<Element>, e: Element): bool {
+   exists x in v: x == e
+}
+
+ + +Check if v1 is equal to the result of adding e at the end of v2 + + + + + +
fun eq_push_back<Element>(v1: vector<Element>, v2: vector<Element>, e: Element): bool {
+   len(v1) == len(v2) + 1 &&
+   v1[len(v1)-1] == e &&
+   v1[0..len(v1)-1] == v2[0..len(v2)]
+}
+
+ + +Check if v is equal to the result of concatenating v1 and v2 + + + + + +
fun eq_append<Element>(v: vector<Element>, v1: vector<Element>, v2: vector<Element>): bool {
+   len(v) == len(v1) + len(v2) &&
+   v[0..len(v1)] == v1 &&
+   v[len(v1)..len(v)] == v2
+}
+
+ + +Check v1 is equal to the result of removing the first element of v2 + + + + + +
fun eq_pop_front<Element>(v1: vector<Element>, v2: vector<Element>): bool {
+   len(v1) + 1 == len(v2) &&
+   v1 == v2[1..len(v2)]
+}
+
+ + +Check that v1 is equal to the result of removing the element at index i from v2. + + + + + +
fun eq_remove_elem_at_index<Element>(i: u64, v1: vector<Element>, v2: vector<Element>): bool {
+   len(v1) + 1 == len(v2) &&
+   v1[0..i] == v2[0..i] &&
+   v1[i..len(v1)] == v2[i + 1..len(v2)]
+}
+
diff --git a/release/v13/docs/Version.md b/release/v13/docs/Version.md new file mode 100644 index 00000000..64012d09 --- /dev/null +++ b/release/v13/docs/Version.md @@ -0,0 +1,146 @@ + + + +# Module `0x1::Version` + +Version tracks version of something, like current VM version. + + +- [Struct `Version`](#0x1_Version_Version) +- [Constants](#@Constants_0) +- [Function `new_version`](#0x1_Version_new_version) +- [Function `get`](#0x1_Version_get) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Config;
+
+ + + + + +## Struct `Version` + +Version. + + +
struct Version has copy, drop, store
+
+ + + +
+Fields + + +
+
+major: u64 +
+
+ major number. +
+
+ + +
+ + + +## Constants + + + + + + +
const EMAJOR_TO_OLD: u64 = 101;
+
+ + + + + +## Function `new_version` + +Create a new version. + + +
public fun new_version(major: u64): Version::Version
+
+ + + +
+Implementation + + +
public fun new_version(major: u64): Version {
+    Version { major }
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if false;
+
+ + + +
+ + + +## Function `get` + +Get version under addr. + + +
public fun get(addr: address): u64
+
+ + + +
+Implementation + + +
public fun get(addr: address): u64 {
+    let version = Config::get_by_address<Self::Version>(addr);
+    version.major
+}
+
+ + + +
+ +
+Specification + + + +
aborts_if !exists<Config::Config<Version>>(addr);
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify;
+pragma aborts_if_is_strict;
+
diff --git a/release/v13/docs/YieldFarming.md b/release/v13/docs/YieldFarming.md new file mode 100644 index 00000000..e1535fd0 --- /dev/null +++ b/release/v13/docs/YieldFarming.md @@ -0,0 +1,886 @@ + + + +# Module `0x1::YieldFarming` + + + +- [Resource `Farming`](#0x1_YieldFarming_Farming) +- [Resource `FarmingAsset`](#0x1_YieldFarming_FarmingAsset) +- [Resource `ParameterModifyCapability`](#0x1_YieldFarming_ParameterModifyCapability) +- [Resource `Stake`](#0x1_YieldFarming_Stake) +- [Struct `Exp`](#0x1_YieldFarming_Exp) +- [Constants](#@Constants_0) +- [Function `exp`](#0x1_YieldFarming_exp) +- [Function `mul_u128`](#0x1_YieldFarming_mul_u128) +- [Function `div_u128`](#0x1_YieldFarming_div_u128) +- [Function `truncate`](#0x1_YieldFarming_truncate) +- [Function `initialize`](#0x1_YieldFarming_initialize) +- [Function `initialize_asset`](#0x1_YieldFarming_initialize_asset) +- [Function `modify_parameter`](#0x1_YieldFarming_modify_parameter) +- [Function `stake`](#0x1_YieldFarming_stake) +- [Function `unstake`](#0x1_YieldFarming_unstake) +- [Function `harvest`](#0x1_YieldFarming_harvest) +- [Function `query_gov_token_amount`](#0x1_YieldFarming_query_gov_token_amount) +- [Function `query_total_stake`](#0x1_YieldFarming_query_total_stake) +- [Function `query_stake`](#0x1_YieldFarming_query_stake) +- [Function `calculate_harvest_index_with_asset`](#0x1_YieldFarming_calculate_harvest_index_with_asset) +- [Function `calculate_harvest_index_weight_zero`](#0x1_YieldFarming_calculate_harvest_index_weight_zero) +- [Function `calculate_harvest_index`](#0x1_YieldFarming_calculate_harvest_index) +- [Function `calculate_withdraw_amount`](#0x1_YieldFarming_calculate_withdraw_amount) +- [Function `exists_at`](#0x1_YieldFarming_exists_at) +- [Function `exists_asset_at`](#0x1_YieldFarming_exists_asset_at) +- [Function `exists_stake_at_address`](#0x1_YieldFarming_exists_stake_at_address) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Token;
+
+ + + + + +## Resource `Farming` + +The object of yield farming +RewardTokenT meaning token of yield farming + + +
struct Farming<PoolType, RewardTokenT> has store, key
+
+ + + +
+Fields + + +
+
+treasury_token: Token::Token<RewardTokenT> +
+
+ +
+
+ + +
+ + + +## Resource `FarmingAsset` + + + +
struct FarmingAsset<PoolType, AssetT> has store, key
+
+ + + +
+Fields + + +
+
+asset_total_weight: u128 +
+
+ +
+
+harvest_index: u128 +
+
+ +
+
+last_update_timestamp: u64 +
+
+ +
+
+release_per_second: u128 +
+
+ +
+
+start_time: u64 +
+
+ +
+
+ + +
+ + + +## Resource `ParameterModifyCapability` + +Capability to modify parameter such as period and release amount + + +
struct ParameterModifyCapability<PoolType, AssetT> has store, key
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Resource `Stake` + +To store user's asset token + + +
struct Stake<PoolType, AssetT> has store, key
+
+ + + +
+Fields + + +
+
+asset: AssetT +
+
+ +
+
+asset_weight: u128 +
+
+ +
+
+last_harvest_index: u128 +
+
+ +
+
+gain: u128 +
+
+ +
+
+ + +
+ + + +## Struct `Exp` + + + +
struct Exp has copy, drop, store
+
+ + + +
+Fields + + +
+
+mantissa: u128 +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const EDEPRECATED_FUNCTION: u64 = 19;
+
+ + + + + + + +
const ERR_EXP_DIVIDE_BY_ZERO: u64 = 107;
+
+ + + + + + + +
const ERR_FARMING_BALANCE_EXCEEDED: u64 = 108;
+
+ + + + + + + +
const ERR_FARMING_HAVERST_NO_GAIN: u64 = 105;
+
+ + + + + + + +
const ERR_FARMING_INIT_REPEATE: u64 = 101;
+
+ + + + + + + +
const ERR_FARMING_NOT_ENOUGH_ASSET: u64 = 109;
+
+ + + + + + + +
const ERR_FARMING_NOT_STILL_FREEZE: u64 = 102;
+
+ + + + + + + +
const ERR_FARMING_STAKE_EXISTS: u64 = 103;
+
+ + + + + + + +
const ERR_FARMING_STAKE_NOT_EXISTS: u64 = 104;
+
+ + + + + + + +
const ERR_FARMING_TIMESTAMP_INVALID: u64 = 110;
+
+ + + + + + + +
const ERR_FARMING_TOTAL_WEIGHT_IS_ZERO: u64 = 106;
+
+ + + + + + + +
const EXP_SCALE: u128 = 1000000000000000000;
+
+ + + + + +## Function `exp` + + + +
fun exp(num: u128, denom: u128): YieldFarming::Exp
+
+ + + +
+Implementation + + +
fun exp(num: u128, denom: u128): Exp {
+    // if overflow move will abort
+    let scaledNumerator = mul_u128(num, EXP_SCALE);
+    let rational = div_u128(scaledNumerator, denom);
+    Exp {
+        mantissa: rational
+    }
+}
+
+ + + +
+ + + +## Function `mul_u128` + + + +
fun mul_u128(a: u128, b: u128): u128
+
+ + + +
+Implementation + + +
fun mul_u128(a: u128, b: u128): u128 {
+    if (a == 0 || b == 0) {
+        return 0
+    };
+
+    a * b
+}
+
+ + + +
+ + + +## Function `div_u128` + + + +
fun div_u128(a: u128, b: u128): u128
+
+ + + +
+Implementation + + +
fun div_u128(a: u128, b: u128): u128 {
+    if ( b == 0) {
+        abort Errors::invalid_argument(ERR_EXP_DIVIDE_BY_ZERO)
+    };
+    if (a == 0) {
+        return 0
+    };
+    a / b
+}
+
+ + + +
+ + + +## Function `truncate` + + + +
fun truncate(exp: YieldFarming::Exp): u128
+
+ + + +
+Implementation + + +
fun truncate(exp: Exp): u128 {
+    return exp.mantissa / EXP_SCALE
+}
+
+ + + +
+ + + +## Function `initialize` + +Called by token issuer +this will declare a yield farming pool + + +
public fun initialize<PoolType: store, RewardTokenT: store>(_account: &signer, _treasury_token: Token::Token<RewardTokenT>)
+
+ + + +
+Implementation + + +
public fun initialize<
+    PoolType: store,
+    RewardTokenT: store>(_account: &signer,
+                         _treasury_token: Token::Token<RewardTokenT>) {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ + + +## Function `initialize_asset` + + + +
public fun initialize_asset<PoolType: store, AssetT: store>(_account: &signer, _release_per_second: u128, _delay: u64): YieldFarming::ParameterModifyCapability<PoolType, AssetT>
+
+ + + +
+Implementation + + +
public fun initialize_asset<PoolType: store, AssetT: store>(
+    _account: &signer,
+    _release_per_second: u128,
+    _delay: u64): ParameterModifyCapability<PoolType, AssetT> {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ + + +## Function `modify_parameter` + + + +
public fun modify_parameter<PoolType: store, RewardTokenT: store, AssetT: store>(_cap: &YieldFarming::ParameterModifyCapability<PoolType, AssetT>, _broker: address, _release_per_second: u128)
+
+ + + +
+Implementation + + +
public fun modify_parameter<PoolType: store, RewardTokenT: store, AssetT: store>(
+    _cap: &ParameterModifyCapability<PoolType, AssetT>,
+    _broker: address,
+    _release_per_second: u128) {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ + + +## Function `stake` + +Call by stake user, staking amount of asset in order to get yield farming token + + +
public fun stake<PoolType: store, RewardTokenT: store, AssetT: store>(_account: &signer, _broker: address, _asset: AssetT, _asset_weight: u128)
+
+ + + +
+Implementation + + +
public fun stake<PoolType: store, RewardTokenT: store, AssetT: store>(
+    _account: &signer,
+    _broker: address,
+    _asset: AssetT,
+    _asset_weight: u128) {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ + + +## Function `unstake` + +Unstake asset from farming pool + + +
public fun unstake<PoolType: store, RewardTokenT: store, AssetT: store>(_account: &signer, _broker: address): (AssetT, Token::Token<RewardTokenT>)
+
+ + + +
+Implementation + + +
public fun unstake<PoolType: store, RewardTokenT: store, AssetT: store>(_account: &signer, _broker: address)
+: (AssetT, Token::Token<RewardTokenT>) {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ + + +## Function `harvest` + +Harvest yield farming token from stake + + +
public fun harvest<PoolType: store, RewardTokenT: store, AssetT: store>(_account: &signer, _broker: address, _amount: u128): Token::Token<RewardTokenT>
+
+ + + +
+Implementation + + +
public fun harvest<PoolType: store,
+                   RewardTokenT: store,
+                   AssetT: store>(
+    _account: &signer,
+    _broker: address,
+    _amount: u128): Token::Token<RewardTokenT> {
+    abort Errors::deprecated(EDEPRECATED_FUNCTION)
+}
+
+ + + +
+ + + +## Function `query_gov_token_amount` + +The user can quering all yield farming amount in any time and scene + + +
public fun query_gov_token_amount<PoolType: store, RewardTokenT: store, AssetT: store>(_account: &signer, _broker: address): u128
+
+ + + +
+Implementation + + +
public fun query_gov_token_amount<PoolType: store,
+                                  RewardTokenT: store,
+                                  AssetT: store>(_account: &signer, _broker: address): u128 {
+    0
+}
+
+ + + +
+ + + +## Function `query_total_stake` + +Query total stake count from yield farming resource + + +
public fun query_total_stake<PoolType: store, AssetT: store>(_broker: address): u128
+
+ + + +
+Implementation + + +
public fun query_total_stake<PoolType: store,
+                             AssetT: store>(_broker: address): u128 {
+    0
+}
+
+ + + +
+ + + +## Function `query_stake` + +Query stake weight from user staking objects. + + +
public fun query_stake<PoolType: store, AssetT: store>(_account: &signer): u128
+
+ + + +
+Implementation + + +
public fun query_stake<PoolType: store,
+                       AssetT: store>(_account: &signer): u128 {
+    0
+}
+
+ + + +
+ + + +## Function `calculate_harvest_index_with_asset` + +Update farming asset + + +
fun calculate_harvest_index_with_asset<PoolType, AssetT>(_farming_asset: &YieldFarming::FarmingAsset<PoolType, AssetT>, _now_seconds: u64): u128
+
+ + + +
+Implementation + + +
fun calculate_harvest_index_with_asset<PoolType, AssetT>(_farming_asset: &FarmingAsset<PoolType, AssetT>, _now_seconds: u64): u128 {
+    0
+}
+
+ + + +
+ + + +## Function `calculate_harvest_index_weight_zero` + +There is calculating from harvest index and global parameters without asset_total_weight + + +
public fun calculate_harvest_index_weight_zero(_harvest_index: u128, _last_update_timestamp: u64, _now_seconds: u64, _release_per_second: u128): u128
+
+ + + +
+Implementation + + +
public fun calculate_harvest_index_weight_zero(_harvest_index: u128,
+                                               _last_update_timestamp: u64,
+                                               _now_seconds: u64,
+                                               _release_per_second: u128): u128 {
+    0
+}
+
+ + + +
+ + + +## Function `calculate_harvest_index` + +There is calculating from harvest index and global parameters + + +
public fun calculate_harvest_index(_harvest_index: u128, _asset_total_weight: u128, _last_update_timestamp: u64, _now_seconds: u64, _release_per_second: u128): u128
+
+ + + +
+Implementation + + +
public fun calculate_harvest_index(_harvest_index: u128,
+                                   _asset_total_weight: u128,
+                                   _last_update_timestamp: u64,
+                                   _now_seconds: u64,
+                                   _release_per_second: u128): u128 {
+    0
+}
+
+ + + +
+ + + +## Function `calculate_withdraw_amount` + +This function will return a gain index + + +
public fun calculate_withdraw_amount(_harvest_index: u128, _last_harvest_index: u128, _asset_weight: u128): u128
+
+ + + +
+Implementation + + +
public fun calculate_withdraw_amount(_harvest_index: u128,
+                                     _last_harvest_index: u128,
+                                     _asset_weight: u128): u128 {
+    0
+}
+
+ + + +
+ + + +## Function `exists_at` + +Check the Farming of TokenT is exists. + + +
public fun exists_at<PoolType: store, RewardTokenT: store>(broker: address): bool
+
+ + + +
+Implementation + + +
public fun exists_at<PoolType: store, RewardTokenT: store>(broker: address): bool {
+    exists<Farming<PoolType, RewardTokenT>>(broker)
+}
+
+ + + +
+ + + +## Function `exists_asset_at` + +Check the Farming of AsssetT is exists. + + +
public fun exists_asset_at<PoolType: store, AssetT: store>(broker: address): bool
+
+ + + +
+Implementation + + +
public fun exists_asset_at<PoolType: store, AssetT: store>(broker: address): bool {
+    exists<FarmingAsset<PoolType, AssetT>>(broker)
+}
+
+ + + +
+ + + +## Function `exists_stake_at_address` + +Check stake at address exists. + + +
public fun exists_stake_at_address<PoolType: store, AssetT: store>(account: address): bool
+
+ + + +
+Implementation + + +
public fun exists_stake_at_address<PoolType: store, AssetT: store>(account: address): bool {
+    exists<Stake<PoolType, AssetT>>(account)
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+
diff --git a/release/v13/docs/YieldFarmingV2.md b/release/v13/docs/YieldFarmingV2.md new file mode 100644 index 00000000..f6d77651 --- /dev/null +++ b/release/v13/docs/YieldFarmingV2.md @@ -0,0 +1,1429 @@ + + + +# Module `0x1::YieldFarmingV2` + + + +- [Struct `Exp`](#0x1_YieldFarmingV2_Exp) +- [Resource `Farming`](#0x1_YieldFarmingV2_Farming) +- [Resource `FarmingAsset`](#0x1_YieldFarmingV2_FarmingAsset) +- [Resource `Stake`](#0x1_YieldFarmingV2_Stake) +- [Resource `ParameterModifyCapability`](#0x1_YieldFarmingV2_ParameterModifyCapability) +- [Resource `HarvestCapability`](#0x1_YieldFarmingV2_HarvestCapability) +- [Constants](#@Constants_0) +- [Function `exp_direct`](#0x1_YieldFarmingV2_exp_direct) +- [Function `exp_direct_expand`](#0x1_YieldFarmingV2_exp_direct_expand) +- [Function `mantissa`](#0x1_YieldFarmingV2_mantissa) +- [Function `add_exp`](#0x1_YieldFarmingV2_add_exp) +- [Function `exp`](#0x1_YieldFarmingV2_exp) +- [Function `add_u128`](#0x1_YieldFarmingV2_add_u128) +- [Function `sub_u128`](#0x1_YieldFarmingV2_sub_u128) +- [Function `mul_u128`](#0x1_YieldFarmingV2_mul_u128) +- [Function `div_u128`](#0x1_YieldFarmingV2_div_u128) +- [Function `truncate`](#0x1_YieldFarmingV2_truncate) +- [Function `initialize`](#0x1_YieldFarmingV2_initialize) +- [Function `add_asset`](#0x1_YieldFarmingV2_add_asset) +- [Function `modify_parameter`](#0x1_YieldFarmingV2_modify_parameter) +- [Function `stake`](#0x1_YieldFarmingV2_stake) +- [Function `stake_for_cap`](#0x1_YieldFarmingV2_stake_for_cap) +- [Function `unstake`](#0x1_YieldFarmingV2_unstake) +- [Function `unstake_with_cap`](#0x1_YieldFarmingV2_unstake_with_cap) +- [Function `harvest`](#0x1_YieldFarmingV2_harvest) +- [Function `harvest_with_cap`](#0x1_YieldFarmingV2_harvest_with_cap) +- [Function `query_gov_token_amount`](#0x1_YieldFarmingV2_query_gov_token_amount) +- [Function `query_total_stake`](#0x1_YieldFarmingV2_query_total_stake) +- [Function `query_stake`](#0x1_YieldFarmingV2_query_stake) +- [Function `query_info`](#0x1_YieldFarmingV2_query_info) +- [Function `calculate_harvest_index_with_asset`](#0x1_YieldFarmingV2_calculate_harvest_index_with_asset) +- [Function `calculate_harvest_index_weight_zero`](#0x1_YieldFarmingV2_calculate_harvest_index_weight_zero) +- [Function `calculate_harvest_index`](#0x1_YieldFarmingV2_calculate_harvest_index) +- [Function `calculate_withdraw_amount`](#0x1_YieldFarmingV2_calculate_withdraw_amount) +- [Function `exists_at`](#0x1_YieldFarmingV2_exists_at) +- [Function `exists_asset_at`](#0x1_YieldFarmingV2_exists_asset_at) +- [Function `exists_stake_at_address`](#0x1_YieldFarmingV2_exists_stake_at_address) +- [Module Specification](#@Module_Specification_1) + + +
use 0x1::Errors;
+use 0x1::Math;
+use 0x1::Signer;
+use 0x1::Timestamp;
+use 0x1::Token;
+
+ + + + + +## Struct `Exp` + + + +
struct Exp has copy, drop, store
+
+ + + +
+Fields + + +
+
+mantissa: u128 +
+
+ +
+
+ + +
+ + + +## Resource `Farming` + +The object of yield farming +RewardTokenT meaning token of yield farming + + +
struct Farming<PoolType, RewardTokenT> has store, key
+
+ + + +
+Fields + + +
+
+treasury_token: Token::Token<RewardTokenT> +
+
+ +
+
+ + +
+ + + +## Resource `FarmingAsset` + + + +
struct FarmingAsset<PoolType, AssetT> has store, key
+
+ + + +
+Fields + + +
+
+asset_total_weight: u128 +
+
+ +
+
+harvest_index: u128 +
+
+ +
+
+last_update_timestamp: u64 +
+
+ +
+
+release_per_second: u128 +
+
+ +
+
+start_time: u64 +
+
+ +
+
+alive: bool +
+
+ +
+
+ + +
+ + + +## Resource `Stake` + +To store user's asset token + + +
struct Stake<PoolType, AssetT> has store, key
+
+ + + +
+Fields + + +
+
+asset: AssetT +
+
+ +
+
+asset_weight: u128 +
+
+ +
+
+last_harvest_index: u128 +
+
+ +
+
+gain: u128 +
+
+ +
+
+ + +
+ + + +## Resource `ParameterModifyCapability` + +Capability to modify parameter such as period and release amount + + +
struct ParameterModifyCapability<PoolType, AssetT> has store, key
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Resource `HarvestCapability` + +Harvest ability to harvest + + +
struct HarvestCapability<PoolType, AssetT> has store, key
+
+ + + +
+Fields + + +
+
+trigger: address +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ERR_EXP_DIVIDE_BY_ZERO: u64 = 107;
+
+ + + + + + + +
const ERR_FARMING_BALANCE_EXCEEDED: u64 = 108;
+
+ + + + + + + +
const ERR_FARMING_HAVERST_NO_GAIN: u64 = 105;
+
+ + + + + + + +
const ERR_FARMING_INIT_REPEATE: u64 = 101;
+
+ + + + + + + +
const ERR_FARMING_NOT_ENOUGH_ASSET: u64 = 109;
+
+ + + + + + + +
const ERR_FARMING_NOT_STILL_FREEZE: u64 = 102;
+
+ + + + + + + +
const ERR_FARMING_STAKE_EXISTS: u64 = 103;
+
+ + + + + + + +
const ERR_FARMING_STAKE_NOT_EXISTS: u64 = 104;
+
+ + + + + + + +
const ERR_FARMING_TIMESTAMP_INVALID: u64 = 110;
+
+ + + + + + + +
const ERR_FARMING_TOTAL_WEIGHT_IS_ZERO: u64 = 106;
+
+ + + + + + + +
const EXP_SCALE: u128 = 1000000000000000000;
+
+ + + + + + + +
const ERR_FARMING_ALIVE_STATE_INVALID: u64 = 114;
+
+ + + + + + + +
const ERR_FARMING_CALC_LAST_IDX_BIGGER_THAN_NOW: u64 = 112;
+
+ + + + + + + +
const ERR_FARMING_NOT_ALIVE: u64 = 113;
+
+ + + + + + + +
const ERR_FARMING_TOKEN_SCALE_OVERFLOW: u64 = 111;
+
+ + + + + + + +
const EXP_MAX_SCALE: u128 = 9;
+
+ + + + + +## Function `exp_direct` + + + +
fun exp_direct(num: u128): YieldFarmingV2::Exp
+
+ + + +
+Implementation + + +
fun exp_direct(num: u128): Exp {
+    Exp {
+        mantissa: num
+    }
+}
+
+ + + +
+ + + +## Function `exp_direct_expand` + + + +
fun exp_direct_expand(num: u128): YieldFarmingV2::Exp
+
+ + + +
+Implementation + + +
fun exp_direct_expand(num: u128): Exp {
+    Exp {
+        mantissa: mul_u128(num, EXP_SCALE)
+    }
+}
+
+ + + +
+ + + +## Function `mantissa` + + + +
fun mantissa(a: YieldFarmingV2::Exp): u128
+
+ + + +
+Implementation + + +
fun mantissa(a: Exp): u128 {
+    a.mantissa
+}
+
+ + + +
+ + + +## Function `add_exp` + + + +
fun add_exp(a: YieldFarmingV2::Exp, b: YieldFarmingV2::Exp): YieldFarmingV2::Exp
+
+ + + +
+Implementation + + +
fun add_exp(a: Exp, b: Exp): Exp {
+    Exp {
+        mantissa: add_u128(a.mantissa, b.mantissa)
+    }
+}
+
+ + + +
+ + + +## Function `exp` + + + +
fun exp(num: u128, denom: u128): YieldFarmingV2::Exp
+
+ + + +
+Implementation + + +
fun exp(num: u128, denom: u128): Exp {
+    // if overflow move will abort
+    let scaledNumerator = mul_u128(num, EXP_SCALE);
+    let rational = div_u128(scaledNumerator, denom);
+    Exp {
+        mantissa: rational
+    }
+}
+
+ + + +
+ + + +## Function `add_u128` + + + +
fun add_u128(a: u128, b: u128): u128
+
+ + + +
+Implementation + + +
fun add_u128(a: u128, b: u128): u128 {
+    a + b
+}
+
+ + + +
+ + + +## Function `sub_u128` + + + +
fun sub_u128(a: u128, b: u128): u128
+
+ + + +
+Implementation + + +
fun sub_u128(a: u128, b: u128): u128 {
+    a - b
+}
+
+ + + +
+ + + +## Function `mul_u128` + + + +
fun mul_u128(a: u128, b: u128): u128
+
+ + + +
+Implementation + + +
fun mul_u128(a: u128, b: u128): u128 {
+    if (a == 0 || b == 0) {
+        return 0
+    };
+    a * b
+}
+
+ + + +
+ + + +## Function `div_u128` + + + +
fun div_u128(a: u128, b: u128): u128
+
+ + + +
+Implementation + + +
fun div_u128(a: u128, b: u128): u128 {
+    if (b == 0) {
+        abort Errors::invalid_argument(ERR_EXP_DIVIDE_BY_ZERO)
+    };
+    if (a == 0) {
+        return 0
+    };
+    a / b
+}
+
+ + + +
+ + + +## Function `truncate` + + + +
fun truncate(exp: YieldFarmingV2::Exp): u128
+
+ + + +
+Implementation + + +
fun truncate(exp: Exp): u128 {
+    return exp.mantissa / EXP_SCALE
+}
+
+ + + +
+ + + +## Function `initialize` + +Called by token issuer +this will declare a yield farming pool + + +
public fun initialize<PoolType: store, RewardTokenT: store>(signer: &signer, treasury_token: Token::Token<RewardTokenT>)
+
+ + + +
+Implementation + + +
public fun initialize<
+    PoolType: store,
+    RewardTokenT: store>(signer: &signer, treasury_token: Token::Token<RewardTokenT>) {
+    let scaling_factor = Math::pow(10, (EXP_MAX_SCALE as u64));
+    let token_scale = Token::scaling_factor<RewardTokenT>();
+    assert!(token_scale <= scaling_factor, Errors::limit_exceeded(ERR_FARMING_TOKEN_SCALE_OVERFLOW));
+    assert!(!exists_at<PoolType, RewardTokenT>(
+        Signer::address_of(signer)), Errors::invalid_state(ERR_FARMING_INIT_REPEATE));
+
+    move_to(signer, Farming<PoolType, RewardTokenT> {
+        treasury_token,
+    });
+}
+
+ + + +
+ + + +## Function `add_asset` + +Add asset pools + + +
public fun add_asset<PoolType: store, AssetT: store>(signer: &signer, release_per_second: u128, delay: u64): YieldFarmingV2::ParameterModifyCapability<PoolType, AssetT>
+
+ + + +
+Implementation + + +
public fun add_asset<PoolType: store, AssetT: store>(
+    signer: &signer,
+    release_per_second: u128,
+    delay: u64): ParameterModifyCapability<PoolType, AssetT> {
+    assert!(!exists_asset_at<PoolType, AssetT>(
+        Signer::address_of(signer)),
+        Errors::invalid_state(ERR_FARMING_INIT_REPEATE));
+
+    let now_seconds = Timestamp::now_seconds();
+
+    move_to(signer, FarmingAsset<PoolType, AssetT> {
+        asset_total_weight: 0,
+        harvest_index: 0,
+        last_update_timestamp: now_seconds,
+        release_per_second,
+        start_time: now_seconds + delay,
+        alive: true
+    });
+    ParameterModifyCapability<PoolType, AssetT> {}
+}
+
+ + + +
+ + + +## Function `modify_parameter` + +Remove asset for make this pool to the state of not alive +Please make sure all user unstaking from this pool + + +
public fun modify_parameter<PoolType: store, RewardTokenT: store, AssetT: store>(_cap: &YieldFarmingV2::ParameterModifyCapability<PoolType, AssetT>, broker: address, release_per_second: u128, alive: bool)
+
+ + + +
+Implementation + + +
public fun modify_parameter<PoolType: store, RewardTokenT: store, AssetT: store>(
+    _cap: &ParameterModifyCapability<PoolType, AssetT>,
+    broker: address,
+    release_per_second: u128,
+    alive: bool) acquires FarmingAsset {
+
+    // Not support to shuttingdown alive state.
+    assert!(alive, Errors::invalid_state(ERR_FARMING_ALIVE_STATE_INVALID));
+
+    let farming_asset = borrow_global_mut<FarmingAsset<PoolType, AssetT>>(broker);
+    // assert!(farming_asset.alive != alive, Errors::invalid_state(ERR_FARMING_ALIVE_STATE_INVALID));
+
+    let now_seconds = Timestamp::now_seconds();
+
+    farming_asset.release_per_second = release_per_second;
+    farming_asset.last_update_timestamp = now_seconds;
+
+    // if the pool is alive, then update index
+    if (farming_asset.alive) {
+        farming_asset.harvest_index = calculate_harvest_index_with_asset<PoolType, AssetT>(farming_asset, now_seconds);
+    };
+    farming_asset.alive = alive;
+}
+
+ + + +
+ + + +## Function `stake` + +Call by stake user, staking amount of asset in order to get yield farming token + + +
public fun stake<PoolType: store, RewardTokenT: store, AssetT: store>(signer: &signer, broker: address, asset: AssetT, asset_weight: u128, _cap: &YieldFarmingV2::ParameterModifyCapability<PoolType, AssetT>)
+
+ + + +
+Implementation + + +
public fun stake<PoolType: store, RewardTokenT: store, AssetT: store>(
+    signer: &signer,
+    broker: address,
+    asset: AssetT,
+    asset_weight: u128,
+    _cap: &ParameterModifyCapability<PoolType, AssetT>) acquires FarmingAsset {
+    let harvest_cap = stake_for_cap<
+        PoolType,
+        RewardTokenT,
+        AssetT>(signer, broker, asset, asset_weight, _cap);
+
+    move_to(signer, harvest_cap);
+}
+
+ + + +
+ + + +## Function `stake_for_cap` + + + +
public fun stake_for_cap<PoolType: store, RewardTokenT: store, AssetT: store>(signer: &signer, broker: address, asset: AssetT, asset_weight: u128, _cap: &YieldFarmingV2::ParameterModifyCapability<PoolType, AssetT>): YieldFarmingV2::HarvestCapability<PoolType, AssetT>
+
+ + + +
+Implementation + + +
public fun stake_for_cap<PoolType: store, RewardTokenT: store, AssetT: store>(
+    signer: &signer,
+    broker: address,
+    asset: AssetT,
+    asset_weight: u128,
+    _cap: &ParameterModifyCapability<PoolType, AssetT>)
+: HarvestCapability<PoolType, AssetT> acquires FarmingAsset {
+    let account = Signer::address_of(signer);
+    assert!(!exists_stake_at_address<PoolType, AssetT>(account),
+        Errors::invalid_state(ERR_FARMING_STAKE_EXISTS));
+
+    let farming_asset = borrow_global_mut<FarmingAsset<PoolType, AssetT>>(broker);
+    assert!(farming_asset.alive, Errors::invalid_state(ERR_FARMING_NOT_ALIVE));
+
+    // Check locking time
+    let now_seconds = Timestamp::now_seconds();
+    assert!(farming_asset.start_time <= now_seconds, Errors::invalid_state(ERR_FARMING_NOT_STILL_FREEZE));
+
+    let time_period = now_seconds - farming_asset.last_update_timestamp;
+
+    if (farming_asset.asset_total_weight <= 0) {
+        // Stake as first user
+        let gain = farming_asset.release_per_second * (time_period as u128);
+        move_to(signer, Stake<PoolType, AssetT> {
+            asset,
+            asset_weight,
+            last_harvest_index: 0,
+            gain,
+        });
+        farming_asset.harvest_index = 0;
+        farming_asset.asset_total_weight = asset_weight;
+    } else {
+        let new_harvest_index = calculate_harvest_index_with_asset<PoolType, AssetT>(farming_asset, now_seconds);
+        move_to(signer, Stake<PoolType, AssetT> {
+            asset,
+            asset_weight,
+            last_harvest_index: new_harvest_index,
+            gain: 0,
+        });
+        farming_asset.asset_total_weight = farming_asset.asset_total_weight + asset_weight;
+        farming_asset.harvest_index = new_harvest_index;
+    };
+    farming_asset.last_update_timestamp = now_seconds;
+    HarvestCapability<PoolType, AssetT> { trigger: account }
+}
+
+ + + +
+ + + +## Function `unstake` + +Unstake asset from farming pool + + +
public fun unstake<PoolType: store, RewardTokenT: store, AssetT: store>(signer: &signer, broker: address): (AssetT, Token::Token<RewardTokenT>)
+
+ + + +
+Implementation + + +
public fun unstake<PoolType: store, RewardTokenT: store, AssetT: store>(
+    signer: &signer,
+    broker: address)
+: (AssetT, Token::Token<RewardTokenT>) acquires HarvestCapability, Farming, FarmingAsset, Stake {
+    let account = Signer::address_of(signer);
+    let cap = move_from<HarvestCapability<PoolType, AssetT>>(account);
+    unstake_with_cap(broker, cap)
+}
+
+ + + +
+ + + +## Function `unstake_with_cap` + + + +
public fun unstake_with_cap<PoolType: store, RewardTokenT: store, AssetT: store>(broker: address, cap: YieldFarmingV2::HarvestCapability<PoolType, AssetT>): (AssetT, Token::Token<RewardTokenT>)
+
+ + + +
+Implementation + + +
public fun unstake_with_cap<PoolType: store, RewardTokenT: store, AssetT: store>(
+    broker: address,
+    cap: HarvestCapability<PoolType, AssetT>)
+: (AssetT, Token::Token<RewardTokenT>) acquires Farming, FarmingAsset, Stake {
+    // Destroy capability
+    let HarvestCapability<PoolType, AssetT> { trigger } = cap;
+
+    let farming = borrow_global_mut<Farming<PoolType, RewardTokenT>>(broker);
+    let farming_asset = borrow_global_mut<FarmingAsset<PoolType, AssetT>>(broker);
+
+    let Stake<PoolType, AssetT> { last_harvest_index, asset_weight, asset, gain } =
+        move_from<Stake<PoolType, AssetT>>(trigger);
+
+    let now_seconds = Timestamp::now_seconds();
+    let new_harvest_index = calculate_harvest_index_with_asset<PoolType, AssetT>(farming_asset, now_seconds);
+
+    let period_gain = calculate_withdraw_amount(new_harvest_index, last_harvest_index, asset_weight);
+    let total_gain = gain + period_gain;
+    let withdraw_token = Token::withdraw<RewardTokenT>(&mut farming.treasury_token, total_gain);
+
+    // Dont update harvest index that because the `Stake` object has droped.
+    // let new_index = calculate_harvest_index_with_asset<PoolType, AssetT>(farming_asset, now_seconds);
+    assert!(farming_asset.asset_total_weight >= asset_weight, Errors::invalid_state(ERR_FARMING_NOT_ENOUGH_ASSET));
+
+    // Update farm asset
+    farming_asset.asset_total_weight = farming_asset.asset_total_weight - asset_weight;
+    farming_asset.last_update_timestamp = now_seconds;
+
+    if (farming_asset.alive) {
+        farming_asset.harvest_index = new_harvest_index;
+    };
+
+    (asset, withdraw_token)
+}
+
+ + + +
+ + + +## Function `harvest` + +Harvest yield farming token from stake + + +
public fun harvest<PoolType: store, RewardTokenT: store, AssetT: store>(signer: &signer, broker: address, amount: u128): Token::Token<RewardTokenT>
+
+ + + +
+Implementation + + +
public fun harvest<PoolType: store,
+                   RewardTokenT: store,
+                   AssetT: store>(
+    signer: &signer,
+    broker: address,
+    amount: u128) : Token::Token<RewardTokenT> acquires HarvestCapability, Farming, FarmingAsset, Stake {
+    let account = Signer::address_of(signer);
+    let cap = borrow_global_mut<HarvestCapability<PoolType, AssetT>>(account);
+    harvest_with_cap(broker, amount, cap)
+}
+
+ + + +
+ + + +## Function `harvest_with_cap` + + + +
public fun harvest_with_cap<PoolType: store, RewardTokenT: store, AssetT: store>(broker: address, amount: u128, _cap: &YieldFarmingV2::HarvestCapability<PoolType, AssetT>): Token::Token<RewardTokenT>
+
+ + + +
+Implementation + + +
public fun harvest_with_cap<PoolType: store,
+                            RewardTokenT: store,
+                            AssetT: store>(
+    broker: address,
+    amount: u128,
+    _cap: &HarvestCapability<PoolType, AssetT>): Token::Token<RewardTokenT> acquires Farming, FarmingAsset, Stake {
+    let farming = borrow_global_mut<Farming<PoolType, RewardTokenT>>(broker);
+    let farming_asset = borrow_global_mut<FarmingAsset<PoolType, AssetT>>(broker);
+    let stake = borrow_global_mut<Stake<PoolType, AssetT>>(_cap.trigger);
+
+    let now_seconds = Timestamp::now_seconds();
+    let new_harvest_index = calculate_harvest_index_with_asset<PoolType, AssetT>(farming_asset, now_seconds);
+
+    let period_gain = calculate_withdraw_amount(
+        new_harvest_index,
+        stake.last_harvest_index,
+        stake.asset_weight
+    );
+
+    let total_gain = stake.gain + period_gain;
+    //assert!(total_gain > 0, Errors::limit_exceeded(ERR_FARMING_HAVERST_NO_GAIN));
+    assert!(total_gain >= amount, Errors::limit_exceeded(ERR_FARMING_BALANCE_EXCEEDED));
+
+    let withdraw_amount = if (amount <= 0) {
+        total_gain
+    } else {
+        amount
+    };
+
+    let withdraw_token = Token::withdraw<RewardTokenT>(&mut farming.treasury_token, withdraw_amount);
+    stake.gain = total_gain - withdraw_amount;
+    stake.last_harvest_index = new_harvest_index;
+
+    if (farming_asset.alive) {
+        farming_asset.harvest_index = new_harvest_index;
+    };
+    farming_asset.last_update_timestamp = now_seconds;
+
+    withdraw_token
+}
+
+ + + +
+ + + +## Function `query_gov_token_amount` + +The user can quering all yield farming amount in any time and scene + + +
public fun query_gov_token_amount<PoolType: store, RewardTokenT: store, AssetT: store>(account: address, broker: address): u128
+
+ + + +
+Implementation + + +
public fun query_gov_token_amount<PoolType: store,
+                                  RewardTokenT: store,
+                                  AssetT: store>(account: address, broker: address): u128 acquires FarmingAsset, Stake {
+    let farming_asset = borrow_global_mut<FarmingAsset<PoolType, AssetT>>(broker);
+    let stake = borrow_global_mut<Stake<PoolType, AssetT>>(account);
+    let now_seconds = Timestamp::now_seconds();
+
+    let new_harvest_index = calculate_harvest_index_with_asset<PoolType, AssetT>(
+        farming_asset,
+        now_seconds
+    );
+
+    let new_gain = calculate_withdraw_amount(
+        new_harvest_index,
+        stake.last_harvest_index,
+        stake.asset_weight
+    );
+    stake.gain + new_gain
+}
+
+ + + +
+ + + +## Function `query_total_stake` + +Query total stake count from yield farming resource + + +
public fun query_total_stake<PoolType: store, AssetT: store>(broker: address): u128
+
+ + + +
+Implementation + + +
public fun query_total_stake<PoolType: store,
+                             AssetT: store>(broker: address): u128 acquires FarmingAsset {
+    let farming_asset = borrow_global_mut<FarmingAsset<PoolType, AssetT>>(broker);
+    farming_asset.asset_total_weight
+}
+
+ + + +
+ + + +## Function `query_stake` + +Query stake weight from user staking objects. + + +
public fun query_stake<PoolType: store, AssetT: store>(account: address): u128
+
+ + + +
+Implementation + + +
public fun query_stake<PoolType: store,
+                       AssetT: store>(account: address): u128 acquires Stake {
+    let stake = borrow_global_mut<Stake<PoolType, AssetT>>(account);
+    stake.asset_weight
+}
+
+ + + +
+ + + +## Function `query_info` + +Queyry pool info from pool type +return value: (alive, release_per_second, asset_total_weight, harvest_index) + + +
public fun query_info<PoolType: store, AssetT: store>(broker: address): (bool, u128, u128, u128)
+
+ + + +
+Implementation + + +
public fun query_info<PoolType: store, AssetT: store>(broker: address): (bool, u128, u128, u128) acquires FarmingAsset {
+    let asset = borrow_global_mut<FarmingAsset<PoolType, AssetT>>(broker);
+    (
+        asset.alive,
+        asset.release_per_second,
+        asset.asset_total_weight,
+        asset.harvest_index
+    )
+}
+
+ + + +
+ + + +## Function `calculate_harvest_index_with_asset` + +Update farming asset + + +
fun calculate_harvest_index_with_asset<PoolType, AssetT>(farming_asset: &YieldFarmingV2::FarmingAsset<PoolType, AssetT>, now_seconds: u64): u128
+
+ + + +
+Implementation + + +
fun calculate_harvest_index_with_asset<PoolType, AssetT>(farming_asset: &FarmingAsset<PoolType, AssetT>, now_seconds: u64): u128 {
+    // Recalculate harvest index
+    if (farming_asset.asset_total_weight <= 0) {
+        calculate_harvest_index_weight_zero(
+            farming_asset.harvest_index,
+            farming_asset.last_update_timestamp,
+            now_seconds,
+            farming_asset.release_per_second
+        )
+    } else {
+        calculate_harvest_index(
+            farming_asset.harvest_index,
+            farming_asset.asset_total_weight,
+            farming_asset.last_update_timestamp,
+            now_seconds,
+            farming_asset.release_per_second
+        )
+    }
+}
+
+ + + +
+ + + +## Function `calculate_harvest_index_weight_zero` + +There is calculating from harvest index and global parameters without asset_total_weight + + +
public fun calculate_harvest_index_weight_zero(harvest_index: u128, last_update_timestamp: u64, now_seconds: u64, release_per_second: u128): u128
+
+ + + +
+Implementation + + +
public fun calculate_harvest_index_weight_zero(harvest_index: u128,
+                                               last_update_timestamp: u64,
+                                               now_seconds: u64,
+                                               release_per_second: u128): u128 {
+    assert!(last_update_timestamp <= now_seconds, Errors::invalid_argument(ERR_FARMING_TIMESTAMP_INVALID));
+    let time_period = now_seconds - last_update_timestamp;
+    let addtion_index = release_per_second * ((time_period as u128));
+    harvest_index + mantissa(exp_direct_expand(addtion_index))
+}
+
+ + + +
+ + + +## Function `calculate_harvest_index` + +There is calculating from harvest index and global parameters + + +
public fun calculate_harvest_index(harvest_index: u128, asset_total_weight: u128, last_update_timestamp: u64, now_seconds: u64, release_per_second: u128): u128
+
+ + + +
+Implementation + + +
public fun calculate_harvest_index(harvest_index: u128,
+                                   asset_total_weight: u128,
+                                   last_update_timestamp: u64,
+                                   now_seconds: u64,
+                                   release_per_second: u128): u128 {
+    assert!(asset_total_weight > 0, Errors::invalid_argument(ERR_FARMING_TOTAL_WEIGHT_IS_ZERO));
+    assert!(last_update_timestamp <= now_seconds, Errors::invalid_argument(ERR_FARMING_TIMESTAMP_INVALID));
+
+    let time_period = now_seconds - last_update_timestamp;
+    let numr = (release_per_second * (time_period as u128));
+    let denom = asset_total_weight;
+    harvest_index + mantissa(exp(numr, denom))
+}
+
+ + + +
+ + + +## Function `calculate_withdraw_amount` + +This function will return a gain index + + +
public fun calculate_withdraw_amount(harvest_index: u128, last_harvest_index: u128, asset_weight: u128): u128
+
+ + + +
+Implementation + + +
public fun calculate_withdraw_amount(harvest_index: u128,
+                                     last_harvest_index: u128,
+                                     asset_weight: u128): u128 {
+    assert!(harvest_index >= last_harvest_index, Errors::invalid_argument(ERR_FARMING_CALC_LAST_IDX_BIGGER_THAN_NOW));
+    let amount = asset_weight * (harvest_index - last_harvest_index);
+    truncate(exp_direct(amount))
+}
+
+ + + +
+ + + +## Function `exists_at` + +Check the Farming of TokenT is exists. + + +
public fun exists_at<PoolType: store, RewardTokenT: store>(broker: address): bool
+
+ + + +
+Implementation + + +
public fun exists_at<PoolType: store, RewardTokenT: store>(broker: address): bool {
+    exists<Farming<PoolType, RewardTokenT>>(broker)
+}
+
+ + + +
+ + + +## Function `exists_asset_at` + +Check the Farming of AsssetT is exists. + + +
public fun exists_asset_at<PoolType: store, AssetT: store>(broker: address): bool
+
+ + + +
+Implementation + + +
public fun exists_asset_at<PoolType: store, AssetT: store>(broker: address): bool {
+    exists<FarmingAsset<PoolType, AssetT>>(broker)
+}
+
+ + + +
+ + + +## Function `exists_stake_at_address` + +Check stake at address exists. + + +
public fun exists_stake_at_address<PoolType: store, AssetT: store>(account: address): bool
+
+ + + +
+Implementation + + +
public fun exists_stake_at_address<PoolType: store, AssetT: store>(account: address): bool {
+    exists<Stake<PoolType, AssetT>>(account)
+}
+
+ + + +
+ + + +## Module Specification + + + +
pragma verify = false;
+
diff --git a/release/v13/source_maps/ACL.mvsm b/release/v13/source_maps/ACL.mvsm new file mode 100644 index 00000000..c09c9cd5 Binary files /dev/null and b/release/v13/source_maps/ACL.mvsm differ diff --git a/release/v13/source_maps/Account.mvsm b/release/v13/source_maps/Account.mvsm new file mode 100644 index 00000000..3faa751a Binary files /dev/null and b/release/v13/source_maps/Account.mvsm differ diff --git a/release/v13/source_maps/AccountScripts.mvsm b/release/v13/source_maps/AccountScripts.mvsm new file mode 100644 index 00000000..d626c5e8 Binary files /dev/null and b/release/v13/source_maps/AccountScripts.mvsm differ diff --git a/release/v13/source_maps/Arith.mvsm b/release/v13/source_maps/Arith.mvsm new file mode 100644 index 00000000..cfebb7fb Binary files /dev/null and b/release/v13/source_maps/Arith.mvsm differ diff --git a/release/v13/source_maps/Authenticator.mvsm b/release/v13/source_maps/Authenticator.mvsm new file mode 100644 index 00000000..d6230cb9 Binary files /dev/null and b/release/v13/source_maps/Authenticator.mvsm differ diff --git a/release/v13/source_maps/BCS.mvsm b/release/v13/source_maps/BCS.mvsm new file mode 100644 index 00000000..ca29a6bb Binary files /dev/null and b/release/v13/source_maps/BCS.mvsm differ diff --git a/release/v13/source_maps/BitOperators.mvsm b/release/v13/source_maps/BitOperators.mvsm new file mode 100644 index 00000000..b0aefd58 Binary files /dev/null and b/release/v13/source_maps/BitOperators.mvsm differ diff --git a/release/v13/source_maps/Block.mvsm b/release/v13/source_maps/Block.mvsm new file mode 100644 index 00000000..63f80aa9 Binary files /dev/null and b/release/v13/source_maps/Block.mvsm differ diff --git a/release/v13/source_maps/BlockReward.mvsm b/release/v13/source_maps/BlockReward.mvsm new file mode 100644 index 00000000..8b86f34d Binary files /dev/null and b/release/v13/source_maps/BlockReward.mvsm differ diff --git a/release/v13/source_maps/ChainId.mvsm b/release/v13/source_maps/ChainId.mvsm new file mode 100644 index 00000000..24fc09e8 Binary files /dev/null and b/release/v13/source_maps/ChainId.mvsm differ diff --git a/release/v13/source_maps/Collection.mvsm b/release/v13/source_maps/Collection.mvsm new file mode 100644 index 00000000..da074d5a Binary files /dev/null and b/release/v13/source_maps/Collection.mvsm differ diff --git a/release/v13/source_maps/Collection2.mvsm b/release/v13/source_maps/Collection2.mvsm new file mode 100644 index 00000000..d25b69fe Binary files /dev/null and b/release/v13/source_maps/Collection2.mvsm differ diff --git a/release/v13/source_maps/Compare.mvsm b/release/v13/source_maps/Compare.mvsm new file mode 100644 index 00000000..9a697c51 Binary files /dev/null and b/release/v13/source_maps/Compare.mvsm differ diff --git a/release/v13/source_maps/Config.mvsm b/release/v13/source_maps/Config.mvsm new file mode 100644 index 00000000..9aa035a5 Binary files /dev/null and b/release/v13/source_maps/Config.mvsm differ diff --git a/release/v13/source_maps/ConsensusConfig.mvsm b/release/v13/source_maps/ConsensusConfig.mvsm new file mode 100644 index 00000000..ff866910 Binary files /dev/null and b/release/v13/source_maps/ConsensusConfig.mvsm differ diff --git a/release/v13/source_maps/ConsensusStrategy.mvsm b/release/v13/source_maps/ConsensusStrategy.mvsm new file mode 100644 index 00000000..c3fbc643 Binary files /dev/null and b/release/v13/source_maps/ConsensusStrategy.mvsm differ diff --git a/release/v13/source_maps/CoreAddresses.mvsm b/release/v13/source_maps/CoreAddresses.mvsm new file mode 100644 index 00000000..bcef7eaf Binary files /dev/null and b/release/v13/source_maps/CoreAddresses.mvsm differ diff --git a/release/v13/source_maps/Dao.mvsm b/release/v13/source_maps/Dao.mvsm new file mode 100644 index 00000000..97084ad8 Binary files /dev/null and b/release/v13/source_maps/Dao.mvsm differ diff --git a/release/v13/source_maps/DaoVoteScripts.mvsm b/release/v13/source_maps/DaoVoteScripts.mvsm new file mode 100644 index 00000000..0c199202 Binary files /dev/null and b/release/v13/source_maps/DaoVoteScripts.mvsm differ diff --git a/release/v13/source_maps/Debug.mvsm b/release/v13/source_maps/Debug.mvsm new file mode 100644 index 00000000..c9280d83 Binary files /dev/null and b/release/v13/source_maps/Debug.mvsm differ diff --git a/release/v13/source_maps/DummyToken.mvsm b/release/v13/source_maps/DummyToken.mvsm new file mode 100644 index 00000000..3be6adc9 Binary files /dev/null and b/release/v13/source_maps/DummyToken.mvsm differ diff --git a/release/v13/source_maps/DummyTokenScripts.mvsm b/release/v13/source_maps/DummyTokenScripts.mvsm new file mode 100644 index 00000000..178119a6 Binary files /dev/null and b/release/v13/source_maps/DummyTokenScripts.mvsm differ diff --git a/release/v13/source_maps/EVMAddress.mvsm b/release/v13/source_maps/EVMAddress.mvsm new file mode 100644 index 00000000..aca4d08e Binary files /dev/null and b/release/v13/source_maps/EVMAddress.mvsm differ diff --git a/release/v13/source_maps/EasyGas.mvsm b/release/v13/source_maps/EasyGas.mvsm new file mode 100644 index 00000000..34dcba80 Binary files /dev/null and b/release/v13/source_maps/EasyGas.mvsm differ diff --git a/release/v13/source_maps/EasyGasScript.mvsm b/release/v13/source_maps/EasyGasScript.mvsm new file mode 100644 index 00000000..542d275b Binary files /dev/null and b/release/v13/source_maps/EasyGasScript.mvsm differ diff --git a/release/v13/source_maps/EmptyScripts.mvsm b/release/v13/source_maps/EmptyScripts.mvsm new file mode 100644 index 00000000..bcba18b6 Binary files /dev/null and b/release/v13/source_maps/EmptyScripts.mvsm differ diff --git a/release/v13/source_maps/Epoch.mvsm b/release/v13/source_maps/Epoch.mvsm new file mode 100644 index 00000000..379bf4fa Binary files /dev/null and b/release/v13/source_maps/Epoch.mvsm differ diff --git a/release/v13/source_maps/Errors.mvsm b/release/v13/source_maps/Errors.mvsm new file mode 100644 index 00000000..eec4d13e Binary files /dev/null and b/release/v13/source_maps/Errors.mvsm differ diff --git a/release/v13/source_maps/Event.mvsm b/release/v13/source_maps/Event.mvsm new file mode 100644 index 00000000..dd2f577c Binary files /dev/null and b/release/v13/source_maps/Event.mvsm differ diff --git a/release/v13/source_maps/EventUtil.mvsm b/release/v13/source_maps/EventUtil.mvsm new file mode 100644 index 00000000..76f76ef5 Binary files /dev/null and b/release/v13/source_maps/EventUtil.mvsm differ diff --git a/release/v13/source_maps/FixedPoint32.mvsm b/release/v13/source_maps/FixedPoint32.mvsm new file mode 100644 index 00000000..caefda27 Binary files /dev/null and b/release/v13/source_maps/FixedPoint32.mvsm differ diff --git a/release/v13/source_maps/FlexiDagConfig.mvsm b/release/v13/source_maps/FlexiDagConfig.mvsm new file mode 100644 index 00000000..d3ecfcbb Binary files /dev/null and b/release/v13/source_maps/FlexiDagConfig.mvsm differ diff --git a/release/v13/source_maps/FromBCS.mvsm b/release/v13/source_maps/FromBCS.mvsm new file mode 100644 index 00000000..0ae0e602 Binary files /dev/null and b/release/v13/source_maps/FromBCS.mvsm differ diff --git a/release/v13/source_maps/GasSchedule.mvsm b/release/v13/source_maps/GasSchedule.mvsm new file mode 100644 index 00000000..dc80c7b7 Binary files /dev/null and b/release/v13/source_maps/GasSchedule.mvsm differ diff --git a/release/v13/source_maps/Genesis.mvsm b/release/v13/source_maps/Genesis.mvsm new file mode 100644 index 00000000..41d1e279 Binary files /dev/null and b/release/v13/source_maps/Genesis.mvsm differ diff --git a/release/v13/source_maps/GenesisNFT.mvsm b/release/v13/source_maps/GenesisNFT.mvsm new file mode 100644 index 00000000..472730d3 Binary files /dev/null and b/release/v13/source_maps/GenesisNFT.mvsm differ diff --git a/release/v13/source_maps/GenesisNFTScripts.mvsm b/release/v13/source_maps/GenesisNFTScripts.mvsm new file mode 100644 index 00000000..17c80709 Binary files /dev/null and b/release/v13/source_maps/GenesisNFTScripts.mvsm differ diff --git a/release/v13/source_maps/GenesisSignerCapability.mvsm b/release/v13/source_maps/GenesisSignerCapability.mvsm new file mode 100644 index 00000000..41be8a29 Binary files /dev/null and b/release/v13/source_maps/GenesisSignerCapability.mvsm differ diff --git a/release/v13/source_maps/Hash.mvsm b/release/v13/source_maps/Hash.mvsm new file mode 100644 index 00000000..ecc3c9d3 Binary files /dev/null and b/release/v13/source_maps/Hash.mvsm differ diff --git a/release/v13/source_maps/IdentifierNFT.mvsm b/release/v13/source_maps/IdentifierNFT.mvsm new file mode 100644 index 00000000..fd313edd Binary files /dev/null and b/release/v13/source_maps/IdentifierNFT.mvsm differ diff --git a/release/v13/source_maps/IdentifierNFTScripts.mvsm b/release/v13/source_maps/IdentifierNFTScripts.mvsm new file mode 100644 index 00000000..f8e13f4d Binary files /dev/null and b/release/v13/source_maps/IdentifierNFTScripts.mvsm differ diff --git a/release/v13/source_maps/LanguageVersion.mvsm b/release/v13/source_maps/LanguageVersion.mvsm new file mode 100644 index 00000000..e8e96ca7 Binary files /dev/null and b/release/v13/source_maps/LanguageVersion.mvsm differ diff --git a/release/v13/source_maps/Math.mvsm b/release/v13/source_maps/Math.mvsm new file mode 100644 index 00000000..aa3cb9ab Binary files /dev/null and b/release/v13/source_maps/Math.mvsm differ diff --git a/release/v13/source_maps/MerkleNFTDistributor.mvsm b/release/v13/source_maps/MerkleNFTDistributor.mvsm new file mode 100644 index 00000000..1293d59f Binary files /dev/null and b/release/v13/source_maps/MerkleNFTDistributor.mvsm differ diff --git a/release/v13/source_maps/MerkleProof.mvsm b/release/v13/source_maps/MerkleProof.mvsm new file mode 100644 index 00000000..731fab40 Binary files /dev/null and b/release/v13/source_maps/MerkleProof.mvsm differ diff --git a/release/v13/source_maps/MintDaoProposal.mvsm b/release/v13/source_maps/MintDaoProposal.mvsm new file mode 100644 index 00000000..e0e6e071 Binary files /dev/null and b/release/v13/source_maps/MintDaoProposal.mvsm differ diff --git a/release/v13/source_maps/MintScripts.mvsm b/release/v13/source_maps/MintScripts.mvsm new file mode 100644 index 00000000..698d3240 Binary files /dev/null and b/release/v13/source_maps/MintScripts.mvsm differ diff --git a/release/v13/source_maps/ModifyDaoConfigProposal.mvsm b/release/v13/source_maps/ModifyDaoConfigProposal.mvsm new file mode 100644 index 00000000..6d5abad8 Binary files /dev/null and b/release/v13/source_maps/ModifyDaoConfigProposal.mvsm differ diff --git a/release/v13/source_maps/ModuleUpgradeScripts.mvsm b/release/v13/source_maps/ModuleUpgradeScripts.mvsm new file mode 100644 index 00000000..2006faec Binary files /dev/null and b/release/v13/source_maps/ModuleUpgradeScripts.mvsm differ diff --git a/release/v13/source_maps/NFT.mvsm b/release/v13/source_maps/NFT.mvsm new file mode 100644 index 00000000..20e778d3 Binary files /dev/null and b/release/v13/source_maps/NFT.mvsm differ diff --git a/release/v13/source_maps/NFTGallery.mvsm b/release/v13/source_maps/NFTGallery.mvsm new file mode 100644 index 00000000..6125477b Binary files /dev/null and b/release/v13/source_maps/NFTGallery.mvsm differ diff --git a/release/v13/source_maps/NFTGalleryScripts.mvsm b/release/v13/source_maps/NFTGalleryScripts.mvsm new file mode 100644 index 00000000..8b6bd42e Binary files /dev/null and b/release/v13/source_maps/NFTGalleryScripts.mvsm differ diff --git a/release/v13/source_maps/Offer.mvsm b/release/v13/source_maps/Offer.mvsm new file mode 100644 index 00000000..60f93aa0 Binary files /dev/null and b/release/v13/source_maps/Offer.mvsm differ diff --git a/release/v13/source_maps/OnChainConfigDao.mvsm b/release/v13/source_maps/OnChainConfigDao.mvsm new file mode 100644 index 00000000..a5978b12 Binary files /dev/null and b/release/v13/source_maps/OnChainConfigDao.mvsm differ diff --git a/release/v13/source_maps/OnChainConfigScripts.mvsm b/release/v13/source_maps/OnChainConfigScripts.mvsm new file mode 100644 index 00000000..69c18b0b Binary files /dev/null and b/release/v13/source_maps/OnChainConfigScripts.mvsm differ diff --git a/release/v13/source_maps/Option.mvsm b/release/v13/source_maps/Option.mvsm new file mode 100644 index 00000000..aec9b2a8 Binary files /dev/null and b/release/v13/source_maps/Option.mvsm differ diff --git a/release/v13/source_maps/Oracle.mvsm b/release/v13/source_maps/Oracle.mvsm new file mode 100644 index 00000000..2e3f1863 Binary files /dev/null and b/release/v13/source_maps/Oracle.mvsm differ diff --git a/release/v13/source_maps/PackageTxnManager.mvsm b/release/v13/source_maps/PackageTxnManager.mvsm new file mode 100644 index 00000000..c59be094 Binary files /dev/null and b/release/v13/source_maps/PackageTxnManager.mvsm differ diff --git a/release/v13/source_maps/PriceOracle.mvsm b/release/v13/source_maps/PriceOracle.mvsm new file mode 100644 index 00000000..663fa990 Binary files /dev/null and b/release/v13/source_maps/PriceOracle.mvsm differ diff --git a/release/v13/source_maps/PriceOracleAggregator.mvsm b/release/v13/source_maps/PriceOracleAggregator.mvsm new file mode 100644 index 00000000..25b41ae7 Binary files /dev/null and b/release/v13/source_maps/PriceOracleAggregator.mvsm differ diff --git a/release/v13/source_maps/PriceOracleScripts.mvsm b/release/v13/source_maps/PriceOracleScripts.mvsm new file mode 100644 index 00000000..c9e747a4 Binary files /dev/null and b/release/v13/source_maps/PriceOracleScripts.mvsm differ diff --git a/release/v13/source_maps/RewardConfig.mvsm b/release/v13/source_maps/RewardConfig.mvsm new file mode 100644 index 00000000..f5b7f758 Binary files /dev/null and b/release/v13/source_maps/RewardConfig.mvsm differ diff --git a/release/v13/source_maps/Ring.mvsm b/release/v13/source_maps/Ring.mvsm new file mode 100644 index 00000000..3a77b929 Binary files /dev/null and b/release/v13/source_maps/Ring.mvsm differ diff --git a/release/v13/source_maps/SIP_2.mvsm b/release/v13/source_maps/SIP_2.mvsm new file mode 100644 index 00000000..203f1efe Binary files /dev/null and b/release/v13/source_maps/SIP_2.mvsm differ diff --git a/release/v13/source_maps/SIP_3.mvsm b/release/v13/source_maps/SIP_3.mvsm new file mode 100644 index 00000000..6cd8b35c Binary files /dev/null and b/release/v13/source_maps/SIP_3.mvsm differ diff --git a/release/v13/source_maps/STC.mvsm b/release/v13/source_maps/STC.mvsm new file mode 100644 index 00000000..5eb95a23 Binary files /dev/null and b/release/v13/source_maps/STC.mvsm differ diff --git a/release/v13/source_maps/STCUSDOracle.mvsm b/release/v13/source_maps/STCUSDOracle.mvsm new file mode 100644 index 00000000..25b7a549 Binary files /dev/null and b/release/v13/source_maps/STCUSDOracle.mvsm differ diff --git a/release/v13/source_maps/Secp256k1.mvsm b/release/v13/source_maps/Secp256k1.mvsm new file mode 100644 index 00000000..f7144ba9 Binary files /dev/null and b/release/v13/source_maps/Secp256k1.mvsm differ diff --git a/release/v13/source_maps/SharedEd25519PublicKey.mvsm b/release/v13/source_maps/SharedEd25519PublicKey.mvsm new file mode 100644 index 00000000..1b25e223 Binary files /dev/null and b/release/v13/source_maps/SharedEd25519PublicKey.mvsm differ diff --git a/release/v13/source_maps/Signature.mvsm b/release/v13/source_maps/Signature.mvsm new file mode 100644 index 00000000..5d15d441 Binary files /dev/null and b/release/v13/source_maps/Signature.mvsm differ diff --git a/release/v13/source_maps/SignedInteger64.mvsm b/release/v13/source_maps/SignedInteger64.mvsm new file mode 100644 index 00000000..882a4e96 Binary files /dev/null and b/release/v13/source_maps/SignedInteger64.mvsm differ diff --git a/release/v13/source_maps/Signer.mvsm b/release/v13/source_maps/Signer.mvsm new file mode 100644 index 00000000..6d140223 Binary files /dev/null and b/release/v13/source_maps/Signer.mvsm differ diff --git a/release/v13/source_maps/SimpleMap.mvsm b/release/v13/source_maps/SimpleMap.mvsm new file mode 100644 index 00000000..ab20871b Binary files /dev/null and b/release/v13/source_maps/SimpleMap.mvsm differ diff --git a/release/v13/source_maps/StarcoinVerifier.mvsm b/release/v13/source_maps/StarcoinVerifier.mvsm new file mode 100644 index 00000000..273b3b0b Binary files /dev/null and b/release/v13/source_maps/StarcoinVerifier.mvsm differ diff --git a/release/v13/source_maps/StdlibUpgradeScripts.mvsm b/release/v13/source_maps/StdlibUpgradeScripts.mvsm new file mode 100644 index 00000000..55adea0f Binary files /dev/null and b/release/v13/source_maps/StdlibUpgradeScripts.mvsm differ diff --git a/release/v13/source_maps/String.mvsm b/release/v13/source_maps/String.mvsm new file mode 100644 index 00000000..86f076b0 Binary files /dev/null and b/release/v13/source_maps/String.mvsm differ diff --git a/release/v13/source_maps/StructuredHash.mvsm b/release/v13/source_maps/StructuredHash.mvsm new file mode 100644 index 00000000..c2e48090 Binary files /dev/null and b/release/v13/source_maps/StructuredHash.mvsm differ diff --git a/release/v13/source_maps/Table.mvsm b/release/v13/source_maps/Table.mvsm new file mode 100644 index 00000000..c510f2a8 Binary files /dev/null and b/release/v13/source_maps/Table.mvsm differ diff --git a/release/v13/source_maps/Timestamp.mvsm b/release/v13/source_maps/Timestamp.mvsm new file mode 100644 index 00000000..9adafc76 Binary files /dev/null and b/release/v13/source_maps/Timestamp.mvsm differ diff --git a/release/v13/source_maps/Token.mvsm b/release/v13/source_maps/Token.mvsm new file mode 100644 index 00000000..6bf0664a Binary files /dev/null and b/release/v13/source_maps/Token.mvsm differ diff --git a/release/v13/source_maps/TransactionFee.mvsm b/release/v13/source_maps/TransactionFee.mvsm new file mode 100644 index 00000000..04c709b0 Binary files /dev/null and b/release/v13/source_maps/TransactionFee.mvsm differ diff --git a/release/v13/source_maps/TransactionManager.mvsm b/release/v13/source_maps/TransactionManager.mvsm new file mode 100644 index 00000000..ae0c7182 Binary files /dev/null and b/release/v13/source_maps/TransactionManager.mvsm differ diff --git a/release/v13/source_maps/TransactionPublishOption.mvsm b/release/v13/source_maps/TransactionPublishOption.mvsm new file mode 100644 index 00000000..1d450f22 Binary files /dev/null and b/release/v13/source_maps/TransactionPublishOption.mvsm differ diff --git a/release/v13/source_maps/TransactionTimeout.mvsm b/release/v13/source_maps/TransactionTimeout.mvsm new file mode 100644 index 00000000..8aab5565 Binary files /dev/null and b/release/v13/source_maps/TransactionTimeout.mvsm differ diff --git a/release/v13/source_maps/TransactionTimeoutConfig.mvsm b/release/v13/source_maps/TransactionTimeoutConfig.mvsm new file mode 100644 index 00000000..1d2e6e97 Binary files /dev/null and b/release/v13/source_maps/TransactionTimeoutConfig.mvsm differ diff --git a/release/v13/source_maps/TransferScripts.mvsm b/release/v13/source_maps/TransferScripts.mvsm new file mode 100644 index 00000000..b94f1ce1 Binary files /dev/null and b/release/v13/source_maps/TransferScripts.mvsm differ diff --git a/release/v13/source_maps/Treasury.mvsm b/release/v13/source_maps/Treasury.mvsm new file mode 100644 index 00000000..a7f35654 Binary files /dev/null and b/release/v13/source_maps/Treasury.mvsm differ diff --git a/release/v13/source_maps/TreasuryScripts.mvsm b/release/v13/source_maps/TreasuryScripts.mvsm new file mode 100644 index 00000000..88327c5c Binary files /dev/null and b/release/v13/source_maps/TreasuryScripts.mvsm differ diff --git a/release/v13/source_maps/TreasuryWithdrawDaoProposal.mvsm b/release/v13/source_maps/TreasuryWithdrawDaoProposal.mvsm new file mode 100644 index 00000000..39c8cba3 Binary files /dev/null and b/release/v13/source_maps/TreasuryWithdrawDaoProposal.mvsm differ diff --git a/release/v13/source_maps/TypeInfo.mvsm b/release/v13/source_maps/TypeInfo.mvsm new file mode 100644 index 00000000..7f887f18 Binary files /dev/null and b/release/v13/source_maps/TypeInfo.mvsm differ diff --git a/release/v13/source_maps/U256.mvsm b/release/v13/source_maps/U256.mvsm new file mode 100644 index 00000000..efe15b74 Binary files /dev/null and b/release/v13/source_maps/U256.mvsm differ diff --git a/release/v13/source_maps/UpgradeModuleDaoProposal.mvsm b/release/v13/source_maps/UpgradeModuleDaoProposal.mvsm new file mode 100644 index 00000000..a1d50eca Binary files /dev/null and b/release/v13/source_maps/UpgradeModuleDaoProposal.mvsm differ diff --git a/release/v13/source_maps/VMConfig.mvsm b/release/v13/source_maps/VMConfig.mvsm new file mode 100644 index 00000000..fe502203 Binary files /dev/null and b/release/v13/source_maps/VMConfig.mvsm differ diff --git a/release/v13/source_maps/Vector.mvsm b/release/v13/source_maps/Vector.mvsm new file mode 100644 index 00000000..cf74146c Binary files /dev/null and b/release/v13/source_maps/Vector.mvsm differ diff --git a/release/v13/source_maps/Version.mvsm b/release/v13/source_maps/Version.mvsm new file mode 100644 index 00000000..4e2fe5b5 Binary files /dev/null and b/release/v13/source_maps/Version.mvsm differ diff --git a/release/v13/source_maps/YieldFarming.mvsm b/release/v13/source_maps/YieldFarming.mvsm new file mode 100644 index 00000000..7da1f429 Binary files /dev/null and b/release/v13/source_maps/YieldFarming.mvsm differ diff --git a/release/v13/source_maps/YieldFarmingV2.mvsm b/release/v13/source_maps/YieldFarmingV2.mvsm new file mode 100644 index 00000000..30291a98 Binary files /dev/null and b/release/v13/source_maps/YieldFarmingV2.mvsm differ diff --git a/release/v13/sources/ACL.move b/release/v13/sources/ACL.move new file mode 100644 index 00000000..c9d5dadd --- /dev/null +++ b/release/v13/sources/ACL.move @@ -0,0 +1,46 @@ +/// Access control list (acl) module. An acl is a list of account addresses who +/// have the access permission to a certain object. +/// This module uses a `vector` to represent the list, but can be refactored to +/// use a "set" instead when it's available in the language in the future. + +module StarcoinFramework::ACL { + use StarcoinFramework::Vector; + use StarcoinFramework::Errors; + + /// The ACL already contains the address. + const ECONTAIN: u64 = 0; + /// The ACL does not contain the address. + const ENOT_CONTAIN: u64 = 1; + + struct ACL has store, drop, copy { + list: vector
+ } + + /// Return an empty ACL. + public fun empty(): ACL { + ACL{ list: Vector::empty
() } + } + + /// Add the address to the ACL. + public fun add(acl: &mut ACL, addr: address) { + assert!(!Vector::contains(&mut acl.list, &addr), Errors::invalid_argument(ECONTAIN)); + Vector::push_back(&mut acl.list, addr); + } + + /// Remove the address from the ACL. + public fun remove(acl: &mut ACL, addr: address) { + let (found, index) = Vector::index_of(&mut acl.list, &addr); + assert!(found, Errors::invalid_argument(ENOT_CONTAIN)); + Vector::remove(&mut acl.list, index); + } + + /// Return true iff the ACL contains the address. + public fun contains(acl: &ACL, addr: address): bool { + Vector::contains(&acl.list, &addr) + } + + /// assert! that the ACL has the address. + public fun assert_contains(acl: &ACL, addr: address) { + assert!(contains(acl, addr), Errors::invalid_argument(ENOT_CONTAIN)); + } +} diff --git a/release/v13/sources/Account.move b/release/v13/sources/Account.move new file mode 100644 index 00000000..0e3be33b --- /dev/null +++ b/release/v13/sources/Account.move @@ -0,0 +1,1228 @@ +address StarcoinFramework { + +/// The module for the account resource that governs every account +module Account { + use StarcoinFramework::Authenticator; + use StarcoinFramework::Event; + use StarcoinFramework::Hash; + use StarcoinFramework::Token::{Self, Token}; + use StarcoinFramework::Vector; + use StarcoinFramework::Signer; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::TransactionFee; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::STC::{Self, STC, is_stc}; + use StarcoinFramework::BCS; + use StarcoinFramework::Math; + friend StarcoinFramework::TransactionManager; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + /// Every account has a Account::Account resource + struct Account has key { + /// The current authentication key. + /// This can be different than the key used to create the account + authentication_key: vector, + /// A `withdrawal_capability` allows whoever holds this capability + /// to withdraw from the account. At the time of account creation + /// this capability is stored in this option. It can later be + /// "extracted" from this field via `extract_withdraw_capability`, + /// and can also be restored via `restore_withdraw_capability`. + withdrawal_capability: Option, + /// A `key_rotation_capability` allows whoever holds this capability + /// the ability to rotate the authentication key for the account. At + /// the time of account creation this capability is stored in this + /// option. It can later be "extracted" from this field via + /// `extract_key_rotation_capability`, and can also be restored via + /// `restore_key_rotation_capability`. + key_rotation_capability: Option, + + /// event handle for account balance withdraw event + withdraw_events: Event::EventHandle, + /// event handle for account balance deposit event + deposit_events: Event::EventHandle, + + /// Event handle for accept_token event + accept_token_events: Event::EventHandle, + + /// The current sequence number. + /// Incremented by one each time a transaction is submitted + sequence_number: u64, + } + + /// A resource that holds the tokens stored in this account + struct Balance has key { + token: Token, + } + + /// The holder of WithdrawCapability for account_address can withdraw Token from + /// account_address/Account::Account/balance. + /// There is at most one WithdrawCapability in existence for a given address. + struct WithdrawCapability has store { + account_address: address, + } + + /// The holder of KeyRotationCapability for account_address can rotate the authentication key for + /// account_address (i.e., write to account_address/Account::Account/authentication_key). + /// There is at most one KeyRotationCapability in existence for a given address. + struct KeyRotationCapability has store { + account_address: address, + } + + /// Message for balance withdraw event. + struct WithdrawEvent has drop, store { + /// The amount of Token sent + amount: u128, + /// The code symbol for the token that was sent + token_code: Token::TokenCode, + /// Metadata associated with the withdraw + metadata: vector, + } + /// Message for balance deposit event. + struct DepositEvent has drop, store { + /// The amount of Token sent + amount: u128, + /// The code symbol for the token that was sent + token_code: Token::TokenCode, + /// Metadata associated with the deposit + metadata: vector, + } + + /// Message for accept token events + struct AcceptTokenEvent has drop, store { + token_code: Token::TokenCode, + } + + // SignerDelegated can only be stored under address, not in other structs. + struct SignerDelegated has key {} + // SignerCapability can only be stored in other structs, not under address. + // So that the capability is always controlled by contracts, not by some EOA. + struct SignerCapability has store { addr: address } + + // Resource marking whether the account enable auto-accept-token feature. + struct AutoAcceptToken has key { enable: bool } + + /// Message for rotate_authentication_key events + struct RotateAuthKeyEvent has drop, store { + account_address: address, + new_auth_key: vector, + } + + /// Message for extract_withdraw_capability events + struct ExtractWithdrawCapEvent has drop, store { + account_address: address, + } + + /// Message for SignerDelegate events + struct SignerDelegateEvent has drop, store { + account_address: address + } + + struct EventStore has key { + /// Event handle for rotate_authentication_key event + rotate_auth_key_events: Event::EventHandle, + /// Event handle for extract_withdraw_capability event + extract_withdraw_cap_events: Event::EventHandle, + /// Event handle for signer delegated event + signer_delegate_events: Event::EventHandle, + } + + const MAX_U64: u128 = 18446744073709551615; + + const EPROLOGUE_ACCOUNT_DOES_NOT_EXIST: u64 = 0; + const EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY: u64 = 1; + const EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD: u64 = 2; + const EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW: u64 = 3; + const EPROLOGUE_CANT_PAY_GAS_DEPOSIT: u64 = 4; + + const EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG: u64 = 9; + + const EINSUFFICIENT_BALANCE: u64 = 10; + const ECOIN_DEPOSIT_IS_ZERO: u64 = 15; + const EBAD_TRANSACTION_FEE_TOKEN: u64 = 18; + + const EDEPRECATED_FUNCTION: u64 = 19; + + const EWITHDRAWAL_CAPABILITY_ALREADY_EXTRACTED: u64 = 101; + const EMALFORMED_AUTHENTICATION_KEY: u64 = 102; + const EKEY_ROTATION_CAPABILITY_ALREADY_EXTRACTED: u64 = 103; + const EADDRESS_PUBLIC_KEY_INCONSISTENT: u64 = 104; + const EADDRESS_AND_AUTH_KEY_MISMATCH: u64 = 105; + const ERR_TOKEN_NOT_ACCEPT: u64 = 106; + const ERR_SIGNER_ALREADY_DELEGATED: u64 = 107; + + const EPROLOGUE_SIGNER_ALREADY_DELEGATED: u64 = 200; + + const DUMMY_AUTH_KEY:vector = x"0000000000000000000000000000000000000000000000000000000000000000"; + // cannot be dummy key, or empty key + const CONTRACT_ACCOUNT_AUTH_KEY_PLACEHOLDER:vector = x"0000000000000000000000000000000000000000000000000000000000000001"; + + /// The address bytes length + const ADDRESS_LENGTH: u64 = 16; + + /// A one-way action, once SignerCapability is removed from signer, the address cannot send txns anymore. + /// This function can only called once by signer. + public fun remove_signer_capability(signer: &signer): SignerCapability + acquires Account, EventStore { + let signer_addr = Signer::address_of(signer); + assert!(!is_signer_delegated(signer_addr), Errors::invalid_state(ERR_SIGNER_ALREADY_DELEGATED)); + + // set to account auth key to noop. + { + let key_rotation_capability = extract_key_rotation_capability(signer); + rotate_authentication_key_with_capability(&key_rotation_capability, CONTRACT_ACCOUNT_AUTH_KEY_PLACEHOLDER); + destroy_key_rotation_capability(key_rotation_capability); + move_to(signer, SignerDelegated {}); + + make_event_store_if_not_exist(signer); + let event_store = borrow_global_mut(signer_addr); + Event::emit_event( + &mut event_store.signer_delegate_events, + SignerDelegateEvent { + account_address: signer_addr + } + ); + }; + + let signer_cap = SignerCapability {addr: signer_addr }; + signer_cap + } + + public fun create_signer_with_cap(cap: &SignerCapability): signer { + create_signer(cap.addr) + } + public fun destroy_signer_cap(cap: SignerCapability) { + let SignerCapability {addr: _} = cap; + } + + public fun signer_address(cap: &SignerCapability): address { + cap.addr + } + public fun is_signer_delegated(addr: address): bool { + exists(addr) + } + + /// Create an genesis account at `new_account_address` and return signer. + /// Genesis authentication_key is zero bytes. + public fun create_genesis_account( + new_account_address: address, + ) :signer { + Timestamp::assert_genesis(); + let new_account = create_signer(new_account_address); + make_account(&new_account, DUMMY_AUTH_KEY); + new_account + } + + spec create_genesis_account { + aborts_if !Timestamp::is_genesis(); + aborts_if len(DUMMY_AUTH_KEY) != 32; + aborts_if exists(new_account_address); + } + + /// Release genesis account signer + public fun release_genesis_signer(_genesis_account: signer){ + } + + spec release_genesis_signer { + aborts_if false; + } + + /// Deprecated since @v5 + public fun create_account(_authentication_key: vector): address { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + spec create_account { + aborts_if true; + } + + /// Creates a new account at `fresh_address` with a balance of zero and empty auth key, the address as init auth key for check transaction. + /// Creating an account at address StarcoinFramework will cause runtime failure as it is a + /// reserved address for the MoveVM. + public fun create_account_with_address(fresh_address: address) acquires Account { + let new_account = create_signer(fresh_address); + make_account(&new_account, DUMMY_AUTH_KEY); + // Make sure all account accept STC. + if (!STC::is_stc()){ + do_accept_token(&new_account); + }; + do_accept_token(&new_account); + } + + spec create_account_with_address { + //abort condition for make_account + aborts_if exists(fresh_address); + //abort condition for do_accept_token + aborts_if Token::spec_token_code() != Token::spec_token_code() && exists>(fresh_address); + //abort condition for do_accept_token + aborts_if exists>(fresh_address); + ensures exists_at(fresh_address); + ensures exists>(fresh_address); + } + + fun make_account( + new_account: &signer, + authentication_key: vector, + ) { + assert!(Vector::length(&authentication_key) == 32, Errors::invalid_argument(EMALFORMED_AUTHENTICATION_KEY)); + let new_account_addr = Signer::address_of(new_account); + Event::publish_generator(new_account); + move_to(new_account, Account { + authentication_key, + withdrawal_capability: Option::some( + WithdrawCapability { + account_address: new_account_addr + }), + key_rotation_capability: Option::some( + KeyRotationCapability { + account_address: new_account_addr + }), + withdraw_events: Event::new_event_handle(new_account), + deposit_events: Event::new_event_handle(new_account), + accept_token_events: Event::new_event_handle(new_account), + sequence_number: 0, + }); + move_to(new_account, AutoAcceptToken{enable: true}); + move_to(new_account, EventStore { + rotate_auth_key_events: Event::new_event_handle(new_account), + extract_withdraw_cap_events: Event::new_event_handle(new_account), + signer_delegate_events: Event::new_event_handle(new_account), + }); + } + + spec make_account { + aborts_if len(authentication_key) != 32; + aborts_if exists(Signer::address_of(new_account)); + aborts_if exists(Signer::address_of(new_account)); + ensures exists_at(Signer::address_of(new_account)); + } + + native fun create_signer(addr: address): signer; + + public entry fun create_account_with_initial_amount(account: signer, fresh_address: address, _auth_key: vector, initial_amount: u128) + acquires Account, Balance, AutoAcceptToken { + create_account_with_initial_amount_entry(account, fresh_address, initial_amount); + } + + public entry fun create_account_with_initial_amount_v2(account: signer, fresh_address: address, initial_amount: u128) + acquires Account, Balance, AutoAcceptToken { + create_account_with_initial_amount_entry(account, fresh_address, initial_amount); + } + + public entry fun create_account_with_initial_amount_entry(account: signer, fresh_address: address, initial_amount: u128) + acquires Account, Balance, AutoAcceptToken { + create_account_with_address(fresh_address); + if (initial_amount > 0) { + pay_from(&account, fresh_address, initial_amount); + }; + } + + spec create_account_with_initial_amount { + pragma verify = false; + } + + spec create_account_with_initial_amount_v2 { + pragma verify = false; + } + + /// Generate an new address and create a new account, then delegate the account and return the new account address and `SignerCapability` + public fun create_delegate_account(sender: &signer) : (address, SignerCapability) acquires Balance, Account, EventStore { + let sender_address = Signer::address_of(sender); + let sequence_number = Self::sequence_number(sender_address); + // use stc balance as part of seed, just for new address more random. + let stc_balance = Self::balance(sender_address); + + let seed_bytes = BCS::to_bytes(&sender_address); + Vector::append(&mut seed_bytes, BCS::to_bytes(&sequence_number)); + Vector::append(&mut seed_bytes, BCS::to_bytes(&stc_balance)); + + let seed_hash = Hash::sha3_256(seed_bytes); + let i = 0; + let address_bytes = Vector::empty(); + while (i < ADDRESS_LENGTH) { + Vector::push_back(&mut address_bytes, *Vector::borrow(&seed_hash,i)); + i = i + 1; + }; + let new_address = BCS::to_address(address_bytes); + Self::create_account_with_address(new_address); + let new_signer = Self::create_signer(new_address); + (new_address, Self::remove_signer_capability(&new_signer)) + } + + + spec create_delegate_account { + pragma verify = false; + //TODO write spec + } + + /// Deposits the `to_deposit` token into the self's account balance + public fun deposit_to_self(account: &signer, to_deposit: Token) + acquires Account, Balance, AutoAcceptToken { + let account_address = Signer::address_of(account); + if (!is_accepts_token(account_address)){ + do_accept_token(account); + }; + deposit(account_address, to_deposit); + } + + spec deposit_to_self { + aborts_if to_deposit.value == 0; + let is_accepts_token = exists>(Signer::address_of(account)); + aborts_if is_accepts_token && global>(Signer::address_of(account)).token.value + to_deposit.value > max_u128(); + aborts_if !exists(Signer::address_of(account)); + ensures exists>(Signer::address_of(account)); + } + + /// Deposits the `to_deposit` token into the `receiver`'s account balance with the no metadata + /// It's a reverse operation of `withdraw`. + public fun deposit( + receiver: address, + to_deposit: Token, + ) acquires Account, Balance, AutoAcceptToken { + deposit_with_metadata(receiver, to_deposit, x"") + } + + spec deposit { + include DepositWithMetadataAbortsIf; + } + + /// Deposits the `to_deposit` token into the `receiver`'s account balance with the attached `metadata` + /// It's a reverse operation of `withdraw_with_metadata`. + public fun deposit_with_metadata( + receiver: address, + to_deposit: Token, + metadata: vector, + ) acquires Account, Balance, AutoAcceptToken { + + if (!exists_at(receiver)) { + create_account_with_address(receiver); + }; + + try_accept_token(receiver); + + let deposit_value = Token::value(&to_deposit); + if (deposit_value > 0u128) { + // Deposit the `to_deposit` token + deposit_to_balance(borrow_global_mut>(receiver), to_deposit); + + // emit deposit event + emit_account_deposit_event(receiver, deposit_value, metadata); + } else { + Token::destroy_zero(to_deposit); + }; + } + + spec deposit_with_metadata { + include DepositWithMetadataAbortsIf; + ensures exists>(receiver); + ensures old(global>(receiver)).token.value + to_deposit.value == global>(receiver).token.value; + } + + spec schema DepositWithMetadataAbortsIf { + receiver: address; + to_deposit: Token; + + aborts_if to_deposit.value == 0; + aborts_if !exists(receiver); + aborts_if !exists>(receiver); + + aborts_if global>(receiver).token.value + to_deposit.value > max_u128(); + + } + + /// Helper to deposit `amount` to the given account balance + fun deposit_to_balance(balance: &mut Balance, token: Token::Token) { + Token::deposit(&mut balance.token, token) + } + + spec deposit_to_balance { + aborts_if balance.token.value + token.value > MAX_U128; + } + + public(friend) fun withdraw_from_balance_v2(sender:address, amount: u128): Token acquires Balance { + let balance = borrow_global_mut>(sender); + Token::withdraw(&mut balance.token, amount) + } + + public (friend) fun set_sequence_number(sender: address, sequence_number: u64) acquires Account { + let account = borrow_global_mut(sender); + account.sequence_number = sequence_number; + } + + public (friend) fun set_authentication_key(sender:address,auth_key:vector) acquires Account{ + let account = borrow_global_mut(sender); + account.authentication_key = auth_key; + } + + /// Helper to withdraw `amount` from the given account balance and return the withdrawn Token + public fun withdraw_from_balance(balance: &mut Balance, amount: u128): Token{ + Token::withdraw(&mut balance.token, amount) + } + + spec withdraw_from_balance { + aborts_if balance.token.value < amount; + } + + + /// Withdraw `amount` Token from the account balance + public fun withdraw(account: &signer, amount: u128): Token + acquires Account, Balance { + withdraw_with_metadata(account, amount, x"") + } + spec withdraw { + aborts_if !exists>(Signer::address_of(account)); + aborts_if !exists(Signer::address_of(account)); + aborts_if global>(Signer::address_of(account)).token.value < amount; + aborts_if Option::is_none(global(Signer::address_of(account)).withdrawal_capability); + } + + + /// Withdraw `amount` tokens from `signer` with given `metadata`. + public fun withdraw_with_metadata(account: &signer, amount: u128, metadata: vector): Token + acquires Account, Balance { + let sender_addr = Signer::address_of(account); + let sender_balance = borrow_global_mut>(sender_addr); + // The sender_addr has delegated the privilege to withdraw from her account elsewhere--abort. + assert!(!delegated_withdraw_capability(sender_addr), Errors::invalid_state(EWITHDRAWAL_CAPABILITY_ALREADY_EXTRACTED)); + if (amount == 0){ + return Token::zero() + }; + emit_account_withdraw_event(sender_addr, amount, metadata); + // The sender_addr has retained her withdrawal privileges--proceed. + withdraw_from_balance(sender_balance, amount) + } + + spec withdraw_with_metadata { + aborts_if !exists>(Signer::address_of(account)); + aborts_if !exists(Signer::address_of(account)); + aborts_if global>(Signer::address_of(account)).token.value < amount; + aborts_if Option::is_none(global(Signer::address_of(account)).withdrawal_capability); + } + + spec fun spec_withdraw(account: signer, amount: u128): Token { + Token { value: amount } + } + + /// Withdraw `amount` Token from the account under cap.account_address with no metadata + public fun withdraw_with_capability( + cap: &WithdrawCapability, amount: u128 + ): Token acquires Balance, Account { + withdraw_with_capability_and_metadata(cap, amount, x"") + } + + spec withdraw_with_capability { + aborts_if !exists>(cap.account_address); + aborts_if !exists(cap.account_address); + aborts_if global>(cap.account_address).token.value < amount; + } + + /// Withdraw `amount` Token from the account under cap.account_address with metadata + public fun withdraw_with_capability_and_metadata( + cap: &WithdrawCapability, amount: u128, metadata: vector + ): Token acquires Balance, Account { + let balance = borrow_global_mut>(cap.account_address); + emit_account_withdraw_event(cap.account_address, amount, metadata); + withdraw_from_balance(balance , amount) + } + + spec withdraw_with_capability_and_metadata { + aborts_if !exists>(cap.account_address); + aborts_if !exists(cap.account_address); + aborts_if global>(cap.account_address).token.value < amount; + } + + + /// Return a unique capability granting permission to withdraw from the sender's account balance. + public fun extract_withdraw_capability( + sender: &signer + ): WithdrawCapability acquires Account, EventStore { + let sender_addr = Signer::address_of(sender); + // Abort if we already extracted the unique withdraw capability for this account. + assert!(!delegated_withdraw_capability(sender_addr), Errors::invalid_state(EWITHDRAWAL_CAPABILITY_ALREADY_EXTRACTED)); + + make_event_store_if_not_exist(sender); + let event_store = borrow_global_mut(sender_addr); + Event::emit_event( + &mut event_store.extract_withdraw_cap_events, + ExtractWithdrawCapEvent { + account_address: sender_addr, + } + ); + let account = borrow_global_mut(sender_addr); + Option::extract(&mut account.withdrawal_capability) + } + + spec extract_withdraw_capability { + aborts_if !exists(Signer::address_of(sender)); + aborts_if Option::is_none(global( Signer::address_of(sender)).withdrawal_capability); + } + + /// Return the withdraw capability to the account it originally came from + public fun restore_withdraw_capability(cap: WithdrawCapability) + acquires Account { + let account = borrow_global_mut(cap.account_address); + Option::fill(&mut account.withdrawal_capability, cap) + } + + spec restore_withdraw_capability { + aborts_if Option::is_some(global(cap.account_address).withdrawal_capability); + aborts_if !exists(cap.account_address); + } + + fun emit_account_withdraw_event(account: address, amount: u128, metadata: vector) + acquires Account { + // emit withdraw event + let account = borrow_global_mut(account); + + Event::emit_event(&mut account.withdraw_events, WithdrawEvent { + amount, + token_code: Token::token_code(), + metadata, + }); + } + spec emit_account_withdraw_event { + aborts_if !exists(account); + } + + fun emit_account_deposit_event(account: address, amount: u128, metadata: vector) + acquires Account { + // emit withdraw event + let account = borrow_global_mut(account); + + Event::emit_event(&mut account.deposit_events, DepositEvent { + amount, + token_code: Token::token_code(), + metadata, + }); + } + spec emit_account_deposit_event { + aborts_if !exists(account); + } + + + /// Withdraws `amount` Token using the passed in WithdrawCapability, and deposits it + /// into the `payee`'s account balance. Creates the `payee` account if it doesn't exist. + public fun pay_from_capability( + cap: &WithdrawCapability, + payee: address, + amount: u128, + metadata: vector, + ) acquires Account, Balance, AutoAcceptToken { + let tokens = withdraw_with_capability_and_metadata(cap, amount, *&metadata); + deposit_with_metadata( + payee, + tokens, + metadata, + ); + } + + spec pay_from_capability { + // condition for withdraw_with_capability_and_metadata() + aborts_if !exists>(cap.account_address); + aborts_if !exists(cap.account_address); + aborts_if global>(cap.account_address).token.value < amount; + // condition for deposit_with_metadata() + aborts_if amount == 0; + aborts_if !exists(payee); + aborts_if !exists>(payee); + aborts_if cap.account_address != payee && global>(payee).token.value + amount > MAX_U128; + } + + /// Withdraw `amount` Token from the transaction sender's + /// account balance and send the token to the `payee` address with the + /// attached `metadata` Creates the `payee` account if it does not exist + public fun pay_from_with_metadata( + account: &signer, + payee: address, + amount: u128, + metadata: vector, + ) acquires Account, Balance, AutoAcceptToken { + let tokens = withdraw_with_metadata(account, amount, *&metadata); + deposit_with_metadata( + payee, + tokens, + metadata, + ); + } + + spec pay_from_with_metadata { + // condition for withdraw_with_metadata() + aborts_if !exists>(Signer::address_of(account)); + aborts_if !exists(Signer::address_of(account)); + aborts_if global>(Signer::address_of(account)).token.value < amount; + aborts_if Option::is_none(global(Signer::address_of(account)).withdrawal_capability); + // condition for deposit_with_metadata() + aborts_if amount == 0; + aborts_if !exists(payee); + aborts_if !exists>(payee); + aborts_if Signer::address_of(account) != payee && global>(payee).token.value + amount > max_u128(); +} + spec schema DepositWithPayerAndMetadataAbortsIf { + payer: address; + payee: address; + to_deposit: Token; + + aborts_if to_deposit.value == 0; + aborts_if !exists(payer); + aborts_if !exists(payee); + aborts_if !exists>(payee); + aborts_if global>(payee).token.value + to_deposit.value > max_u128(); + } + + + /// Withdraw `amount` Token from the transaction sender's + /// account balance and send the token to the `payee` address + /// Creates the `payee` account if it does not exist + public fun pay_from( + account: &signer, + payee: address, + amount: u128 + ) acquires Account, Balance, AutoAcceptToken { + pay_from_with_metadata(account, payee, amount, x""); + } + + spec pay_from { + // condition for withdraw_with_metadata() + aborts_if !exists>(Signer::address_of(account)); + aborts_if !exists(Signer::address_of(account)); + aborts_if global>(Signer::address_of(account)).token.value < amount; + aborts_if Option::is_none(global(Signer::address_of(account)).withdrawal_capability); + // condition for deposit_with_metadata() + aborts_if amount == 0; + aborts_if !exists(payee); + aborts_if !exists>(payee); + aborts_if Signer::address_of(account) != payee && global>(payee).token.value + amount > max_u128(); + } + + /// Rotate the authentication key for the account under cap.account_address + public fun rotate_authentication_key_with_capability( + cap: &KeyRotationCapability, + new_authentication_key: vector, + ) acquires Account { + let sender_account_resource = borrow_global_mut(cap.account_address); + // Don't allow rotating to clearly invalid key + assert!(Vector::length(&new_authentication_key) == 32, Errors::invalid_argument(EMALFORMED_AUTHENTICATION_KEY)); + sender_account_resource.authentication_key = new_authentication_key; + } + + spec rotate_authentication_key_with_capability { + aborts_if !exists(cap.account_address); + aborts_if len(new_authentication_key) != 32; + ensures global(cap.account_address).authentication_key == new_authentication_key; + } + + spec fun spec_rotate_authentication_key_with_capability(addr: address, new_authentication_key: vector): bool { + global(addr).authentication_key == new_authentication_key + } + + + /// Return a unique capability granting permission to rotate the sender's authentication key + public fun extract_key_rotation_capability(account: &signer): KeyRotationCapability + acquires Account { + let account_address = Signer::address_of(account); + // Abort if we already extracted the unique key rotation capability for this account. + assert!(!delegated_key_rotation_capability(account_address), Errors::invalid_state(EKEY_ROTATION_CAPABILITY_ALREADY_EXTRACTED)); + let account = borrow_global_mut(account_address); + Option::extract(&mut account.key_rotation_capability) + } + + spec extract_key_rotation_capability { + aborts_if !exists(Signer::address_of(account)); + aborts_if Option::is_none(global(Signer::address_of(account)).key_rotation_capability); + } + + /// Return the key rotation capability to the account it originally came from + public fun restore_key_rotation_capability(cap: KeyRotationCapability) + acquires Account { + let account = borrow_global_mut(cap.account_address); + Option::fill(&mut account.key_rotation_capability, cap) + } + public fun destroy_key_rotation_capability(cap: KeyRotationCapability) { + let KeyRotationCapability {account_address: _} = cap; + } + + spec restore_key_rotation_capability { + aborts_if Option::is_some(global(cap.account_address).key_rotation_capability); + aborts_if !exists(cap.account_address); + } + + public entry fun rotate_authentication_key(account: signer, new_key: vector) acquires Account, EventStore { + rotate_authentication_key_entry(account, new_key); + } + + public entry fun rotate_authentication_key_entry(account: signer, new_key: vector) acquires Account, EventStore { + do_rotate_authentication_key(&account, new_key); + } + + public fun do_rotate_authentication_key(account: &signer, new_key: vector) acquires Account, EventStore { + let key_rotation_capability = extract_key_rotation_capability(account); + rotate_authentication_key_with_capability(&key_rotation_capability, copy new_key); + restore_key_rotation_capability(key_rotation_capability); + + make_event_store_if_not_exist(account); + let signer_addr = Signer::address_of(account); + let event_store = borrow_global_mut(signer_addr); + Event::emit_event( + &mut event_store.rotate_auth_key_events, + RotateAuthKeyEvent { + account_address: signer_addr, + new_auth_key: new_key, + } + ); + } + + spec rotate_authentication_key { + pragma verify = false; + } + + /// Helper to return the u128 value of the `balance` for `account` + fun balance_for(balance: &Balance): u128 { + Token::value(&balance.token) + } + + spec balance_for { + aborts_if false; + } + + /// Return the current TokenType balance of the account at `addr`. + public fun balance(addr: address): u128 acquires Balance { + if (exists>(addr)) { + balance_for(borrow_global>(addr)) + } else { + 0u128 + } + } + + + /// Add a balance of `Token` type to the sending account. + public fun do_accept_token(account: &signer) acquires Account { + move_to(account, Balance{ token: Token::zero() }); + let token_code = Token::token_code(); + // Load the sender's account + let sender_account_ref = borrow_global_mut(Signer::address_of(account)); + // Log a sent event + Event::emit_event( + &mut sender_account_ref.accept_token_events, + AcceptTokenEvent { + token_code: token_code, + }, + ); + } + + spec do_accept_token { + aborts_if exists>(Signer::address_of(account)); + aborts_if !exists(Signer::address_of(account)); + } + + public entry fun accept_token(account: signer) acquires Account { + accept_token_entry(account); + } + + public entry fun accept_token_entry(account: signer) acquires Account { + do_accept_token(&account); + } + + spec accept_token { + pragma verify = false; + } + + /// This is a alias of is_accept_token + public fun is_accepts_token(addr: address): bool acquires AutoAcceptToken { + Self::is_accept_token(addr) + } + + spec is_accepts_token { + aborts_if false; + } + + /// Return whether the account at `addr` accept `Token` type tokens + public fun is_accept_token(addr: address): bool acquires AutoAcceptToken { + if (can_auto_accept_token(addr)) { + true + } else { + exists>(addr) + } + } + + spec is_accept_token { + aborts_if false; + } + + /// Check whether the address can auto accept token. + public fun can_auto_accept_token(addr: address): bool acquires AutoAcceptToken { + if (exists(addr)) { + borrow_global(addr).enable + } else { + false + } + } + + public entry fun set_auto_accept_token_entry(account: signer, enable: bool) acquires AutoAcceptToken { + set_auto_accept_token(&account, enable); + } + + /// Configure whether auto-accept tokens. + public fun set_auto_accept_token(account: &signer, enable: bool) acquires AutoAcceptToken { + let addr = Signer::address_of(account); + if (exists(addr)) { + let config = borrow_global_mut(addr); + config.enable = enable; + } else { + move_to(account, AutoAcceptToken{enable}); + }; + } + spec set_auto_accept_token { + aborts_if false; + } + + /// try to accept token for `addr`. + fun try_accept_token(addr: address) acquires AutoAcceptToken, Account { + if (!exists>(addr)) { + if (can_auto_accept_token(addr)) { + let signer = create_signer(addr); + do_accept_token(&signer); + }else{ + abort Errors::not_published(ERR_TOKEN_NOT_ACCEPT) + } + }; + } + spec try_accept_token { + aborts_if false; + } + + /// Helper to return the sequence number field for given `account` + fun sequence_number_for_account(account: &Account): u64 { + account.sequence_number + } + + spec is_accepts_token { + aborts_if false; + } + + /// Return the current sequence number at `addr` + public fun sequence_number(addr: address): u64 acquires Account { + sequence_number_for_account(borrow_global(addr)) + } + + spec sequence_number { + aborts_if !exists(addr); + } + + /// Return the authentication key for this account + public fun authentication_key(addr: address): vector acquires Account { + *&borrow_global(addr).authentication_key + } + + spec authentication_key { + aborts_if !exists(addr); + } + + /// Return true if the account at `addr` has delegated its key rotation capability + public fun delegated_key_rotation_capability(addr: address): bool + acquires Account { + Option::is_none(&borrow_global(addr).key_rotation_capability) + } + + spec delegated_key_rotation_capability { + aborts_if !exists(addr); + } + + /// Return true if the account at `addr` has delegated its withdraw capability + public fun delegated_withdraw_capability(addr: address): bool + acquires Account { + Option::is_none(&borrow_global(addr).withdrawal_capability) + } + + spec delegated_withdraw_capability { + aborts_if !exists(addr); + } + + /// Return a reference to the address associated with the given withdraw capability + public fun withdraw_capability_address(cap: &WithdrawCapability): &address { + &cap.account_address + } + + spec withdraw_capability_address { + aborts_if false; + } + + /// Return a reference to the address associated with the given key rotation capability + public fun key_rotation_capability_address(cap: &KeyRotationCapability): &address { + &cap.account_address + } + + spec key_rotation_capability_address { + aborts_if false; + } + + /// Checks if an account exists at `check_addr` + public fun exists_at(check_addr: address): bool { + exists(check_addr) + } + + spec exists_at { + aborts_if false; + } + + fun is_dummy_auth_key(account: &Account): bool { + *&account.authentication_key == DUMMY_AUTH_KEY + } + + public fun is_dummy_auth_key_v2(account: address): bool acquires Account { + let account = borrow_global_mut(account); + account.authentication_key == DUMMY_AUTH_KEY + } + + /// The prologue is invoked at the beginning of every transaction + /// It verifies: + /// - The account's auth key matches the transaction's public key + /// - That the account has enough balance to pay for all of the gas + /// - That the sequence number matches the transaction's sequence key + public fun txn_prologue( + account: &signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + ) acquires Account, Balance { + txn_prologue_v2( + account, + txn_sender, + txn_sequence_number, + txn_authentication_key_preimage, + txn_gas_price, + txn_max_gas_units, + 1, + 1, + ) + } + spec txn_prologue { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(txn_sender); + aborts_if global(txn_sender).authentication_key == DUMMY_AUTH_KEY && Authenticator::spec_derived_address(Hash::sha3_256(txn_authentication_key_preimage)) != txn_sender; + aborts_if global(txn_sender).authentication_key != DUMMY_AUTH_KEY && Hash::sha3_256(txn_authentication_key_preimage) != global(txn_sender).authentication_key; + aborts_if txn_sequence_number < global(txn_sender).sequence_number; + } + + public fun txn_prologue_v2( + account: &signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + stc_price: u128, + stc_price_scaling: u128 + ) acquires Account, Balance { + CoreAddresses::assert_genesis_address(account); + + // Verify that the transaction sender's account exists + assert!(exists_at(txn_sender), Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST)); + // Verify the account has not delegate its signer cap. + assert!(!is_signer_delegated(txn_sender), Errors::invalid_state(EPROLOGUE_SIGNER_ALREADY_DELEGATED)); + + // Load the transaction sender's account + let sender_account = borrow_global_mut(txn_sender); + + if (is_dummy_auth_key(sender_account)){ + // if sender's auth key is empty, use address as auth key for check transaction. + assert!( + Authenticator::derived_address(Hash::sha3_256(txn_authentication_key_preimage)) == txn_sender, + Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY) + ); + }else{ + // Check that the hash of the transaction's public key matches the account's auth key + assert!( + Hash::sha3_256(txn_authentication_key_preimage) == *&sender_account.authentication_key, + Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY) + ); + }; + // Check that the account has enough balance for all of the gas + let (max_transaction_fee_stc,max_transaction_fee_token) = transaction_fee_simulate(txn_gas_price,txn_max_gas_units,0, stc_price, stc_price_scaling); + assert!( + max_transaction_fee_stc <= MAX_U64, + Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT), + ); + if (max_transaction_fee_stc > 0) { + assert!( + (txn_sequence_number as u128) < MAX_U64, + Errors::limit_exceeded(EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG) + ); + let balance_amount_token = balance(txn_sender); + assert!(balance_amount_token >= max_transaction_fee_token, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT)); + if (!is_stc()){ + let balance_amount_stc= balance(CoreAddresses::GENESIS_ADDRESS()); + assert!(balance_amount_stc >= max_transaction_fee_stc, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT)); + } + }; + // Check that the transaction sequence number matches the sequence number of the account + assert!(txn_sequence_number >= sender_account.sequence_number, Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD)); + assert!(txn_sequence_number == sender_account.sequence_number, Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW)); + + } + + + /// The epilogue is invoked at the end of transactions. + /// It collects gas and bumps the sequence number + public fun txn_epilogue( + account: &signer, + txn_sender: address, + txn_sequence_number: u64, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + ) acquires Account, Balance { + txn_epilogue_v3(account, txn_sender, txn_sequence_number, Vector::empty(), txn_gas_price, txn_max_gas_units, gas_units_remaining,1,1) + } + + spec txn_epilogue { + pragma verify = false; + } + + public fun transaction_fee_simulate( + txn_gas_price:u64, + txn_max_gas_units: u64, + gas_units_remaining:u64, + stc_price: u128, + stc_price_scaling: u128, + ): (u128, u128){ + let transaction_fee_stc =(txn_gas_price * (txn_max_gas_units - gas_units_remaining) as u128); + let transaction_fee_token= Math::mul_div((transaction_fee_stc as u128), stc_price, stc_price_scaling); + transaction_fee_token = if (transaction_fee_token == 0 && transaction_fee_stc > 0 ) { 1 } else { transaction_fee_token}; + (transaction_fee_stc, transaction_fee_token) + } + /// The epilogue is invoked at the end of transactions. + /// It collects gas and bumps the sequence number + public fun txn_epilogue_v2( + account: &signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + ) acquires Account, Balance { + txn_epilogue_v3( + account, + txn_sender, + txn_sequence_number, + txn_authentication_key_preimage, + txn_gas_price, + txn_max_gas_units, + gas_units_remaining,1,1) + } + + spec txn_epilogue_v2 { + pragma verify = false; // Todo: fix me, cost too much time + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(txn_sender); + aborts_if !exists>(txn_sender); + aborts_if txn_max_gas_units < gas_units_remaining; + let transaction_fee_amount = txn_gas_price * (txn_max_gas_units - gas_units_remaining); + aborts_if transaction_fee_amount > max_u128(); + aborts_if global>(txn_sender).token.value < transaction_fee_amount; + aborts_if txn_sequence_number + 1 > max_u64(); + aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > 0 && + global>(txn_sender).token.value < txn_gas_price * (txn_max_gas_units - gas_units_remaining); + aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > 0 && + !exists>(CoreAddresses::GENESIS_ADDRESS()); + aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > 0 && + global>(CoreAddresses::GENESIS_ADDRESS()).fee.value + txn_gas_price * (txn_max_gas_units - gas_units_remaining) > max_u128(); + } + + /// The epilogue is invoked at the end of transactions. + /// It collects gas and bumps the sequence number + public fun txn_epilogue_v3( + account: &signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + stc_price: u128, + stc_price_scaling: u128, + ) acquires Account, Balance { + CoreAddresses::assert_genesis_address(account); + // Charge for gas + let (transaction_fee_amount_stc,transaction_fee_amount_token) = transaction_fee_simulate( + txn_gas_price, + txn_max_gas_units, + gas_units_remaining, + stc_price, + stc_price_scaling); + assert!( + balance(txn_sender) >= transaction_fee_amount_token, + Errors::limit_exceeded(EINSUFFICIENT_BALANCE) + ); + if (!is_stc()){ + let genesis_balance_amount_stc=balance(CoreAddresses::GENESIS_ADDRESS()); + assert!(genesis_balance_amount_stc >= transaction_fee_amount_stc, + Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT) + ); + }; + // Load the transaction sender's account and balance resources + let sender_account = borrow_global_mut(txn_sender); + // Bump the sequence number + sender_account.sequence_number = txn_sequence_number + 1; + // Set auth key when user send transaction first. + if (is_dummy_auth_key(sender_account) && !Vector::is_empty(&txn_authentication_key_preimage)){ + sender_account.authentication_key = Hash::sha3_256(txn_authentication_key_preimage); + }; + if (transaction_fee_amount_stc > 0) { + let transaction_fee_token = withdraw_from_balance( + borrow_global_mut>(txn_sender), + transaction_fee_amount_token + ); + deposit_to_balance(borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()), transaction_fee_token); + let stc_fee_token = withdraw_from_balance(borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()), transaction_fee_amount_stc); + TransactionFee::pay_fee(stc_fee_token); + }; + } + + spec txn_epilogue_v2 { + pragma verify = false; // Todo: fix me, cost too much time + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(txn_sender); + aborts_if !exists>(txn_sender); + aborts_if txn_sequence_number + 1 > max_u64(); + aborts_if !exists>(txn_sender); + aborts_if txn_max_gas_units < gas_units_remaining; + } + + public entry fun remove_zero_balance_entry(account: signer) acquires Balance { + remove_zero_balance(&account); + } + + /// Remove zero Balance + public fun remove_zero_balance(account: &signer) acquires Balance { + let addr: address = Signer::address_of(account); + let Balance { token } = move_from>(addr); + Token::destroy_zero(token); + } + + spec remove_zero_balance { + let addr = Signer::address_of(account); + aborts_if !exists>(addr); + ensures !exists>(addr); + } + + /// Make a event store if it's not exist. + fun make_event_store_if_not_exist(account: &signer) { + if (!exists(Signer::address_of(account))) { + move_to(account, EventStore { + rotate_auth_key_events: Event::new_event_handle(account), + extract_withdraw_cap_events: Event::new_event_handle(account), + signer_delegate_events: Event::new_event_handle(account), + }) + }; + } +} + +} diff --git a/release/v13/sources/AccountScripts.move b/release/v13/sources/AccountScripts.move new file mode 100644 index 00000000..ce11e3b6 --- /dev/null +++ b/release/v13/sources/AccountScripts.move @@ -0,0 +1,21 @@ +address StarcoinFramework { +module AccountScripts { + use StarcoinFramework::Account; + /// Enable account's auto-accept-token feature. + /// The script function is reenterable. + public entry fun enable_auto_accept_token(account: signer) { + Account::set_auto_accept_token_entry(account, true); + } + + /// Disable account's auto-accept-token feature. + /// The script function is reenterable. + public entry fun disable_auto_accept_token(account: signer) { + Account::set_auto_accept_token_entry(account, false); + } + + /// Remove zero Balance + public entry fun remove_zero_balance(account: signer) { + Account::remove_zero_balance_entry(account); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Arith.move b/release/v13/sources/Arith.move new file mode 100644 index 00000000..83cbdc27 --- /dev/null +++ b/release/v13/sources/Arith.move @@ -0,0 +1,442 @@ +address StarcoinFramework { +/// Helper module to do u64 arith. +module Arith { + use StarcoinFramework::Errors; + const ERR_INVALID_CARRY: u64 = 301; + const ERR_INVALID_BORROW: u64 = 302; + + const P32: u64 = 0x100000000; + const P64: u128 = 0x10000000000000000; + + spec module { + pragma verify = true; + pragma aborts_if_is_strict; + } + + /// split u64 to (high, low) + public fun split_u64(i: u64): (u64, u64) { + (i >> 32, i & 0xFFFFFFFF) + } + + spec split_u64 { + pragma verify = false; + pragma opaque; // MVP cannot reason about bitwise operation + ensures [abstract] result_1 == i / P32; + ensures [abstract] result_2 == i % P32; + } + + /// combine (high, low) to u64, + /// any lower bits of `high` will be erased, any higher bits of `low` will be erased. + public fun combine_u64(hi: u64, lo: u64): u64 { + (hi << 32) | (lo & 0xFFFFFFFF) + } + + spec combine_u64 { + pragma verify = false; + pragma opaque = true; // MVP cannot reason about bitwise operation + let hi_32 = hi % P32; + let lo_32 = lo % P32; + ensures [abstract] result == hi_32 * P32 + lo_32; + } + + /// a + b, with carry + public fun adc(a: u64, b: u64, carry: &mut u64) : u64 { + assert!(*carry <= 1, Errors::invalid_argument(ERR_INVALID_CARRY)); + let (a1, a0) = split_u64(a); + let (b1, b0) = split_u64(b); + let (c, r0) = split_u64(a0 + b0 + *carry); + let (c, r1) = split_u64(a1 + b1 + c); + *carry = c; + combine_u64(r1, r0) + } + + spec adc { + // Carry has either to be 0 or 1 + aborts_if !(carry == 0 || carry == 1); + ensures carry == 0 || carry == 1; + // Result with or without carry + ensures carry == 0 ==> result == a + b + old(carry); + ensures carry == 1 ==> P64 + result == a + b + old(carry); + } + + /// a - b, with borrow + public fun sbb(a: u64, b: u64, borrow: &mut u64): u64 { + assert!(*borrow <= 1, Errors::invalid_argument(ERR_INVALID_BORROW)); + let (a1, a0) = split_u64(a); + let (b1, b0) = split_u64(b); + let (b, r0) = split_u64(P32 + a0 - b0 - *borrow); + let borrowed = 1 - b; + let (b, r1) = split_u64(P32 + a1 - b1 - borrowed); + *borrow = 1 - b; + + combine_u64(r1, r0) + } + + spec sbb { + // Borrow has either to be 0 or 1 + aborts_if !(borrow == 0 || borrow == 1); + ensures borrow == 0 || borrow == 1; + // Result with or without borrow + ensures borrow == 0 ==> result == a - b - old(borrow); + ensures borrow == 1 ==> result == P64 + a - b - old(borrow); + } +} + +/// Implementation u256. +module U256 { + + spec module { + pragma verify = true; + } + + use StarcoinFramework::Vector; + use StarcoinFramework::Errors; + + const WORD: u8 = 4; + const P32: u64 = 0x100000000; + const P64: u128 = 0x10000000000000000; + + const ERR_INVALID_LENGTH: u64 = 100; + const ERR_OVERFLOW: u64 = 200; + /// use vector to represent data. + /// so that we can use buildin vector ops later to construct U256. + /// vector should always has two elements. + struct U256 has copy, drop, store { + /// little endian representation + bits: vector, + } + + spec U256 { + invariant len(bits) == 4; + } + + spec fun value_of_U256(a: U256): num { + a.bits[0] + + a.bits[1] * P64 + + a.bits[2] * P64 * P64 + + a.bits[3] * P64 * P64 * P64 + } + + public fun zero(): U256 { + from_u128(0u128) + } + + public fun one(): U256 { + from_u128(1u128) + } + + public fun from_u64(v: u64): U256 { + from_u128((v as u128)) + } + + public fun from_u128(v: u128): U256 { + let low = ((v & 0xffffffffffffffff) as u64); + let high = ((v >> 64) as u64); + let bits = Vector::singleton(low); + Vector::push_back(&mut bits, high); + Vector::push_back(&mut bits, 0u64); + Vector::push_back(&mut bits, 0u64); + U256 { bits } + } + + spec from_u128 { + pragma verify = false; + pragma opaque; // Original function has bitwise operator + ensures value_of_U256(result) == v; + } + + #[test] + fun test_from_u128() { + // 2^64 + 1 + let v = from_u128(18446744073709551617u128); + assert!(*Vector::borrow(&v.bits, 0) == 1, 0); + assert!(*Vector::borrow(&v.bits, 1) == 1, 1); + assert!(*Vector::borrow(&v.bits, 2) == 0, 2); + assert!(*Vector::borrow(&v.bits, 3) == 0, 3); + } + + public fun from_big_endian(data: vector): U256 { + // TODO: define error code. + assert!(Vector::length(&data) <= 32, Errors::invalid_argument(ERR_INVALID_LENGTH)); + from_bytes(&data, true) + } + + spec from_big_endian { + pragma verify = false; // TODO: How to interpret the value of vector data of bytes + } + + public fun from_little_endian(data: vector): U256 { + // TODO: define error code. + assert!(Vector::length(&data) <= 32, Errors::invalid_argument(ERR_INVALID_LENGTH)); + from_bytes(&data, false) + } + + spec from_little_endian { + pragma verify = false; // TODO: How to interpret the value of vector data of bytes + } + + public fun to_u128(v: &U256): u128 { + assert!(*Vector::borrow(&v.bits, 3) == 0, Errors::invalid_state(ERR_OVERFLOW)); + assert!(*Vector::borrow(&v.bits, 2) == 0, Errors::invalid_state(ERR_OVERFLOW)); + ((*Vector::borrow(&v.bits, 1) as u128) << 64) | (*Vector::borrow(&v.bits, 0) as u128) + } + + spec to_u128 { + pragma verify = false; + pragma opaque; // Original function has bitwise operator + aborts_if value_of_U256(v) >= P64 * P64; + ensures value_of_U256(v) == result; + } + + #[test] + fun test_to_u128() { + // 2^^128 - 1 + let i = 340282366920938463463374607431768211455u128; + let v = from_u128(i); + assert!(to_u128(&v) == i, 128); + } + #[test] + #[expected_failure] + fun test_to_u128_overflow() { + // 2^^128 - 1 + let i = 340282366920938463463374607431768211455u128; + let v = from_u128(i); + let v = add(v, one()); + to_u128(&v); + } + + const EQUAL: u8 = 0; + const LESS_THAN: u8 = 1; + const GREATER_THAN: u8 = 2; + + public fun compare(a: &U256, b: &U256): u8 { + let i = (WORD as u64); + while (i > 0) { + i = i - 1; + let a_bits = *Vector::borrow(&a.bits, i); + let b_bits = *Vector::borrow(&b.bits, i); + if (a_bits != b_bits) { + if (a_bits < b_bits) { + return LESS_THAN + } else { + return GREATER_THAN + } + } + }; + return EQUAL + } + + // TODO: MVP interprets it wrong + // spec compare { + // let va = value_of_U256(a); + // let vb = value_of_U256(b); + // ensures (va > vb) ==> (result == GREATER_THAN); + // ensures (va < vb) ==> (result == LESS_THAN); + // ensures (va == vb) ==> (result == EQUAL); + // } + + #[test] + fun test_compare() { + let a = from_u64(111); + let b = from_u64(111); + let c = from_u64(112); + let d = from_u64(110); + assert!(compare(&a, &b) == EQUAL, 0); + assert!(compare(&a, &c) == LESS_THAN, 1); + assert!(compare(&a, &d) == GREATER_THAN, 2); + } + + + public fun add(a: U256, b: U256): U256 { + native_add(&mut a, &b); + a + } + + spec add { + aborts_if value_of_U256(a) + value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(result) == value_of_U256(a) + value_of_U256(b); + } + + #[test] + fun test_add() { + let a = Self::one(); + let b = Self::from_u128(10); + let ret = Self::add(a, b); + assert!(compare(&ret, &from_u64(11)) == EQUAL, 0); + } + + public fun sub(a: U256, b: U256): U256 { + native_sub(&mut a, &b); + a + } + + spec sub { + aborts_if value_of_U256(a) < value_of_U256(b); + ensures value_of_U256(result) == value_of_U256(a) - value_of_U256(b); + } + + #[test] + #[expected_failure] + fun test_sub_overflow() { + let a = Self::one(); + let b = Self::from_u128(10); + let _ = Self::sub(a, b); + } + + #[test] + fun test_sub_ok() { + let a = Self::from_u128(10); + let b = Self::one(); + let ret = Self::sub(a, b); + assert!(compare(&ret, &from_u64(9)) == EQUAL, 0); + } + + public fun mul(a: U256, b: U256): U256 { + native_mul(&mut a, &b); + a + } + + spec mul { + pragma verify = false; + pragma timeout = 200; // Take longer time + aborts_if value_of_U256(a) * value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(result) == value_of_U256(a) * value_of_U256(b); + } + + #[test] + fun test_mul() { + let a = Self::from_u128(10); + let b = Self::from_u64(10); + let ret = Self::mul(a, b); + assert!(compare(&ret, &from_u64(100)) == EQUAL, 0); + } + + public fun div(a: U256, b: U256): U256 { + native_div(&mut a, &b); + a + } + + spec div { + pragma verify = false; + pragma timeout = 160; // Might take longer time + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(result) == value_of_U256(a) / value_of_U256(b); + } + + #[test] + fun test_div() { + let a = Self::from_u128(10); + let b = Self::from_u64(2); + let c = Self::from_u64(3); + // as U256 cannot be implicitly copied, we need to add copy keyword. + assert!(compare(&Self::div(copy a, b), &from_u64(5)) == EQUAL, 0); + assert!(compare(&Self::div(copy a, c), &from_u64(3)) == EQUAL, 0); + } + + public fun rem(a: U256, b: U256): U256 { + native_rem(&mut a, &b); + a + } + + spec rem { + pragma verify = false; + pragma timeout = 160; // Might take longer time + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(result) == value_of_U256(a) % value_of_U256(b); + } + + #[test] + fun test_rem() { + let a = Self::from_u128(10); + let b = Self::from_u64(2); + let c = Self::from_u64(3); + assert!(compare(&Self::rem(copy a, b), &from_u64(0)) == EQUAL, 0); + assert!(compare(&Self::rem(copy a, c), &from_u64(1)) == EQUAL, 0); + } + + public fun pow(a: U256, b: U256): U256 { + native_pow(&mut a, &b); + a + } + + spec pow { + // Verfication of Pow takes enormous amount of time + // Don't verify it, and make it opaque so that the caller + // can make use of the properties listed here. + pragma verify = false; + pragma opaque; + pragma timeout = 600; + let p = pow_spec(value_of_U256(a), value_of_U256(b)); + aborts_if p >= P64 * P64 * P64 * P64; + ensures value_of_U256(result) == p; + } + + #[test] + fun test_pow() { + let a = Self::from_u128(10); + let b = Self::from_u64(1); + let c = Self::from_u64(2); + let d = Self::zero(); + assert!(compare(&Self::pow(copy a, b), &from_u64(10)) == EQUAL, 0); + assert!(compare(&Self::pow(copy a, c), &from_u64(100)) == EQUAL, 0); + assert!(compare(&Self::pow(copy a, d), &from_u64(1)) == EQUAL, 0); + } + + native fun from_bytes(data: &vector, be: bool): U256; + native fun native_add(a: &mut U256, b: &U256); + native fun native_sub(a: &mut U256, b: &U256); + native fun native_mul(a: &mut U256, b: &U256); + native fun native_div(a: &mut U256, b: &U256); + native fun native_rem(a: &mut U256, b: &U256); + native fun native_pow(a: &mut U256, b: &U256); + + spec native_add { + pragma opaque; + aborts_if value_of_U256(a) + value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(a) == value_of_U256(old(a)) + value_of_U256(b); + } + + spec native_sub { + pragma opaque; + aborts_if value_of_U256(a) - value_of_U256(b) < 0; + ensures value_of_U256(a) == value_of_U256(old(a)) - value_of_U256(b); + } + + spec native_mul { + pragma opaque; + aborts_if value_of_U256(a) * value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(a) == value_of_U256(old(a)) * value_of_U256(b); + } + + spec native_div { + pragma opaque; + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(a) == value_of_U256(old(a)) / value_of_U256(b); + } + + spec native_rem { + pragma opaque; + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(a) == value_of_U256(old(a)) % value_of_U256(b); + } + + spec native_pow { + pragma opaque; + aborts_if pow_spec(value_of_U256(a), value_of_U256(b)) >= P64 * P64 * P64 * P64; + ensures value_of_U256(a) == pow_spec(value_of_U256(old(a)), value_of_U256(b)); + } + + spec fun pow_spec(base: num, expon: num): num { + // This actually doesn't follow a strict definition as 0^0 is undefined + // mathematically. But the U256::pow of Rust is defined to be like this: + // Link: https://docs.rs/uint/0.9.3/src/uint/uint.rs.html#1000-1003 + if (expon > 0) { + let x = pow_spec(base, expon / 2); + if (expon % 2 == 0) { x * x } else { x * x * base } + } else { + 1 + } + } + +} +} diff --git a/release/v13/sources/Authenticator.move b/release/v13/sources/Authenticator.move new file mode 100644 index 00000000..1acda180 --- /dev/null +++ b/release/v13/sources/Authenticator.move @@ -0,0 +1,155 @@ + +address StarcoinFramework { +/// Move representation of the authenticator types +/// - Ed25519 (single-sig) +/// - MultiEd25519 (K-of-N multisig) +module Authenticator { + use StarcoinFramework::Hash; + use StarcoinFramework::BCS; + use StarcoinFramework::Vector; + use StarcoinFramework::Errors; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + const AUTHENTICATION_KEY_LENGTH: u64 = 32; + + const ED25519_SCHEME_ID: u8 = 0; + const MULTI_ED25519_SCHEME_ID: u8 = 1; + + /// A multi-ed25519 public key + struct MultiEd25519PublicKey has copy, drop, store { + /// vector of ed25519 public keys + public_keys: vector>, + /// approval threshold + threshold: u8, + } + + /// Maximum number of keys allowed in a MultiEd25519 public/private key + const MAX_MULTI_ED25519_KEYS: u64 = 32; + + const EWRONG_AUTHENTICATION_KEY_LENGTH: u64 = 101; + /// Threshold provided was 0 which can't be used to create a `MultiEd25519` key + const EZERO_THRESHOLD: u64 = 102; + /// Not enough keys were provided for the specified threshold when creating an `MultiEd25519` key + const ENOT_ENOUGH_KEYS_FOR_THRESHOLD: u64 = 103; + /// Too many keys were provided for the specified threshold when creating an `MultiEd25519` key + const ENUM_KEYS_ABOVE_MAX_THRESHOLD: u64 = 104; + + /// Create a a multisig policy from a vector of ed25519 public keys and a threshold. + /// Note: this does *not* check uniqueness of keys. Repeated keys are convenient to + /// encode weighted multisig policies. For example Alice AND 1 of Bob or Carol is + /// public_key: {alice_key, alice_key, bob_key, carol_key}, threshold: 3 + /// Aborts if threshold is zero or bigger than the length of `public_keys`. + public fun create_multi_ed25519( + public_keys: vector>, + threshold: u8 + ): MultiEd25519PublicKey { + // check threshold requirements + let len = Vector::length(&public_keys); + assert!(threshold != 0, Errors::invalid_argument(EZERO_THRESHOLD)); + assert!( + (threshold as u64) <= len, + Errors::invalid_argument(ENOT_ENOUGH_KEYS_FOR_THRESHOLD) + ); + // the multied25519 signature scheme allows at most 32 keys + assert!( + len <= MAX_MULTI_ED25519_KEYS, + Errors::invalid_argument(ENUM_KEYS_ABOVE_MAX_THRESHOLD) + ); + + MultiEd25519PublicKey { public_keys, threshold } + } + + spec create_multi_ed25519 { + aborts_if threshold == 0; + aborts_if threshold > Vector::length(public_keys); + aborts_if Vector::length(public_keys) > 32; + } + + /// Compute an authentication key for the ed25519 public key `public_key` + public fun ed25519_authentication_key(public_key: vector): vector { + Vector::push_back(&mut public_key, ED25519_SCHEME_ID); + Hash::sha3_256(public_key) + } + + spec ed25519_authentication_key { + pragma opaque = true; + aborts_if false; + ensures [abstract] result == spec_ed25519_authentication_key(public_key); + } + + /// We use an uninterpreted function to represent the result of key construction. The actual value + /// does not matter for the verification of callers. + spec fun spec_ed25519_authentication_key(public_key: vector): vector; + + /// convert authentication key to address + public fun derived_address(authentication_key: vector): address { + assert!(Vector::length(&authentication_key) == AUTHENTICATION_KEY_LENGTH, Errors::invalid_argument(EWRONG_AUTHENTICATION_KEY_LENGTH)); + let address_bytes = Vector::empty(); + + let i = 16; + while (i < 32) { + let b = *Vector::borrow(&authentication_key, i); + Vector::push_back(&mut address_bytes, b); + i = i + 1; + }; + + BCS::to_address(address_bytes) + } + + spec derived_address { + pragma opaque = true; + aborts_if len(authentication_key) != 32; + ensures [abstract] result == spec_derived_address(authentication_key); + } + + /// We use an uninterpreted function to represent the result of derived address. The actual value + /// does not matter for the verification of callers. + spec fun spec_derived_address(authentication_key: vector): address; + + /// Compute a multied25519 account authentication key for the policy `k` + public fun multi_ed25519_authentication_key(k: &MultiEd25519PublicKey): vector { + let public_keys = &k.public_keys; + let len = Vector::length(public_keys); + let authentication_key_preimage = Vector::empty(); + let i = 0; + while (i < len) { + let public_key = *Vector::borrow(public_keys, i); + Vector::append( + &mut authentication_key_preimage, + public_key + ); + i = i + 1; + }; + Vector::append(&mut authentication_key_preimage, BCS::to_bytes(&k.threshold)); + Vector::push_back(&mut authentication_key_preimage, MULTI_ED25519_SCHEME_ID); + Hash::sha3_256(authentication_key_preimage) + } + + spec multi_ed25519_authentication_key { + aborts_if false; + } + + /// Return the public keys involved in the multisig policy `k` + public fun public_keys(k: &MultiEd25519PublicKey): &vector> { + &k.public_keys + } + + spec public_keys { + aborts_if false; + } + + /// Return the threshold for the multisig policy `k` + public fun threshold(k: &MultiEd25519PublicKey): u8 { + *&k.threshold + } + + spec threshold { + aborts_if false; + } + +} +} diff --git a/release/v13/sources/BCS.move b/release/v13/sources/BCS.move new file mode 100644 index 00000000..2c524dd5 --- /dev/null +++ b/release/v13/sources/BCS.move @@ -0,0 +1,696 @@ +address StarcoinFramework { +/// Utility for converting a Move value to its binary representation in BCS (Diem Canonical +/// Serialization). BCS is the binary encoding for Move resources and other non-module values +/// published on-chain. +module BCS { + + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + use StarcoinFramework::Option; + use StarcoinFramework::BCS; + + const ERR_INPUT_NOT_LARGE_ENOUGH: u64 = 201; + const ERR_UNEXPECTED_BOOL_VALUE: u64 = 205; + const ERR_OVERFLOW_PARSING_ULEB128_ENCODED_UINT32: u64 = 206; + const ERR_INVALID_ULEB128_NUMBER_UNEXPECTED_ZERO_DIGIT: u64 = 207; + const INTEGER32_MAX_VALUE: u64 = 2147483647; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + /// Return the binary representation of `v` in BCS (Starcoin Canonical Serialization) format + native public fun to_bytes(v: &MoveValue): vector; + + /// Return the address of key bytes + native public fun to_address(key_bytes: vector): address; + // ------------------------------------------------------------------------ + // Specification + // ------------------------------------------------------------------------ + + + spec native fun serialize(v: &MoveValue): vector; + + + public fun deserialize_option_bytes_vector(input: &vector, offset: u64): (vector>>, u64) { + let (len, new_offset) = deserialize_len(input, offset); + let i = 0; + let vec = Vector::empty>>(); + while (i < len) { + let (opt_bs, o) = deserialize_option_bytes(input, new_offset); + Vector::push_back(&mut vec, opt_bs); + new_offset = o; + i = i + 1; + }; + (vec, new_offset) + } + spec deserialize_option_bytes_vector { + pragma verify = false; + } + + public fun deserialize_bytes_vector(input: &vector, offset: u64): (vector>, u64) { + let (len, new_offset) = deserialize_len(input, offset); + let i = 0; + let vec = Vector::empty>(); + while (i < len) { + let (opt_bs, o) = deserialize_bytes(input, new_offset); + Vector::push_back(&mut vec, opt_bs); + new_offset = o; + i = i + 1; + }; + (vec, new_offset) + } + + spec deserialize_bytes_vector { + pragma verify = false; + } + + #[test] + public fun test_deserialize_bytes_array() { + let hello = b"hello"; + let world = b"world"; + let hello_world = Vector::empty>(); + Vector::push_back(&mut hello_world, hello); + Vector::push_back(&mut hello_world, world); + let bs = BCS::to_bytes>>(&hello_world); + let (r, _) = deserialize_bytes_vector(&bs, 0); + assert!(hello_world == r, 1001); + } + + public fun deserialize_u64_vector(input: &vector, offset: u64): (vector, u64) { + let (len, new_offset) = deserialize_len(input, offset); + let i = 0; + let vec = Vector::empty(); + while (i < len) { + let (opt_bs, o) = deserialize_u64(input, new_offset); + Vector::push_back(&mut vec, opt_bs); + new_offset = o; + i = i + 1; + }; + (vec, new_offset) + } + + spec deserialize_u64_vector { + pragma verify = false; + } + + public fun deserialize_u128_vector(input: &vector, offset: u64): (vector, u64) { + let (len, new_offset) = deserialize_len(input, offset); + let i = 0; + let vec = Vector::empty(); + while (i < len) { + let (opt_bs, o) = deserialize_u128(input, new_offset); + Vector::push_back(&mut vec, opt_bs); + new_offset = o; + i = i + 1; + }; + (vec, new_offset) + } + + spec deserialize_u128_vector { + pragma verify = false; + } + + #[test] + public fun test_deserialize_u128_array() { + let hello:u128 = 1111111; + let world:u128 = 2222222; + let hello_world = Vector::empty(); + Vector::push_back(&mut hello_world, hello); + Vector::push_back(&mut hello_world, world); + let bs = BCS::to_bytes>(&hello_world); + let (r, _) = deserialize_u128_vector(&bs, 0); + assert!(hello_world == r, 1002); + } + + public fun deserialize_option_bytes(input: &vector, offset: u64): (Option::Option>, u64) { + let (tag, new_offset) = deserialize_option_tag(input, offset); + if (!tag) { + return (Option::none>(), new_offset) + } else { + let (bs, new_offset) = deserialize_bytes(input, new_offset); + return (Option::some>(bs), new_offset) + } + } + + spec deserialize_option_bytes { + pragma verify = false; + } + + public fun deserialize_address(input: &vector, offset: u64): (address, u64) { + let (content, new_offset) = deserialize_16_bytes(input, offset); + (BCS::to_address(content), new_offset) + } + + spec deserialize_address { + pragma verify = false; + } + + #[test] + fun test_deserialize_address() { + let addr = @0x18351d311d32201149a4df2a9fc2db8a; + let bs = BCS::to_bytes
(&addr); + let (r, offset) = deserialize_address(&bs, 0); + assert!(addr == r, 1003); + assert!(offset == 16, 1004); + } + + public fun deserialize_16_bytes(input: &vector, offset: u64): (vector, u64) { + let content = get_n_bytes(input, offset, 16); + (content, offset + 16) + } + + spec deserialize_16_bytes { + pragma verify = false; + } + + public fun deserialize_bytes(input: &vector, offset: u64): (vector, u64) { + let (len, new_offset) = deserialize_len(input, offset); + let content = get_n_bytes(input, new_offset, len); + (content, new_offset + len) + } + + spec deserialize_bytes { + pragma verify = false; + } + + #[test] + public fun test_deserialize_bytes() { + let hello = b"hello world"; + let bs = BCS::to_bytes>(&hello); + let (r, _) = deserialize_bytes(&bs, 0); + assert!(hello == r, 1005); + } + + public fun deserialize_u128(input: &vector, offset: u64): (u128, u64) { + let u = get_n_bytes_as_u128(input, offset, 16); + (u, offset + 16) + } + + spec deserialize_u128 { + pragma verify = false; + } + + #[test] + fun test_deserialize_u128() { + let max_int128 = 170141183460469231731687303715884105727; + let u: u128 = max_int128; + let bs = BCS::to_bytes(&u); + let (r, offset) = deserialize_u128(&bs, 0); + assert!(u == r, 1006); + assert!(offset == 16, 1007); + } + + + public fun deserialize_u64(input: &vector, offset: u64): (u64, u64) { + let u = get_n_bytes_as_u128(input, offset, 8); + ((u as u64), offset + 8) + } + + spec deserialize_u64 { + pragma verify = false; + } + + #[test] + fun test_deserialize_u64() { + let u: u64 = 12811111111111; + let bs = BCS::to_bytes(&u); + let (r, offset) = deserialize_u64(&bs, 0); + assert!(u == r, 1008); + assert!(offset == 8, 1009); + } + + public fun deserialize_u32(input: &vector, offset: u64): (u64, u64) { + let u = get_n_bytes_as_u128(input, offset, 4); + ((u as u64), offset + 4) + } + + spec deserialize_u32 { + pragma verify = false; + } + + #[test] + fun test_deserialize_u32() { + let u: u64 = 1281111; + let bs = BCS::to_bytes(&u); + let (r, offset) = deserialize_u32(&bs, 0); + _ = r; + assert!(u == r, 1010); + assert!(offset == 4, 1011); + } + + public fun deserialize_u16(input: &vector, offset: u64): (u64, u64) { + let u = get_n_bytes_as_u128(input, offset, 2); + ((u as u64), offset + 2) + } + + spec deserialize_u16 { + pragma verify = false; + } + + public fun deserialize_u8(input: &vector, offset: u64): (u8, u64) { + let u = get_byte(input, offset); + (u, offset + 1) + } + + spec deserialize_u8 { + pragma verify = false; + } + + #[test] + fun test_deserialize_u8() { + let u: u8 = 128; + let bs = BCS::to_bytes(&u); + let (r, offset) = deserialize_u8(&bs, 0); + assert!(u == r, 1012); + assert!(offset == 1, 1013); + } + + public fun deserialize_option_tag(input: &vector, offset: u64): (bool, u64) { + deserialize_bool(input, offset) + } + + spec deserialize_option_tag { + pragma verify = false; + } + + public fun deserialize_len(input: &vector, offset: u64): (u64, u64) { + deserialize_uleb128_as_u32(input, offset) + } + + spec deserialize_len { + pragma verify = false; + } + + public fun deserialize_bool(input: &vector, offset: u64): (bool, u64) { + let b = get_byte(input, offset); + if (b == 1) { + return (true, offset + 1) + } else if (b == 0) { + return (false, offset + 1) + } else { + abort ERR_UNEXPECTED_BOOL_VALUE + } + } + + spec deserialize_bool { + pragma verify = false; + } + + #[test] + public fun test_deserialize_bool() { + let t = true; + let bs = BCS::to_bytes(&t); + let (d, _) = deserialize_bool(&bs, 0); + assert!(d, 1014); + + let f = false; + bs = BCS::to_bytes(&f); + (d, _) = deserialize_bool(&bs, 0); + assert!(!d, 1015); + } + + fun get_byte(input: &vector, offset: u64): u8 { + assert!(((offset + 1) <= Vector::length(input)) && (offset < offset + 1), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH)); + *Vector::borrow(input, offset) + } + + spec get_byte { + pragma verify = false; + } + + fun get_n_bytes(input: &vector, offset: u64, n: u64): vector { + assert!(((offset + n) <= Vector::length(input)) && (offset < offset + n), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH)); + let i = 0; + let content = Vector::empty(); + while (i < n) { + let b = *Vector::borrow(input, offset + i); + Vector::push_back(&mut content, b); + i = i + 1; + }; + content + } + + spec get_n_bytes { + pragma verify = false; + } + + fun get_n_bytes_as_u128(input: &vector, offset: u64, n: u64): u128 { + assert!(((offset + n) <= Vector::length(input)) && (offset < offset + n), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH)); + let number: u128 = 0; + let i = 0; + while (i < n) { + let byte = *Vector::borrow(input, offset + i); + let s = (i as u8) * 8; + number = number + ((byte as u128) << s); + i = i + 1; + }; + number + } + + spec get_n_bytes_as_u128 { + pragma verify = false; + } + + public fun deserialize_uleb128_as_u32(input: &vector, offset: u64): (u64, u64) { + let value: u64 = 0; + let shift = 0; + let new_offset = offset; + while (shift < 32) { + let x = get_byte(input, new_offset); + new_offset = new_offset + 1; + let digit: u8 = x & 0x7F; + value = value | (digit as u64) << shift; + if ((value < 0) || (value > INTEGER32_MAX_VALUE)) { + abort ERR_OVERFLOW_PARSING_ULEB128_ENCODED_UINT32 + }; + if (digit == x) { + if (shift > 0 && digit == 0) { + abort ERR_INVALID_ULEB128_NUMBER_UNEXPECTED_ZERO_DIGIT + }; + return (value, new_offset) + }; + shift = shift + 7 + }; + abort ERR_OVERFLOW_PARSING_ULEB128_ENCODED_UINT32 + } + + spec deserialize_uleb128_as_u32 { + pragma opaque; + pragma verify = false; + } + + #[test] + public fun test_deserialize_uleb128_as_u32() { + let i: u64 = 0x7F; + let bs = serialize_u32_as_uleb128(i); + let (len, _) = deserialize_uleb128_as_u32(&bs, 0); + assert!(len == i, 1016); + + let i2: u64 = 0x8F; + let bs2 = serialize_u32_as_uleb128(i2); + (len, _) = deserialize_uleb128_as_u32(&bs2, 0); + assert!(len == i2, 1017); + } + + + #[test] + public fun test_deserialize_uleb128_as_u32_max_int() { + let max_int: u64 = 2147483647; + + let bs = serialize_u32_as_uleb128(max_int); + let (len, _) = deserialize_uleb128_as_u32(&bs, 0); + assert!(len == max_int, 1018); + } + + #[test] + #[expected_failure(abort_code = 206, location=StarcoinFramework::BCS)] + public fun test_deserialize_uleb128_as_u32_exceeded_max_int() { + let max_int: u64 = 2147483647; + let exceeded_max_int: u64 = max_int + 1; + + let bs = serialize_u32_as_uleb128(exceeded_max_int); + let (_, _) = deserialize_uleb128_as_u32(&bs, 0); + } + + + fun serialize_u32_as_uleb128(value: u64): vector { + let output = Vector::empty(); + while ((value >> 7) != 0) { + Vector::push_back(&mut output, (((value & 0x7f) | 0x80) as u8)); + value = value >> 7; + }; + Vector::push_back(&mut output, (value as u8)); + output + } + + spec serialize_u32_as_uleb128 { + pragma verify = false; + } + + // skip Vector>> + public fun skip_option_bytes_vector(input: &vector, offset: u64): u64 { + let (len, new_offset) = deserialize_len(input, offset); + let i = 0; + while (i < len) { + new_offset = skip_option_bytes(input, new_offset); + i = i + 1; + }; + new_offset + } + + spec skip_option_bytes_vector { + pragma verify = false; + } + + #[test] + fun test_skip_option_bytes_vector(){ + let vec = Vector::empty>>(); + Vector::push_back(&mut vec, Option::some(x"01020304")); + Vector::push_back(&mut vec, Option::some(x"04030201")); + let vec = to_bytes(&vec); + //vec : [2, 1, 4, 1, 2, 3, 4, 1, 4, 4, 3, 2, 1] + assert!(skip_option_bytes_vector(&vec, 0) == 13,2000); + } + + // skip Option::Option> + public fun skip_option_bytes(input: &vector, offset: u64): u64 { + let (tag, new_offset) = deserialize_option_tag(input, offset); + if (!tag) { + new_offset + } else { + skip_bytes(input, new_offset) + } + } + + spec skip_option_bytes { + pragma verify = false; + } + + #[test] + fun test_skip_none_option_bytes(){ + let op = Option::none>(); + let op = to_bytes(&op); + let vec = to_bytes(&x"01020304"); + Vector::append(&mut op, vec); + // op : [0, 4, 1, 2, 3, 4] + assert!(skip_option_bytes(&op, 0) == 1,2007); + } + + #[test] + fun test_skip_some_option_bytes(){ + let op = Option::some(x"01020304"); + let op = to_bytes(&op); + let vec = to_bytes(&x"01020304"); + Vector::append(&mut op, vec); + // op : [1, 4, 1, 2, 3, 4, 4, 1, 2, 3, 4] + assert!(skip_option_bytes(&op, 0) == 6,2008); + } + + // skip vector> + public fun skip_bytes_vector(input: &vector, offset: u64): u64 { + let (len, new_offset) = deserialize_len(input, offset); + let i = 0; + while (i < len) { + new_offset = skip_bytes(input, new_offset); + i = i + 1; + }; + new_offset + } + + spec skip_bytes_vector { + pragma verify = false; + } + + // skip vector + public fun skip_bytes(input: &vector, offset: u64): u64 { + let (len, new_offset) = deserialize_len(input, offset); + new_offset + len + } + + spec skip_bytes { + pragma verify = false; + } + + #[test] + fun test_skip_bytes(){ + let vec = to_bytes(&x"01020304"); + let u_64 = to_bytes(&10); + Vector::append(&mut vec, u_64); + // vec : [4, 1, 2, 3, 4, 10, 0, 0, 0, 0, 0, 0, 0] + assert!(skip_bytes(&vec, 0) == 5,2001); + } + + // skip some bytes + public fun skip_n_bytes(input: &vector, offset: u64, n:u64): u64 { + can_skip(input, offset, n ); + offset + n + } + + spec skip_n_bytes { + pragma verify = false; + } + + #[test] + fun test_skip_n_bytes(){ + let vec = to_bytes(&x"01020304"); + let u_64 = to_bytes(&10); + Vector::append(&mut vec, u_64); + // vec : [4, 1, 2, 3, 4, 10, 0, 0, 0, 0, 0, 0, 0] + assert!(skip_n_bytes(&vec, 0, 1) == 1,2002); + } + + // skip vector + public fun skip_u64_vector(input: &vector, offset: u64): u64 { + let (len, new_offset) = deserialize_len(input, offset); + can_skip(input, new_offset, len * 8); + new_offset + len * 8 + } + + spec skip_u64_vector { + pragma verify = false; + } + + #[test] + fun test_skip_u64_vector(){ + let vec = Vector::empty(); + Vector::push_back(&mut vec, 11111); + Vector::push_back(&mut vec, 22222); + let u_64 = to_bytes(&10); + let vec = to_bytes(&vec); + Vector::append(&mut vec, u_64); + // vec : [2, 103, 43, 0, 0, 0, 0, 0, 0, 206, 86, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0] + assert!(skip_u64_vector(&vec, 0) == 17,2004); + } + + // skip vector + public fun skip_u128_vector(input: &vector, offset: u64): u64 { + let (len, new_offset) = deserialize_len(input, offset); + can_skip(input, new_offset, len * 16); + new_offset + len * 16 + } + + spec skip_u128_vector { + pragma verify = false; + } + + #[test] + fun test_skip_u128_vector(){ + let vec = Vector::empty(); + Vector::push_back(&mut vec, 11111); + Vector::push_back(&mut vec, 22222); + let u_64 = to_bytes(&10); + let vec = to_bytes(&vec); + Vector::append(&mut vec, u_64); + // vec : [2, 103, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0] + assert!(skip_u128_vector(&vec, 0) == 33,2003); + } + + // skip u256 + public fun skip_u256(input: &vector, offset: u64): u64 { + can_skip(input, offset, 32 ); + offset + 32 + } + + spec skip_u256 { + pragma verify = false; + } + + // skip u128 + public fun skip_u128(input: &vector, offset: u64): u64 { + can_skip(input, offset, 16 ); + offset + 16 + } + + spec skip_u128 { + pragma verify = false; + } + + #[test] + fun test_skip_u128(){ + let u_128:u128 = 100; + let u_128 = to_bytes(&u_128); + let vec = to_bytes(&x"01020304"); + Vector::append(&mut u_128, vec); + // u_128 : [100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 2, 3, 4] + assert!(skip_u128(&u_128, 0) == 16,2005); + } + + // skip u64 + public fun skip_u64(input: &vector, offset: u64): u64 { + can_skip(input, offset, 8 ); + offset + 8 + } + + spec skip_u64 { + pragma verify = false; + } + + #[test] + fun test_skip_u64(){ + let u_64:u64 = 100; + let u_64 = to_bytes(&u_64); + let vec = to_bytes(&x"01020304"); + Vector::append(&mut u_64, vec); + // u_64 : [100, 0, 0, 0, 0, 0, 0, 0, 4, 1, 2, 3, 4] + assert!(skip_u64(&u_64, 0) == 8,2006); + } + + // skip u32 + public fun skip_u32(input: &vector, offset: u64): u64 { + can_skip(input, offset, 4 ); + offset + 4 + } + + spec skip_u32 { + pragma verify = false; + } + + // skip u16 + public fun skip_u16(input: &vector, offset: u64): u64 { + can_skip(input, offset, 2 ); + offset + 2 + } + + spec skip_u16 { + pragma verify = false; + } + + // skip address + public fun skip_address(input: &vector, offset: u64): u64 { + skip_n_bytes(input, offset, 16) + } + + spec skip_address { + pragma verify = false; + } + + #[test] + fun test_address(){ + let addr:address = @0x1; + let addr = to_bytes(&addr); + let vec = to_bytes(&x"01020304"); + Vector::append(&mut addr, vec); + // addr : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, 2, 3, 4] + assert!(skip_address(&addr, 0) == 16,2006); + } + + // skip bool + public fun skip_bool(input: &vector, offset: u64): u64{ + can_skip(input, offset, 1); + offset + 1 + } + + spec skip_bool { + pragma verify = false; + } + + fun can_skip(input: &vector, offset: u64, n: u64){ + assert!(((offset + n) <= Vector::length(input)) && (offset < offset + n), Errors::invalid_state(ERR_INPUT_NOT_LARGE_ENOUGH)); + } + + spec can_skip { + pragma verify = false; + } +} +} diff --git a/release/v13/sources/BitOperators.move b/release/v13/sources/BitOperators.move new file mode 100644 index 00000000..9f8f0138 --- /dev/null +++ b/release/v13/sources/BitOperators.move @@ -0,0 +1,39 @@ +address StarcoinFramework { +/// Functions for bit operations. +module BitOperators { + + spec module { + pragma verify = false; + } + + /// bit and: x & y + public fun and(x: u64, y: u64): u64 { + (x & y as u64) + } + + /// bit or: x | y + public fun or(x: u64, y: u64): u64 { + (x | y as u64) + } + + /// bit xor: x ^ y + public fun xor(x: u64, y: u64): u64 { + (x ^ y as u64) + } + + /// bit not: !x + public fun not(x: u64): u64 { + (x ^ 18446744073709551615u64 as u64) + } + + /// left shift n bits. + public fun lshift(x: u64, n: u8): u64 { + (x << n as u64) + } + + /// right shift n bits. + public fun rshift(x: u64, n: u8): u64 { + (x >> n as u64) + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Block.move b/release/v13/sources/Block.move new file mode 100644 index 00000000..f8fb1528 --- /dev/null +++ b/release/v13/sources/Block.move @@ -0,0 +1,534 @@ +address StarcoinFramework { +/// Block module provide metadata for generated blocks. +module Block { + use StarcoinFramework::Event; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + use StarcoinFramework::Option; + use StarcoinFramework::Ring; + use StarcoinFramework::BCS; + use StarcoinFramework::Hash; + + spec module { + pragma verify; + pragma aborts_if_is_strict = true; + } + + /// Block metadata struct. + struct BlockMetadata has key { + /// number of the current block + number: u64, + /// Hash of the parent block. + parent_hash: vector, + /// Author of the current block. + author: address, + /// number of uncles. + uncles: u64, + /// Handle of events when new blocks are emitted + new_block_events: Event::EventHandle, + } + + /// Events emitted when new block generated. + struct NewBlockEvent has drop, store { + number: u64, + author: address, + timestamp: u64, + uncles: u64, + } + + /// Block metadata struct. + // parents_hash is for FLexiDag block + struct BlockMetadataV2 has key { + /// number of the current block + number: u64, + /// Hash of the parent block. + parent_hash: vector, + /// Author of the current block. + author: address, + /// number of uncles. + uncles: u64, + /// An Array of the parents hash for a Dag block. + parents_hash: vector, + /// Handle of events when new blocks are emitted + new_block_events: Event::EventHandle, + } + + /// Events emitted when new block generated. + // parents_hash is for FLexiDag block + struct NewBlockEventV2 has drop, store { + number: u64, + author: address, + timestamp: u64, + uncles: u64, + parents_hash: vector, + } + + // + struct Checkpoint has copy,drop,store{ + //number of the block + block_number: u64, + //Hash of the block + block_hash: vector, + //State root of the block + state_root: Option::Option>, + } + + // + struct Checkpoints has key,store{ + //all checkpoints + checkpoints : Ring::Ring, + index : u64, + last_number : u64, + } + + const EBLOCK_NUMBER_MISMATCH : u64 = 17; + const ERROR_NO_HAVE_CHECKPOINT: u64 = 18; + const ERROR_NOT_BLOCK_HEADER : u64 = 19; + const ERROR_INTERVAL_TOO_LITTLE: u64 = 20; + + const CHECKPOINT_LENGTH : u64 = 60; + const BLOCK_HEADER_LENGTH : u64 = 247; + const BLOCK_INTERVAL_NUMBER : u64 = 5; + + /// This can only be invoked by the GENESIS_ACCOUNT at genesis + public fun initialize(account: &signer, parent_hash: vector) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + + move_to( + account, + BlockMetadata { + number: 0, + parent_hash, + author: CoreAddresses::GENESIS_ADDRESS(), + uncles: 0, + new_block_events: Event::new_event_handle(account), + }); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists(Signer::address_of(account)); + } + + public fun initialize_blockmetadata_v2(account: &signer) acquires BlockMetadata { + CoreAddresses::assert_genesis_address(account); + + let block_meta_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + + // create new resource base on current block metadata + move_to( + account, + BlockMetadataV2 { + number: block_meta_ref.number, + parent_hash: block_meta_ref.parent_hash, + author: block_meta_ref.author, + uncles: block_meta_ref.uncles, + parents_hash: Vector::empty(), + new_block_events: Event::new_event_handle(account), + }); + } + + spec initialize_blockmetadata_v2 { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists(Signer::address_of(account)); + + ensures exists(Signer::address_of(account)); + } + + /// Get the current block number + public fun get_current_block_number(): u64 acquires BlockMetadataV2 { + borrow_global(CoreAddresses::GENESIS_ADDRESS()).number + } + + spec get_current_block_number { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get the hash of the parent block. + public fun get_parent_hash(): vector acquires BlockMetadataV2 { + *&borrow_global(CoreAddresses::GENESIS_ADDRESS()).parent_hash + } + + spec get_parent_hash { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Gets the address of the author of the current block + public fun get_current_author(): address acquires BlockMetadataV2 { + borrow_global(CoreAddresses::GENESIS_ADDRESS()).author + } + + spec get_current_author { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + public fun get_parents_hash(): vector acquires BlockMetadataV2 { + *&borrow_global(CoreAddresses::GENESIS_ADDRESS()).parents_hash + } + + spec get_parents_hash { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Call at block prologue + public fun process_block_metadata(account: &signer, + parent_hash: vector, + author: address, + timestamp: u64, + uncles:u64, + number:u64) acquires BlockMetadataV2{ + Self::process_block_metadata_v2(account, parent_hash, author, timestamp, uncles, number, Vector::empty()) + + } + + spec process_block_metadata { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if number != global(CoreAddresses::GENESIS_ADDRESS()).number + 1; + } + + /// Call at block prologue for flexidag + public fun process_block_metadata_v2(account: &signer, + parent_hash: vector, + author: address, + timestamp: u64, + uncles:u64, + number:u64, + parents_hash: vector) acquires BlockMetadataV2 { + CoreAddresses::assert_genesis_address(account); + + let block_metadata_ref = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + assert!(number == (block_metadata_ref.number + 1), Errors::invalid_argument(EBLOCK_NUMBER_MISMATCH)); + block_metadata_ref.number = number; + block_metadata_ref.author= author; + block_metadata_ref.parent_hash = parent_hash; + block_metadata_ref.uncles = uncles; + block_metadata_ref.parents_hash = parents_hash; + + Event::emit_event( + &mut block_metadata_ref.new_block_events, + NewBlockEventV2 { + number, + author, + timestamp, + uncles, + parents_hash, + } + ); + } + + spec process_block_metadata_v2 { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if number != global(CoreAddresses::GENESIS_ADDRESS()).number + 1; + } + + spec schema AbortsIfBlockMetadataNotExist { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + public fun checkpoints_init(account: &signer){ + CoreAddresses::assert_genesis_address(account); + + let checkpoints = Ring::create_with_capacity(CHECKPOINT_LENGTH); + move_to( + account, + Checkpoints { + checkpoints, + index : 0, + last_number : 0, + }); + } + + spec checkpoints_init { + pragma verify = false; + } + + public entry fun checkpoint_entry(_account: signer) acquires BlockMetadataV2, Checkpoints { + checkpoint(); + } + + spec checkpoint_entry { + pragma verify = false; + } + + public fun checkpoint() acquires BlockMetadataV2, Checkpoints{ + let parent_block_number = get_current_block_number() - 1; + let parent_block_hash = get_parent_hash(); + + let checkpoints = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + base_checkpoint(checkpoints, parent_block_number, parent_block_hash); + + } + + spec checkpoint { + pragma verify = false; + } + + fun base_checkpoint(checkpoints: &mut Checkpoints, parent_block_number: u64, parent_block_hash:vector){ + assert!(checkpoints.last_number + BLOCK_INTERVAL_NUMBER <= parent_block_number || checkpoints.last_number == 0, Errors::invalid_argument(ERROR_INTERVAL_TOO_LITTLE)); + + checkpoints.index = checkpoints.index + 1; + checkpoints.last_number = parent_block_number; + let op_checkpoint = Ring::push(&mut checkpoints.checkpoints, Checkpoint { + block_number: parent_block_number, + block_hash: parent_block_hash, + state_root: Option::none>(), + } ); + if(Option::is_some(&op_checkpoint)){ + Option::destroy_some(op_checkpoint); + }else{ + Option::destroy_none(op_checkpoint); + } + } + + spec base_checkpoint { + pragma verify = false; + } + + public fun latest_state_root():(u64,vector) acquires Checkpoints{ + let checkpoints = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + base_latest_state_root(checkpoints) + } + + spec latest_state_root { + pragma verify = false; + } + + fun base_latest_state_root(checkpoints: &Checkpoints):(u64,vector){ + let len = Ring::capacity(&checkpoints.checkpoints); + let j = if(checkpoints.index < len - 1){ + checkpoints.index + }else{ + len + }; + let i = checkpoints.index; + while( j > 0){ + let op_checkpoint = Ring::borrow(&checkpoints.checkpoints, i - 1 ); + if( Option::is_some(op_checkpoint) && Option::is_some(&Option::borrow(op_checkpoint).state_root) ) { + let state_root = Option::borrow(&Option::borrow(op_checkpoint).state_root); + return (Option::borrow(op_checkpoint).block_number, *state_root) + }; + j = j - 1; + i = i - 1; + }; + + abort Errors::invalid_state(ERROR_NO_HAVE_CHECKPOINT) + } + + spec base_latest_state_root { + pragma verify = false; + } + + public entry fun update_state_root_entry(_account: signer , header: vector) + acquires Checkpoints { + update_state_root(header); + } + + spec update_state_root_entry { + pragma verify = false; + } + + public fun update_state_root(header: vector) acquires Checkpoints { + let checkpoints = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + base_update_state_root(checkpoints, header); + } + + spec update_state_root { + pragma verify = false; + } + + fun base_update_state_root(checkpoints: &mut Checkpoints, header: vector){ + let prefix = Hash::sha3_256(b"STARCOIN::BlockHeader"); + + //parent_hash + let new_offset = BCS::skip_bytes(&header,0); + //timestamp + let new_offset = BCS::skip_u64(&header,new_offset); + //number + let (number,new_offset) = BCS::deserialize_u64(&header,new_offset); + //author + new_offset = BCS::skip_address(&header,new_offset); + //author_auth_key + new_offset = BCS::skip_option_bytes(&header,new_offset); + //txn_accumulator_root + new_offset = BCS::skip_bytes(&header,new_offset); + //block_accumulator_root + new_offset = BCS::skip_bytes(&header,new_offset); + //state_root + let (state_root,_new_offset) = BCS::deserialize_bytes(&header,new_offset); + + Vector::append(&mut prefix,header); + let block_hash = Hash::sha3_256(prefix); + + let len = Ring::capacity(&checkpoints.checkpoints); + let j = if(checkpoints.index < len - 1){ + checkpoints.index + }else{ + len + }; + let i = checkpoints.index; + while( j > 0){ + let op_checkpoint = Ring::borrow_mut(&mut checkpoints.checkpoints, i - 1); + + if( Option::is_some(op_checkpoint) && &Option::borrow(op_checkpoint).block_hash == &block_hash && Option::borrow(op_checkpoint).block_number == number) { + + let op_state_root = &mut Option::borrow_mut(op_checkpoint).state_root; + if(Option::is_some(op_state_root)){ + Option::swap(op_state_root, state_root); + }else{ + Option::fill(op_state_root, state_root); + }; + return + }; + j = j - 1; + i = i - 1; + }; + + abort Errors::invalid_state(ERROR_NO_HAVE_CHECKPOINT) + } + + spec base_update_state_root { + pragma verify = false; + } + + #[test] + fun test_header(){ + // Block header Unit test + // Use Main Genesis BlockHeader in Rust + // BlockHeader { + // id: Some(HashValue(0x80848150abee7e9a3bfe9542a019eb0b8b01f124b63b011f9c338fdb935c417d)), + // parent_hash: HashValue(0xb82a2c11f2df62bf87c2933d0281e5fe47ea94d5f0049eec1485b682df29529a), + // timestamp: 1621311100863, + // number: 0, + // author: 0x00000000000000000000000000000001, + // author_auth_key: None, + // txn_accumulator_root: HashValue(0x43609d52fdf8e4a253c62dfe127d33c77e1fb4afdefb306d46ec42e21b9103ae), + // block_accumulator_root: HashValue(0x414343554d554c41544f525f504c414345484f4c4445525f4841534800000000), + // state_root: HashValue(0x61125a3ab755b993d72accfea741f8537104db8e022098154f3a66d5c23e828d), + // gas_used: 0, + // difficulty: 11660343, + // body_hash: HashValue(0x7564db97ee270a6c1f2f73fbf517dc0777a6119b7460b7eae2890d1ce504537b), + // chain_id: ChainId { id: 1 }, + // nonce: 0, + // extra: BlockHeaderExtra([0, 0, 0, 0]) + // } + // Blockheader BCS : 20b82a2c11f2df62bf87c2933d0281e5fe47ea94d5f0049eec1485b682df29529abf17ac7d79010000000000000000000000000000000000000000000000000001002043609d52fdf8e4a253c62dfe127d33c77e1fb4afdefb306d46ec42e21b9103ae20414343554d554c41544f525f504c414345484f4c4445525f48415348000000002061125a3ab755b993d72accfea741f8537104db8e022098154f3a66d5c23e828d00000000000000000000000000000000000000000000000000000000000000000000000000b1ec37207564db97ee270a6c1f2f73fbf517dc0777a6119b7460b7eae2890d1ce504537b010000000000000000 + + let prefix = Hash::sha3_256(b"STARCOIN::BlockHeader"); + let header = x"20b82a2c11f2df62bf87c2933d0281e5fe47ea94d5f0049eec1485b682df29529abf17ac7d79010000000000000000000000000000000000000000000000000001002043609d52fdf8e4a253c62dfe127d33c77e1fb4afdefb306d46ec42e21b9103ae20414343554d554c41544f525f504c414345484f4c4445525f48415348000000002061125a3ab755b993d72accfea741f8537104db8e022098154f3a66d5c23e828d00000000000000000000000000000000000000000000000000000000000000000000000000b1ec37207564db97ee270a6c1f2f73fbf517dc0777a6119b7460b7eae2890d1ce504537b010000000000000000"; + let (_parent_hash,new_offset) = BCS::deserialize_bytes(&header,0); + let (_timestamp,new_offset) = BCS::deserialize_u64(&header,new_offset); + let (number,new_offset) = BCS::deserialize_u64(&header,new_offset); + let (_author,new_offset) = BCS::deserialize_address(&header,new_offset); + let (_author_auth_key,new_offset) = BCS::deserialize_option_bytes(&header,new_offset); + let (_txn_accumulator_root,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (_block_accumulator_root,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (state_root,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (_gas_used,new_offset) = BCS::deserialize_u64(&header,new_offset); + let (_difficultyfirst,new_offset) = BCS::deserialize_u128(&header,new_offset); + let (_difficultylast,new_offset) = BCS::deserialize_u128(&header,new_offset); + let (_body_hash,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (_chain_id,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_nonce,new_offset) = BCS::deserialize_u32(&header,new_offset); + let (_extra1,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_extra2,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_extra3,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_extra4,_new_offset) = BCS::deserialize_u8(&header,new_offset); + + Vector::append(&mut prefix,header); + let block_hash = Hash::sha3_256(prefix); + assert!(block_hash == x"80848150abee7e9a3bfe9542a019eb0b8b01f124b63b011f9c338fdb935c417d" ,1001); + assert!(number == 0,1002); + assert!(state_root == x"61125a3ab755b993d72accfea741f8537104db8e022098154f3a66d5c23e828d",1003); + } + + #[test] + fun test_header2(){ + // Block header Unit test + // Use BlockHeader in integration test + //"number":"2", + //"block_hash":"0x9433bb7b56333dfc33e012f3b22b67277a3026448eb5043747d59284f648343d" + //"parent_hash":"0x9be97e678afa8a0a4cf9ca612be6f64810a6f7d5f8b4b4ddf5e4971ef4b5eb48" + //"state_root":"0xd2df4c8c579f9e05b0adf14b53785379fb245465d703834eb19fba74d9114a9a" + //"header":"0x209be97e678afa8a0a4cf9ca612be6f64810a6f7d5f8b4b4ddf5e4971ef4b5eb4820aa26050000000002000000000000000000000000000000000000000000000200205c79e9493845327132ab3011c7c6c9d8bddcfde5553abb90cf5ef7fdfb39a4aa20c4d8e6cdb52520794dad5241f51a4eed46a5e5264dd148032cc3bdb8e3bdbe7a20d2df4c8c579f9e05b0adf14b53785379fb245465d703834eb19fba74d9114a9a0000000000000000000000000000000000000000000000000000000000000000000000000000000020c01e0329de6d899348a8ef4bd51db56175b3fa0988e57c3dcec8eaf13a164d97fe0000000000000000" + + let prefix = Hash::sha3_256(b"STARCOIN::BlockHeader"); + let header = x"209be97e678afa8a0a4cf9ca612be6f64810a6f7d5f8b4b4ddf5e4971ef4b5eb4820aa26050000000002000000000000000000000000000000000000000000000200205c79e9493845327132ab3011c7c6c9d8bddcfde5553abb90cf5ef7fdfb39a4aa20c4d8e6cdb52520794dad5241f51a4eed46a5e5264dd148032cc3bdb8e3bdbe7a20d2df4c8c579f9e05b0adf14b53785379fb245465d703834eb19fba74d9114a9a0000000000000000000000000000000000000000000000000000000000000000000000000000000020c01e0329de6d899348a8ef4bd51db56175b3fa0988e57c3dcec8eaf13a164d97fe0000000000000000"; + let (_parent_hash,new_offset) = BCS::deserialize_bytes(&header,0); + let (_timestamp,new_offset) = BCS::deserialize_u64(&header,new_offset); + let (number,new_offset) = BCS::deserialize_u64(&header,new_offset); + let (_author,new_offset) = BCS::deserialize_address(&header,new_offset); + let (_author_auth_key,new_offset) = BCS::deserialize_option_bytes(&header,new_offset); + let (_txn_accumulator_root,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (_block_accumulator_root,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (state_root,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (_gas_used,new_offset) = BCS::deserialize_u64(&header,new_offset); + let (_difficultyfirst,new_offset) = BCS::deserialize_u128(&header,new_offset); + let (_difficultylast,new_offset) = BCS::deserialize_u128(&header,new_offset); + let (_body_hash,new_offset) = BCS::deserialize_bytes(&header,new_offset); + let (_chain_id,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_nonce,new_offset) = BCS::deserialize_u32(&header,new_offset); + let (_extra1,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_extra2,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_extra3,new_offset) = BCS::deserialize_u8(&header,new_offset); + let (_extra4,_new_offset) = BCS::deserialize_u8(&header,new_offset); + + Vector::append(&mut prefix,header); + let block_hash = Hash::sha3_256(prefix); + assert!(block_hash == x"9433bb7b56333dfc33e012f3b22b67277a3026448eb5043747d59284f648343d" ,1001); + assert!(number == 2,1002); + assert!(state_root == x"d2df4c8c579f9e05b0adf14b53785379fb245465d703834eb19fba74d9114a9a",1003); + } + + #[test] + fun test_checkpoint(){ + let checkpoints = Checkpoints { + checkpoints : Ring::create_with_capacity(3), + index : 0, + last_number : 0 + }; + + base_checkpoint(&mut checkpoints, 0, x"80848150abee7e9a3bfe9542a019eb0b8b01f124b63b011f9c338fdb935c417d"); + + let Checkpoints{ + checkpoints: ring, + index : index, + last_number: last_number + } = checkpoints; + assert!( index == 1 && last_number == 0 , 10020); + Ring::destroy(ring); + } + + #[test] + fun test_latest_state_root(){ + let header = x"20b82a2c11f2df62bf87c2933d0281e5fe47ea94d5f0049eec1485b682df29529abf17ac7d79010000000000000000000000000000000000000000000000000001002043609d52fdf8e4a253c62dfe127d33c77e1fb4afdefb306d46ec42e21b9103ae20414343554d554c41544f525f504c414345484f4c4445525f48415348000000002061125a3ab755b993d72accfea741f8537104db8e022098154f3a66d5c23e828d00000000000000000000000000000000000000000000000000000000000000000000000000b1ec37207564db97ee270a6c1f2f73fbf517dc0777a6119b7460b7eae2890d1ce504537b010000000000000000"; + + let checkpoints = Checkpoints { + checkpoints : Ring::create_with_capacity(3), + index : 0, + last_number : 0 + }; + + base_checkpoint(&mut checkpoints, 0, x"80848150abee7e9a3bfe9542a019eb0b8b01f124b63b011f9c338fdb935c417d"); + + base_update_state_root(&mut checkpoints, copy header); + + let (number , state_root ) = base_latest_state_root(&checkpoints); + let Checkpoints{ + checkpoints: ring, + index : index, + last_number: last_number + } = checkpoints; + assert!( index == 1 && last_number == 0 , 10020); + assert!( number == 0 && state_root == x"61125a3ab755b993d72accfea741f8537104db8e022098154f3a66d5c23e828d" , 10020); + Ring::destroy(ring); + } + +} +} \ No newline at end of file diff --git a/release/v13/sources/BlockReward.move b/release/v13/sources/BlockReward.move new file mode 100644 index 00000000..c5bd5078 --- /dev/null +++ b/release/v13/sources/BlockReward.move @@ -0,0 +1,192 @@ +address StarcoinFramework { +/// The module provide block rewarding calculation logic. +module BlockReward { + use StarcoinFramework::Timestamp; + use StarcoinFramework::Token; + use StarcoinFramework::STC::{STC}; + use StarcoinFramework::Vector; + use StarcoinFramework::Account; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::RewardConfig; + use StarcoinFramework::Config; + use StarcoinFramework::Event; + use StarcoinFramework::Treasury; + use StarcoinFramework::TreasuryWithdrawDaoProposal; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + /// Queue of rewards distributed to miners. + struct RewardQueue has key { + /// How many block rewards has been handled. + reward_number: u64, + /// informations about the reward distribution. + infos: vector, + /// event handle used to emit block reward event. + reward_events: Event::EventHandle, + } + + /// Reward info of miners. + struct RewardInfo has store { + /// number of the block miner minted. + number: u64, + /// how many stc rewards. + reward: u128, + /// miner who mint the block. + miner: address, + /// store the gas fee that users consumed. + gas_fees: Token::Token, + } + + /// block reward event + struct BlockRewardEvent has drop, store { + /// block number + block_number: u64, + /// STC reward. + block_reward: u128, + /// gas fees in STC. + gas_fees: u128, + /// block miner + miner: address, + } + + const EAUTHOR_AUTH_KEY_IS_EMPTY: u64 = 101; + const ECURRENT_NUMBER_IS_WRONG: u64 = 102; + const EREWARD_NUMBER_IS_WRONG: u64 = 103; + const EMINER_EXIST: u64 = 104; + const EAUTHOR_ADDRESS_AND_AUTH_KEY_MISMATCH: u64 = 105; + + /// Initialize the module, should be called in genesis. + public fun initialize(account: &signer, reward_delay: u64) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + + RewardConfig::initialize(account, reward_delay); + move_to(account, RewardQueue { + reward_number: 0, + infos: Vector::empty(), + reward_events: Event::new_event_handle(account), + }); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + include Config::PublishNewConfigAbortsIf; + include Config::PublishNewConfigEnsures; + aborts_if exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Process the given block rewards. + public fun process_block_reward(account: &signer, current_number: u64, current_reward: u128, + current_author: address, _auth_key_vec: vector, + previous_block_gas_fees: Token::Token) acquires RewardQueue { + CoreAddresses::assert_genesis_address(account); + if (current_number == 0) { + Token::destroy_zero(previous_block_gas_fees); + return + }; + + let rewards = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + let len = Vector::length(&rewards.infos); + assert!((current_number == (rewards.reward_number + len + 1)), Errors::invalid_argument(ECURRENT_NUMBER_IS_WRONG)); + + // distribute gas fee to last block reward info. + // if not last block reward info, the passed in gas fee must be zero. + if (len == 0) { + Token::destroy_zero(previous_block_gas_fees); + } else { + let reward_info = Vector::borrow_mut(&mut rewards.infos, len - 1); + assert!(current_number == reward_info.number + 1, Errors::invalid_argument(ECURRENT_NUMBER_IS_WRONG)); + Token::deposit(&mut reward_info.gas_fees, previous_block_gas_fees); + }; + + let reward_delay = RewardConfig::reward_delay(); + if (len >= reward_delay) {//pay and remove + let i = len; + while (i > 0 && i >= reward_delay) { + let RewardInfo { number: reward_block_number, reward: block_reward, gas_fees, miner } = Vector::remove(&mut rewards.infos, 0); + + let gas_fee_value = Token::value(&gas_fees); + let total_reward = gas_fees; + // add block reward to total. + if (block_reward > 0) { + // if no STC in Treasury, BlockReward will been 0. + let treasury_balance = Treasury::balance(); + if (treasury_balance < block_reward) { + block_reward = treasury_balance; + }; + if (block_reward > 0) { + let reward = TreasuryWithdrawDaoProposal::withdraw_for_block_reward(account, block_reward); + Token::deposit(&mut total_reward, reward); + }; + }; + // distribute total. + if (Token::value(&total_reward) > 0) { + Account::deposit(miner, total_reward); + } else { + Token::destroy_zero(total_reward); + }; + // emit reward event. + Event::emit_event( + &mut rewards.reward_events, + BlockRewardEvent { + block_number: reward_block_number, + block_reward: block_reward, + gas_fees: gas_fee_value, + miner, + } + ); + + rewards.reward_number = rewards.reward_number + 1; + i = i - 1; + } + }; + + if (!Account::exists_at(current_author)) { + Account::create_account_with_address(current_author); + }; + let current_info = RewardInfo { + number: current_number, + reward: current_reward, + miner: current_author, + gas_fees: Token::zero(), + }; + Vector::push_back(&mut rewards.infos, current_info); + + } + + spec process_block_reward { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + // abort if current block is genesis, and previous block gas fees != 0 + aborts_if current_number == 0 && Token::value(previous_block_gas_fees) != 0; + + aborts_if current_number > 0 && !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if current_number > 0 && (global(CoreAddresses::GENESIS_ADDRESS()).reward_number + Vector::length(global(CoreAddresses::GENESIS_ADDRESS()).infos) + 1) != current_number; + aborts_if current_number > 0 && !exists>(CoreAddresses::GENESIS_ADDRESS()); + + + let reward_info_length = Vector::length(global(CoreAddresses::GENESIS_ADDRESS()).infos); + + // abort if no previous block but has gas fees != 0. + aborts_if current_number > 0 && reward_info_length == 0 && Token::value(previous_block_gas_fees) != 0; + // abort if previous block number != current_block_number - 1. + aborts_if current_number > 0 && reward_info_length != 0 && Vector::borrow(global(CoreAddresses::GENESIS_ADDRESS()).infos, reward_info_length - 1).number != current_number - 1; + + aborts_if current_number > 0 && Vector::length(global(CoreAddresses::GENESIS_ADDRESS()).infos) >= global>(CoreAddresses::GENESIS_ADDRESS()).payload.reward_delay + && (global(CoreAddresses::GENESIS_ADDRESS()).reward_number + 1) != Vector::borrow(global(CoreAddresses::GENESIS_ADDRESS()).infos, 0).number; + + aborts_if current_number > 0 && Vector::length(global(CoreAddresses::GENESIS_ADDRESS()).infos) >= global>(CoreAddresses::GENESIS_ADDRESS()).payload.reward_delay + && (global(CoreAddresses::GENESIS_ADDRESS()).reward_number + 1) > max_u64(); + + aborts_if current_number > 0 && !Account::exists_at(current_author) ; + + pragma verify = false; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/ChainId.move b/release/v13/sources/ChainId.move new file mode 100644 index 00000000..927777a8 --- /dev/null +++ b/release/v13/sources/ChainId.move @@ -0,0 +1,94 @@ +address StarcoinFramework { +/// The module provides chain id information. +module ChainId { + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// chain id data structure. + struct ChainId has key { + /// real id. + id: u8 + } + + const MAIN_CHAIN_ID: u8 = 1; + const BARNARD_CHAIN_ID: u8 = 251; + const PROXIMA_CHAIN_ID: u8 = 252; + const HALLEY_CHAIN_ID: u8 = 253; + const DEV_CHAIN_ID: u8 = 254; + const TEST_CHAIN_ID: u8 = 255; + + /// Publish the chain ID under the genesis account + public fun initialize(account: &signer, id: u8) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + move_to(account, ChainId { id }); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists(Signer::address_of(account)); + ensures exists(Signer::address_of(account)); + } + + /// Return the chain ID of this chain + public fun get(): u8 acquires ChainId { + borrow_global(CoreAddresses::GENESIS_ADDRESS()).id + } + + public fun is_dev(): bool acquires ChainId { + get() == DEV_CHAIN_ID + } + public fun is_test(): bool acquires ChainId { + get() == TEST_CHAIN_ID + } + public fun is_halley(): bool acquires ChainId { + get() == HALLEY_CHAIN_ID + } + public fun is_proxima(): bool acquires ChainId { + get() == PROXIMA_CHAIN_ID + } + public fun is_barnard(): bool acquires ChainId { + get() == BARNARD_CHAIN_ID + } + public fun is_main(): bool acquires ChainId { + get() == MAIN_CHAIN_ID + } + + spec is_dev { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } + spec is_test { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } + spec is_halley { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } + spec is_proxima { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } + spec is_barnard { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } + spec is_main { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } + + spec get { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures exists(CoreAddresses::GENESIS_ADDRESS()); + } +} +} diff --git a/release/v13/sources/Collection.move b/release/v13/sources/Collection.move new file mode 100644 index 00000000..d2ffcbd0 --- /dev/null +++ b/release/v13/sources/Collection.move @@ -0,0 +1,118 @@ +address StarcoinFramework { +/// Deprecated since @v3 please use Collection2 +/// Provide a account based vector for save resource. +module Collection { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + use StarcoinFramework::Option::{Self, Option}; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = false; + } + + /// Collection in memory, can not drop & store. + struct Collection{ + items:vector, + /// the owner of Collection. + owner: address, + } + + /// Collection in global store. + struct CollectionStore has key { + /// items in the CollectionStore. + /// use Option at here is for temporary take away from store to construct Collection. + items: Option>, + } + + const EDEPRECATED_FUNCTION: u64 = 19; + + const ECOLLECTION_NOT_EXIST: u64 = 101; + /// The operator require the collection owner. + const ECOLLECTION_NOT_OWNER: u64 = 102; + + /// Acquire an immutable reference to the `i`th element of the collection `c`. + /// Aborts if `i` is out of bounds. + public fun borrow(c: &Collection, i: u64): &T{ + Vector::borrow(&c.items, i) + } + + /// Pop an element from the end of vector `v`. + /// Aborts if `v` is empty. + public fun pop_back(account: &signer, c: &mut Collection): T { + assert!(Signer::address_of(account) == c.owner, Errors::requires_address(ECOLLECTION_NOT_OWNER)); + Vector::pop_back(&mut c.items) + } + + /// check the Collection exists in `addr` + fun exists_at(addr: address): bool{ + exists>(addr) + } + + spec exists_at {aborts_if false;} + + /// Deprecated since @v3 + /// Put items to account's Collection last position. + public fun put(_account: &signer, _item: T) { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + spec put {aborts_if false;} + + /// Take last item from account's Collection of T. + public fun take(account: &signer): T acquires CollectionStore{ + let addr = Signer::address_of(account); + let c = borrow_collection(addr); + let item = pop_back(account, &mut c); + return_collection(c); + item + } + + spec take { + aborts_if false; + } + + /// Borrow collection of T from `addr` + public fun borrow_collection(addr: address): Collection acquires CollectionStore{ + assert!(exists_at(addr), Errors::invalid_state(ECOLLECTION_NOT_EXIST)); + let c = borrow_global_mut>(addr); + let items = Option::extract(&mut c.items); + Collection{ + items, + owner: addr + } + } + + spec borrow_collection { + aborts_if false; + } + + /// Return the Collection of T + public fun return_collection(c: Collection) acquires CollectionStore{ + let Collection{ items, owner } = c; + if (Vector::is_empty(&items)) { + let c = move_from>(owner); + destroy_empty(c); + Vector::destroy_empty(items); + }else{ + let c = borrow_global_mut>(owner); + Option::fill(&mut c.items, items); + } + } + + spec return_collection { + aborts_if false; + } + + fun destroy_empty(c: CollectionStore){ + let CollectionStore{ items } = c; + Option::destroy_none(items); + } + + spec destroy_empty { + aborts_if false; + } + +} +} \ No newline at end of file diff --git a/release/v13/sources/Collection2.move b/release/v13/sources/Collection2.move new file mode 100644 index 00000000..db5f44e3 --- /dev/null +++ b/release/v13/sources/Collection2.move @@ -0,0 +1,235 @@ +address StarcoinFramework { +/// Provide a account based vector for save resource item. +/// The resource in CollectionStore can borrowed by anyone, anyone can get immutable ref of item. +/// and the owner of Collection can allow others to add item to Collection or get mut ref from Collection.git +module Collection2 { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + use StarcoinFramework::Option::{Self, Option}; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = false; + } + + /// Collection in memory, can not drop & store. + struct Collection{ + items: vector, + owner: address, + can_put: bool, + can_mut: bool, + can_take: bool, + } + + /// Collection in global store. + struct CollectionStore has key { + /// items in the CollectionStore. + /// use Option at here is for temporary take away from store to construct Collection. + items: Option>, + anyone_can_put: bool, + anyone_can_mut: bool, + } + + const ERR_COLLECTION_NOT_EXIST: u64 = 101; + /// The operator require the collection owner or collection set anyone_can_put to true. + const ERR_COLLECTION_CAN_NOT_ADD: u64 = 102; + /// The operator require the collection owner or collection set anyone_can_mut to true. + const ERR_COLLECTION_CAN_NOT_MUT: u64 = 103; + /// The operator require the collection owner + const ERR_COLLECTION_CAN_NOT_TAKE: u64 = 104; + const ERR_COLLECTION_INVALID_BORROW_STATE: u64 = 105; + const ERR_COLLECTION_IS_NOT_EMPTY: u64 = 106; + + /// Return the length of the collection. + public fun length(c: &Collection): u64{ + Vector::length(&c.items) + } + + spec length {aborts_if false;} + + /// Acquire an immutable reference to the `i`th element of the collection `c`. + /// Aborts if `i` is out of bounds. + public fun borrow(c: &Collection, i: u64): &T{ + Vector::borrow(&c.items, i) + } + + /// Add item `v` to the end of the collection `c`. + /// require owner of Collection. + public fun push_back(c: &mut Collection, t: T){ + assert!(c.can_put, Errors::requires_address(ERR_COLLECTION_CAN_NOT_ADD)); + Vector::push_back(&mut c.items, t); + } + + /// Return a mutable reference to the `i`th item in the Collection `c`. + /// Aborts if `i` is out of bounds. + public fun borrow_mut(c: &mut Collection, i: u64): &mut T{ + assert!(c.can_mut, Errors::requires_address(ERR_COLLECTION_CAN_NOT_MUT)); + Vector::borrow_mut(&mut c.items, i) + } + + /// Pop an element from the end of vector `v`. + /// Aborts if `v` is empty. + public fun pop_back(c: &mut Collection): T { + assert!(c.can_take, Errors::requires_address(ERR_COLLECTION_CAN_NOT_TAKE)); + Vector::pop_back(&mut c.items) + } + + public fun remove(c: &mut Collection, i: u64): T{ + assert!(c.can_take, Errors::requires_address(ERR_COLLECTION_CAN_NOT_TAKE)); + Vector::remove(&mut c.items, i) + } + + public fun append(c: &mut Collection, other: T) { + assert!(c.can_put, Errors::requires_address(ERR_COLLECTION_CAN_NOT_ADD)); + Vector::append(&mut c.items, Vector::singleton(other)) + } + + public fun append_all(c: &mut Collection, other: vector) { + assert!(c.can_put, Errors::requires_address(ERR_COLLECTION_CAN_NOT_ADD)); + Vector::append(&mut c.items, other) + } + + /// check the Collection exists in `addr` + public fun exists_at(addr: address): bool{ + exists>(addr) + } + + spec exists_at {aborts_if false;} + + /// check `addr` is accept T and other can send T to `addr`, + /// it means exists a Collection of T at `addr` and anyone_can_put of the Collection is true + public fun is_accept(addr: address): bool acquires CollectionStore { + if (!exists>(addr)){ + return false + }; + let cs = borrow_global>(addr); + cs.anyone_can_put + } + + /// signer allow other send T to self + /// create a Collection of T and set anyone_can_put to true + /// if the Collection exists, just update anyone_can_put to true + public fun accept(signer: &signer) acquires CollectionStore { + let addr = Signer::address_of(signer); + if (!exists>(addr)){ + Self::create_collection(signer, true, false); + }; + let cs = borrow_global_mut>(addr); + if (!cs.anyone_can_put) { + cs.anyone_can_put = true; + } + } + + /// Put items to `to_addr`'s Collection of T + /// put = borrow_collection + append + return_collection. + public fun put(signer: &signer, owner: address, item: T) acquires CollectionStore{ + let c = Self::borrow_collection(signer, owner); + Self::append(&mut c, item); + Self::return_collection(c); + } + + spec put {aborts_if false;} + + /// Put all items to owner's collection of T. + public fun put_all(signer: &signer, owner: address, items: vector) acquires CollectionStore{ + let c = Self::borrow_collection(signer, owner); + Self::append_all(&mut c, items); + Self::return_collection(c); + } + + spec put_all {aborts_if false;} + + /// Take last item from signer's Collection of T. + public fun take(signer: &signer): T acquires CollectionStore{ + let addr = Signer::address_of(signer); + let c = borrow_collection(signer, addr); + let item = pop_back(&mut c); + return_collection(c); + item + } + + spec take { + aborts_if false; + } + + public fun create_collection(signer: &signer, anyone_can_put: bool, anyone_can_mut: bool) { + move_to(signer, CollectionStore{items: Option::some(Vector::empty()), anyone_can_put, anyone_can_mut}) + } + + /// Return the length of Collection from `owner`, if collection do not exist, return 0. + public fun length_of(owner: address) : u64 acquires CollectionStore{ + if (exists_at(owner)){ + let cs = borrow_global_mut>(owner); + //if items is None, indicate it is borrowed + assert!(!Option::is_none(&cs.items), Errors::invalid_state(ERR_COLLECTION_INVALID_BORROW_STATE)); + let items = Option::borrow(&cs.items); + Vector::length(items) + }else{ + 0 + } + } + + /// Borrow collection of T from `owner`, auto detected the collection's can_put|can_mut|can_take by the `sender` and Collection config. + public fun borrow_collection(sender: &signer, owner: address): Collection acquires CollectionStore{ + assert!(exists_at(owner), Errors::invalid_state(ERR_COLLECTION_NOT_EXIST)); + let cs = borrow_global_mut>(owner); + //if items is None, indicate it is borrowed + assert!(!Option::is_none(&cs.items), Errors::invalid_state(ERR_COLLECTION_INVALID_BORROW_STATE)); + let items = Option::extract(&mut cs.items); + let is_owner = owner == Signer::address_of(sender); + let can_put = cs.anyone_can_put || is_owner; + let can_mut = cs.anyone_can_mut || is_owner; + let can_take = is_owner; + Collection{ + items, + owner, + can_put, + can_mut, + can_take, + } + } + + spec borrow_collection { + aborts_if false; + } + + /// Return the Collection of T + public fun return_collection(c: Collection) acquires CollectionStore{ + let Collection{ items, owner, can_put:_, can_mut:_, can_take:_ } = c; + let cs = borrow_global_mut>(owner); + assert!(Option::is_none(&cs.items), Errors::invalid_state(ERR_COLLECTION_INVALID_BORROW_STATE)); + Option::fill(&mut cs.items, items); + } + + spec return_collection { + aborts_if false; + } + + public fun destroy_collection(signer: &signer) acquires CollectionStore{ + let c = move_from>(Signer::address_of(signer)); + destroy_empty(c); + } + + spec destroy_collection { + aborts_if false; + } + + fun destroy_empty(c: CollectionStore){ + let CollectionStore{ items, anyone_can_put:_, anyone_can_mut:_,} = c; + if (Option::is_some(&items)) { + let item_vec = Option::extract(&mut items); + assert!(Vector::is_empty(&item_vec), Errors::invalid_state(ERR_COLLECTION_IS_NOT_EMPTY)); + Vector::destroy_empty(item_vec); + Option::destroy_none(items); + }else{ + Option::destroy_none(items); + } + } + + spec destroy_empty { + aborts_if false; + } + +} +} \ No newline at end of file diff --git a/release/v13/sources/Compare.move b/release/v13/sources/Compare.move new file mode 100644 index 00000000..41110b9b --- /dev/null +++ b/release/v13/sources/Compare.move @@ -0,0 +1,126 @@ +address StarcoinFramework { +module Compare { + use StarcoinFramework::Vector; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + const EQUAL: u8 = 0; + const LESS_THAN: u8 = 1; + const GREATER_THAN: u8 = 2; + + /// Compare `v1` and `v2` using + /// (1) byte-by-byte comparison from right to left until we reach the end of the shorter vector, + /// then + /// (2) vector length to break ties. + /// Returns either `EQUAL` (0u8), `LESS_THAN` (1u8), or `GREATER_THAN` (2u8). + /// This function is designed to compare BCS (Starcoin Canonical Serialization)-encoded values + /// (i.e., vectors produced by `BCS::to_bytes`). A typical client will call + /// `Compare::cmp_bcs_bytes(BCS::to_bytes(&t1), BCS::to_bytes(&t2))`. The comparison provides the + /// following guarantees w.r.t the original values t1 and t2: + /// - `cmp_bcs_bytes(bcs_ext(t1), bcs_ext(t2)) == LESS_THAN` iff `cmp_bcs_bytes(t2, t1) == GREATER_THAN` + /// - `Compare::cmp(t1, t2) == EQUAL` iff `t1 == t2` and (similarly) + /// `Compare::cmp(t1, t2) != EQUAL` iff `t1 != t2`, where `==` and `!=` denote the Move + /// bytecode operations for polymorphic equality. + /// - for all primitive types `T` with `<` and `>` comparison operators exposed in Move bytecode + /// (`u8`, `u64`, `u128`), we have + /// `compare_bcs_bytes(bcs_ext(t1), bcs_ext(t2)) == LESS_THAN` iff `t1 < t2` and (similarly) + /// `compare_bcs_bytes(bcs_ext(t1), bcs_ext(t2)) == LESS_THAN` iff `t1 > t2`. + /// For all other types, the order is whatever the BCS encoding of the type and the comparison + /// strategy above gives you. One case where the order might be surprising is the `address` type. + /// CoreAddresses are 16 byte hex values that BCS encodes with the identity function. The right to + /// left, byte-by-byte comparison means that (for example) + /// `compare_bcs_bytes(bcs_ext(0x01), bcs_ext(0x10)) == LESS_THAN` (as you'd expect), but + /// `compare_bcs_bytes(bcs_ext(0x100), bcs_ext(0x001)) == LESS_THAN` (as you probably wouldn't expect). + /// Keep this in mind when using this function to compare addresses. + public fun cmp_bcs_bytes(v1: &vector, v2: &vector): u8 { + let i1 = Vector::length(v1); + let i2 = Vector::length(v2); + let len_cmp = cmp_u64(i1, i2); + + // BCS uses little endian encoding for all integer types, so we choose to compare from left + // to right. Going right to left would make the behavior of Compare.cmp diverge from the + // bytecode operators < and > on integer values (which would be confusing). + while (i1 > 0 && i2 > 0) { + i1 = i1 - 1; + i2 = i2 - 1; + let v1 = *Vector::borrow(v1, i1); + let v2 = *Vector::borrow(v2, i2); + let elem_cmp = if (v1 == v2) EQUAL + else if (v1 < v2) LESS_THAN + else GREATER_THAN; + if (elem_cmp != 0) return elem_cmp + // else, compare next element + }; + // all compared elements equal; use length comparison to break the tie + len_cmp + } + + public fun cmp_bytes(v1: &vector, v2: &vector): u8 { + let l1 = Vector::length(v1); + let l2 = Vector::length(v2); + let len_cmp = cmp_u64(l1, l2); + let i = 0; + while (i < l1 && i < l2) { + let v1 = *Vector::borrow(v1, i); + let v2 = *Vector::borrow(v2, i); + let elem_cmp = if (v1 == v2) EQUAL + else if (v1 < v2) LESS_THAN + else GREATER_THAN; + if (elem_cmp != 0) { + return elem_cmp + }; + // else, compare next element + i = i + 1; + }; + // all compared elements equal; use length comparison to break the tie + len_cmp + } + + spec cmp_bytes { + pragma verify = false; + } + + spec cmp_bcs_bytes { + pragma verify = false; + } + + // Compare two `u64`'s + fun cmp_u64(i1: u64, i2: u64): u8 { + if (i1 == i2) EQUAL + else if (i1 < i2) LESS_THAN + else GREATER_THAN + } + + spec cmp_u64 { + aborts_if false; + } + + public fun is_equal(result: u8): bool { + result == EQUAL + } + + spec is_equal { + aborts_if false; + } + + public fun is_less_than(result: u8): bool { + result == LESS_THAN + } + + spec is_less_than { + aborts_if false; + } + + public fun is_greater_than(result: u8): bool { + result == GREATER_THAN + } + + spec is_greater_than { + aborts_if false; + } +} + +} diff --git a/release/v13/sources/Config.move b/release/v13/sources/Config.move new file mode 100644 index 00000000..54e28ab0 --- /dev/null +++ b/release/v13/sources/Config.move @@ -0,0 +1,281 @@ +address StarcoinFramework { +/// The module provides a general implmentation of configuration for onchain contracts. +module Config { + use StarcoinFramework::Event; + use StarcoinFramework::Signer; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::Errors; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// A generic singleton resource that holds a value of a specific type. + struct Config has key { payload: ConfigValue } + + /// Accounts with this privilege can modify config of type ConfigValue under account_address + struct ModifyConfigCapability has store { + account_address: address, + events: Event::EventHandle>, + } + + /// A holder for ModifyConfigCapability, for extraction and restoration of ModifyConfigCapability. + struct ModifyConfigCapabilityHolder has key, store { + cap: Option>, + } + + /// Event emitted when config value is changed. + struct ConfigChangeEvent has drop, store { + account_address: address, + value: ConfigValue, + } + + const ECONFIG_VALUE_DOES_NOT_EXIST: u64 = 13; + const ECAPABILITY_HOLDER_NOT_EXISTS: u64 = 101; + + + + spec fun spec_get(addr: address): ConfigValue { + global>(addr).payload + } + + + /// Get a copy of `ConfigValue` value stored under `addr`. + public fun get_by_address(addr: address): ConfigValue acquires Config { + assert!(exists>(addr), Errors::invalid_state(ECONFIG_VALUE_DOES_NOT_EXIST)); + *&borrow_global>(addr).payload + } + + spec get_by_address { + aborts_if !exists>(addr); + ensures exists>(addr); + ensures result == spec_get(addr); + } + + /// Check whether the config of `ConfigValue` type exists under `addr`. + public fun config_exist_by_address(addr: address): bool { + exists>(addr) + } + + spec config_exist_by_address { + aborts_if false; + ensures result == exists>(addr); + } + + /// Set a config item to a new value with capability stored under signer + public fun set( + account: &signer, + payload: ConfigValue, + ) acquires Config, ModifyConfigCapabilityHolder { + let signer_address = Signer::address_of(account); + assert!( + exists>(signer_address), + Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS), + ); + let cap_holder = borrow_global_mut>(signer_address); + assert!(Option::is_some(&cap_holder.cap), Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS)); + set_with_capability(Option::borrow_mut(&mut cap_holder.cap), payload); + } + + spec set { + let addr = Signer::address_of(account); + let cap_opt = spec_cap(addr); + let cap = Option::borrow(spec_cap(Signer::address_of(account))); + + aborts_if !exists>(addr); + aborts_if Option::is_none>(cap_opt); + ensures exists>(addr); + + // TODO: For unknown reason we can't specify the strict abort conditions. + // Intuitively, the commented out spec should be able to be verified because + // it is exactly the spec of the callee `set_with_capability()`. + //aborts_if !exists>(Option::borrow(spec_cap(Signer::address_of(account))).account_address); + pragma aborts_if_is_partial; + ensures exists>( + Option::borrow(spec_cap(Signer::address_of(account))).account_address, + ); + ensures global>( + Option::borrow(spec_cap(Signer::address_of(account))).account_address, + ).payload == payload; + } + + + spec fun spec_cap(addr: address): Option> { + global>(addr).cap + } + + + /// Set a config item to a new value with cap. + public fun set_with_capability( + cap: &mut ModifyConfigCapability, + payload: ConfigValue, + ) acquires Config { + let addr = cap.account_address; + assert!(exists>(addr), Errors::invalid_state(ECONFIG_VALUE_DOES_NOT_EXIST)); + let config = borrow_global_mut>(addr); + config.payload = copy payload; + emit_config_change_event(cap, payload); + } + + spec set_with_capability { + aborts_if !exists>(cap.account_address); + ensures exists>(cap.account_address); + ensures global>(cap.account_address).payload == payload; + } + + /// Publish a new config item. The caller will use the returned ModifyConfigCapability to specify the access control + /// policy for who can modify the config. + public fun publish_new_config_with_capability( + account: &signer, + payload: ConfigValue, + ): ModifyConfigCapability acquires ModifyConfigCapabilityHolder{ + publish_new_config(account, payload); + extract_modify_config_capability(account) + } + + spec publish_new_config_with_capability { + include PublishNewConfigAbortsIf; + + ensures exists>(Signer::address_of(account)); + ensures global>(Signer::address_of(account)).payload == payload; + + ensures exists>(Signer::address_of(account)); + ensures Option::is_none(global>(Signer::address_of(account)).cap); + } + + /// Publish a new config item under account address. + public fun publish_new_config(account: &signer, payload: ConfigValue) { + move_to(account, Config{ payload }); + let cap = ModifyConfigCapability { + account_address: Signer::address_of(account), + events: Event::new_event_handle>(account), + }; + move_to(account, ModifyConfigCapabilityHolder{cap: Option::some(cap)}); + } + + spec publish_new_config { + include PublishNewConfigAbortsIf; + + ensures exists>(Signer::address_of(account)); + ensures global>(Signer::address_of(account)).payload == payload; + + ensures exists>(Signer::address_of(account)); + ensures Option::is_some(global>(Signer::address_of(account)).cap); + } + + spec schema PublishNewConfigAbortsIf { + account: signer; + aborts_if exists>(Signer::address_of(account)); + aborts_if exists>(Signer::address_of(account)); + } + + spec schema AbortsIfConfigNotExist { + addr: address; + + aborts_if !exists>(addr); + } + + spec schema AbortsIfConfigOrCapabilityNotExist { + addr: address; + + aborts_if !exists>(addr); + aborts_if !exists>(addr); + } + + spec schema PublishNewConfigEnsures { + account: signer; + ensures exists>(Signer::address_of(account)); + ensures exists>(Signer::address_of(account)); + } + + spec schema AbortsIfCapNotExist { + address: address; + aborts_if !exists>(address); + aborts_if Option::is_none>( + global>(address).cap, + ); + } + + /// Extract account's ModifyConfigCapability for ConfigValue type + public fun extract_modify_config_capability( + account: &signer, + ): ModifyConfigCapability acquires ModifyConfigCapabilityHolder { + let signer_address = Signer::address_of(account); + assert!( + exists>(signer_address), + Errors::requires_capability(ECAPABILITY_HOLDER_NOT_EXISTS) + ); + let cap_holder = borrow_global_mut>(signer_address); + Option::extract(&mut cap_holder.cap) + } + + spec extract_modify_config_capability { + let address = Signer::address_of(account); + include AbortsIfCapNotExist; + + ensures exists>(address); + ensures Option::is_none>( + global>(address).cap + ); + ensures result == old(Option::borrow(global>(address).cap)); + } + + /// Restore account's ModifyConfigCapability + public fun restore_modify_config_capability( + cap: ModifyConfigCapability, + ) acquires ModifyConfigCapabilityHolder { + let cap_holder = borrow_global_mut>(cap.account_address); + Option::fill(&mut cap_holder.cap, cap); + } + + spec restore_modify_config_capability { + aborts_if !exists>(cap.account_address); + aborts_if Option::is_some(global>(cap.account_address).cap); + + ensures exists>(cap.account_address); + ensures Option::is_some(global>(cap.account_address).cap); + ensures Option::borrow(global>(cap.account_address).cap) == cap; + } + + /// Destroy the given ModifyConfigCapability + public fun destroy_modify_config_capability( + cap: ModifyConfigCapability, + ) { + let ModifyConfigCapability{account_address:_, events} = cap; + Event::destroy_handle(events) + } + + spec destroy_modify_config_capability { + aborts_if false; + } + + /// Return the address of the given ModifyConfigCapability + public fun account_address(cap: &ModifyConfigCapability): address { + cap.account_address + } + + spec account_address { + aborts_if false; + ensures result == cap.account_address; + } + + /// Emit a config change event. + fun emit_config_change_event( + cap: &mut ModifyConfigCapability, + value: ConfigValue, + ) { + Event::emit_event>( + &mut cap.events, + ConfigChangeEvent { + account_address: cap.account_address, + value, + }, + ); + } + + spec emit_config_change_event { + aborts_if false; + } +} +} diff --git a/release/v13/sources/ConsensusConfig.move b/release/v13/sources/ConsensusConfig.move new file mode 100644 index 00000000..03644354 --- /dev/null +++ b/release/v13/sources/ConsensusConfig.move @@ -0,0 +1,230 @@ +address StarcoinFramework { +/// The module provide configuration of consensus parameters. +module ConsensusConfig { + use StarcoinFramework::Config; + use StarcoinFramework::Signer; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Math; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_strict; + } + + /// consensus configurations. + struct ConsensusConfig has copy, drop, store { + /// Uncle block rate per epoch + uncle_rate_target: u64, + /// Average target time to calculate a block's difficulty + base_block_time_target: u64, + /// Rewards per block + base_reward_per_block: u128, + /// Percentage of `base_reward_per_block` to reward a uncle block + base_reward_per_uncle_percent: u64, + /// Blocks each epoch + epoch_block_count: u64, + /// How many ancestor blocks which use to calculate next block's difficulty + base_block_difficulty_window: u64, + /// Minimum target time to calculate a block's difficulty + min_block_time_target: u64, + /// Maximum target time to calculate a block's difficulty + max_block_time_target: u64, + /// Maximum number of uncle block per block + base_max_uncles_per_block: u64, + /// Maximum gases per block + base_block_gas_limit: u64, + /// Strategy to calculate difficulty + strategy: u8, + } + + const EINVALID_ARGUMENT: u64 = 18; + + /// Initialization of the module. + public fun initialize( + account: &signer, + uncle_rate_target: u64, + epoch_block_count: u64, + base_block_time_target: u64, + base_block_difficulty_window: u64, + base_reward_per_block: u128, + base_reward_per_uncle_percent: u64, + min_block_time_target: u64, + max_block_time_target: u64, + base_max_uncles_per_block: u64, + base_block_gas_limit: u64, + strategy: u8, + ) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + + Config::publish_new_config( + account, + new_consensus_config( + uncle_rate_target, + base_block_time_target, + base_reward_per_block, + base_reward_per_uncle_percent, + epoch_block_count, + base_block_difficulty_window, + min_block_time_target, + max_block_time_target, + base_max_uncles_per_block, + base_block_gas_limit, + strategy, + ), + ); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if uncle_rate_target == 0; + aborts_if epoch_block_count == 0; + aborts_if base_reward_per_block == 0; + aborts_if base_block_time_target == 0; + aborts_if base_block_difficulty_window == 0; + aborts_if min_block_time_target == 0; + aborts_if max_block_time_target < min_block_time_target; + + include Config::PublishNewConfigAbortsIf; + include Config::PublishNewConfigEnsures; + } + + /// Create a new consensus config mainly used in DAO. + public fun new_consensus_config(uncle_rate_target: u64, + base_block_time_target: u64, + base_reward_per_block: u128, + base_reward_per_uncle_percent: u64, + epoch_block_count: u64, + base_block_difficulty_window: u64, + min_block_time_target: u64, + max_block_time_target: u64, + base_max_uncles_per_block: u64, + base_block_gas_limit: u64, + strategy: u8,): ConsensusConfig { + assert!(uncle_rate_target > 0, Errors::invalid_argument(EINVALID_ARGUMENT)); + assert!(base_block_time_target > 0, Errors::invalid_argument(EINVALID_ARGUMENT)); + assert!(base_reward_per_block > 0, Errors::invalid_argument(EINVALID_ARGUMENT)); + assert!(epoch_block_count > 0, Errors::invalid_argument(EINVALID_ARGUMENT)); + assert!(base_block_difficulty_window > 0, Errors::invalid_argument(EINVALID_ARGUMENT)); + // base_reward_per_uncle_percent can been zero. + assert!(min_block_time_target > 0, Errors::invalid_argument(EINVALID_ARGUMENT)); + assert!(max_block_time_target >= min_block_time_target, Errors::invalid_argument(EINVALID_ARGUMENT)); + + ConsensusConfig { + uncle_rate_target, + base_block_time_target, + base_reward_per_block, + epoch_block_count, + base_block_difficulty_window, + base_reward_per_uncle_percent, + min_block_time_target, + max_block_time_target, + base_max_uncles_per_block, + base_block_gas_limit, + strategy, + } + } + + spec new_consensus_config { + aborts_if uncle_rate_target == 0; + aborts_if epoch_block_count == 0; + aborts_if base_reward_per_block == 0; + aborts_if base_block_time_target == 0; + aborts_if base_block_difficulty_window == 0; + aborts_if min_block_time_target == 0; + aborts_if max_block_time_target < min_block_time_target; + } + + /// Get config data. + public fun get_config(): ConsensusConfig { + Config::get_by_address(CoreAddresses::GENESIS_ADDRESS()) + } + + spec get_config { + aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + } + + spec fun spec_get_config(): ConsensusConfig { + global>(CoreAddresses::GENESIS_ADDRESS()).payload + } + + /// Get uncle_rate_target + public fun uncle_rate_target(config: &ConsensusConfig): u64 { + config.uncle_rate_target + } + /// Get base_block_time_target + public fun base_block_time_target(config: &ConsensusConfig): u64 { + config.base_block_time_target + } + + /// Get base_reward_per_block + public fun base_reward_per_block(config: &ConsensusConfig): u128 { + config.base_reward_per_block + } + + /// Get epoch_block_count + public fun epoch_block_count(config: &ConsensusConfig): u64 { + config.epoch_block_count + } + + /// Get base_block_difficulty_window + public fun base_block_difficulty_window(config: &ConsensusConfig): u64 { + config.base_block_difficulty_window + } + + /// Get base_reward_per_uncle_percent + public fun base_reward_per_uncle_percent(config: &ConsensusConfig): u64 { + config.base_reward_per_uncle_percent + } + + /// Get min_block_time_target + public fun min_block_time_target(config: &ConsensusConfig): u64 { + config.min_block_time_target + } + + /// Get max_block_time_target + public fun max_block_time_target(config: &ConsensusConfig): u64 { + config.max_block_time_target + } + + /// Get base_max_uncles_per_block + public fun base_max_uncles_per_block(config: &ConsensusConfig): u64 { + config.base_max_uncles_per_block + } + + /// Get base_block_gas_limit + public fun base_block_gas_limit(config: &ConsensusConfig): u64 { + config.base_block_gas_limit + } + + /// Get strategy + public fun strategy(config: &ConsensusConfig): u8 { + config.strategy + } + + /// Compute block reward given the `new_epoch_block_time_target`. + public fun compute_reward_per_block(new_epoch_block_time_target: u64): u128 { + let config = get_config(); + do_compute_reward_per_block(&config, new_epoch_block_time_target) + } + + spec compute_reward_per_block { + aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + include Math::MulDivAbortsIf{x: spec_get_config().base_reward_per_block, y: new_epoch_block_time_target, z: spec_get_config().base_block_time_target}; + } + + /// Compute block reward given the `new_epoch_block_time_target`, and the consensus config. + public fun do_compute_reward_per_block(config: &ConsensusConfig, new_epoch_block_time_target: u64): u128 { + Math::mul_div(config.base_reward_per_block, (new_epoch_block_time_target as u128), (config.base_block_time_target as u128)) + } + + spec do_compute_reward_per_block { + include Math::MulDivAbortsIf{x: config.base_reward_per_block, y: new_epoch_block_time_target, z: config.base_block_time_target}; + } + + +} +} \ No newline at end of file diff --git a/release/v13/sources/ConsensusStrategy.move b/release/v13/sources/ConsensusStrategy.move new file mode 100644 index 00000000..99cb8b61 --- /dev/null +++ b/release/v13/sources/ConsensusStrategy.move @@ -0,0 +1,49 @@ +address StarcoinFramework { +/// The module provides the information of current consensus strategy. +module ConsensusStrategy { + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::Config; + + /// ConsensusStrategy data. + struct ConsensusStrategy has copy, drop, store { + /// Value of strategy + value: u8 + } + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + /// Publish the chain ID under the genesis account + public fun initialize(account: &signer, consensus_strategy: u8) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + let cap = Config::publish_new_config_with_capability( + account, + ConsensusStrategy { value:consensus_strategy } + ); + //destroy the cap, so ConsensusStrategy can not been change. + Config::destroy_modify_config_capability(cap); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists>(Signer::address_of(account)); + aborts_if exists>(Signer::address_of(account)); + ensures exists>(Signer::address_of(account)); + } + + /// Return the consensus strategy type of this chain + public fun get(): u8 { + Config::get_by_address(CoreAddresses::GENESIS_ADDRESS()).value + } + + spec get { + aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + } +} +} diff --git a/release/v13/sources/CoreAddresses.move b/release/v13/sources/CoreAddresses.move new file mode 100644 index 00000000..3f234c86 --- /dev/null +++ b/release/v13/sources/CoreAddresses.move @@ -0,0 +1,51 @@ +address StarcoinFramework { +/// The module provide addresses used in stdlib. +module CoreAddresses { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + const ENOT_GENESIS_ACCOUNT: u64 = 11; + + /// The address of the genesis + public fun GENESIS_ADDRESS(): address { + @0x1 + } + + /// Assert signer is genesis. + public fun assert_genesis_address(account: &signer) { + assert!(Signer::address_of(account) == GENESIS_ADDRESS(), + Errors::requires_address(ENOT_GENESIS_ACCOUNT)) + } + spec assert_genesis_address { + pragma opaque; + include AbortsIfNotGenesisAddress; + } + + /// Specifies that a function aborts if the account does not have the Diem root address. + spec schema AbortsIfNotGenesisAddress { + account: signer; + aborts_if Signer::address_of(account) != GENESIS_ADDRESS(); + } + + /// The address of the root association account. This account is + /// created in genesis, and cannot be changed. This address has + /// ultimate authority over the permissions granted (or removed) from + /// accounts on-chain. + public fun ASSOCIATION_ROOT_ADDRESS(): address { + @0xA550C18 + } + + /// The reserved address for transactions inserted by the VM into blocks (e.g. + /// block metadata transactions). Because the transaction is sent from + /// the VM, an account _cannot_ exist at the `0x0` address since there + /// is no signer for the transaction. + public fun VM_RESERVED_ADDRESS(): address { + @0x0 + } +} +} diff --git a/release/v13/sources/Dao.move b/release/v13/sources/Dao.move new file mode 100644 index 00000000..ba82079f --- /dev/null +++ b/release/v13/sources/Dao.move @@ -0,0 +1,1073 @@ +address StarcoinFramework { +module Dao { + use StarcoinFramework::Token; + use StarcoinFramework::Signer; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Option; + use StarcoinFramework::Config; + use StarcoinFramework::Event; + use StarcoinFramework::Errors; + use StarcoinFramework::Treasury; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// Proposal state + const PENDING: u8 = 1; + const ACTIVE: u8 = 2; + const DEFEATED: u8 = 3; + const AGREED: u8 = 4; + const QUEUED: u8 = 5; + const EXECUTABLE: u8 = 6; + const EXTRACTED: u8 = 7; + + /// global DAO info of the specified token type `Token`. + struct DaoGlobalInfo has key { + /// next proposal id. + next_proposal_id: u64, + /// proposal creating event. + proposal_create_event: Event::EventHandle, + /// voting event. + vote_changed_event: Event::EventHandle, + } + + /// Configuration of the `Token`'s DAO. + struct DaoConfig has copy, drop, store { + /// after proposal created, how long use should wait before he can vote (in milliseconds) + voting_delay: u64, + /// how long the voting window is (in milliseconds). + voting_period: u64, + /// the quorum rate to agree on the proposal. + /// if 50% votes needed, then the voting_quorum_rate should be 50. + /// it should between (0, 100]. + voting_quorum_rate: u8, + /// how long the proposal should wait before it can be executed (in milliseconds). + min_action_delay: u64, + } + + spec DaoConfig { + invariant voting_quorum_rate > 0 && voting_quorum_rate <= 100; + invariant voting_delay > 0; + invariant voting_period > 0; + invariant min_action_delay > 0; + } + + /// emitted when proposal created. + struct ProposalCreatedEvent has drop, store { + /// the proposal id. + proposal_id: u64, + /// proposer is the user who create the proposal. + proposer: address, + } + + /// emitted when user vote/revoke_vote. + struct VoteChangedEvent has drop, store { + /// the proposal id. + proposal_id: u64, + /// the voter. + voter: address, + /// creator of the proposal. + proposer: address, + /// agree with the proposal or not + agree: bool, + /// latest vote count of the voter. + vote: u128, + } + + /// Proposal data struct. + struct Proposal has key { + /// id of the proposal + id: u64, + /// creator of the proposal + proposer: address, + /// when voting begins. + start_time: u64, + /// when voting ends. + end_time: u64, + /// count of voters who agree with the proposal + for_votes: u128, + /// count of voters who're against the proposal + against_votes: u128, + /// executable after this time. + eta: u64, + /// after how long, the agreed proposal can be executed. + action_delay: u64, + /// how many votes to reach to make the proposal pass. + quorum_votes: u128, + /// proposal action. + action: Option::Option, + } + + /// User vote info. + struct Vote has key { + /// vote for the proposal under the `proposer`. + proposer: address, + /// proposal id. + id: u64, + /// how many tokens to stake. + stake: Token::Token, + /// vote for or vote against. + agree: bool, + } + + const ERR_NOT_AUTHORIZED: u64 = 1401; + const ERR_ACTION_DELAY_TOO_SMALL: u64 = 1402; + const ERR_PROPOSAL_STATE_INVALID: u64 = 1403; + const ERR_PROPOSAL_ID_MISMATCH: u64 = 1404; + const ERR_PROPOSER_MISMATCH: u64 = 1405; + const ERR_QUORUM_RATE_INVALID: u64 = 1406; + const ERR_CONFIG_PARAM_INVALID: u64 = 1407; + const ERR_VOTE_STATE_MISMATCH: u64 = 1408; + const ERR_ACTION_MUST_EXIST: u64 = 1409; + const ERR_VOTED_OTHERS_ALREADY: u64 = 1410; + + /// plugin function, can only be called by token issuer. + /// Any token who wants to have gov functionality + /// can optin this module by call this `register function`. + public fun plugin( + signer: &signer, + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + ) { + let token_issuer = Token::token_address(); + assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + // let proposal_id = ProposalId {next: 0}; + let gov_info = DaoGlobalInfo { + next_proposal_id: 0, + proposal_create_event: Event::new_event_handle(signer), + vote_changed_event: Event::new_event_handle(signer), + }; + move_to(signer, gov_info); + let config = new_dao_config( + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + ); + Config::publish_new_config(signer, config); + } + + spec plugin { + let sender = Signer::address_of(signer); + aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS(); + + include NewDaoConfigParamSchema; + + include Config::PublishNewConfigAbortsIf>{account: signer}; + + aborts_if exists>(sender); + } + + spec schema RequirePluginDao { + let token_addr = Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if !exists>(token_addr); + aborts_if !exists>>(token_addr); + } + spec schema AbortIfDaoInfoNotExist { + let token_addr = Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if !exists>(token_addr); + } + spec schema AbortIfDaoConfigNotExist { + let token_addr = Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if !exists>>(token_addr); + } + spec schema AbortIfTimestampNotExist { + use StarcoinFramework::CoreAddresses; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + spec module { + apply + AbortIfDaoInfoNotExist + to + generate_next_proposal_id; + + apply + AbortIfDaoConfigNotExist + to + get_config, + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + quorum_votes; + } + + /// create a dao config + public fun new_dao_config( + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + ): DaoConfig { + assert!(voting_delay > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID)); + assert!(voting_period > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID)); + assert!( + voting_quorum_rate > 0 && voting_quorum_rate <= 100, + Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID), + ); + assert!(min_action_delay > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID)); + DaoConfig { voting_delay, voting_period, voting_quorum_rate, min_action_delay } + } + + spec new_dao_config { + include NewDaoConfigParamSchema; + } + + spec schema NewDaoConfigParamSchema { + voting_delay: u64; + voting_period: u64; + voting_quorum_rate: u8; + min_action_delay: u64; + + aborts_if voting_delay == 0; + aborts_if voting_period == 0; + aborts_if voting_quorum_rate == 0 || voting_quorum_rate > 100; + aborts_if min_action_delay == 0; + } + + /// propose a proposal. + /// `action`: the actual action to execute. + /// `action_delay`: the delay to execute after the proposal is agreed + public fun propose( + signer: &signer, + action: ActionT, + action_delay: u64, + ) acquires DaoGlobalInfo { + if (action_delay == 0) { + action_delay = min_action_delay(); + } else { + assert!(action_delay >= min_action_delay(), Errors::invalid_argument(ERR_ACTION_DELAY_TOO_SMALL)); + }; + let proposal_id = generate_next_proposal_id(); + let proposer = Signer::address_of(signer); + let start_time = Timestamp::now_milliseconds() + voting_delay(); + let quorum_votes = quorum_votes(); + let proposal = Proposal { + id: proposal_id, + proposer, + start_time, + end_time: start_time + voting_period(), + for_votes: 0, + against_votes: 0, + eta: 0, + action_delay, + quorum_votes, + action: Option::some(action), + }; + move_to(signer, proposal); + // emit event + let gov_info = borrow_global_mut>(Token::token_address()); + Event::emit_event( + &mut gov_info.proposal_create_event, + ProposalCreatedEvent { proposal_id, proposer }, + ); + } + + spec propose { + use StarcoinFramework::CoreAddresses; + pragma verify = false; + let proposer = Signer::address_of(signer); + + include GenerateNextProposalIdSchema; + + pragma addition_overflow_unchecked = true; // start_time calculation + + include AbortIfDaoConfigNotExist; + include AbortIfDaoInfoNotExist; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + + aborts_if action_delay > 0 && action_delay < spec_dao_config().min_action_delay; + include CheckQuorumVotes; + + let sender = Signer::address_of(signer); + aborts_if exists>(sender); + modifies global>(Token::SPEC_TOKEN_TEST_ADDRESS()); + + ensures exists>(sender); + } + + /// votes for a proposal. + /// User can only vote once, then the stake is locked, + /// which can only be unstaked by user after the proposal is expired, or cancelled, or executed. + /// So think twice before casting vote. + public fun cast_vote( + signer: &signer, + proposer_address: address, + proposal_id: u64, + stake: Token::Token, + agree: bool, + ) acquires Proposal, DaoGlobalInfo, Vote { + { + let state = proposal_state(proposer_address, proposal_id); + // only when proposal is active, use can cast vote. + assert!(state == ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID)); + }; + let proposal = borrow_global_mut>(proposer_address); + assert!(proposal.id == proposal_id, Errors::invalid_argument(ERR_PROPOSAL_ID_MISMATCH)); + let sender = Signer::address_of(signer); + let total_voted = if (exists>(sender)) { + let my_vote = borrow_global_mut>(sender); + assert!(my_vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY)); + assert!(my_vote.agree == agree, Errors::invalid_state(ERR_VOTE_STATE_MISMATCH)); + + do_cast_vote(proposal, my_vote, stake); + Token::value(&my_vote.stake) + } else { + let my_vote = Vote { + proposer: proposer_address, + id: proposal_id, + stake: Token::zero(), + agree, + }; + do_cast_vote(proposal, &mut my_vote, stake); + let total_voted = Token::value(&my_vote.stake); + move_to(signer, my_vote); + total_voted + }; + + // emit event + let gov_info = borrow_global_mut>(Token::token_address()); + Event::emit_event( + &mut gov_info.vote_changed_event, + VoteChangedEvent { + proposal_id, + proposer: proposer_address, + voter: sender, + agree, + vote: total_voted, + }, + ); + } + + spec schema CheckVoteOnCast { + proposal_id: u64; + agree: bool; + voter: address; + stake_value: u128; + let vote = global>(voter); + aborts_if vote.id != proposal_id; + aborts_if vote.agree != agree; + aborts_if vote.stake.value + stake_value > MAX_U128; + } + + spec cast_vote { + pragma addition_overflow_unchecked = true; + + include AbortIfDaoInfoNotExist; + + let expected_states = vec(ACTIVE); + include CheckProposalStates {expected_states}; + let sender = Signer::address_of(signer); + let vote_exists = exists>(sender); + include vote_exists ==> CheckVoteOnCast { + voter: sender, + proposal_id: proposal_id, + agree: agree, + stake_value: stake.value, + }; + + modifies global>(proposer_address); + ensures !vote_exists ==> global>(sender).stake.value == stake.value; + } + + fun do_cast_vote(proposal: &mut Proposal, vote: &mut Vote, stake: Token::Token) { + let stake_value = Token::value(&stake); + Token::deposit(&mut vote.stake, stake); + if (vote.agree) { + proposal.for_votes = proposal.for_votes + stake_value; + } else { + proposal.against_votes = proposal.against_votes + stake_value; + }; + } + + spec do_cast_vote { + pragma addition_overflow_unchecked = true; + aborts_if vote.stake.value + stake.value > MAX_U128; + ensures vote.stake.value == old(vote).stake.value + stake.value; + ensures vote.agree ==> old(proposal).for_votes + stake.value == proposal.for_votes; + ensures vote.agree ==> old(proposal).against_votes == proposal.against_votes; + ensures !vote.agree ==> old(proposal).against_votes + stake.value == proposal.against_votes; + ensures !vote.agree ==> old(proposal).for_votes == proposal.for_votes; + } + + + /// Let user change their vote during the voting time. + public fun change_vote( + signer: &signer, + proposer_address: address, + proposal_id: u64, + agree: bool, + ) acquires Proposal, DaoGlobalInfo, Vote { + { + let state = proposal_state(proposer_address, proposal_id); + // only when proposal is active, user can change vote. + assert!(state == ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID)); + }; + let proposal = borrow_global_mut>(proposer_address); + assert!(proposal.id == proposal_id, Errors::invalid_argument(ERR_PROPOSAL_ID_MISMATCH)); + let my_vote = borrow_global_mut>(Signer::address_of(signer)); + { + assert!(my_vote.proposer == proposer_address, Errors::invalid_argument(ERR_PROPOSER_MISMATCH)); + assert!(my_vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY)); + }; + + // flip the vote + if (my_vote.agree != agree) { + let total_voted = do_flip_vote(my_vote, proposal); + // emit event + let gov_info = borrow_global_mut>(Token::token_address()); + Event::emit_event( + &mut gov_info.vote_changed_event, + VoteChangedEvent { + proposal_id, + proposer: proposer_address, + voter: Signer::address_of(signer), + agree, + vote: total_voted, + }, + ); + }; + } + spec schema CheckVoteOnProposal { + vote: Vote; + proposer_address: address; + proposal_id: u64; + + aborts_if vote.id != proposal_id; + aborts_if vote.proposer != proposer_address; + } + spec schema CheckChangeVote { + vote: Vote; + proposer_address: address; + let proposal = global>(proposer_address); + include AbortIfDaoInfoNotExist; + include CheckFlipVote {my_vote: vote, proposal}; + } + spec change_vote { + pragma verify = false; + let expected_states = vec(ACTIVE); + include CheckProposalStates{expected_states}; + + let sender = Signer::address_of(signer); + aborts_if !exists>(sender); + let vote = global>(sender); + include CheckVoteOnProposal{vote, proposer_address, proposal_id}; + include vote.agree != agree ==> CheckChangeVote{vote, proposer_address}; + + ensures vote.agree != agree ==> vote.agree == agree; + } + + fun do_flip_vote(my_vote: &mut Vote, proposal: &mut Proposal): u128 { + my_vote.agree = !my_vote.agree; + let total_voted = Token::value(&my_vote.stake); + if (my_vote.agree) { + proposal.for_votes = proposal.for_votes + total_voted; + proposal.against_votes = proposal.against_votes - total_voted; + } else { + proposal.for_votes = proposal.for_votes - total_voted; + proposal.against_votes = proposal.against_votes + total_voted; + }; + total_voted + } + spec schema CheckFlipVote { + my_vote: Vote; + proposal: Proposal; + aborts_if my_vote.agree && proposal.for_votes < my_vote.stake.value; + aborts_if my_vote.agree && proposal.against_votes + my_vote.stake.value > MAX_U128; + aborts_if !my_vote.agree && proposal.against_votes < my_vote.stake.value; + aborts_if !my_vote.agree && proposal.for_votes + my_vote.stake.value > MAX_U128; + } + + spec do_flip_vote { + include CheckFlipVote; + ensures my_vote.agree == !old(my_vote).agree; + } + + /// Revoke some voting powers from vote on `proposal_id` of `proposer_address`. + public fun revoke_vote( + signer: &signer, + proposer_address: address, + proposal_id: u64, + voting_power: u128, + ): Token::Token acquires Proposal, Vote, DaoGlobalInfo { + { + let state = proposal_state(proposer_address, proposal_id); + // only when proposal is active, user can revoke vote. + assert!(state == ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID)); + }; + // get proposal + let proposal = borrow_global_mut>(proposer_address); + + // get vote + let my_vote = move_from>(Signer::address_of(signer)); + { + assert!(my_vote.proposer == proposer_address, Errors::invalid_argument(ERR_PROPOSER_MISMATCH)); + assert!(my_vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY)); + }; + // revoke vote on proposal + let reverted_stake =do_revoke_vote(proposal, &mut my_vote, voting_power); + // emit vote changed event + let gov_info = borrow_global_mut>(Token::token_address()); + Event::emit_event( + &mut gov_info.vote_changed_event, + VoteChangedEvent { + proposal_id, + proposer: proposer_address, + voter: Signer::address_of(signer), + agree: my_vote.agree, + vote: Token::value(&my_vote.stake), + }, + ); + + // if user has no stake, destroy his vote. resolve https://github.com/starcoinorg/starcoin/issues/2925. + if (Token::value(&my_vote.stake) == 0u128) { + let Vote {stake, proposer: _, id: _, agree: _} = my_vote; + Token::destroy_zero(stake); + } else { + move_to(signer, my_vote); + }; + + reverted_stake + } + + spec revoke_vote { + pragma verify = false; + include AbortIfDaoInfoNotExist; + let expected_states = vec(ACTIVE); + include CheckProposalStates {expected_states}; + let sender = Signer::address_of(signer); + + aborts_if !exists>(sender); + let vote = global>(sender); + include CheckVoteOnProposal {vote, proposer_address, proposal_id}; + include CheckRevokeVote { + vote, + proposal: global>(proposer_address), + to_revoke: voting_power, + }; + + modifies global>(sender); + modifies global>(proposer_address); + modifies global>(Token::SPEC_TOKEN_TEST_ADDRESS()); + + ensures global>(sender).stake.value + result.value == old(global>(sender)).stake.value; + ensures result.value == voting_power; + } + + fun do_revoke_vote(proposal: &mut Proposal, vote: &mut Vote, to_revoke: u128): Token::Token { + spec { + assume vote.stake.value >= to_revoke; + }; + let reverted_stake = Token::withdraw(&mut vote.stake, to_revoke); + if (vote.agree) { + proposal.for_votes = proposal.for_votes - to_revoke; + } else { + proposal.against_votes = proposal.against_votes - to_revoke; + }; + spec { + assert Token::value(reverted_stake) == to_revoke; + }; + reverted_stake + } + spec schema CheckRevokeVote { + vote: Vote; + proposal: Proposal; + to_revoke: u128; + aborts_if vote.stake.value < to_revoke; + aborts_if vote.agree && proposal.for_votes < to_revoke; + aborts_if !vote.agree && proposal.against_votes < to_revoke; + } + + spec do_revoke_vote { + include CheckRevokeVote; + ensures vote.agree ==> old(proposal).for_votes == proposal.for_votes + to_revoke; + ensures !vote.agree ==> old(proposal).against_votes == proposal.against_votes + to_revoke; + ensures result.value == to_revoke; + } + + /// Retrieve back my staked token voted for a proposal. + public fun unstake_votes( + signer: &signer, + proposer_address: address, + proposal_id: u64, + ): Token::Token acquires Proposal, Vote { + // only check state when proposal exists. + // because proposal can be destroyed after it ends in DEFEATED or EXTRACTED state. + if (proposal_exists(proposer_address, proposal_id)) { + let state = proposal_state(proposer_address, proposal_id); + // Only after vote period end, user can unstake his votes. + assert!(state > ACTIVE, Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID)); + }; + let Vote { proposer, id, stake, agree: _ } = move_from>( + Signer::address_of(signer), + ); + // these checks are still required. + assert!(proposer == proposer_address, Errors::requires_address(ERR_PROPOSER_MISMATCH)); + assert!(id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY)); + stake + } + + spec unstake_votes { + pragma verify = false; + let expected_states = vec(DEFEATED); + let expected_states1 = concat(expected_states,vec(AGREED)); + let expected_states2 = concat(expected_states1,vec(QUEUED)); + let expected_states3 = concat(expected_states2,vec(EXECUTABLE)); + let expected_states4 = concat(expected_states3,vec(EXTRACTED)); + aborts_if expected_states4[0] != DEFEATED; + aborts_if expected_states4[1] != AGREED; + aborts_if expected_states4[2] != QUEUED; + aborts_if expected_states4[3] != EXECUTABLE; + aborts_if expected_states4[4] != EXTRACTED; + include spec_proposal_exists(proposer_address, proposal_id) ==> + CheckProposalStates{expected_states: expected_states4}; + let sender = Signer::address_of(signer); + aborts_if !exists>(sender); + let vote = global>(sender); + include CheckVoteOnProposal{vote, proposer_address, proposal_id}; + ensures !exists>(sender); + ensures result.value == old(vote).stake.value; + } + + + /// queue agreed proposal to execute. + public entry fun queue_proposal_action( + proposer_address: address, + proposal_id: u64, + ) acquires Proposal { + // Only agreed proposal can be submitted. + assert!( + proposal_state(proposer_address, proposal_id) == AGREED, + Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID) + ); + let proposal = borrow_global_mut>(proposer_address); + proposal.eta = Timestamp::now_milliseconds() + proposal.action_delay; + } + spec queue_proposal_action { + pragma verify = false; + let expected_states = vec(AGREED); + include CheckProposalStates{expected_states}; + + let proposal = global>(proposer_address); + aborts_if Timestamp::spec_now_millseconds() + proposal.action_delay > MAX_U64; + ensures proposal.eta >= Timestamp::spec_now_millseconds(); + } + + /// extract proposal action to execute. + public fun extract_proposal_action( + proposer_address: address, + proposal_id: u64, + ): ActionT acquires Proposal { + // Only executable proposal's action can be extracted. + assert!( + proposal_state(proposer_address, proposal_id) == EXECUTABLE, + Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID), + ); + let proposal = borrow_global_mut>(proposer_address); + let action: ActionT = Option::extract(&mut proposal.action); + action + } + spec extract_proposal_action { + pragma aborts_if_is_partial = false; + let expected_states = vec(EXECUTABLE); + include CheckProposalStates{expected_states}; + modifies global>(proposer_address); + ensures Option::is_none(global>(proposer_address).action); + } + + + /// remove terminated proposal from proposer + public entry fun destroy_terminated_proposal( + proposer_address: address, + proposal_id: u64, + ) acquires Proposal { + let proposal_state = proposal_state(proposer_address, proposal_id); + assert!( + proposal_state == DEFEATED || proposal_state == EXTRACTED, + Errors::invalid_state(ERR_PROPOSAL_STATE_INVALID), + ); + let Proposal { + id: _, + proposer: _, + start_time: _, + end_time: _, + for_votes: _, + against_votes: _, + eta: _, + action_delay: _, + quorum_votes: _, + action, + } = move_from>(proposer_address); + if (proposal_state == DEFEATED) { + let _ = Option::extract(&mut action); + }; + Option::destroy_none(action); + } + + spec destroy_terminated_proposal { + let expected_states = concat(vec(DEFEATED), vec(EXTRACTED)); + aborts_if len(expected_states) != 2; + aborts_if expected_states[0] != DEFEATED; + aborts_if expected_states[1] != EXTRACTED; + + aborts_if !exists>(proposer_address); + let proposal = global>(proposer_address); + aborts_if proposal.id != proposal_id; + include AbortIfTimestampNotExist; + let current_time = Timestamp::spec_now_millseconds(); + let state = do_proposal_state(proposal, current_time); + aborts_if (forall s in expected_states : s != state); + aborts_if state == DEFEATED && Option::is_none(global>(proposer_address).action); + aborts_if state == EXTRACTED && Option::is_some(global>(proposer_address).action); + modifies global>(proposer_address); + } + + /// check whether a proposal exists in `proposer_address` with id `proposal_id`. + public fun proposal_exists( + proposer_address: address, + proposal_id: u64, + ): bool acquires Proposal { + if (exists>(proposer_address)) { + let proposal = borrow_global>(proposer_address); + return proposal.id == proposal_id + }; + false + } + spec proposal_exists { + ensures exists>(proposer_address) && + borrow_global>(proposer_address).id == proposal_id ==> + result; + } + + spec fun spec_proposal_exists( + proposer_address: address, + proposal_id: u64, + ): bool { + if (exists>(proposer_address)) { + let proposal = global>(proposer_address); + proposal.id == proposal_id + } else { + false + } + } + + /// Get the proposal state. + public fun proposal_state( + proposer_address: address, + proposal_id: u64, + ): u8 acquires Proposal { + let proposal = borrow_global>(proposer_address); + assert!(proposal.id == proposal_id, Errors::invalid_argument(ERR_PROPOSAL_ID_MISMATCH)); + let current_time = Timestamp::now_milliseconds(); + do_proposal_state(proposal, current_time) + } + + spec schema CheckProposalStates { + proposer_address: address; + proposal_id: u64; + expected_states: vector; + aborts_if !exists>(proposer_address); + + let proposal = global>(proposer_address); + aborts_if proposal.id != proposal_id; + + include AbortIfTimestampNotExist; + let current_time = Timestamp::spec_now_millseconds(); + let state = do_proposal_state(proposal, current_time); + aborts_if (forall s in expected_states : s != state); + } + + spec proposal_state { + use StarcoinFramework::CoreAddresses; + include AbortIfTimestampNotExist; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists>(proposer_address); + + let proposal = global>(proposer_address); + aborts_if proposal.id != proposal_id; + } + + fun do_proposal_state( + proposal: &Proposal, + current_time: u64, + ): u8 { + if (current_time < proposal.start_time) { + // Pending + PENDING + } else if (current_time <= proposal.end_time) { + // Active + ACTIVE + } else if (proposal.for_votes <= proposal.against_votes || + proposal.for_votes < proposal.quorum_votes) { + // Defeated + DEFEATED + } else if (proposal.eta == 0) { + // Agreed. + AGREED + } else if (current_time < proposal.eta) { + // Queued, waiting to execute + QUEUED + } else if (Option::is_some(&proposal.action)) { + EXECUTABLE + } else { + EXTRACTED + } + } + + + /// get proposal's information. + /// return: (id, start_time, end_time, for_votes, against_votes). + public fun proposal_info( + proposer_address: address, + ): (u64, u64, u64, u128, u128) acquires Proposal { + let proposal = borrow_global>(proposer_address); + (proposal.id, proposal.start_time, proposal.end_time, proposal.for_votes, proposal.against_votes) + } + + spec proposal_info { + aborts_if !exists>(proposer_address); + } + + /// Get voter's vote info on proposal with `proposal_id` of `proposer_address`. + public fun vote_of( + voter: address, + proposer_address: address, + proposal_id: u64, + ): (bool, u128) acquires Vote { + let vote = borrow_global>(voter); + assert!(vote.proposer == proposer_address, Errors::requires_address(ERR_PROPOSER_MISMATCH)); + assert!(vote.id == proposal_id, Errors::invalid_argument(ERR_VOTED_OTHERS_ALREADY)); + (vote.agree, Token::value(&vote.stake)) + } + + spec vote_of { + aborts_if !exists>(voter); + let vote = global>(voter); + include CheckVoteOnProposal{vote, proposer_address, proposal_id}; + } + + /// Check whether voter has voted on proposal with `proposal_id` of `proposer_address`. + public fun has_vote( + voter: address, + proposer_address: address, + proposal_id: u64, + ): bool acquires Vote { + if (!exists>(voter)) { + return false + }; + + let vote = borrow_global>(voter); + vote.proposer == proposer_address && vote.id == proposal_id + } + + fun generate_next_proposal_id(): u64 acquires DaoGlobalInfo { + let gov_info = borrow_global_mut>(Token::token_address()); + let proposal_id = gov_info.next_proposal_id; + gov_info.next_proposal_id = proposal_id + 1; + proposal_id + } + + spec generate_next_proposal_id { + include GenerateNextProposalIdSchema; + ensures result == old(global>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id); + } + + spec schema GenerateNextProposalIdSchema { + aborts_if global>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id >= MAX_U64; + modifies global>(Token::SPEC_TOKEN_TEST_ADDRESS()); + ensures + global>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id == + old(global>(Token::SPEC_TOKEN_TEST_ADDRESS()).next_proposal_id) + 1; + } + + //// Helper functions + + //// Query functions + + /// get default voting delay of the DAO. + public fun voting_delay(): u64 { + get_config().voting_delay + } + + spec voting_delay { + aborts_if false; + } + + /// get the default voting period of the DAO. + public fun voting_period(): u64 { + get_config().voting_period + } + + spec voting_period { + aborts_if false; + } + + /// Quorum votes to make proposal pass. + public fun quorum_votes(): u128 { + let market_cap = Token::market_cap(); + let balance_in_treasury = Treasury::balance(); + let supply = market_cap - balance_in_treasury; + let rate = voting_quorum_rate(); + let rate = (rate as u128); + supply * rate / 100 + } + spec schema CheckQuorumVotes { + aborts_if Token::spec_abstract_total_value() * spec_dao_config().voting_quorum_rate > MAX_U128; + } + spec quorum_votes { + pragma verify = false; + include CheckQuorumVotes; + } + + spec fun spec_quorum_votes(): u128 { + let supply = Token::spec_abstract_total_value() - Treasury::spec_balance(); + supply * spec_dao_config().voting_quorum_rate / 100 + } + + /// Get the quorum rate in percent. + public fun voting_quorum_rate(): u8 { + get_config().voting_quorum_rate + } + + spec voting_quorum_rate { + aborts_if false; + ensures result == global>>((Token::SPEC_TOKEN_TEST_ADDRESS())).payload.voting_quorum_rate; + } + + /// Get the min_action_delay of the DAO. + public fun min_action_delay(): u64 { + get_config().min_action_delay + } + + spec min_action_delay { + aborts_if false; + ensures result == spec_dao_config().min_action_delay; + } + + fun get_config(): DaoConfig { + let token_issuer = Token::token_address(); + Config::get_by_address>(token_issuer) + } + + spec get_config { + aborts_if false; + ensures result == global>>(Token::SPEC_TOKEN_TEST_ADDRESS()).payload; + } + + + spec fun spec_dao_config(): DaoConfig { + global>>((Token::SPEC_TOKEN_TEST_ADDRESS())).payload + } + + + spec schema CheckModifyConfigWithCap { + cap: Config::ModifyConfigCapability>; + aborts_if cap.account_address != Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if !exists>>(cap.account_address); + } + + /// update function, modify dao config. + /// if any param is 0, it means no change to that param. + public fun modify_dao_config( + cap: &mut Config::ModifyConfigCapability>, + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + ) { + assert!(Config::account_address(cap) == Token::token_address(), Errors::invalid_argument(ERR_NOT_AUTHORIZED)); + let config = get_config(); + if (voting_period > 0) { + config.voting_period = voting_period; + }; + if (voting_delay > 0) { + config.voting_delay = voting_delay; + }; + if (voting_quorum_rate > 0) { + assert!(voting_quorum_rate <= 100, Errors::invalid_argument(ERR_QUORUM_RATE_INVALID)); + config.voting_quorum_rate = voting_quorum_rate; + }; + if (min_action_delay > 0) { + config.min_action_delay = min_action_delay; + }; + Config::set_with_capability>(cap, config); + } + + spec modify_dao_config { + include CheckModifyConfigWithCap; + aborts_if voting_quorum_rate > 0 && voting_quorum_rate > 100; + } + + /// set voting delay + public fun set_voting_delay( + cap: &mut Config::ModifyConfigCapability>, + value: u64, + ) { + assert!(Config::account_address(cap) == Token::token_address(), Errors::invalid_argument(ERR_NOT_AUTHORIZED)); + assert!(value > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID)); + let config = get_config(); + config.voting_delay = value; + Config::set_with_capability>(cap, config); + } + + spec set_voting_delay { + include CheckModifyConfigWithCap; + aborts_if value == 0; + } + + /// set voting period + public fun set_voting_period( + cap: &mut Config::ModifyConfigCapability>, + value: u64, + ) { + assert!(Config::account_address(cap) == Token::token_address(), Errors::invalid_argument(ERR_NOT_AUTHORIZED)); + assert!(value > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID)); + let config = get_config(); + config.voting_period = value; + Config::set_with_capability>(cap, config); + } + + spec set_voting_period { + include CheckModifyConfigWithCap; + aborts_if value == 0; + } + + /// set voting quorum rate + public fun set_voting_quorum_rate( + cap: &mut Config::ModifyConfigCapability>, + value: u8, + ) { + assert!(Config::account_address(cap) == Token::token_address(), Errors::invalid_argument(ERR_NOT_AUTHORIZED)); + assert!(value <= 100 && value > 0, Errors::invalid_argument(ERR_QUORUM_RATE_INVALID)); + let config = get_config(); + config.voting_quorum_rate = value; + Config::set_with_capability>(cap, config); + } + + spec set_voting_quorum_rate { + aborts_if !(value > 0 && value <= 100); + include CheckModifyConfigWithCap; + } + + /// set min action delay + public fun set_min_action_delay( + cap: &mut Config::ModifyConfigCapability>, + value: u64, + ) { + assert!(Config::account_address(cap) == Token::token_address(), Errors::invalid_argument(ERR_NOT_AUTHORIZED)); + assert!(value > 0, Errors::invalid_argument(ERR_CONFIG_PARAM_INVALID)); + let config = get_config(); + config.min_action_delay = value; + Config::set_with_capability>(cap, config); + } + spec set_min_action_delay { + aborts_if value == 0; + include CheckModifyConfigWithCap; + } +} +} diff --git a/release/v13/sources/DaoVoteScripts.move b/release/v13/sources/DaoVoteScripts.move new file mode 100644 index 00000000..3f4ca178 --- /dev/null +++ b/release/v13/sources/DaoVoteScripts.move @@ -0,0 +1,77 @@ +address StarcoinFramework { +module DaoVoteScripts { + use StarcoinFramework::Dao; + use StarcoinFramework::Account; + use StarcoinFramework::Signer; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_partial = false; + pragma aborts_if_is_strict = true; + } + + public entry fun cast_vote( + signer: signer, + proposer_address: address, + proposal_id: u64, + agree: bool, + votes: u128, + ) { + let sender = Signer::address_of(&signer); + if (Dao::has_vote(sender, proposer_address, proposal_id)) { + // if already voted, and vote is not same as the current cast, change the existing vote. + // resolve https://github.com/starcoinorg/starcoin/issues/2925. + let (agree_voted, _) = Dao::vote_of(sender, proposer_address, proposal_id); + if (agree_voted != agree) { + Dao::change_vote(&signer, proposer_address, proposal_id, agree); + } + }; + + let votes = Account::withdraw(&signer, votes); + Dao::cast_vote(&signer, proposer_address, proposal_id, votes, agree); + } + + /// revoke all votes on a proposal + public entry fun revoke_vote( + signer: signer, + proposer_address: address, + proposal_id: u64, + ) { + let sender = Signer::address_of(&signer); + let (_, power) = Dao::vote_of(sender, proposer_address, proposal_id); + let my_token = Dao::revoke_vote(&signer, proposer_address, proposal_id, power); + Account::deposit(sender, my_token); + } + + /// Let user change their vote during the voting time. + public entry fun flip_vote( + signer: signer, + proposer_address: address, + proposal_id: u64, + ) { + let (agree, _) = Dao::vote_of(Signer::address_of(&signer), proposer_address, proposal_id); + Dao::change_vote(&signer, proposer_address, proposal_id, !agree); + } + + /// revoke some votes on a proposal + public entry fun revoke_vote_of_power( + signer: signer, + proposer_address: address, + proposal_id: u64, + power: u128, + ) { + let sender = Signer::address_of(&signer); + let my_token = Dao::revoke_vote(&signer, proposer_address, proposal_id, power); + Account::deposit(sender, my_token); + } + + public entry fun unstake_vote( + signer: signer, + proposer_address: address, + proposal_id: u64, + ) { + let my_token = Dao::unstake_votes(&signer, proposer_address, proposal_id); + Account::deposit(Signer::address_of(&signer), my_token); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Debug.move b/release/v13/sources/Debug.move new file mode 100644 index 00000000..7fb0f1c2 --- /dev/null +++ b/release/v13/sources/Debug.move @@ -0,0 +1,16 @@ +address StarcoinFramework { +/// The module provide debug print for Move. +module Debug { + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// Print data of Type `T`. + native public fun print(x: &T); + + /// Print current stack. + native public fun print_stack_trace(); +} + +} diff --git a/release/v13/sources/DummyToken.move b/release/v13/sources/DummyToken.move new file mode 100644 index 00000000..af6f684b --- /dev/null +++ b/release/v13/sources/DummyToken.move @@ -0,0 +1,77 @@ +address StarcoinFramework{ +/// The module provide a dummy token implementation. +module DummyToken { + use StarcoinFramework::Token::{Self, Token}; + use StarcoinFramework::Errors; + + /// The DummyToken type. + struct DummyToken has copy, drop, store { } + + + const EMINT_TOO_MUCH:u64 = 101; + + const PRECISION: u8 = 3; + + /// Burn capability of the token. + struct SharedBurnCapability has key { + cap: Token::BurnCapability, + } + + /// Mint capability of the token. + struct SharedMintCapability has key, store { + cap: Token::MintCapability, + } + + /// Initialization of the module. + public fun initialize(account: &signer) { + Token::register_token( + account, + PRECISION, + ); + + let burn_cap = Token::remove_burn_capability(account); + move_to(account, SharedBurnCapability{cap: burn_cap}); + + let burn_cap = Token::remove_mint_capability(account); + move_to(account, SharedMintCapability{cap: burn_cap}); + } + + /// Returns true if `TokenType` is `DummyToken::DummyToken` + public fun is_dummy_token(): bool { + Token::is_same_token() + } + + /// Burn the given token. + public fun burn(token: Token) acquires SharedBurnCapability{ + let cap = borrow_global(token_address()); + Token::burn_with_capability(&cap.cap, token); + } + + /// Anyone can mint DummyToken, amount should < 10000 + public fun mint(_account: &signer, amount: u128) : Token acquires SharedMintCapability{ + assert!(amount <= 10000, Errors::invalid_argument(EMINT_TOO_MUCH)); + let cap = borrow_global(token_address()); + Token::mint_with_capability(&cap.cap, amount) + } + + /// Return the token address. + public fun token_address(): address { + Token::token_address() + } +} + +module DummyTokenScripts{ + use StarcoinFramework::DummyToken::{Self,DummyToken}; + use StarcoinFramework::Account; + use StarcoinFramework::Signer; + + public entry fun mint(sender: signer, amount: u128){ + let token = DummyToken::mint(&sender, amount); + let sender_addr = Signer::address_of(&sender); + if(Account::is_accept_token(sender_addr)){ + Account::do_accept_token(&sender); + }; + Account::deposit(sender_addr, token); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/DummyTokenScripts.move b/release/v13/sources/DummyTokenScripts.move new file mode 100644 index 00000000..af6f684b --- /dev/null +++ b/release/v13/sources/DummyTokenScripts.move @@ -0,0 +1,77 @@ +address StarcoinFramework{ +/// The module provide a dummy token implementation. +module DummyToken { + use StarcoinFramework::Token::{Self, Token}; + use StarcoinFramework::Errors; + + /// The DummyToken type. + struct DummyToken has copy, drop, store { } + + + const EMINT_TOO_MUCH:u64 = 101; + + const PRECISION: u8 = 3; + + /// Burn capability of the token. + struct SharedBurnCapability has key { + cap: Token::BurnCapability, + } + + /// Mint capability of the token. + struct SharedMintCapability has key, store { + cap: Token::MintCapability, + } + + /// Initialization of the module. + public fun initialize(account: &signer) { + Token::register_token( + account, + PRECISION, + ); + + let burn_cap = Token::remove_burn_capability(account); + move_to(account, SharedBurnCapability{cap: burn_cap}); + + let burn_cap = Token::remove_mint_capability(account); + move_to(account, SharedMintCapability{cap: burn_cap}); + } + + /// Returns true if `TokenType` is `DummyToken::DummyToken` + public fun is_dummy_token(): bool { + Token::is_same_token() + } + + /// Burn the given token. + public fun burn(token: Token) acquires SharedBurnCapability{ + let cap = borrow_global(token_address()); + Token::burn_with_capability(&cap.cap, token); + } + + /// Anyone can mint DummyToken, amount should < 10000 + public fun mint(_account: &signer, amount: u128) : Token acquires SharedMintCapability{ + assert!(amount <= 10000, Errors::invalid_argument(EMINT_TOO_MUCH)); + let cap = borrow_global(token_address()); + Token::mint_with_capability(&cap.cap, amount) + } + + /// Return the token address. + public fun token_address(): address { + Token::token_address() + } +} + +module DummyTokenScripts{ + use StarcoinFramework::DummyToken::{Self,DummyToken}; + use StarcoinFramework::Account; + use StarcoinFramework::Signer; + + public entry fun mint(sender: signer, amount: u128){ + let token = DummyToken::mint(&sender, amount); + let sender_addr = Signer::address_of(&sender); + if(Account::is_accept_token(sender_addr)){ + Account::do_accept_token(&sender); + }; + Account::deposit(sender_addr, token); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/EVMAddress.move b/release/v13/sources/EVMAddress.move new file mode 100644 index 00000000..044636e4 --- /dev/null +++ b/release/v13/sources/EVMAddress.move @@ -0,0 +1,131 @@ +address StarcoinFramework { + +/// Contains functions for [ed25519](https://en.wikipedia.org/wiki/EdDSA) digital signatures. +module Signature { + + use StarcoinFramework::Vector; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::EVMAddress::{Self, EVMAddress}; + + native public fun ed25519_validate_pubkey(public_key: vector): bool; + native public fun ed25519_verify(signature: vector, public_key: vector, message: vector): bool; + + /// recover address from ECDSA signature, if recover fail, return an empty vector + native fun native_ecrecover(hash: vector, signature: vector): vector; + + /// recover address from ECDSA signature, if recover fail, return None + public fun ecrecover(hash: vector, signature: vector):Option{ + let bytes = native_ecrecover(hash, signature); + if (Vector::is_empty(&bytes)){ + Option::none() + }else{ + Option::some(EVMAddress::new(bytes)) + } + } + + // verify eth secp256k1 sign and compare addr, if add equal return true + public fun secp256k1_verify(signature: vector, addr: vector, message: vector) : bool{ + let receover_address_opt:Option = ecrecover(message, signature); + let expect_address = EVMAddress::new(addr); + &Option::destroy_some(receover_address_opt) == &expect_address + } + + spec module { + pragma intrinsic = true; + } + + #[test] + fun test_ecrecover_invalid(){ + let h = b"00"; + let s = b"00"; + let addr = ecrecover(h, s); + assert!(Option::is_none(&addr), 1001); + } +} + +module EVMAddress{ + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + use StarcoinFramework::Vector; + + const EVM_ADDR_LENGTH:u64 = 20; + + struct EVMAddress has copy, store, drop{ + bytes: vector, + } + + /// Create EVMAddress from bytes, If bytes is larger than EVM_ADDR_LENGTH(20), bytes will be cropped from the left. + /// keep same as https://github.com/ethereum/go-ethereum/blob/master/common/types.go#L302 + public fun new(bytes: vector): EVMAddress{ + let len = Vector::length(&bytes); + let bytes = if (len > EVM_ADDR_LENGTH){ + let new_bytes = Vector::empty(); + let i = 0; + while (i < EVM_ADDR_LENGTH) { + Vector::push_back(&mut new_bytes, *Vector::borrow(&bytes, i)); + i = i + 1; + }; + new_bytes + }else if (len == EVM_ADDR_LENGTH){ + bytes + }else{ + let i = 0; + let new_bytes = Vector::empty(); + while (i < EVM_ADDR_LENGTH - len) { + // pad zero to address + Vector::push_back(&mut new_bytes, 0); + i = i + 1; + }; + Vector::append(&mut new_bytes, bytes); + new_bytes + }; + EVMAddress{ + bytes + } + } + + spec new { + pragma verify = false; + //TODO + } + + /// Get the inner bytes of the `addr` as a reference + public fun as_bytes(addr: &EVMAddress): &vector { + &addr.bytes + } + + spec as_bytes { + pragma verify = false; + //TODO + } + + /// Unpack the `addr` to get its backing bytes + public fun into_bytes(addr: EVMAddress): vector { + let EVMAddress { bytes } = addr; + bytes + } + + spec into_bytes { + pragma verify = false; + //TODO + } + + #[test] + fun test_evm_address_padding(){ + let addr1 = new(x"00"); + let addr2 = new(x"0000"); + assert!(&addr1.bytes == &addr2.bytes, 1001); + } + + #[test] + fun test_evm_address_crop(){ + let addr1 = new(x"01234567890123456789012345678901234567891111"); + let addr2 = new(x"01234567890123456789012345678901234567892222"); + assert!(&addr1.bytes == &addr2.bytes, 1001); + } +} +} diff --git a/release/v13/sources/EasyGas.move b/release/v13/sources/EasyGas.move new file mode 100644 index 00000000..e5ba4710 --- /dev/null +++ b/release/v13/sources/EasyGas.move @@ -0,0 +1,147 @@ +address StarcoinFramework { + +module EasyGas { + use StarcoinFramework::Account; + use StarcoinFramework::Account::{extract_withdraw_capability, withdraw_with_capability, restore_withdraw_capability, + deposit, SignerCapability + }; + use StarcoinFramework::Signer::address_of; + use StarcoinFramework::TypeInfo::{type_of, module_name, account_address, struct_name}; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::GenesisSignerCapability; + use StarcoinFramework::PriceOracle; + use StarcoinFramework::Errors; + + const EBAD_TRANSACTION_FEE_TOKEN: u64 = 18; + struct STCToken has copy, store, drop {} + + struct GasTokenEntry has key, store, drop { + account_address: address, + module_name: vector, + struct_name: vector, + data_source: address, + } + + struct GasFeeAddress has key, store { + gas_fee_address: address, + cap: SignerCapability, + } + + public fun initialize( + sender: &signer, + token_account_address: address, + token_module_name: vector, + token_struct_name: vector, + data_source: address, + ) acquires GasTokenEntry { + register_gas_token(sender, token_account_address, token_module_name, token_struct_name, data_source); + create_gas_fee_address(sender); + } + + public fun register_oracle(sender: &signer, precision: u8) { + PriceOracle::register_oracle>(sender, precision); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + //todo:check gas token entry + Account::do_accept_token(&genesis_account); + } + + public fun init_oracle_source(sender: &signer, init_value: u128) { + PriceOracle::init_data_source>(sender, init_value); + } + + public fun update_oracle(sender: &signer, value: u128) { + PriceOracle::update>(sender, value); + } + + public fun get_scaling_factor(): u128 { + PriceOracle::get_scaling_factor>() + } + + public fun gas_oracle_read(): u128 acquires GasTokenEntry { + let data_source = get_data_source_address(); + PriceOracle::read>(data_source) + } + + + fun register_gas_token( + sender: &signer, + account_address: address, + module_name: vector, + struct_name: vector, + data_source: address, + ) acquires GasTokenEntry { + CoreAddresses::assert_genesis_address(sender); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let gas_token_entry = GasTokenEntry { account_address, module_name, struct_name, data_source }; + if (exists(address_of(&genesis_account))) { + move_from(address_of(&genesis_account)); + }; + move_to(&genesis_account, gas_token_entry); + } + + fun get_data_source_address(): address acquires GasTokenEntry { + let token_type_info = type_of(); + let genesis = CoreAddresses::GENESIS_ADDRESS(); + let gas_token_entry = borrow_global(genesis); + assert!(module_name(&token_type_info) == *&gas_token_entry.module_name && account_address( + &token_type_info + ) == *&gas_token_entry.account_address && struct_name(&token_type_info) == *&gas_token_entry.struct_name, Errors::invalid_argument(EBAD_TRANSACTION_FEE_TOKEN)); + gas_token_entry.data_source + } + + fun create_gas_fee_address( + sender: &signer, + ) { + CoreAddresses::assert_genesis_address(sender); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let (gas_fee_address, cap) = Account::create_delegate_account(&genesis_account); + let gas_fee_signer = Account::create_signer_with_cap(&cap); + Account::set_auto_accept_token(&gas_fee_signer, true); + let gas_fee_address_entry = GasFeeAddress { gas_fee_address, cap }; + move_to(&genesis_account, gas_fee_address_entry); + } + + public fun get_gas_fee_address(): address acquires GasFeeAddress { + let genesis = CoreAddresses::GENESIS_ADDRESS(); + let gas_fee_address_entry = borrow_global(genesis); + + return gas_fee_address_entry.gas_fee_address + } + + public fun withdraw_gas_fee(_sender: &signer, amount: u128) acquires GasFeeAddress { + let genesis = CoreAddresses::GENESIS_ADDRESS(); + let gas_fee_address_entry = borrow_global(genesis); + let gas_fee_signer = Account::create_signer_with_cap(&gas_fee_address_entry.cap); + let withdraw_cap = extract_withdraw_capability(&gas_fee_signer); + let token = withdraw_with_capability(&withdraw_cap, amount); + restore_withdraw_capability(withdraw_cap); + deposit(CoreAddresses::ASSOCIATION_ROOT_ADDRESS(), token); + } +} + + +module EasyGasScript { + use StarcoinFramework::TransferScripts::peer_to_peer_v2; + use StarcoinFramework::EasyGas; + + public entry fun register(sender: signer, precision: u8) { + EasyGas::register_oracle(&sender, precision) + } + + public entry fun init_data_source(sender: signer, init_value: u128) { + EasyGas::init_oracle_source(&sender, init_value); + } + + public entry fun update(sender: signer, value: u128) { + EasyGas::update_oracle(&sender, value) + } + + public entry fun withdraw_gas_fee_entry(sender: signer, amount: u128) { + EasyGas::withdraw_gas_fee(&sender, amount); + } + public entry fun deposit(sender: signer, amount:u128) { + let address = EasyGas::get_gas_fee_address(); + peer_to_peer_v2(sender, address, amount) + } +} +} diff --git a/release/v13/sources/EasyGasScript.move b/release/v13/sources/EasyGasScript.move new file mode 100644 index 00000000..e5ba4710 --- /dev/null +++ b/release/v13/sources/EasyGasScript.move @@ -0,0 +1,147 @@ +address StarcoinFramework { + +module EasyGas { + use StarcoinFramework::Account; + use StarcoinFramework::Account::{extract_withdraw_capability, withdraw_with_capability, restore_withdraw_capability, + deposit, SignerCapability + }; + use StarcoinFramework::Signer::address_of; + use StarcoinFramework::TypeInfo::{type_of, module_name, account_address, struct_name}; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::GenesisSignerCapability; + use StarcoinFramework::PriceOracle; + use StarcoinFramework::Errors; + + const EBAD_TRANSACTION_FEE_TOKEN: u64 = 18; + struct STCToken has copy, store, drop {} + + struct GasTokenEntry has key, store, drop { + account_address: address, + module_name: vector, + struct_name: vector, + data_source: address, + } + + struct GasFeeAddress has key, store { + gas_fee_address: address, + cap: SignerCapability, + } + + public fun initialize( + sender: &signer, + token_account_address: address, + token_module_name: vector, + token_struct_name: vector, + data_source: address, + ) acquires GasTokenEntry { + register_gas_token(sender, token_account_address, token_module_name, token_struct_name, data_source); + create_gas_fee_address(sender); + } + + public fun register_oracle(sender: &signer, precision: u8) { + PriceOracle::register_oracle>(sender, precision); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + //todo:check gas token entry + Account::do_accept_token(&genesis_account); + } + + public fun init_oracle_source(sender: &signer, init_value: u128) { + PriceOracle::init_data_source>(sender, init_value); + } + + public fun update_oracle(sender: &signer, value: u128) { + PriceOracle::update>(sender, value); + } + + public fun get_scaling_factor(): u128 { + PriceOracle::get_scaling_factor>() + } + + public fun gas_oracle_read(): u128 acquires GasTokenEntry { + let data_source = get_data_source_address(); + PriceOracle::read>(data_source) + } + + + fun register_gas_token( + sender: &signer, + account_address: address, + module_name: vector, + struct_name: vector, + data_source: address, + ) acquires GasTokenEntry { + CoreAddresses::assert_genesis_address(sender); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let gas_token_entry = GasTokenEntry { account_address, module_name, struct_name, data_source }; + if (exists(address_of(&genesis_account))) { + move_from(address_of(&genesis_account)); + }; + move_to(&genesis_account, gas_token_entry); + } + + fun get_data_source_address(): address acquires GasTokenEntry { + let token_type_info = type_of(); + let genesis = CoreAddresses::GENESIS_ADDRESS(); + let gas_token_entry = borrow_global(genesis); + assert!(module_name(&token_type_info) == *&gas_token_entry.module_name && account_address( + &token_type_info + ) == *&gas_token_entry.account_address && struct_name(&token_type_info) == *&gas_token_entry.struct_name, Errors::invalid_argument(EBAD_TRANSACTION_FEE_TOKEN)); + gas_token_entry.data_source + } + + fun create_gas_fee_address( + sender: &signer, + ) { + CoreAddresses::assert_genesis_address(sender); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let (gas_fee_address, cap) = Account::create_delegate_account(&genesis_account); + let gas_fee_signer = Account::create_signer_with_cap(&cap); + Account::set_auto_accept_token(&gas_fee_signer, true); + let gas_fee_address_entry = GasFeeAddress { gas_fee_address, cap }; + move_to(&genesis_account, gas_fee_address_entry); + } + + public fun get_gas_fee_address(): address acquires GasFeeAddress { + let genesis = CoreAddresses::GENESIS_ADDRESS(); + let gas_fee_address_entry = borrow_global(genesis); + + return gas_fee_address_entry.gas_fee_address + } + + public fun withdraw_gas_fee(_sender: &signer, amount: u128) acquires GasFeeAddress { + let genesis = CoreAddresses::GENESIS_ADDRESS(); + let gas_fee_address_entry = borrow_global(genesis); + let gas_fee_signer = Account::create_signer_with_cap(&gas_fee_address_entry.cap); + let withdraw_cap = extract_withdraw_capability(&gas_fee_signer); + let token = withdraw_with_capability(&withdraw_cap, amount); + restore_withdraw_capability(withdraw_cap); + deposit(CoreAddresses::ASSOCIATION_ROOT_ADDRESS(), token); + } +} + + +module EasyGasScript { + use StarcoinFramework::TransferScripts::peer_to_peer_v2; + use StarcoinFramework::EasyGas; + + public entry fun register(sender: signer, precision: u8) { + EasyGas::register_oracle(&sender, precision) + } + + public entry fun init_data_source(sender: signer, init_value: u128) { + EasyGas::init_oracle_source(&sender, init_value); + } + + public entry fun update(sender: signer, value: u128) { + EasyGas::update_oracle(&sender, value) + } + + public entry fun withdraw_gas_fee_entry(sender: signer, amount: u128) { + EasyGas::withdraw_gas_fee(&sender, amount); + } + public entry fun deposit(sender: signer, amount:u128) { + let address = EasyGas::get_gas_fee_address(); + peer_to_peer_v2(sender, address, amount) + } +} +} diff --git a/release/v13/sources/EmptyScripts.move b/release/v13/sources/EmptyScripts.move new file mode 100644 index 00000000..2735276b --- /dev/null +++ b/release/v13/sources/EmptyScripts.move @@ -0,0 +1,14 @@ +address StarcoinFramework { + // A empty scripts module for call a script but do nothing. + module EmptyScripts { + + spec module { + pragma verify = false; + pragma aborts_if_is_partial = false; + pragma aborts_if_is_strict = false; + } + + public entry fun empty_script() { + } + } +} \ No newline at end of file diff --git a/release/v13/sources/Epoch.move b/release/v13/sources/Epoch.move new file mode 100644 index 00000000..bc6ef8ad --- /dev/null +++ b/release/v13/sources/Epoch.move @@ -0,0 +1,441 @@ +address StarcoinFramework { +/// The module provide epoch functionality for starcoin. +module Epoch { + use StarcoinFramework::Config; + use StarcoinFramework::Signer; + use StarcoinFramework::CoreAddresses; + + use StarcoinFramework::Event; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Math; + use StarcoinFramework::Option; + use StarcoinFramework::ConsensusConfig::{Self, ConsensusConfig}; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// Current epoch info. + struct Epoch has key { + /// Number of current epoch + number: u64, + /// Start time of current epoch + start_time: u64, + /// Start block's number of current epoch + start_block_number: u64, + /// End block's number of current epoch + end_block_number: u64, + /// Average target time to calculate a block's difficulty in current epoch + block_time_target: u64, + /// Rewards per block in current epoch + reward_per_block: u128, + /// Percentage of `reward_per_block` to reward a uncle block in current epoch + reward_per_uncle_percent: u64, + /// How many ancestor blocks which use to calculate next block's difficulty in current epoch + block_difficulty_window: u64, + /// Maximum number of uncle block per block in current epoch + max_uncles_per_block: u64, + /// Maximum gases per block in current epoch + block_gas_limit: u64, + /// Strategy to calculate difficulty in current epoch + strategy: u8, + /// Switch Epoch Event + new_epoch_events: Event::EventHandle, + } + + /// New epoch event. + struct NewEpochEvent has drop, store { + /// Epoch::number + number: u64, + /// Epoch::start_time + start_time: u64, + /// Epoch::start_block_number + start_block_number: u64, + /// Epoch::end_block_number + end_block_number: u64, + /// Epoch::block_time_target + block_time_target: u64, + /// Epoch::reward_per_block + reward_per_block: u128, + /// Total rewards during previous epoch + previous_epoch_total_reward: u128, + } + + /// Epoch data. + struct EpochData has key { + /// Up to now, Number of uncle block during current epoch + uncles: u64, + /// Up to now, Total rewards during current epoch + total_reward: u128, + /// Up to now, Total gases during current epoch + total_gas: u128, + } + + const THOUSAND: u64 = 1000; + const THOUSAND_U128: u128 = 1000; + const HUNDRED: u64 = 100; + + const EUNREACHABLE: u64 = 19; + const EINVALID_UNCLES_COUNT: u64 = 101; + + /// Initialization of the module. + public fun initialize( + account: &signer, + ) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + + let config = ConsensusConfig::get_config(); + move_to( + account, + Epoch { + number: 0, + start_time: Timestamp::now_milliseconds(), + start_block_number: 0, + end_block_number: ConsensusConfig::epoch_block_count(&config), + block_time_target: ConsensusConfig::base_block_time_target(&config), + reward_per_block: ConsensusConfig::base_reward_per_block(&config), + reward_per_uncle_percent: ConsensusConfig::base_reward_per_uncle_percent(&config), + block_difficulty_window: ConsensusConfig::base_block_difficulty_window(&config), + max_uncles_per_block: ConsensusConfig::base_max_uncles_per_block(&config), + block_gas_limit: ConsensusConfig::base_block_gas_limit(&config), + strategy: ConsensusConfig::strategy(&config), + new_epoch_events: Event::new_event_handle(account), + }, + ); + move_to(account, EpochData { uncles: 0, total_reward: 0, total_gas: 0 }); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + + aborts_if exists(Signer::address_of(account)); + aborts_if exists(Signer::address_of(account)); + } + + /// compute next block time_target. + public fun compute_next_block_time_target( + config: &ConsensusConfig, + last_epoch_time_target: u64, + epoch_start_time: u64, + now_milli_second: u64, + start_block_number: u64, + end_block_number: u64, + total_uncles: u64 + ): u64 { + let total_time = now_milli_second - epoch_start_time; + let blocks = end_block_number - start_block_number; + let avg_block_time = total_time / blocks; + let uncles_rate = total_uncles * THOUSAND / blocks; + let new_epoch_block_time_target = (THOUSAND + uncles_rate) * avg_block_time / + (ConsensusConfig::uncle_rate_target(config) + THOUSAND); + if (new_epoch_block_time_target > last_epoch_time_target * 2) { + new_epoch_block_time_target = last_epoch_time_target * 2; + }; + if (new_epoch_block_time_target < last_epoch_time_target / 2) { + new_epoch_block_time_target = last_epoch_time_target / 2; + }; + let min_block_time_target = ConsensusConfig::min_block_time_target(config); + let max_block_time_target = ConsensusConfig::max_block_time_target(config); + if (new_epoch_block_time_target < min_block_time_target) { + new_epoch_block_time_target = min_block_time_target; + }; + if (new_epoch_block_time_target > max_block_time_target) { + new_epoch_block_time_target = max_block_time_target; + }; + new_epoch_block_time_target + } + + spec compute_next_block_time_target { + pragma verify = false; + } + + /// adjust_epoch try to advance to next epoch if current epoch ends. + public fun adjust_epoch( + account: &signer, + block_number: u64, + timestamp: u64, + uncles: u64, + parent_gas_used: u64 + ): u128 + acquires Epoch, EpochData { + CoreAddresses::assert_genesis_address(account); + + let epoch_ref = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + // assert!(epoch_ref.max_uncles_per_block >= uncles, Errors::invalid_argument(EINVALID_UNCLES_COUNT)); + + let epoch_data = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + let (new_epoch, reward_per_block) = if (block_number < epoch_ref.end_block_number) { + (false, epoch_ref.reward_per_block) + } else if (block_number == epoch_ref.end_block_number) { + //start a new epoch + // assert!(uncles == 0, Errors::invalid_argument(EINVALID_UNCLES_COUNT)); + // block time target unit is milli_seconds. + let now_milli_seconds = timestamp; + + let config = ConsensusConfig::get_config(); + let last_epoch_time_target = epoch_ref.block_time_target; + let new_epoch_block_time_target = compute_next_block_time_target( + &config, + last_epoch_time_target, + epoch_ref.start_time, + now_milli_seconds, + epoch_ref.start_block_number, + epoch_ref.end_block_number, + epoch_data.uncles + ); + let new_reward_per_block = ConsensusConfig::do_compute_reward_per_block( + &config, + new_epoch_block_time_target + ); + + //update epoch by adjust result or config, because ConsensusConfig may be updated. + epoch_ref.number = epoch_ref.number + 1; + epoch_ref.start_time = now_milli_seconds; + epoch_ref.start_block_number = block_number; + epoch_ref.end_block_number = block_number + ConsensusConfig::epoch_block_count(&config); + epoch_ref.block_time_target = new_epoch_block_time_target; + epoch_ref.reward_per_block = new_reward_per_block; + epoch_ref.reward_per_uncle_percent = ConsensusConfig::base_reward_per_uncle_percent(&config); + epoch_ref.block_difficulty_window = ConsensusConfig::base_block_difficulty_window(&config); + epoch_ref.max_uncles_per_block = ConsensusConfig::base_max_uncles_per_block(&config); + epoch_ref.strategy = ConsensusConfig::strategy(&config); + + epoch_data.uncles = 0; + let last_epoch_total_gas = epoch_data.total_gas + (parent_gas_used as u128); + adjust_gas_limit( + &config, + epoch_ref, + last_epoch_time_target, + new_epoch_block_time_target, + last_epoch_total_gas + ); + emit_epoch_event(epoch_ref, epoch_data.total_reward); + (true, new_reward_per_block) + } else { + //This should never happened. + abort EUNREACHABLE + }; + let reward = reward_per_block + + reward_per_block * (epoch_ref.reward_per_uncle_percent as u128) * (uncles as u128) / (HUNDRED as u128); + update_epoch_data(epoch_data, new_epoch, reward, uncles, parent_gas_used); + reward + } + + spec adjust_epoch { + pragma verify = false; //timeout + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(Signer::address_of(account)); + aborts_if global(Signer::address_of(account)).max_uncles_per_block < uncles; + aborts_if exists(Signer::address_of(account)); + aborts_if block_number == global(Signer::address_of(account)).end_block_number && uncles != 0; + // ... + } + + fun adjust_gas_limit( + config: &ConsensusConfig, + epoch_ref: &mut Epoch, + last_epoch_time_target: u64, + new_epoch_time_target: u64, + last_epoch_total_gas: u128 + ) { + let new_gas_limit = compute_gas_limit( + config, + last_epoch_time_target, + new_epoch_time_target, + epoch_ref.block_gas_limit, + last_epoch_total_gas + ); + if (Option::is_some(&new_gas_limit)) { + epoch_ref.block_gas_limit = Option::destroy_some(new_gas_limit); + } + } + + spec adjust_gas_limit { + pragma verify = false; //mul_div() timeout + } + + /// Compute block's gas limit of next epoch. + public fun compute_gas_limit( + config: &ConsensusConfig, + last_epoch_time_target: u64, + new_epoch_time_target: u64, + last_epoch_block_gas_limit: u64, + last_epoch_total_gas: u128 + ): Option::Option { + let epoch_block_count = (ConsensusConfig::epoch_block_count(config) as u128); + let gas_limit_threshold = (last_epoch_total_gas >= Math::mul_div( + (last_epoch_block_gas_limit as u128) * epoch_block_count, + (80 as u128), + (HUNDRED as u128) + )); + let new_gas_limit = Option::none(); + + let min_block_time_target = ConsensusConfig::min_block_time_target(config); + let max_block_time_target = ConsensusConfig::max_block_time_target(config); + let base_block_gas_limit = ConsensusConfig::base_block_gas_limit(config); + if (last_epoch_time_target == new_epoch_time_target) { + if (new_epoch_time_target == min_block_time_target && gas_limit_threshold) { + let increase_gas_limit = in_or_decrease_gas_limit( + last_epoch_block_gas_limit, + 110, + base_block_gas_limit + ); + new_gas_limit = Option::some(increase_gas_limit); + } else if (new_epoch_time_target == max_block_time_target && !gas_limit_threshold) { + let decrease_gas_limit = in_or_decrease_gas_limit(last_epoch_block_gas_limit, 90, base_block_gas_limit); + new_gas_limit = Option::some(decrease_gas_limit); + } + }; + + new_gas_limit + } + + spec compute_gas_limit { + pragma verify = false; //mul_div() timeout + } + + fun in_or_decrease_gas_limit(last_epoch_block_gas_limit: u64, percent: u64, min_block_gas_limit: u64): u64 { + let tmp_gas_limit = Math::mul_div((last_epoch_block_gas_limit as u128), (percent as u128), (HUNDRED as u128)); + let new_gas_limit = if (tmp_gas_limit > (min_block_gas_limit as u128)) { + (tmp_gas_limit as u64) + } else { + min_block_gas_limit + }; + + new_gas_limit + } + + spec in_or_decrease_gas_limit { + include Math::MulDivAbortsIf { x: last_epoch_block_gas_limit, y: percent, z: HUNDRED }; + aborts_if Math::spec_mul_div() > MAX_U64; + } + + fun update_epoch_data( + epoch_data: &mut EpochData, + new_epoch: bool, + reward: u128, + uncles: u64, + parent_gas_used: u64 + ) { + if (new_epoch) { + epoch_data.total_reward = reward; + epoch_data.uncles = uncles; + epoch_data.total_gas = 0; + } else { + epoch_data.total_reward = epoch_data.total_reward + reward; + epoch_data.uncles = epoch_data.uncles + uncles; + epoch_data.total_gas = epoch_data.total_gas + (parent_gas_used as u128); + } + } + + spec update_epoch_data { + aborts_if !new_epoch && epoch_data.total_reward + reward > MAX_U128; + aborts_if !new_epoch && epoch_data.uncles + uncles > MAX_U64; + aborts_if !new_epoch && epoch_data.total_gas + parent_gas_used > MAX_U128; + } + + fun emit_epoch_event(epoch_ref: &mut Epoch, previous_epoch_total_reward: u128) { + Event::emit_event( + &mut epoch_ref.new_epoch_events, + NewEpochEvent { + number: epoch_ref.number, + start_time: epoch_ref.start_time, + start_block_number: epoch_ref.start_block_number, + end_block_number: epoch_ref.end_block_number, + block_time_target: epoch_ref.block_time_target, + reward_per_block: epoch_ref.reward_per_block, + previous_epoch_total_reward, + }, + ); + } + + spec emit_epoch_event { + aborts_if false; + } + + /// Get start time of current epoch + public fun start_time(): u64 acquires Epoch { + let epoch_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_ref.start_time + } + + spec start_time { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get uncles number of current epoch + public fun uncles(): u64 acquires EpochData { + let epoch_data = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_data.uncles + } + + spec uncles { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get total gas of current epoch + public fun total_gas(): u128 acquires EpochData { + let epoch_data = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_data.total_gas + } + + spec total_gas { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get block's gas_limit of current epoch + public fun block_gas_limit(): u64 acquires Epoch { + let epoch_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_ref.block_gas_limit + } + + spec block_gas_limit { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get start block's number of current epoch + public fun start_block_number(): u64 acquires Epoch { + let epoch_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_ref.start_block_number + } + + spec start_block_number { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get end block's number of current epoch + public fun end_block_number(): u64 acquires Epoch { + let epoch_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_ref.end_block_number + } + + spec end_block_number { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get current epoch number + public fun number(): u64 acquires Epoch { + let epoch_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_ref.number + } + + spec number { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get current block time target + public fun block_time_target(): u64 acquires Epoch { + let epoch_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + epoch_ref.block_time_target + } + + spec block_time_target { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } +} +} diff --git a/release/v13/sources/Errors.move b/release/v13/sources/Errors.move new file mode 100644 index 00000000..4a181039 --- /dev/null +++ b/release/v13/sources/Errors.move @@ -0,0 +1,169 @@ +address StarcoinFramework { + +/// Module defining error codes used in Move aborts throughout the framework. +/// +/// A `u64` error code is constructed from two values: +/// +/// 1. The *error category* which is encoded in the lower 8 bits of the code. Error categories are +/// declared in this module and are globally unique across the Diem framework. There is a limited +/// fixed set of predefined categories, and the framework is guaranteed to use those consistently. +/// +/// 2. The *error reason* which is encoded in the remaining 56 bits of the code. The reason is a unique +/// number relative to the module which raised the error and can be used to obtain more information about +/// the error at hand. It is mostly used for diagnosis purposes. Error reasons may change over time as the +/// framework evolves. +/// +/// Rules to declare or use *error reason*: +/// 1. error reason is declared as const in the user module +/// 2. error reason name must start with "E", for example, const EACCOUNT_DOES_NOT_EXIST = ... +/// 3. value less than 100 is reserved for general purpose and shared by all modules +/// 4. don't change general purpose error reason value, it's co-related with error code in starcoin vm +/// 5. self-defined error reason value must be large than 100 +/// 6. error reason must be used together with error category +/// +module Errors { + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// A function to create an error from from a category and a reason. + fun make(category: u8, reason: u64): u64 { + (category as u64) + (reason << 8) + } + spec make { + pragma opaque = true; + pragma verify = false; + //ensures [concrete] result == category + (reason << 8); + aborts_if [abstract] false; + ensures [abstract] result == category; + } + + /// The system is in a state where the performed operation is not allowed. Example: call to a function only allowed + /// in genesis + const INVALID_STATE: u8 = 1; + + /// The signer of a transaction does not have the expected address for this operation. Example: a call to a function + /// which publishes a resource under a particular address. + const REQUIRES_ADDRESS: u8 = 2; + + /// The signer of a transaction does not have the expected role for this operation. Example: a call to a function + /// which requires the signer to have the role of treasury compliance. + const REQUIRES_ROLE: u8 = 3; + + /// The signer of a transaction does not have a required capability. + const REQUIRES_CAPABILITY: u8 = 4; + + /// A resource is required but not published. Example: access to non-existing resource. + const NOT_PUBLISHED: u8 = 5; + + /// Attempting to publish a resource that is already published. Example: calling an initialization function + /// twice. + const ALREADY_PUBLISHED: u8 = 6; + + /// An argument provided to an operation is invalid. Example: a signing key has the wrong format. + const INVALID_ARGUMENT: u8 = 7; + + /// A limit on an amount, e.g. a currency, is exceeded. Example: withdrawal of money after account limits window + /// is exhausted. + const LIMIT_EXCEEDED: u8 = 8; + + /// An internal error (bug) has occurred. + const INTERNAL: u8 = 10; + + /// deprecated code + const DEPRECATED: u8 = 11; + + /// A custom error category for extension points. + const CUSTOM: u8 = 255; + + /// Create an error of `invalid_state` + public fun invalid_state(reason: u64): u64 { make(INVALID_STATE, reason) } + spec invalid_state { + pragma opaque = true; + aborts_if false; + ensures result == INVALID_STATE; + } + + /// Create an error of `requires_address`. + public fun requires_address(reason: u64): u64 { make(REQUIRES_ADDRESS, reason) } + spec requires_address { + pragma opaque = true; + aborts_if false; + ensures result == REQUIRES_ADDRESS; + } + + /// Create an error of `requires_role`. + public fun requires_role(reason: u64): u64 { make(REQUIRES_ROLE, reason) } + spec requires_role { + pragma opaque = true; + aborts_if false; + ensures result == REQUIRES_ROLE; + } + + /// Create an error of `requires_capability`. + public fun requires_capability(reason: u64): u64 { make(REQUIRES_CAPABILITY, reason) } + spec requires_capability { + pragma opaque = true; + aborts_if false; + ensures result == REQUIRES_CAPABILITY; + } + + /// Create an error of `not_published`. + public fun not_published(reason: u64): u64 { make(NOT_PUBLISHED, reason) } + spec not_published { + pragma opaque = true; + aborts_if false; + ensures result == NOT_PUBLISHED; + } + + /// Create an error of `already_published`. + public fun already_published(reason: u64): u64 { make(ALREADY_PUBLISHED, reason) } + spec already_published { + pragma opaque = true; + aborts_if false; + ensures result == ALREADY_PUBLISHED; + } + + /// Create an error of `invalid_argument`. + public fun invalid_argument(reason: u64): u64 { make(INVALID_ARGUMENT, reason) } + spec invalid_argument { + pragma opaque = true; + aborts_if false; + ensures result == INVALID_ARGUMENT; + } + + /// Create an error of `limit_exceeded`. + public fun limit_exceeded(reason: u64): u64 { make(LIMIT_EXCEEDED, reason) } + spec limit_exceeded { + pragma opaque = true; + aborts_if false; + ensures result == LIMIT_EXCEEDED; + } + + /// Create an error of `internal`. + public fun internal(reason: u64): u64 { make(INTERNAL, reason) } + spec internal { + pragma opaque = true; + aborts_if false; + ensures result == INTERNAL; + } + + /// Create an error of `deprecated`. + public fun deprecated(reason: u64): u64 { make(DEPRECATED, reason) } + spec deprecated { + pragma opaque = true; + aborts_if false; + ensures result == DEPRECATED; + } + + /// Create an error of `custom`. + public fun custom(reason: u64): u64 { make(CUSTOM, reason) } + spec custom { + pragma opaque = true; + aborts_if false; + ensures result == CUSTOM; + } +} + +} diff --git a/release/v13/sources/Event.move b/release/v13/sources/Event.move new file mode 100644 index 00000000..69fedeac --- /dev/null +++ b/release/v13/sources/Event.move @@ -0,0 +1,95 @@ +address StarcoinFramework { + +/// The Event module defines an `EventHandleGenerator` that is used to create +/// `EventHandle`s with unique GUIDs. It contains a counter for the number +/// of `EventHandle`s it generates. An `EventHandle` is used to count the number of +/// events emitted to a handle and emit events to the event store. +module Event { + use StarcoinFramework::Errors; + use StarcoinFramework::BCS; + use StarcoinFramework::Signer; + use StarcoinFramework::Vector; + + /// A resource representing the counter used to generate uniqueness under each account. There won't be destructor for + /// this resource to guarantee the uniqueness of the generated handle. + struct EventHandleGenerator has key { + // A monotonically increasing counter + counter: u64, + addr: address, + } + + /// A handle for an event such that: + /// 1. Other modules can emit events to this handle. + /// 2. Storage can use this handle to prove the total number of events that happened in the past. + struct EventHandle has store { + /// Total number of events emitted to this event stream. + counter: u64, + /// A globally unique ID for this event stream. + guid: vector, + } + + /// The event generator resource was in an invalid state + const EEVENT_GENERATOR: u64 = 0; + + /// Publishs a new event handle generator. + public fun publish_generator(account: &signer) { + let addr = Signer::address_of(account); + assert!(!exists(addr), Errors::already_published(EEVENT_GENERATOR)); + move_to(account, EventHandleGenerator{ counter: 0, addr }) + } + + /// Derive a fresh unique id by using sender's EventHandleGenerator. The generated vector is indeed unique because it + /// was derived from the hash(sender's EventHandleGenerator || sender_address). This module guarantees that the + /// EventHandleGenerator is only going to be monotonically increased and there's no way to revert it or destroy it. Thus + /// such counter is going to give distinct value for each of the new event stream under each sender. And since we + /// hash it with the sender's address, the result is guaranteed to be globally unique. + fun fresh_guid(counter: &mut EventHandleGenerator): vector { + let sender_bytes = BCS::to_bytes(&counter.addr); + let count_bytes = BCS::to_bytes(&counter.counter); + counter.counter = counter.counter + 1; + + // EventHandleGenerator goes first just in case we want to extend address in the future. + Vector::append(&mut count_bytes, sender_bytes); + + count_bytes + } + + /// Use EventHandleGenerator to generate a unique event handle for `sig` + public fun new_event_handle(account: &signer): EventHandle + acquires EventHandleGenerator { + let addr = Signer::address_of(account); + assert!(exists(addr), Errors::not_published(EEVENT_GENERATOR)); + EventHandle { + counter: 0, + guid: fresh_guid(borrow_global_mut(addr)) + } + } + + /// Emit an event with payload `msg` by using `handle_ref`'s key and counter. + public fun emit_event(handle_ref: &mut EventHandle, msg: T) { + let guid = *&handle_ref.guid; + + write_to_event_store(guid, handle_ref.counter, msg); + handle_ref.counter = handle_ref.counter + 1; + } + + /// Native procedure that writes to the actual event stream in Event store + /// This will replace the "native" portion of EmitEvent bytecode + native fun write_to_event_store(guid: vector, count: u64, msg: T); + + /// Destroy a unique handle. + public fun destroy_handle(handle: EventHandle) { + EventHandle { counter: _, guid: _ } = handle; + } + + // ****************** SPECIFICATIONS ******************* + spec module {} // switch documentation context to module + + spec module { + /// Functions of the event module are mocked out using the intrinsic + /// pragma. They are implemented in the prover's prelude. + pragma intrinsic = true; + } +} + +} diff --git a/release/v13/sources/EventUtil.move b/release/v13/sources/EventUtil.move new file mode 100644 index 00000000..1ecfceb5 --- /dev/null +++ b/release/v13/sources/EventUtil.move @@ -0,0 +1,36 @@ +module StarcoinFramework::EventUtil { + use StarcoinFramework::Event; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + const ERR_INIT_REPEATE: u64 = 101; + const ERR_RESOURCE_NOT_EXISTS: u64 = 102; + + struct EventHandleWrapper has key { + handle: Event::EventHandle, + } + + public fun init_event(sender: &signer) { + let broker = Signer::address_of(sender); + assert!(!exists>(broker), Errors::invalid_state(ERR_INIT_REPEATE)); + move_to(sender, EventHandleWrapper { + handle: Event::new_event_handle(sender) + }); + } + + public fun uninit_event(sender: &signer) acquires EventHandleWrapper { + let broker = Signer::address_of(sender); + assert!(exists>(broker), Errors::invalid_state(ERR_RESOURCE_NOT_EXISTS)); + let EventHandleWrapper { handle } = move_from>(broker); + Event::destroy_handle(handle); + } + + public fun emit_event(broker: address, event: EventT) acquires EventHandleWrapper { + let event_handle = borrow_global_mut>(broker); + Event::emit_event(&mut event_handle.handle, event); + } + + public fun exist_event(broker: address): bool { + exists>(broker) + } +} diff --git a/release/v13/sources/FixedPoint32.move b/release/v13/sources/FixedPoint32.move new file mode 100644 index 00000000..0cf4450c --- /dev/null +++ b/release/v13/sources/FixedPoint32.move @@ -0,0 +1,157 @@ +address StarcoinFramework { +/// The module provide operations for FixedPoint32. +module FixedPoint32 { + + use StarcoinFramework::Errors; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// Define a fixed-point numeric type with 32 fractional bits. + /// This is just a u64 integer but it is wrapped in a struct to + /// make a unique type. + struct FixedPoint32 has copy, drop, store { value: u64 } + + const MAX_U64: u128 = 18446744073709551615; + + /// The denominator provided was zero + const EDENOMINATOR: u64 = 101; + /// The quotient value would be too large to be held in a `u64` + const EDIVISION: u64 = 102; + /// The multiplied value would be too large to be held in a `u64` + const EMULTIPLICATION: u64 = 103; + /// A division by zero was encountered + const EDIVISION_BY_ZERO: u64 = 104; + /// The computed ratio when converting to a `FixedPoint32` would be unrepresentable + const ERATIO_OUT_OF_RANGE: u64 = 105; + + /// Multiply a u64 integer by a fixed-point number, truncating any + /// fractional part of the product. This will abort if the product + /// overflows. + public fun multiply_u64(val: u64, multiplier: FixedPoint32): u64 { + // The product of two 64 bit values has 128 bits, so perform the + // multiplication with u128 types and keep the full 128 bit product + // to avoid losing accuracy. + let unscaled_product = (val as u128) * (multiplier.value as u128); + // The unscaled product has 32 fractional bits (from the multiplier) + // so rescale it by shifting away the low bits. + let product = unscaled_product >> 32; + // Check whether the value is too large. + assert!(product <= MAX_U64, Errors::limit_exceeded(EMULTIPLICATION)); + (product as u64) + } + spec multiply_u64 { + /// Currently, we ignore the actual implementation of this function in verification + /// and treat it as uninterpreted, which simplifies the verification problem significantly. + /// This way we avoid the non-linear arithmetic problem presented by this function. + /// + /// Abstracting this and related functions is possible because the correctness of currency + /// conversion (where `FixedPoint32` is used for) is not relevant for the rest of the contract + /// control flow, so we can assume some arbitrary (but fixed) behavior here. + pragma opaque = true; + include MultiplyAbortsIf; + ensures result == spec_multiply_u64(val, multiplier); + } + spec schema MultiplyAbortsIf { + val: num; + multiplier: FixedPoint32; + aborts_if spec_multiply_u64(val, multiplier) > MAX_U64 with Errors::LIMIT_EXCEEDED; + } + spec fun spec_multiply_u64(val: num, multiplier: FixedPoint32): num { + (val * multiplier.value) >> 32 + } + + /// Divide a u64 integer by a fixed-point number, truncating any + /// fractional part of the quotient. This will abort if the divisor + /// is zero or if the quotient overflows. + public fun divide_u64(val: u64, divisor: FixedPoint32): u64 { + // Check for division by zero. + assert!(divisor.value != 0, Errors::invalid_argument(EDIVISION_BY_ZERO)); + // First convert to 128 bits and then shift left to + // add 32 fractional zero bits to the dividend. + let scaled_value = (val as u128) << 32; + let quotient = scaled_value / (divisor.value as u128); + // Check whether the value is too large. + assert!(quotient <= MAX_U64, Errors::limit_exceeded(EDIVISION)); + // the value may be too large, which will cause the cast to fail + // with an arithmetic error. + (quotient as u64) + } + spec divide_u64 { + /// See comment at `Self::multiply_64`. + pragma opaque = true; + include DivideAbortsIf; + ensures result == spec_divide_u64(val, divisor); + } + spec schema DivideAbortsIf { + val: num; + divisor: FixedPoint32; + aborts_if divisor.value == 0 with Errors::INVALID_ARGUMENT; + aborts_if spec_divide_u64(val, divisor) > MAX_U64 with Errors::LIMIT_EXCEEDED; + } + spec fun spec_divide_u64(val: num, divisor: FixedPoint32): num { + (val << 32) / divisor.value + } + + /// Create a fixed-point value from a rational number specified by its + /// numerator and denominator. This function is for convenience; it is also + /// perfectly fine to create a fixed-point value by directly specifying the + /// raw value. This will abort if the denominator is zero or if the ratio is + /// not in the range 2^-32 .. 2^32-1. + public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32 { + // If the denominator is zero, this will abort. + // Scale the numerator to have 64 fractional bits and the denominator + // to have 32 fractional bits, so that the quotient will have 32 + // fractional bits. + let scaled_numerator = (numerator as u128) << 64; + let scaled_denominator = (denominator as u128) << 32; + assert!(scaled_denominator != 0, Errors::invalid_argument(EDENOMINATOR)); + let quotient = scaled_numerator / scaled_denominator; + assert!(quotient != 0 || numerator == 0, Errors::invalid_argument(ERATIO_OUT_OF_RANGE)); + // Return the quotient as a fixed-point number. We first need to check whether the cast + // can succeed. + assert!(quotient <= MAX_U64, Errors::limit_exceeded(ERATIO_OUT_OF_RANGE)); + FixedPoint32 { value: (quotient as u64) } + } + spec create_from_rational { + /// See comment at `Self::multiply_64`. + pragma verify = false; + pragma opaque = true; + include CreateFromRationalAbortsIf; + ensures result == spec_create_from_rational(numerator, denominator); + } + spec schema CreateFromRationalAbortsIf { + numerator: u64; + denominator: u64; + let scaled_numerator = numerator << 64; + let scaled_denominator = denominator << 32; + let quotient = scaled_numerator / scaled_denominator; + aborts_if scaled_denominator == 0 with Errors::INVALID_ARGUMENT; + aborts_if quotient == 0 && scaled_numerator != 0 with Errors::INVALID_ARGUMENT; + aborts_if quotient > MAX_U64 with Errors::LIMIT_EXCEEDED; + } + spec fun spec_create_from_rational(numerator: num, denominator: num): FixedPoint32 { + FixedPoint32{value: (numerator << 64) / (denominator << 32)} + } + + /// create a fixedpoint 32 from u64. + public fun create_from_raw_value(value: u64): FixedPoint32 { + FixedPoint32 { value } + } + spec create_from_raw_value { + pragma opaque; + aborts_if false; + ensures result.value == value; + } + + /// Accessor for the raw u64 value. Other less common operations, such as + /// adding or subtracting FixedPoint32 values, can be done using the raw + /// values directly. + public fun get_raw_value(num: FixedPoint32): u64 { + num.value + } +} + +} diff --git a/release/v13/sources/FlexiDagConfig.move b/release/v13/sources/FlexiDagConfig.move new file mode 100644 index 00000000..75fa8e7a --- /dev/null +++ b/release/v13/sources/FlexiDagConfig.move @@ -0,0 +1,54 @@ +address StarcoinFramework { + module FlexiDagConfig { + + use StarcoinFramework::Block; + use StarcoinFramework::Config; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Signer; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict; + } + + /// The struct to hold all config data needed for Flexidag. + struct FlexiDagConfig has copy, drop, store { + // todo: double check, epoch might be better? + // the height of dag genesis block + effective_height: u64, + } + + /// Create a new configuration for flexidag, mainly used in DAO. + public fun new_flexidag_config(effective_height: u64): FlexiDagConfig { + FlexiDagConfig { + effective_height, + } + } + + public fun initialize(account: &signer, effective_height: u64) { + CoreAddresses::assert_genesis_address(account); + Config::publish_new_config(account, new_flexidag_config(effective_height)); + Block::initialize_blockmetadata_v2(account); + } + + spec initialize { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists>(Signer::address_of(account)); + aborts_if exists>(Signer::address_of(account)); + ensures exists>(Signer::address_of(account)); + ensures + exists>( + Signer::address_of(account), + ); + } + + public fun effective_height(account: address): u64 { + let flexi_dag_config = Config::get_by_address(account); + flexi_dag_config.effective_height + } + + spec effective_height { + include Config::AbortsIfConfigNotExist { addr: account }; + } + } +} diff --git a/release/v13/sources/FromBCS.move b/release/v13/sources/FromBCS.move new file mode 100644 index 00000000..8f6376a0 --- /dev/null +++ b/release/v13/sources/FromBCS.move @@ -0,0 +1,85 @@ +/// This module provides a number of functions to convert _primitive_ types from their representation in `std::bcs` +/// to values. This is the opposite of `bcs::to_bytes`. Note that it is not safe to define a generic public `from_bytes` +/// function because this can violate implicit struct invariants, therefore only primitive types are offerred. If +/// a general conversion back-and-force is needed, consider the `StarcoinFramework::Any` type which preserves invariants. +/// +/// Example: +/// ``` +/// use std::bcs; +/// use StarcoinFramework::from_bcs; +/// +/// assert!(from_bcs::to_address(bcs::to_bytes(&@0xabcdef)) == @0xabcdef, 0); +/// ``` +module StarcoinFramework::FromBCS { + //use std::string::{Self, String}; + + /// UTF8 check failed in conversion from bytes to string + const EINVALID_UTF8: u64 = 0x1; + + public fun to_bool(v: vector): bool { + from_bytes(v) + } + + public fun to_u8(v: vector): u8 { + from_bytes(v) + } + + public fun to_u64(v: vector): u64 { + from_bytes(v) + } + + public fun to_u128(v: vector): u128 { + from_bytes(v) + } + + public fun to_address(v: vector): address { + from_bytes
(v) + } + + // public fun to_string(v: vector): vector { + // // To make this safe, we need to evaluate the utf8 invariant. + // let s = from_bytes(v); + // assert!(string::internal_check_utf8(string::bytes(&s)), EINVALID_UTF8); + // s + // } + + /// Package private native function to deserialize a type T. + /// + /// Note that this function does not put any constraint on `T`. If code uses this function to + /// deserialize a linear value, its their responsibility that the data they deserialize is + /// owned. + public(friend) native fun from_bytes(bytes: vector): T; + + spec from_bytes { + pragma opaque; // internal + } + + // friend StarcoinFramework::any; + // friend StarcoinFramework::copyable_any; + + + #[test_only] + use StarcoinFramework::BCS; + #[test_only] + use StarcoinFramework::Debug; + + #[test] + fun test_address() { + let addr = @0x1; + //let addr_vec = x"0000000000000000000000000000000000000000000000000000000000000001"; + let addr_vec = x"00000000000000000000000000000001"; + let addr_out = to_address(addr_vec); + let addr_vec_out = BCS::to_bytes(&addr_out); + Debug::print(&addr); + Debug::print(&addr_vec_out); + assert!(addr == addr_out, 0); + assert!(addr_vec == addr_vec_out, 1); + } + + #[test] + #[expected_failure(abort_code = 0x10001, location = Self)] + fun test_address_fail() { + let bad_vec = b"01"; + to_address(bad_vec); + } +} diff --git a/release/v13/sources/GasSchedule.move b/release/v13/sources/GasSchedule.move new file mode 100644 index 00000000..dc57048d --- /dev/null +++ b/release/v13/sources/GasSchedule.move @@ -0,0 +1,355 @@ +address StarcoinFramework { +/// Gas schedule configuration. +module GasSchedule { + use StarcoinFramework::Vector; + use StarcoinFramework::ChainId; + use StarcoinFramework::Config; + use StarcoinFramework::CoreAddresses; + spec module { + pragma verify = false; + pragma aborts_if_is_strict; + } + + struct GasEntry has store, copy, drop { + key: vector, + val: u64, + } + + struct GasSchedule has copy, drop, store, key { + entries: vector, + } + + /// The `GasCost` tracks: + /// - instruction cost: how much time/computational power is needed to perform the instruction + /// - memory cost: how much memory is required for the instruction, and storage overhead + public fun gas_schedule(): vector { + let table = Vector::empty(); + + // instruction_schedule + // POP + Vector::push_back(&mut table, new_gas_entry(b"instr.pop", 1, 1)); + // RET + Vector::push_back(&mut table, new_gas_entry(b"instr.ret", 638, 1)); + // BR_TRUE + Vector::push_back(&mut table, new_gas_entry(b"instr.br_true", 1, 1)); + // BR_FALSE + Vector::push_back(&mut table, new_gas_entry(b"instr.br_false", 1, 1)); + // BRANCH + Vector::push_back(&mut table, new_gas_entry(b"instr.branch", 1, 1)); + // LD_U64 + Vector::push_back(&mut table, new_gas_entry(b"instr.ld_u64", 1, 1)); + // LD_CONST + Vector::push_back(&mut table, new_gas_entry(b"instr.ld_const.per_byte", 1, 1)); + // LD_TRUE + Vector::push_back(&mut table, new_gas_entry(b"instr.ld_true", 1, 1)); + // LD_FALSE + Vector::push_back(&mut table, new_gas_entry(b"instr.ld_false", 1, 1)); + // COPY_LOC + Vector::push_back(&mut table, new_gas_entry(b"instr.copy_loc.per_abs_mem_unit", 1, 1)); + // MOVE_LOC + Vector::push_back(&mut table, new_gas_entry(b"instr.move_loc.per_abs_mem_unit", 1, 1)); + // ST_LOC + Vector::push_back(&mut table, new_gas_entry(b"instr.st_loc.per_abs_mem_unit", 1, 1)); + // MUT_BORROW_LOC + Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_loc", 2, 1)); + // IMM_BORROW_LOC + Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_loc", 1, 1)); + // MUT_BORROW_FIELD + Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_field", 1, 1)); + // IMM_BORROW_FIELD + Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_field", 1, 1)); + // CALL + Vector::push_back(&mut table, new_gas_entry(b"instr.call.per_arg", 1132, 1)); + // PACK + Vector::push_back(&mut table, new_gas_entry(b"instr.pack.per_abs_mem_unit", 2, 1)); + // UNPACK + Vector::push_back(&mut table, new_gas_entry(b"instr.unpack.per_abs_mem_unit", 2, 1)); + // READ_REF + Vector::push_back(&mut table, new_gas_entry(b"instr.read_ref.per_abs_mem_unit", 1, 1)); + // WRITE_REF + Vector::push_back(&mut table, new_gas_entry(b"instr.write_ref.per_abs_mem_unit", 1, 1)); + // ADD + Vector::push_back(&mut table, new_gas_entry(b"instr.add", 1, 1)); + // SUB + Vector::push_back(&mut table, new_gas_entry(b"instr.sub", 1, 1)); + // MUL + Vector::push_back(&mut table, new_gas_entry(b"instr.mul", 1, 1)); + // MOD + Vector::push_back(&mut table, new_gas_entry(b"instr.mod", 1, 1)); + // DIV + Vector::push_back(&mut table, new_gas_entry(b"instr.div", 3, 1)); + // BIT_OR + Vector::push_back(&mut table, new_gas_entry(b"instr.bit_or", 2, 1)); + // BIT_AND + Vector::push_back(&mut table, new_gas_entry(b"instr.bit_and", 2, 1)); + // XOR + Vector::push_back(&mut table, new_gas_entry(b"instr.xor", 1, 1)); + // OR + Vector::push_back(&mut table, new_gas_entry(b"instr.or", 2, 1)); + // AND + Vector::push_back(&mut table, new_gas_entry(b"instr.and", 1, 1)); + // NOT + Vector::push_back(&mut table, new_gas_entry(b"instr.not", 1, 1)); + // EQ + Vector::push_back(&mut table, new_gas_entry(b"instr.eq.per_abs_mem_unit", 1, 1)); + // NEQ + Vector::push_back(&mut table, new_gas_entry(b"instr.neq.per_abs_mem_unit", 1, 1)); + // LT + Vector::push_back(&mut table, new_gas_entry(b"instr.lt", 1, 1)); + // GT + Vector::push_back(&mut table, new_gas_entry(b"instr.gt", 1, 1)); + // LE + Vector::push_back(&mut table, new_gas_entry(b"instr.le", 2, 1)); + // GE + Vector::push_back(&mut table, new_gas_entry(b"instr.ge", 1, 1)); + // ABORT + Vector::push_back(&mut table, new_gas_entry(b"instr.abort", 1, 1)); + // NOP + Vector::push_back(&mut table, new_gas_entry(b"instr.nop", 1, 1)); + // EXISTS + Vector::push_back(&mut table, new_gas_entry(b"instr.exists.per_abs_mem_unit", 41, 1)); + // MUT_BORROW_GLOBAL + Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_global.per_abs_mem_unit", 21, 1)); + // IML_BORROW_GLOBAL + Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_global.per_abs_mem_unit", 23, 1)); + // MOVE_FROM + Vector::push_back(&mut table, new_gas_entry(b"instr.move_from.per_abs_mem_unit", 459, 1)); + // MOVE_TO + Vector::push_back(&mut table, new_gas_entry(b"instr.move_to.per_abs_mem_unit", 13, 1)); + // FREEZE_REF + Vector::push_back(&mut table, new_gas_entry(b"instr.freeze_ref", 1, 1)); + // SHL + Vector::push_back(&mut table, new_gas_entry(b"instr.shl", 2, 1)); + // SHR + Vector::push_back(&mut table, new_gas_entry(b"instr.shr", 1, 1)); + // LD_U8 + Vector::push_back(&mut table, new_gas_entry(b"instr.ld_u8", 1, 1)); + // LD_U128 + Vector::push_back(&mut table, new_gas_entry(b"instr.ld_u128", 1, 1)); + + // CAST_U8 + Vector::push_back(&mut table, new_gas_entry(b"instr.cast_u8", 2, 1)); + // CAST_U64 + Vector::push_back(&mut table, new_gas_entry(b"instr.cast_u64", 1, 1)); + // CAST_U128 + Vector::push_back(&mut table, new_gas_entry(b"instr.cast_u128", 1, 1)); + // MUT_BORORW_FIELD_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_field_generic.base", 1, 1)); + // IMM_BORORW_FIELD_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_field_generic.base", 1, 1)); + // CALL_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.call_generic.per_arg", 582, 1)); + // PACK_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.pack_generic.per_abs_mem_unit", 2, 1)); + // UNPACK_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.unpack_generic.per_abs_mem_unit", 2, 1)); + // EXISTS_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.exists_generic.per_abs_mem_unit", 34, 1)); + // MUT_BORROW_GLOBAL_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.mut_borrow_global_generic.per_abs_mem_unit", 15, 1)); + // IMM_BORROW_GLOBAL_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.imm_borrow_global_generic.per_abs_mem_unit", 14, 1)); + // MOVE_FROM_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.move_from_generic.per_abs_mem_unit", 13, 1)); + // MOVE_TO_GENERIC + Vector::push_back(&mut table, new_gas_entry(b"instr.move_to_generic.per_abs_mem_unit", 27, 1)); + + // VEC_PACK + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_pack.per_elem", 84, 1)); + // VEC_LEN + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_len.base", 98, 1)); + // VEC_IMM_BORROW + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_imm_borrow.base", 1334, 1)); + // VEC_MUT_BORROW + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_mut_borrow.base", 1902, 1)); + // VEC_PUSH_BACK + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_push_back.per_abs_mem_unit", 53, 1)); + // VEC_POP_BACK + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_pop_back.base", 227, 1)); + // VEC_UNPACK + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_unpack.per_expected_elem", 572, 1)); + // VEC_SWAP + Vector::push_back(&mut table, new_gas_entry(b"instr.vec_swap.base", 1436, 1)); + + Vector::push_back(&mut table, new_constant_entry(b"instr.ld_u16", 3)); + Vector::push_back(&mut table, new_constant_entry(b"instr.ld_u32", 2)); + Vector::push_back(&mut table, new_constant_entry(b"instr.ld_u256", 3)); + Vector::push_back(&mut table, new_constant_entry(b"instr.cast_u16", 3)); + Vector::push_back(&mut table, new_constant_entry(b"instr.cast_u32", 2)); + Vector::push_back(&mut table, new_constant_entry(b"instr.cast_u256", 3)); + + // native_schedule + //Hash::sha2_256 0 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.hash.sha2_256.per_byte", 21, 1)); + //Hash::sha3_256 1 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.hash.sha3_256.per_byte", 64, 1)); + //Signature::ed25519_verify 2 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.signature.ed25519_verify.per_byte", 61, 1)); + //ED25519_THRESHOLD_VERIFY 3 this native funciton is deprecated + //Vector::push_back(&mut table, new_gas_entry(b"", 3351, 1)); + //BSC::to_bytes 4 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.bcs.to_bytes.per_byte_serialized", 181, 1)); + //Vector::length 5 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.length.base", 98, 1)); + //Vector::empty 6 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.empty.base", 84, 1)); + //Vector::borrow 7 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.borrow.base", 1334, 1)); + //Vector::borrow_mut 8 + //Vector::push_back(&mut table, new_gas_entry(b"", 1902, 1)); + //Vector::push_back 9 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.push_back.legacy_per_abstract_memory_unit", 53, 1)); + //Vector::pop_back 10 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.pop_back.base", 227, 1)); + //Vector::destory_empty 11 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.destroy_empty.base", 572, 1)); + //Vector::swap 12 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.swap.base", 1436, 1)); + //Signature::ed25519_validate_pubkey 13 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.signature.ed25519_validate_key.per_byte", 26, 1)); + //Signer::borrow_address 14 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.signer.borrow_address.base", 353, 1)); + //Account::creator_signer 15 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.account.create_signer.base", 24, 1)); + //Account::destroy_signer 16 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.account.destroy_signer.base", 212, 1)); + //Event::emit_event 17 + Vector::push_back(&mut table, new_gas_entry(b"nursery.event.write_to_event_store.unit_cost", 52, 1)); + //BCS::to_address 18 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.bcs.to_address.per_byte", 26, 1)); + //Token::name_of 19 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.token.name_of.base", 2002, 1)); + //Hash::keccak_256 20 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.hash.keccak256.per_byte", 64, 1)); + //Hash::ripemd160 21 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.hash.ripemd160.per_byte", 64, 1)); + //Signature::native_ecrecover 22 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.signature.ec_recover.per_byte", 128, 1)); + //U256::from_bytes 23 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.from_bytes.per_byte", 2, 1)); + //U256::add 24 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.add.base", 4, 1)); + //U256::sub 25 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.sub.base", 4, 1)); + //U256::mul 26 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.mul.base", 4, 1)); + //U256::div 27 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.div.base", 10, 1)); + // U256::rem 28 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.rem.base", 4, 1)); + // U256::pow 29 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.u256.pow.base", 8, 1)); + // TODO: settle down the gas cost + // Vector::append 30 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.append.legacy_per_abstract_memory_unit", 40, 1)); + // Vector::remove 31 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.remove.legacy_per_abstract_memory_unit", 20, 1)); + // Vector::reverse 32 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.reverse.legacy_per_abstract_memory_unit", 10, 1)); + // Table::new_table_handle 33 + Vector::push_back(&mut table, new_gas_entry(b"table.new_table_handle.base", 4, 1)); + // Table::add_box 34 + Vector::push_back(&mut table, new_gas_entry(b"table.add_box.per_byte_serialized", 4, 1)); + // Table::borrow_box 35 + Vector::push_back(&mut table, new_gas_entry(b"table.borrow_box.per_byte_serialized", 10, 1)); + // Table::remove_box 36 + Vector::push_back(&mut table, new_gas_entry(b"table.remove_box.per_byte_serialized", 8, 1)); + // Table::contains_box 37 + Vector::push_back(&mut table, new_gas_entry(b"table.contains_box.per_byte_serialized", 40, 1)); + // Table::destroy_empty_box 38 + Vector::push_back(&mut table, new_gas_entry(b"table.destroy_empty_box.base", 20, 1)); + // Table::drop_unchecked_box 39 + Vector::push_back(&mut table, new_gas_entry(b"table.drop_unchecked_box.base", 73, 1)); + // string.check_utf8 40 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.check_utf8.per_byte", 4, 1)); + // string.sub_str 41 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.sub_string.per_byte", 4, 1)); + // string.is_char_boundary 42 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.is_char_boundary.base", 4, 1)); + // Table::string.index_of 43 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.string.index_of.per_byte_searched", 4, 1)); + // Table::string.index_of 44 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.frombcs.base", 4, 1)); + // Table::string.index_of 45 + Vector::push_back(&mut table, new_gas_entry(b"starcoin_natives.secp256k1.base", 4, 1)); + // Table::string.index_of 46 + Vector::push_back(&mut table, new_gas_entry(b"move_stdlib.vector.spawn_from.legacy_per_abstract_memory_unit", 4, 1)); + + Vector::push_back(&mut table, new_constant_entry(b"nursery.debug.print.base_cost", 1)); + Vector::push_back(&mut table, new_constant_entry(b"nursery.debug.print_stack_trace.base_cost", 1)); + Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.hash.sha2_256.legacy_min_input_len", 1)); + Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.hash.sha3_256.legacy_min_input_len", 1)); + Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.bcs.to_bytes.failure", 182)); + Vector::push_back(&mut table, new_constant_entry(b"move_stdlib.bcs.to_bytes.legacy_min_output_size", 1)); + + // constant config values + Vector::push_back(&mut table, new_constant_entry(b"txn.global_memory_per_byte_cost", 4)); + Vector::push_back(&mut table, new_constant_entry(b"txn.global_memory_per_byte_write_cost", 9)); + Vector::push_back(&mut table, new_constant_entry(b"txn.min_transaction_gas_units", 600)); + Vector::push_back(&mut table, new_constant_entry(b"txn.large_transaction_cutoff", 600)); + Vector::push_back(&mut table, new_constant_entry(b"txn.intrinsic_gas_per_byte", 8)); + let maximum_number_of_gas_units: u64 = 40000000;//must less than base_block_gas_limit + if (ChainId::is_test() || ChainId::is_dev() || ChainId::is_halley()) { + maximum_number_of_gas_units = maximum_number_of_gas_units * 10 + }; + Vector::push_back(&mut table, new_constant_entry(b"txn.maximum_number_of_gas_units", maximum_number_of_gas_units)); + Vector::push_back(&mut table, new_constant_entry(b"txn.min_price_per_gas_unit", if (ChainId::is_test()) { 0 } else { 1 })); + Vector::push_back(&mut table, new_constant_entry(b"txn.max_price_per_gas_unit", 10000)); + Vector::push_back(&mut table, new_constant_entry(b"txn.max_transaction_size_in_bytes", 1024 * 128)); + Vector::push_back(&mut table, new_constant_entry(b"txn.gas_unit_scaling_factor", 1)); + Vector::push_back(&mut table, new_constant_entry(b"txn.default_account_size", 800)); + + table + } + + public fun new_gas_entry(key: vector, instr_gas: u64, mem_gas: u64): GasEntry { + GasEntry { + key, + val: instr_gas + mem_gas, + } + } + + fun new_constant_entry(key: vector, val: u64): GasEntry { + GasEntry { + key, + val, + } + } + + /// Initialize the gas schedule under the genesis account + public fun initialize(account: &signer, gas_schedule: GasSchedule) { + CoreAddresses::assert_genesis_address(account); + Config::publish_new_config( + account, + gas_schedule, + ); + } + + public fun new_gas_schedule(): GasSchedule { + GasSchedule { + entries: gas_schedule(), + } + } + + public fun new_gas_schedule_for_test(): GasSchedule { + let entry = GasEntry { + key: Vector::empty(), + val: 1, + }; + let entries = Vector::empty(); + Vector::push_back(&mut entries, entry); + + GasSchedule { + entries, + } + } + + #[test] + fun test_gas_schedule_initialized() { + use StarcoinFramework::Account; + let genesis_account = Account::create_genesis_account(CoreAddresses::GENESIS_ADDRESS()); + Self::initialize(&genesis_account, Self::new_gas_schedule_for_test()); + assert!(Config::config_exist_by_address(CoreAddresses::GENESIS_ADDRESS()), 0); + } +} +} diff --git a/release/v13/sources/Genesis.move b/release/v13/sources/Genesis.move new file mode 100644 index 00000000..368c8699 --- /dev/null +++ b/release/v13/sources/Genesis.move @@ -0,0 +1,560 @@ +address StarcoinFramework { +/// The module for init Genesis +module Genesis { + + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Account; + use StarcoinFramework::Signer; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Token; + use StarcoinFramework::STC::{Self, STC}; + use StarcoinFramework::DummyToken; + use StarcoinFramework::PackageTxnManager; + use StarcoinFramework::ConsensusConfig; + use StarcoinFramework::VMConfig; + use StarcoinFramework::Vector; + use StarcoinFramework::Block; + use StarcoinFramework::TransactionFee; + use StarcoinFramework::BlockReward; + use StarcoinFramework::ChainId; + use StarcoinFramework::ConsensusStrategy; + use StarcoinFramework::TransactionPublishOption; + use StarcoinFramework::Collection; + use StarcoinFramework::TransactionTimeoutConfig; + use StarcoinFramework::Epoch; + use StarcoinFramework::Version; + use StarcoinFramework::Config; + use StarcoinFramework::Option; + use StarcoinFramework::Treasury; + use StarcoinFramework::TreasuryWithdrawDaoProposal; + use StarcoinFramework::StdlibUpgradeScripts; + use StarcoinFramework::GenesisSignerCapability; + use StarcoinFramework::STCUSDOracle; + use StarcoinFramework::GenesisNFT; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_partial = false; + pragma aborts_if_is_strict = true; + } + + + public entry fun initialize( + stdlib_version: u64, + + // block reward config + reward_delay: u64, + + pre_mine_stc_amount: u128, + time_mint_stc_amount: u128, + time_mint_stc_period: u64, + parent_hash: vector, + association_auth_key: vector, + genesis_auth_key: vector, + chain_id: u8, + genesis_timestamp: u64, + + //consensus config + uncle_rate_target: u64, + epoch_block_count: u64, + base_block_time_target: u64, + base_block_difficulty_window: u64, + base_reward_per_block: u128, + base_reward_per_uncle_percent: u64, + min_block_time_target: u64, + max_block_time_target: u64, + base_max_uncles_per_block: u64, + base_block_gas_limit: u64, + strategy: u8, + + //vm config + script_allowed: bool, + module_publishing_allowed: bool, + instruction_schedule: vector, + native_schedule: vector, + + //gas constants + global_memory_per_byte_cost: u64, + global_memory_per_byte_write_cost: u64, + min_transaction_gas_units: u64, + large_transaction_cutoff: u64, + instrinsic_gas_per_byte: u64, + maximum_number_of_gas_units: u64, + min_price_per_gas_unit: u64, + max_price_per_gas_unit: u64, + max_transaction_size_in_bytes: u64, + gas_unit_scaling_factor: u64, + default_account_size: u64, + + // dao config + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + + // transaction timeout config + transaction_timeout: u64, + ) { + assert!(Timestamp::is_genesis(), 1); + // create genesis account + let genesis_account = Account::create_genesis_account(CoreAddresses::GENESIS_ADDRESS()); + //Init global time + Timestamp::initialize(&genesis_account, genesis_timestamp); + ChainId::initialize(&genesis_account, chain_id); + ConsensusStrategy::initialize(&genesis_account, strategy); + Block::initialize(&genesis_account, parent_hash); + TransactionPublishOption::initialize( + &genesis_account, + script_allowed, + module_publishing_allowed, + ); + // init config + VMConfig::initialize( + &genesis_account, + instruction_schedule, + native_schedule, + global_memory_per_byte_cost, + global_memory_per_byte_write_cost, + min_transaction_gas_units, + large_transaction_cutoff, + instrinsic_gas_per_byte, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit, + max_transaction_size_in_bytes, + gas_unit_scaling_factor, + default_account_size, + ); + TransactionTimeoutConfig::initialize(&genesis_account, transaction_timeout); + ConsensusConfig::initialize( + &genesis_account, + uncle_rate_target, + epoch_block_count, + base_block_time_target, + base_block_difficulty_window, + base_reward_per_block, + base_reward_per_uncle_percent, + min_block_time_target, + max_block_time_target, + base_max_uncles_per_block, + base_block_gas_limit, + strategy, + ); + Epoch::initialize(&genesis_account); + BlockReward::initialize(&genesis_account, reward_delay); + TransactionFee::initialize(&genesis_account); + let association = Account::create_genesis_account( + CoreAddresses::ASSOCIATION_ROOT_ADDRESS(), + ); + Config::publish_new_config(&genesis_account, Version::new_version(stdlib_version)); + // stdlib use two phase upgrade strategy. + PackageTxnManager::update_module_upgrade_strategy( + &genesis_account, + PackageTxnManager::get_strategy_two_phase(), + Option::some(0u64), + ); + // stc should be initialized after genesis_account's module upgrade strategy set. + { + STC::initialize(&genesis_account, voting_delay, voting_period, voting_quorum_rate, min_action_delay); + Account::do_accept_token(&genesis_account); + DummyToken::initialize(&genesis_account); + Account::do_accept_token(&association); + }; + if (pre_mine_stc_amount > 0) { + let stc = Token::mint(&genesis_account, pre_mine_stc_amount); + Account::deposit(Signer::address_of(&association), stc); + }; + if (time_mint_stc_amount > 0) { + let cap = Token::remove_mint_capability(&genesis_account); + let key = Token::issue_linear_mint_key(&cap, time_mint_stc_amount, time_mint_stc_period); + Token::add_mint_capability(&genesis_account, cap); + Collection::put(&association, key); + }; + // only dev network set genesis auth key. + if (!Vector::is_empty(&genesis_auth_key)) { + let genesis_rotate_key_cap = Account::extract_key_rotation_capability(&genesis_account); + Account::rotate_authentication_key_with_capability(&genesis_rotate_key_cap, genesis_auth_key); + Account::restore_key_rotation_capability(genesis_rotate_key_cap); + }; + let assoc_rotate_key_cap = Account::extract_key_rotation_capability(&association); + Account::rotate_authentication_key_with_capability(&assoc_rotate_key_cap, association_auth_key); + Account::restore_key_rotation_capability(assoc_rotate_key_cap); + //Start time, Timestamp::is_genesis() will return false. this call should at the end of genesis init. + Timestamp::set_time_has_started(&genesis_account); + Account::release_genesis_signer(genesis_account); + Account::release_genesis_signer(association); + } + + public entry fun initialize_v2( + stdlib_version: u64, + + // block reward and stc config + reward_delay: u64, + total_stc_amount: u128, + pre_mine_stc_amount: u128, + time_mint_stc_amount: u128, + time_mint_stc_period: u64, + + parent_hash: vector, + association_auth_key: vector, + genesis_auth_key: vector, + chain_id: u8, + genesis_timestamp: u64, + + //consensus config + uncle_rate_target: u64, + epoch_block_count: u64, + base_block_time_target: u64, + base_block_difficulty_window: u64, + base_reward_per_block: u128, + base_reward_per_uncle_percent: u64, + min_block_time_target: u64, + max_block_time_target: u64, + base_max_uncles_per_block: u64, + base_block_gas_limit: u64, + strategy: u8, + + //vm config + script_allowed: bool, + module_publishing_allowed: bool, + instruction_schedule: vector, + native_schedule: vector, + + //gas constants + global_memory_per_byte_cost: u64, + global_memory_per_byte_write_cost: u64, + min_transaction_gas_units: u64, + large_transaction_cutoff: u64, + instrinsic_gas_per_byte: u64, + maximum_number_of_gas_units: u64, + min_price_per_gas_unit: u64, + max_price_per_gas_unit: u64, + max_transaction_size_in_bytes: u64, + gas_unit_scaling_factor: u64, + default_account_size: u64, + + // dao config + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + + // transaction timeout config + transaction_timeout: u64, + ) { + Self::do_initialize( + stdlib_version, + reward_delay, + total_stc_amount, + pre_mine_stc_amount, + time_mint_stc_amount, + time_mint_stc_period, + parent_hash, + association_auth_key, + genesis_auth_key, + chain_id, + genesis_timestamp, + uncle_rate_target, + epoch_block_count, + base_block_time_target, + base_block_difficulty_window, + base_reward_per_block, + base_reward_per_uncle_percent, + min_block_time_target, + max_block_time_target, + base_max_uncles_per_block, + base_block_gas_limit, + strategy, + script_allowed, + module_publishing_allowed, + instruction_schedule, + native_schedule, + global_memory_per_byte_cost, + global_memory_per_byte_write_cost, + min_transaction_gas_units, + large_transaction_cutoff, + instrinsic_gas_per_byte, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit, + max_transaction_size_in_bytes, + gas_unit_scaling_factor, + default_account_size, + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + transaction_timeout, + ); + } + + fun do_initialize( + stdlib_version: u64, + + // block reward and stc config + reward_delay: u64, + total_stc_amount: u128, + pre_mine_stc_amount: u128, + time_mint_stc_amount: u128, + time_mint_stc_period: u64, + + parent_hash: vector, + association_auth_key: vector, + genesis_auth_key: vector, + chain_id: u8, + genesis_timestamp: u64, + + //consensus config + uncle_rate_target: u64, + epoch_block_count: u64, + base_block_time_target: u64, + base_block_difficulty_window: u64, + base_reward_per_block: u128, + base_reward_per_uncle_percent: u64, + min_block_time_target: u64, + max_block_time_target: u64, + base_max_uncles_per_block: u64, + base_block_gas_limit: u64, + strategy: u8, + + //vm config + script_allowed: bool, + module_publishing_allowed: bool, + instruction_schedule: vector, + native_schedule: vector, + + //gas constants + global_memory_per_byte_cost: u64, + global_memory_per_byte_write_cost: u64, + min_transaction_gas_units: u64, + large_transaction_cutoff: u64, + instrinsic_gas_per_byte: u64, + maximum_number_of_gas_units: u64, + min_price_per_gas_unit: u64, + max_price_per_gas_unit: u64, + max_transaction_size_in_bytes: u64, + gas_unit_scaling_factor: u64, + default_account_size: u64, + + // dao config + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + + // transaction timeout config + transaction_timeout: u64, + ){ + Timestamp::assert_genesis(); + // create genesis account + let genesis_account = Account::create_genesis_account(CoreAddresses::GENESIS_ADDRESS()); + //Init global time + Timestamp::initialize(&genesis_account, genesis_timestamp); + ChainId::initialize(&genesis_account, chain_id); + ConsensusStrategy::initialize(&genesis_account, strategy); + Block::initialize(&genesis_account, parent_hash); + TransactionPublishOption::initialize( + &genesis_account, + script_allowed, + module_publishing_allowed, + ); + // init config + VMConfig::initialize( + &genesis_account, + instruction_schedule, + native_schedule, + global_memory_per_byte_cost, + global_memory_per_byte_write_cost, + min_transaction_gas_units, + large_transaction_cutoff, + instrinsic_gas_per_byte, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit, + max_transaction_size_in_bytes, + gas_unit_scaling_factor, + default_account_size, + ); + TransactionTimeoutConfig::initialize(&genesis_account, transaction_timeout); + ConsensusConfig::initialize( + &genesis_account, + uncle_rate_target, + epoch_block_count, + base_block_time_target, + base_block_difficulty_window, + base_reward_per_block, + base_reward_per_uncle_percent, + min_block_time_target, + max_block_time_target, + base_max_uncles_per_block, + base_block_gas_limit, + strategy, + ); + Epoch::initialize(&genesis_account); + let association = Account::create_genesis_account( + CoreAddresses::ASSOCIATION_ROOT_ADDRESS(), + ); + Config::publish_new_config(&genesis_account, Version::new_version(stdlib_version)); + // stdlib use two phase upgrade strategy. + PackageTxnManager::update_module_upgrade_strategy( + &genesis_account, + PackageTxnManager::get_strategy_two_phase(), + Option::some(0u64), + ); + BlockReward::initialize(&genesis_account, reward_delay); + + // stc should be initialized after genesis_account's module upgrade strategy set and all on chain config init. + let withdraw_cap = STC::initialize_v2(&genesis_account, total_stc_amount, voting_delay, voting_period, voting_quorum_rate, min_action_delay); + Account::do_accept_token(&genesis_account); + Account::do_accept_token(&association); + + DummyToken::initialize(&genesis_account); + + if (pre_mine_stc_amount > 0) { + let stc = Treasury::withdraw_with_capability(&mut withdraw_cap, pre_mine_stc_amount); + Account::deposit(Signer::address_of(&association), stc); + }; + if (time_mint_stc_amount > 0) { + let liner_withdraw_cap = Treasury::issue_linear_withdraw_capability(&mut withdraw_cap, time_mint_stc_amount, time_mint_stc_period); + Treasury::add_linear_withdraw_capability(&association, liner_withdraw_cap); + }; + + // Lock the TreasuryWithdrawCapability to Dao + TreasuryWithdrawDaoProposal::plugin(&genesis_account, withdraw_cap); + + TransactionFee::initialize(&genesis_account); + + // only test/dev network set genesis auth key. + if (!Vector::is_empty(&genesis_auth_key)) { + let genesis_rotate_key_cap = Account::extract_key_rotation_capability(&genesis_account); + Account::rotate_authentication_key_with_capability(&genesis_rotate_key_cap, genesis_auth_key); + Account::restore_key_rotation_capability(genesis_rotate_key_cap); + }; + + let assoc_rotate_key_cap = Account::extract_key_rotation_capability(&association); + Account::rotate_authentication_key_with_capability(&assoc_rotate_key_cap, association_auth_key); + Account::restore_key_rotation_capability(assoc_rotate_key_cap); + + // v5 -> v6 + { + let cap = Account::remove_signer_capability(&genesis_account); + GenesisSignerCapability::initialize(&genesis_account, cap); + //register oracle + STCUSDOracle::register(&genesis_account); + let merkle_root = x"5969f0e8e19f8769276fb638e6060d5c02e40088f5fde70a6778dd69d659ee6d"; + let image = b"ipfs://QmSPcvcXgdtHHiVTAAarzTeubk5X3iWymPAoKBfiRFjPMY"; + GenesisNFT::initialize(&genesis_account, merkle_root, 1639u64, image); + }; + StdlibUpgradeScripts::do_upgrade_from_v6_to_v7_with_language_version(&genesis_account, 6); + StdlibUpgradeScripts::do_upgrade_from_v11_to_v12(&genesis_account); + StdlibUpgradeScripts::do_upgrade_from_v12_to_v13(&genesis_account); + //Start time, Timestamp::is_genesis() will return false. this call should at the end of genesis init. + Timestamp::set_time_has_started(&genesis_account); + Account::release_genesis_signer(genesis_account); + Account::release_genesis_signer(association); + } + + /// Init the genesis for unit tests + public fun initialize_for_unit_tests(){ + let stdlib_version: u64 = 6; + let reward_delay: u64 = 7; + let total_stc_amount: u128 = 3185136000000000000u128; + let pre_mine_stc_amount: u128 = 159256800000000000u128; + let time_mint_stc_amount: u128 = (85043130u128 * 3u128 + 74213670u128 * 3u128)*1000000000u128; + let time_mint_stc_period: u64 = 1000000000; + + let parent_hash: vector = x"0000000000000000000000000000000000000000000000000000000000000000"; + let association_auth_key: vector = x"0000000000000000000000000000000000000000000000000000000000000000"; + let genesis_auth_key: vector = x"0000000000000000000000000000000000000000000000000000000000000000"; + let chain_id: u8 = 255; + let genesis_timestamp: u64 =0; + + //consensus config + let uncle_rate_target: u64 = 80; + let epoch_block_count: u64 = 240; + let base_block_time_target: u64 = 10000; + let base_block_difficulty_window: u64 = 24; + let base_reward_per_block: u128 = 1000000000; + let base_reward_per_uncle_percent: u64 = 10; + let min_block_time_target: u64 = 1000; + let max_block_time_target: u64 = 20000; + let base_max_uncles_per_block: u64 = 2; + let base_block_gas_limit: u64 = 500000000; + let strategy: u8 = 0; + + //vm config + let script_allowed: bool = true; + let module_publishing_allowed: bool = true; + //TODO init the gas table. + let instruction_schedule: vector = Vector::empty(); + let native_schedule: vector = Vector::empty(); + + //gas constants + let global_memory_per_byte_cost: u64 = 1; + let global_memory_per_byte_write_cost: u64 = 1; + let min_transaction_gas_units: u64 = 1; + let large_transaction_cutoff: u64 = 1; + let instrinsic_gas_per_byte: u64 = 1; + let maximum_number_of_gas_units: u64 = 1; + let min_price_per_gas_unit: u64 = 1; + let max_price_per_gas_unit: u64 = 10000; + let max_transaction_size_in_bytes: u64 = 1024*1024; + let gas_unit_scaling_factor: u64 = 1; + let default_account_size: u64 = 600; + + // dao config + let voting_delay: u64 = 1000; + let voting_period: u64 = 6000; + let voting_quorum_rate: u8 = 4; + let min_action_delay: u64 = 1000; + + // transaction timeout config + let transaction_timeout: u64 = 10000; + + Self::do_initialize( + stdlib_version, + reward_delay, + total_stc_amount, + pre_mine_stc_amount, + time_mint_stc_amount, + time_mint_stc_period, + parent_hash, + association_auth_key, + genesis_auth_key, + chain_id, + genesis_timestamp, + uncle_rate_target, + epoch_block_count, + base_block_time_target, + base_block_difficulty_window, + base_reward_per_block, + base_reward_per_uncle_percent, + min_block_time_target, + max_block_time_target, + base_max_uncles_per_block, + base_block_gas_limit, + strategy, + script_allowed, + module_publishing_allowed, + instruction_schedule, + native_schedule, + global_memory_per_byte_cost, + global_memory_per_byte_write_cost, + min_transaction_gas_units, + large_transaction_cutoff, + instrinsic_gas_per_byte, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit, + max_transaction_size_in_bytes, + gas_unit_scaling_factor, + default_account_size, + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + transaction_timeout, + ); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/GenesisNFT.move b/release/v13/sources/GenesisNFT.move new file mode 100644 index 00000000..a8cc72a0 --- /dev/null +++ b/release/v13/sources/GenesisNFT.move @@ -0,0 +1,82 @@ +module StarcoinFramework::GenesisNFT { + use StarcoinFramework::IdentifierNFT; + use StarcoinFramework::Option::Option; + use StarcoinFramework::NFT::{Self, MintCapability}; + use StarcoinFramework::MerkleNFTDistributor; + use StarcoinFramework::CoreAddresses; + + spec module { + pragma verify = false; + } + + struct GenesisNFT has store{} + struct GenesisNFTMeta has copy, store, drop{ + index: u64 + } + struct GenesisNFTInfo has key, copy, store, drop{ + merkle_root: vector, + total_supply: u64, + } + struct GenesisNFTMintCapability has key{ + cap: MintCapability + } + + public fun initialize(sender: &signer, merkle_root: vector, leafs: u64, image: vector){ + CoreAddresses::assert_genesis_address(sender); + let metadata = NFT::new_meta_with_image(b"StarcoinGenesisNFT", image, b"The starcoin genesis NFT"); + let nft_info = GenesisNFTInfo{merkle_root: *&merkle_root, total_supply: leafs}; + let cap = MerkleNFTDistributor::register(sender, merkle_root, leafs, nft_info, metadata); + move_to(sender, GenesisNFTMintCapability{cap}); + } + + public fun upgrade_to_nft_type_info_v2(sender: &signer) acquires GenesisNFTMintCapability{ + CoreAddresses::assert_genesis_address(sender); + let cap = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + NFT::upgrade_nft_type_info_from_v1_to_v2(sender, &mut cap.cap); + let nft_info = NFT::remove_compat_info(&mut cap.cap); + move_to(sender, nft_info); + } + + public entry fun mint_entry(sender: signer, index: u64, merkle_proof:vector>) + acquires GenesisNFTMintCapability { + mint(&sender, index, merkle_proof); + } + + public fun mint(sender: &signer, index: u64, merkle_proof:vector>) + acquires GenesisNFTMintCapability { + let metadata = NFT::empty_meta(); + let cap = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + let nft = MerkleNFTDistributor::mint_with_cap(sender, &mut cap.cap, CoreAddresses::GENESIS_ADDRESS(), index, metadata, GenesisNFTMeta{index}, GenesisNFT{}, merkle_proof); + IdentifierNFT::grant(&mut cap.cap, sender, nft); + } + + public fun verify(account: address, index: u64, merkle_proof: vector>): bool { + MerkleNFTDistributor::verify_proof(account, CoreAddresses::GENESIS_ADDRESS(), index, merkle_proof) + } + + public fun get_info(owner: address): Option>{ + IdentifierNFT::get_nft_info(owner) + } + + public fun is_minted(index: u64): bool { + let creator = CoreAddresses::GENESIS_ADDRESS(); + MerkleNFTDistributor::is_minted(creator, index) + } + + public fun genesis_nft_info(): GenesisNFTInfo acquires GenesisNFTInfo{ + *borrow_global(CoreAddresses::GENESIS_ADDRESS()) + } +} + +module StarcoinFramework::GenesisNFTScripts { + use StarcoinFramework::GenesisNFT; + + spec module { + pragma verify = false; + } + + /// Mint a GenesisNFT + public entry fun mint(sender: signer, index: u64, merkle_proof:vector>) { + GenesisNFT::mint_entry(sender, index, merkle_proof); + } +} diff --git a/release/v13/sources/GenesisNFTScripts.move b/release/v13/sources/GenesisNFTScripts.move new file mode 100644 index 00000000..a8cc72a0 --- /dev/null +++ b/release/v13/sources/GenesisNFTScripts.move @@ -0,0 +1,82 @@ +module StarcoinFramework::GenesisNFT { + use StarcoinFramework::IdentifierNFT; + use StarcoinFramework::Option::Option; + use StarcoinFramework::NFT::{Self, MintCapability}; + use StarcoinFramework::MerkleNFTDistributor; + use StarcoinFramework::CoreAddresses; + + spec module { + pragma verify = false; + } + + struct GenesisNFT has store{} + struct GenesisNFTMeta has copy, store, drop{ + index: u64 + } + struct GenesisNFTInfo has key, copy, store, drop{ + merkle_root: vector, + total_supply: u64, + } + struct GenesisNFTMintCapability has key{ + cap: MintCapability + } + + public fun initialize(sender: &signer, merkle_root: vector, leafs: u64, image: vector){ + CoreAddresses::assert_genesis_address(sender); + let metadata = NFT::new_meta_with_image(b"StarcoinGenesisNFT", image, b"The starcoin genesis NFT"); + let nft_info = GenesisNFTInfo{merkle_root: *&merkle_root, total_supply: leafs}; + let cap = MerkleNFTDistributor::register(sender, merkle_root, leafs, nft_info, metadata); + move_to(sender, GenesisNFTMintCapability{cap}); + } + + public fun upgrade_to_nft_type_info_v2(sender: &signer) acquires GenesisNFTMintCapability{ + CoreAddresses::assert_genesis_address(sender); + let cap = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + NFT::upgrade_nft_type_info_from_v1_to_v2(sender, &mut cap.cap); + let nft_info = NFT::remove_compat_info(&mut cap.cap); + move_to(sender, nft_info); + } + + public entry fun mint_entry(sender: signer, index: u64, merkle_proof:vector>) + acquires GenesisNFTMintCapability { + mint(&sender, index, merkle_proof); + } + + public fun mint(sender: &signer, index: u64, merkle_proof:vector>) + acquires GenesisNFTMintCapability { + let metadata = NFT::empty_meta(); + let cap = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + let nft = MerkleNFTDistributor::mint_with_cap(sender, &mut cap.cap, CoreAddresses::GENESIS_ADDRESS(), index, metadata, GenesisNFTMeta{index}, GenesisNFT{}, merkle_proof); + IdentifierNFT::grant(&mut cap.cap, sender, nft); + } + + public fun verify(account: address, index: u64, merkle_proof: vector>): bool { + MerkleNFTDistributor::verify_proof(account, CoreAddresses::GENESIS_ADDRESS(), index, merkle_proof) + } + + public fun get_info(owner: address): Option>{ + IdentifierNFT::get_nft_info(owner) + } + + public fun is_minted(index: u64): bool { + let creator = CoreAddresses::GENESIS_ADDRESS(); + MerkleNFTDistributor::is_minted(creator, index) + } + + public fun genesis_nft_info(): GenesisNFTInfo acquires GenesisNFTInfo{ + *borrow_global(CoreAddresses::GENESIS_ADDRESS()) + } +} + +module StarcoinFramework::GenesisNFTScripts { + use StarcoinFramework::GenesisNFT; + + spec module { + pragma verify = false; + } + + /// Mint a GenesisNFT + public entry fun mint(sender: signer, index: u64, merkle_proof:vector>) { + GenesisNFT::mint_entry(sender, index, merkle_proof); + } +} diff --git a/release/v13/sources/GenesisSignerCapability.move b/release/v13/sources/GenesisSignerCapability.move new file mode 100644 index 00000000..618eb536 --- /dev/null +++ b/release/v13/sources/GenesisSignerCapability.move @@ -0,0 +1,39 @@ +module StarcoinFramework::GenesisSignerCapability { + use StarcoinFramework::Account; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + + friend StarcoinFramework::NFT; + friend StarcoinFramework::Oracle; + friend StarcoinFramework::Genesis; + friend StarcoinFramework::StdlibUpgradeScripts; + friend StarcoinFramework::EasyGas; + + const ENOT_GENESIS_ACCOUNT: u64 = 11; + + struct GenesisSignerCapability has key { + cap: Account::SignerCapability, + } + + public(friend) fun initialize(signer: &signer, cap: Account::SignerCapability) { + CoreAddresses::assert_genesis_address(signer); + assert!( + Account::signer_address(&cap) == CoreAddresses::GENESIS_ADDRESS(), + Errors::invalid_argument(ENOT_GENESIS_ACCOUNT) + ); + move_to(signer, GenesisSignerCapability{cap}); + } + + public(friend) fun get_genesis_signer(): signer acquires GenesisSignerCapability { + let cap = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + Account::create_signer_with_cap(&cap.cap) + } + #[test_only] + public fun initialize_for_test(signer: &signer, cap: Account::SignerCapability) { + initialize(signer, cap); + } + #[test_only] + public fun get_genesis_signer_for_test(): signer acquires GenesisSignerCapability { + get_genesis_signer() + } +} \ No newline at end of file diff --git a/release/v13/sources/Hash.move b/release/v13/sources/Hash.move new file mode 100644 index 00000000..df8d473d --- /dev/null +++ b/release/v13/sources/Hash.move @@ -0,0 +1,14 @@ +address StarcoinFramework { +/// The module provide sha-hash functionality for Move. +module Hash { + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + native public fun sha2_256(data: vector): vector; + native public fun sha3_256(data: vector): vector; + native public fun keccak_256(data: vector): vector; + native public fun ripemd160(data: vector): vector; +} + +} diff --git a/release/v13/sources/IdentifierNFT.move b/release/v13/sources/IdentifierNFT.move new file mode 100644 index 00000000..d1ac25f8 --- /dev/null +++ b/release/v13/sources/IdentifierNFT.move @@ -0,0 +1,1023 @@ +address StarcoinFramework { +/// Non-fungible token standard and implementations. +module NFT { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Account; + use StarcoinFramework::Vector; + use StarcoinFramework::Event; + use StarcoinFramework::GenesisSignerCapability; + + const ERR_NO_MINT_CAPABILITY: u64 = 101; + const ERR_NO_BURN_CAPABILITY: u64 = 102; + const ERR_NO_UPDATE_CAPABILITY: u64 = 103; + const ERR_CANOT_EMPTY: u64 = 104; + const ERR_NFT_TYPE_ALREADY_REGISTERED: u64 = 105; + const ERR_NFT_TYPE_NO_REGISTERED: u64 = 106; + + spec module { + pragma verify = false; + } + + struct MintEvent has drop, store { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + struct BurnEvent has drop, store { + id: u64, + } + + /// The info of NFT type, this type is deprecated, please use NFTTypeInfoV2 + struct NFTTypeInfo has key, store { + counter: u64, + meta: Metadata, + info: NFTTypeInfoExt, + mint_events: Event::EventHandle>, + } + + /// The info of NFT type + struct NFTTypeInfoV2 has key, store { + register: address, + counter: u64, + meta: Metadata, + mint_events: Event::EventHandle>, + burn_events: Event::EventHandle>, + } + + struct NFTTypeInfoCompat has key { + info: NFTTypeInfoExt, + } + + /// Deprecated. Use `new_nft_type_info_v2` instead. + fun new_nft_type_info( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ): NFTTypeInfo { + NFTTypeInfo { + counter: 0, + info, + meta, + mint_events: Event::new_event_handle>(sender), + } + } + + fun new_nft_type_info_v2(sender: &signer, meta: Metadata): NFTTypeInfoV2 { + NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter: 0, + meta, + mint_events: Event::new_event_handle>(sender), + burn_events: Event::new_event_handle>(sender), + } + } + + /// Note: this function is deprecated + public fun nft_type_info_ex_info( + ): NFTTypeInfoExt acquires NFTTypeInfo, NFTTypeInfoCompat { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } + } + + /// Note: this function is deprecated, please use nft_type_info_counter_v2 + public fun nft_type_info_counter( + ): u64 acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + Self::nft_type_info_counter_v2() + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + } + + public fun nft_type_info_counter_v2(): u64 acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + + public fun nft_type_info_meta(): Metadata acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.meta + } + + public fun upgrade_nft_type_info_from_v1_to_v2( + sender: &signer, + _cap: &mut MintCapability + ) acquires NFTTypeInfo { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfo { counter, meta, info, mint_events } = nft_type_info; + + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let nft_type_info_v2 = NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter, + meta, + mint_events, + burn_events: Event::new_event_handle>(sender), + }; + move_to(&genesis_account, nft_type_info_v2); + move_to(&genesis_account, NFTTypeInfoCompat { info }); + } + } + + public fun remove_compat_info( + _cap: &mut MintCapability + ): NFTTypeInfoExt acquires NFTTypeInfoCompat { + let compat_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfoCompat{info} = compat_info; + info + } + /// deprecated. + struct GenesisSignerCapability has key { + cap: Account::SignerCapability, + } + /// The capability to mint the nft. + struct MintCapability has key, store {} + /// The Capability to burn the nft. + struct BurnCapability has key, store {} + /// The Capability to update the nft metadata. + struct UpdateCapability has key, store {} + + struct Metadata has copy, store, drop { + /// NFT name's utf8 bytes. + name: vector, + /// Image link, such as ipfs://xxxx + image: vector, + /// Image bytes data, image or image_data can not empty for both. + image_data: vector, + /// NFT description utf8 bytes. + description: vector, + } + + public fun empty_meta(): Metadata { + Metadata { + name: Vector::empty(), + image: Vector::empty(), + image_data: Vector::empty(), + description: Vector::empty(), + } + } + + public fun new_meta(name: vector, description: vector): Metadata { + Metadata { + name, + image: Vector::empty(), + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image(name: vector, image: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image, + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image_data(name: vector, image_data: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image_data), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image: Vector::empty(), + image_data, + description, + } + } + + public fun meta_name(metadata: &Metadata): vector { + *&metadata.name + } + + public fun meta_image(metadata: &Metadata): vector { + *&metadata.image + } + + public fun meta_image_data(metadata: &Metadata): vector { + *&metadata.image_data + } + + public fun meta_description(metadata: &Metadata): vector { + *&metadata.description + } + + struct NFT has store { + /// The creator of NFT + creator: address, + /// The unique id of NFT under NFTMeta type + id: u64, + /// The metadata of NFT + base_meta: Metadata, + /// The extension metadata of NFT + type_meta: NFTMeta, + /// The body of NFT, NFT is a box for NFTBody + body: NFTBody, + } + + /// The information of NFT instance return by get_nft_info + struct NFTInfo has copy, store, drop { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + public fun get_info(nft: &NFT): NFTInfo { + NFTInfo { + id: nft.id, + creator: nft.creator, + base_meta: *&nft.base_meta, + type_meta: *&nft.type_meta, + } + } + + public fun unpack_info( + nft_info: NFTInfo + ): (u64, address, Metadata, NFTMeta) { + let NFTInfo { id, creator, base_meta, type_meta } = nft_info; + (id, creator, base_meta, type_meta) + } + + public fun get_id(nft: &NFT): u64 { + return nft.id + } + + public fun get_base_meta(nft: &NFT): &Metadata { + return &nft.base_meta + } + + public fun get_type_meta(nft: &NFT): &NFTMeta { + return &nft.type_meta + } + + public fun get_creator(nft: &NFT): address { + return nft.creator + } + + /// deprecated. + public fun initialize(_signer: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, + /// in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability { + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register a NFT type to genesis + /// Note: this function is deprecated, please use `register_v2` + public fun register( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ) acquires NFTTypeInfo { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let type_info = new_nft_type_info(sender, info, meta); + move_to>(&genesis_account, type_info); + let mint_cap = MintCapability {}; + + Self::upgrade_nft_type_info_from_v1_to_v2(sender, &mut mint_cap); + + move_to>(sender, mint_cap); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Register a NFT type to genesis + public fun register_v2(sender: &signer, meta: Metadata) { + assert!(!is_registered(), Errors::invalid_argument(ERR_NFT_TYPE_ALREADY_REGISTERED)); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let type_info = new_nft_type_info_v2(sender, meta); + move_to>(&genesis_account, type_info); + move_to>(sender, MintCapability {}); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Check the NFTMeta is register + public fun is_registered(): bool { + exists>(CoreAddresses::GENESIS_ADDRESS()) + } + + /// deprecated. Use "is_registered" instead. + public fun is_register(): bool { + is_registered() + } + + /// Add MintCapability to `sender` + public fun add_mint_capability(sender: &signer, cap: MintCapability) { + move_to(sender, cap); + } + + /// Remove the MintCapability from `sender` + public fun remove_mint_capability( + sender: &signer + ): MintCapability acquires MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the MintCapability + public fun destroy_mint_capability(cap: MintCapability) { + let MintCapability {} = cap; + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + /// Note: this function is deprecated, please use `mint_with_cap_v2` + public fun mint_with_cap( + creator: address, + cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + mint_with_cap_v2(creator, cap, base_meta, type_meta, body) + } else { + let nft_type_info = + borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id: id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + public fun mint_with_cap_v2( + creator: address, + _cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2 { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + + /// Mint nft, the `sender` must have MintCapability + /// Note: this function is deprecated, please use `mint_v2` + public fun mint( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap(addr, cap, base_meta, type_meta, body) + } + + /// Mint nft, the `sender` must have MintCapability + public fun mint_v2( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap_v2(addr, cap, base_meta, type_meta, body) + } + + /// Add BurnCapability to `sender` + public fun add_burn_capability(sender: &signer, cap: BurnCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_burn_capability( + sender: &signer + ): BurnCapability acquires BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the BurnCapability + public fun destroy_burn_capability(cap: BurnCapability) { + let BurnCapability {} = cap; + } + + /// Burn nft with BurnCapability + public fun burn_with_cap( + _cap: &mut BurnCapability, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2 { + let NFT { creator: _, id: id, base_meta: _, type_meta: _, body } = nft; + // only NFTTypeInfoV2 has burn_events EventHandle + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + Event::emit_event(&mut nft_type_info.burn_events, BurnEvent { + id, + }); + }; + body + } + + /// Burn nft, the `sender` must have BurnCapability + public fun burn( + sender: &signer, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2, BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + let cap = borrow_global_mut>(addr); + burn_with_cap(cap, nft) + } + + /// Add UpdateCapability to `sender` + public fun add_update_capability(sender: &signer, cap: UpdateCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_update_capability( + sender: &signer + ): UpdateCapability acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the UpdateCapability + public fun destroy_update_capability(cap: UpdateCapability) { + let UpdateCapability {} = cap; + } + + /// Update the NFTTypeInfoV2 metadata with UpdateCapability + public fun update_nft_type_info_meta_with_cap( + _cap: &mut UpdateCapability, + new_meta: Metadata + ) acquires NFTTypeInfoV2{ + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + info.meta = new_meta; + } + + /// Update the NFTTypeInfoV2 metadata, the `sender` must have UpdateCapability + public fun update_nft_type_info_meta( + sender: &signer, + new_meta: Metadata + ) acquires UpdateCapability, NFTTypeInfoV2 { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_nft_type_info_meta_with_cap(cap, new_meta) + } + + /// Update the nft's base_meta and type_meta with UpdateCapability + public fun update_meta_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT, + base_meta: Metadata, type_meta: NFTMeta + ) { + nft.base_meta = base_meta; + nft.type_meta = type_meta; + } + + /// Update the nft's base_meta and type_meta, the `sender` must have UpdateCapability + public fun update_meta( + sender: &signer, + nft: &mut NFT, + base_meta: Metadata, + type_meta: NFTMeta + ) acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_meta_with_cap(cap, nft, base_meta, type_meta) + } + + /// Borrow NFTBody ref + public fun borrow_body(nft: &NFT): &NFTBody { + &nft.body + } + + /// Borrow NFTBody mut ref for update body with UpdateCapability + public fun borrow_body_mut_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT + ): &mut NFTBody { + &mut nft.body + } +} + +/// IdentifierNFT using NFT as identifier for an on chain account +/// The NFT can not been transfer by owner. +module IdentifierNFT { + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::NFT::{Self, NFT, MintCapability, BurnCapability, UpdateCapability}; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + const ERR_NFT_EXISTS: u64 = 101; + const ERR_NFT_NOT_EXISTS: u64 = 102; + const ERR_NFT_NOT_ACCEPT: u64 = 103; + const ERR_BORROW_ADDR_NOT_SAME: u64 = 104; + + spec module { + pragma verify = false; + } + + struct IdentifierNFT has key { + nft: Option>, + } + + //Used when borrowing or returning NFT, note: there is no drop ability, it must be returned after borrowing + struct BorrowNFT { + nft: NFT, + addr:address + } + + /// Check the `owner` is prepared with IdentifierNFT for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Accept NFT, prepare an empty IdentifierNFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let addr = Signer::address_of(sender); + if (!is_accept(addr)) { + move_to(sender, IdentifierNFT { + nft: Option::none(), + }); + } + } + + /// Destroy the empty IdentifierNFT + public entry fun destroy_empty_entry(sender: signer) acquires IdentifierNFT { + destroy_empty(&sender); + } + + public fun destroy_empty(sender: &signer) acquires IdentifierNFT { + let addr = Signer::address_of(sender); + if (exists>(addr)) { + let id_nft = move_from>(addr); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_none(nft); + } + } + + /// Grant nft as IdentifierNFT to `sender` with MintCapability, sender will auto accept the NFT. + public fun grant( + cap: &mut MintCapability, + sender: &signer, + nft: NFT + ) acquires IdentifierNFT { + Self::accept(sender); + Self::grant_to(cap, Signer::address_of(sender), nft); + } + + /// Grant nft as IdentifierNFT to `receiver` with MintCapability, the receiver should accept the NFT first. + public fun grant_to( + _cap: &mut MintCapability, + receiver: address, + nft: NFT + ) acquires IdentifierNFT { + assert!(exists>(receiver), Errors::not_published(ERR_NFT_NOT_ACCEPT)); + let id_nft = borrow_global_mut>(receiver); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + Option::fill(&mut id_nft.nft, nft); + } + + /// Revoke the NFT from owner. + public fun revoke( + _cap: &mut BurnCapability, + owner: address + ): NFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = move_from>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_some(nft) + } + + /// borrow_out the NFT from owner. + public fun borrow_out( + _cap: &mut UpdateCapability, + owner: address + ): BorrowNFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let id_nft = borrow_global_mut>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let nft = Option::extract(&mut id_nft.nft); + + BorrowNFT{ + nft : nft, + addr: owner + } + } + + /// return_back the NFT to owner. + public fun return_back( + borrownft: BorrowNFT, + ) acquires IdentifierNFT { + + let BorrowNFT{ + nft: nft, + addr: owner + } = borrownft ; + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = borrow_global_mut>(owner); + + Option::fill(&mut id_nft.nft , nft) + } + + public fun borrow_nft( + borrownft:&BorrowNFT + ) : & NFT { + & borrownft.nft + } + + public fun borrow_nft_mut ( + borrownft:&mut BorrowNFT + ) : &mut NFT { + &mut borrownft.nft + } + + /// Check `owner` is owns the IdentifierNFT + public fun owns(owner: address): bool acquires IdentifierNFT { + if (!exists>(owner)) { + return false + }; + let id_nft = borrow_global>(owner); + Option::is_some(&id_nft.nft) + } + /// deprecated. Use `owns()` instead. + public fun is_owns(owner: address): bool acquires IdentifierNFT { + owns(owner) + } + + public fun get_nft_info( + owner: address + ): Option> acquires IdentifierNFT { + if (!exists>(owner)) { + return Option::none>() + }; + let id_nft = borrow_global>(owner); + let info = if (Option::is_some(&id_nft.nft)) { + let nft = Option::borrow(&id_nft.nft); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + info + } +} + +module IdentifierNFTScripts { + use StarcoinFramework::IdentifierNFT; + spec module { + pragma verify = false; + } + + /// Init IdentifierNFT for accept NFT as Identifier. + public entry fun accept(sender: signer) { + IdentifierNFT::accept_entry(sender); + } + + /// Destroy empty IdentifierNFT + public entry fun destroy_empty(sender: signer) { + IdentifierNFT::destroy_empty_entry(sender); + } +} + +/// NFTGallery is user collection of NFT. +module NFTGallery { + use StarcoinFramework::Signer; + use StarcoinFramework::NFT::{Self, NFT}; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::Event; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + + const ERR_NFT_NOT_EXISTS: u64 = 101; + + const ERR_NFTGALLERY_NOT_EXISTS:u64 = 102; + + spec module { + pragma verify = false; + } + + struct WithdrawEvent has drop, store { + owner: address, + id: u64, + } + + struct DepositEvent has drop, store { + owner: address, + id: u64, + } + + struct NFTGallery has key, store { + withdraw_events: Event::EventHandle>, + deposit_events: Event::EventHandle>, + items: vector>, + } + + /// Check the `owner` is prepared with NFTGallery for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Init a NFTGallery to accept NFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let sender_addr = Signer::address_of(sender); + if (!is_accept(sender_addr)) { + let gallery = NFTGallery { + withdraw_events: Event::new_event_handle>(sender), + deposit_events: Event::new_event_handle>(sender), + items: Vector::empty>(), + }; + move_to(sender, gallery); + } + } + + /// Transfer NFT from `sender` to `receiver` + public entry fun transfer_entry( + sender: signer, + id: u64, receiver: address + ) acquires NFTGallery { + transfer(&sender, id, receiver); + } + + public fun transfer( + sender: &signer, + id: u64, + receiver: address + ) acquires NFTGallery { + let nft = withdraw(sender, id); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let nft = Option::destroy_some(nft); + deposit_to(receiver, nft) + } + + /// Get the NFT info by the NFT id. + public fun get_nft_info_by_id( + owner: address, + id: u64 + ): Option> acquires NFTGallery { + if(!is_accept(owner)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(owner); + let idx = find_by_id(&gallery.items, id); + + let info = if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::borrow>(&gallery.items, i); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + return info + } + + /// Get the NFT info by the NFT idx in NFTGallery + public fun get_nft_info_by_idx( + owner: address, + idx: u64 + ): NFT::NFTInfo acquires NFTGallery { + assert!(exists>(owner), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(owner); + let nft = Vector::borrow>(&gallery.items, idx); + NFT::get_info(nft) + } + + /// Get the all NFT info + public fun get_nft_infos( + owner: address + ): vector> acquires NFTGallery { + if(!is_accept(owner)){ + return Vector::empty>() + }; + let gallery = borrow_global_mut>(owner); + let infos = Vector::empty(); + let len = Vector::length(&gallery.items); + let idx = 0; + while (len > idx) { + let nft = Vector::borrow>(&gallery.items, idx); + Vector::push_back(&mut infos, NFT::get_info(nft)); + idx = idx + 1; + }; + infos + } + + /// Deposit nft to `sender` NFTGallery + public fun deposit( + sender: &signer, + nft: NFT + ) acquires NFTGallery { + Self::accept(sender); + let sender_addr = Signer::address_of(sender); + deposit_to(sender_addr, nft) + } + + /// Deposit nft to `receiver` NFTGallery + public fun deposit_to( + receiver: address, + nft: NFT + ) acquires NFTGallery { + assert!(exists>(receiver), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(receiver); + Event::emit_event(&mut gallery.deposit_events, DepositEvent { id: NFT::get_id(&nft), owner: receiver }); + Vector::push_back(&mut gallery.items, nft); + } + + /// Withdraw one nft of NFTMeta from `sender`, caller should ensure at least one NFT in the Gallery. + public fun withdraw_one( + sender: &signer + ): NFT acquires NFTGallery { + let nft = do_withdraw(sender, Option::none()); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + Option::destroy_some(nft) + } + + /// Withdraw nft of NFTMeta and id from `sender` + public fun withdraw( + sender: &signer, + id: u64 + ): Option> acquires NFTGallery { + do_withdraw(sender, Option::some(id)) + } + + /// Withdraw nft of NFTMeta and id from `sender` + fun do_withdraw( + sender: &signer, + id: Option + ): Option> acquires NFTGallery { + let sender_addr = Signer::address_of(sender); + if(!is_accept(sender_addr)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(sender_addr); + let len = Vector::length(&gallery.items); + let nft = if (len == 0) { + Option::none() + } else { + let idx = if (Option::is_some(&id)) { + let id = Option::extract(&mut id); + find_by_id(&gallery.items, id) + } else { + //default withdraw the last nft. + Option::some(len - 1) + }; + + if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::remove>(&mut gallery.items, i); + Event::emit_event( + &mut gallery.withdraw_events, + WithdrawEvent { id: NFT::get_id(&nft), owner: sender_addr } + ); + Option::some(nft) + } else { + Option::none() + } + }; + nft + } + + fun find_by_id( + c: &vector>, + id: u64 + ): Option { + let len = Vector::length(c); + if (len == 0) { + return Option::none() + }; + let idx = len - 1; + loop { + let nft = Vector::borrow(c, idx); + if (NFT::get_id(nft) == id) { + return Option::some(idx) + }; + if (idx == 0) { + return Option::none() + }; + idx = idx - 1; + } + } + + /// Count all NFTs assigned to an owner + public fun count_of(owner: address): u64 acquires NFTGallery { + if(!is_accept(owner)){ + return 0 + }; + let gallery = borrow_global_mut>(owner); + Vector::length(&gallery.items) + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery_entry(sender: signer) acquires NFTGallery { + remove_empty_gallery(&sender); + } + + public fun remove_empty_gallery(sender: &signer) acquires NFTGallery{ + let sender_addr = Signer::address_of(sender); + assert!(exists>(sender_addr), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let NFTGallery {withdraw_events, deposit_events, items} = move_from>(sender_addr); + + Event::destroy_handle>(withdraw_events); + Event::destroy_handle>(deposit_events); + Vector::destroy_empty>(items); + } + + spec remove_empty_gallery { + let sender_addr = Signer::address_of(sender); + aborts_if !exists>(sender_addr); + + let gallery = global>(sender_addr); + aborts_if Vector::length>(gallery.items) > 0; + + ensures !exists>(sender_addr); + } + +} + +module NFTGalleryScripts { + use StarcoinFramework::NFTGallery; + + spec module { + pragma verify = false; + } + + /// Init a NFTGallery for accept NFT + public entry fun accept(sender: signer) { + NFTGallery::accept_entry(sender); + } + /// Transfer NFT with `id` from `sender` to `receiver` + public entry fun transfer( + sender: signer, + id: u64, receiver: address + ) { + NFTGallery::transfer_entry(sender, id, receiver); + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery(sender: signer) { + NFTGallery::remove_empty_gallery_entry(sender); + } +} +} diff --git a/release/v13/sources/IdentifierNFTScripts.move b/release/v13/sources/IdentifierNFTScripts.move new file mode 100644 index 00000000..d1ac25f8 --- /dev/null +++ b/release/v13/sources/IdentifierNFTScripts.move @@ -0,0 +1,1023 @@ +address StarcoinFramework { +/// Non-fungible token standard and implementations. +module NFT { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Account; + use StarcoinFramework::Vector; + use StarcoinFramework::Event; + use StarcoinFramework::GenesisSignerCapability; + + const ERR_NO_MINT_CAPABILITY: u64 = 101; + const ERR_NO_BURN_CAPABILITY: u64 = 102; + const ERR_NO_UPDATE_CAPABILITY: u64 = 103; + const ERR_CANOT_EMPTY: u64 = 104; + const ERR_NFT_TYPE_ALREADY_REGISTERED: u64 = 105; + const ERR_NFT_TYPE_NO_REGISTERED: u64 = 106; + + spec module { + pragma verify = false; + } + + struct MintEvent has drop, store { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + struct BurnEvent has drop, store { + id: u64, + } + + /// The info of NFT type, this type is deprecated, please use NFTTypeInfoV2 + struct NFTTypeInfo has key, store { + counter: u64, + meta: Metadata, + info: NFTTypeInfoExt, + mint_events: Event::EventHandle>, + } + + /// The info of NFT type + struct NFTTypeInfoV2 has key, store { + register: address, + counter: u64, + meta: Metadata, + mint_events: Event::EventHandle>, + burn_events: Event::EventHandle>, + } + + struct NFTTypeInfoCompat has key { + info: NFTTypeInfoExt, + } + + /// Deprecated. Use `new_nft_type_info_v2` instead. + fun new_nft_type_info( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ): NFTTypeInfo { + NFTTypeInfo { + counter: 0, + info, + meta, + mint_events: Event::new_event_handle>(sender), + } + } + + fun new_nft_type_info_v2(sender: &signer, meta: Metadata): NFTTypeInfoV2 { + NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter: 0, + meta, + mint_events: Event::new_event_handle>(sender), + burn_events: Event::new_event_handle>(sender), + } + } + + /// Note: this function is deprecated + public fun nft_type_info_ex_info( + ): NFTTypeInfoExt acquires NFTTypeInfo, NFTTypeInfoCompat { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } + } + + /// Note: this function is deprecated, please use nft_type_info_counter_v2 + public fun nft_type_info_counter( + ): u64 acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + Self::nft_type_info_counter_v2() + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + } + + public fun nft_type_info_counter_v2(): u64 acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + + public fun nft_type_info_meta(): Metadata acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.meta + } + + public fun upgrade_nft_type_info_from_v1_to_v2( + sender: &signer, + _cap: &mut MintCapability + ) acquires NFTTypeInfo { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfo { counter, meta, info, mint_events } = nft_type_info; + + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let nft_type_info_v2 = NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter, + meta, + mint_events, + burn_events: Event::new_event_handle>(sender), + }; + move_to(&genesis_account, nft_type_info_v2); + move_to(&genesis_account, NFTTypeInfoCompat { info }); + } + } + + public fun remove_compat_info( + _cap: &mut MintCapability + ): NFTTypeInfoExt acquires NFTTypeInfoCompat { + let compat_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfoCompat{info} = compat_info; + info + } + /// deprecated. + struct GenesisSignerCapability has key { + cap: Account::SignerCapability, + } + /// The capability to mint the nft. + struct MintCapability has key, store {} + /// The Capability to burn the nft. + struct BurnCapability has key, store {} + /// The Capability to update the nft metadata. + struct UpdateCapability has key, store {} + + struct Metadata has copy, store, drop { + /// NFT name's utf8 bytes. + name: vector, + /// Image link, such as ipfs://xxxx + image: vector, + /// Image bytes data, image or image_data can not empty for both. + image_data: vector, + /// NFT description utf8 bytes. + description: vector, + } + + public fun empty_meta(): Metadata { + Metadata { + name: Vector::empty(), + image: Vector::empty(), + image_data: Vector::empty(), + description: Vector::empty(), + } + } + + public fun new_meta(name: vector, description: vector): Metadata { + Metadata { + name, + image: Vector::empty(), + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image(name: vector, image: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image, + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image_data(name: vector, image_data: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image_data), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image: Vector::empty(), + image_data, + description, + } + } + + public fun meta_name(metadata: &Metadata): vector { + *&metadata.name + } + + public fun meta_image(metadata: &Metadata): vector { + *&metadata.image + } + + public fun meta_image_data(metadata: &Metadata): vector { + *&metadata.image_data + } + + public fun meta_description(metadata: &Metadata): vector { + *&metadata.description + } + + struct NFT has store { + /// The creator of NFT + creator: address, + /// The unique id of NFT under NFTMeta type + id: u64, + /// The metadata of NFT + base_meta: Metadata, + /// The extension metadata of NFT + type_meta: NFTMeta, + /// The body of NFT, NFT is a box for NFTBody + body: NFTBody, + } + + /// The information of NFT instance return by get_nft_info + struct NFTInfo has copy, store, drop { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + public fun get_info(nft: &NFT): NFTInfo { + NFTInfo { + id: nft.id, + creator: nft.creator, + base_meta: *&nft.base_meta, + type_meta: *&nft.type_meta, + } + } + + public fun unpack_info( + nft_info: NFTInfo + ): (u64, address, Metadata, NFTMeta) { + let NFTInfo { id, creator, base_meta, type_meta } = nft_info; + (id, creator, base_meta, type_meta) + } + + public fun get_id(nft: &NFT): u64 { + return nft.id + } + + public fun get_base_meta(nft: &NFT): &Metadata { + return &nft.base_meta + } + + public fun get_type_meta(nft: &NFT): &NFTMeta { + return &nft.type_meta + } + + public fun get_creator(nft: &NFT): address { + return nft.creator + } + + /// deprecated. + public fun initialize(_signer: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, + /// in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability { + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register a NFT type to genesis + /// Note: this function is deprecated, please use `register_v2` + public fun register( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ) acquires NFTTypeInfo { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let type_info = new_nft_type_info(sender, info, meta); + move_to>(&genesis_account, type_info); + let mint_cap = MintCapability {}; + + Self::upgrade_nft_type_info_from_v1_to_v2(sender, &mut mint_cap); + + move_to>(sender, mint_cap); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Register a NFT type to genesis + public fun register_v2(sender: &signer, meta: Metadata) { + assert!(!is_registered(), Errors::invalid_argument(ERR_NFT_TYPE_ALREADY_REGISTERED)); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let type_info = new_nft_type_info_v2(sender, meta); + move_to>(&genesis_account, type_info); + move_to>(sender, MintCapability {}); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Check the NFTMeta is register + public fun is_registered(): bool { + exists>(CoreAddresses::GENESIS_ADDRESS()) + } + + /// deprecated. Use "is_registered" instead. + public fun is_register(): bool { + is_registered() + } + + /// Add MintCapability to `sender` + public fun add_mint_capability(sender: &signer, cap: MintCapability) { + move_to(sender, cap); + } + + /// Remove the MintCapability from `sender` + public fun remove_mint_capability( + sender: &signer + ): MintCapability acquires MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the MintCapability + public fun destroy_mint_capability(cap: MintCapability) { + let MintCapability {} = cap; + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + /// Note: this function is deprecated, please use `mint_with_cap_v2` + public fun mint_with_cap( + creator: address, + cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + mint_with_cap_v2(creator, cap, base_meta, type_meta, body) + } else { + let nft_type_info = + borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id: id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + public fun mint_with_cap_v2( + creator: address, + _cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2 { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + + /// Mint nft, the `sender` must have MintCapability + /// Note: this function is deprecated, please use `mint_v2` + public fun mint( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap(addr, cap, base_meta, type_meta, body) + } + + /// Mint nft, the `sender` must have MintCapability + public fun mint_v2( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap_v2(addr, cap, base_meta, type_meta, body) + } + + /// Add BurnCapability to `sender` + public fun add_burn_capability(sender: &signer, cap: BurnCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_burn_capability( + sender: &signer + ): BurnCapability acquires BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the BurnCapability + public fun destroy_burn_capability(cap: BurnCapability) { + let BurnCapability {} = cap; + } + + /// Burn nft with BurnCapability + public fun burn_with_cap( + _cap: &mut BurnCapability, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2 { + let NFT { creator: _, id: id, base_meta: _, type_meta: _, body } = nft; + // only NFTTypeInfoV2 has burn_events EventHandle + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + Event::emit_event(&mut nft_type_info.burn_events, BurnEvent { + id, + }); + }; + body + } + + /// Burn nft, the `sender` must have BurnCapability + public fun burn( + sender: &signer, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2, BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + let cap = borrow_global_mut>(addr); + burn_with_cap(cap, nft) + } + + /// Add UpdateCapability to `sender` + public fun add_update_capability(sender: &signer, cap: UpdateCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_update_capability( + sender: &signer + ): UpdateCapability acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the UpdateCapability + public fun destroy_update_capability(cap: UpdateCapability) { + let UpdateCapability {} = cap; + } + + /// Update the NFTTypeInfoV2 metadata with UpdateCapability + public fun update_nft_type_info_meta_with_cap( + _cap: &mut UpdateCapability, + new_meta: Metadata + ) acquires NFTTypeInfoV2{ + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + info.meta = new_meta; + } + + /// Update the NFTTypeInfoV2 metadata, the `sender` must have UpdateCapability + public fun update_nft_type_info_meta( + sender: &signer, + new_meta: Metadata + ) acquires UpdateCapability, NFTTypeInfoV2 { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_nft_type_info_meta_with_cap(cap, new_meta) + } + + /// Update the nft's base_meta and type_meta with UpdateCapability + public fun update_meta_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT, + base_meta: Metadata, type_meta: NFTMeta + ) { + nft.base_meta = base_meta; + nft.type_meta = type_meta; + } + + /// Update the nft's base_meta and type_meta, the `sender` must have UpdateCapability + public fun update_meta( + sender: &signer, + nft: &mut NFT, + base_meta: Metadata, + type_meta: NFTMeta + ) acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_meta_with_cap(cap, nft, base_meta, type_meta) + } + + /// Borrow NFTBody ref + public fun borrow_body(nft: &NFT): &NFTBody { + &nft.body + } + + /// Borrow NFTBody mut ref for update body with UpdateCapability + public fun borrow_body_mut_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT + ): &mut NFTBody { + &mut nft.body + } +} + +/// IdentifierNFT using NFT as identifier for an on chain account +/// The NFT can not been transfer by owner. +module IdentifierNFT { + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::NFT::{Self, NFT, MintCapability, BurnCapability, UpdateCapability}; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + const ERR_NFT_EXISTS: u64 = 101; + const ERR_NFT_NOT_EXISTS: u64 = 102; + const ERR_NFT_NOT_ACCEPT: u64 = 103; + const ERR_BORROW_ADDR_NOT_SAME: u64 = 104; + + spec module { + pragma verify = false; + } + + struct IdentifierNFT has key { + nft: Option>, + } + + //Used when borrowing or returning NFT, note: there is no drop ability, it must be returned after borrowing + struct BorrowNFT { + nft: NFT, + addr:address + } + + /// Check the `owner` is prepared with IdentifierNFT for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Accept NFT, prepare an empty IdentifierNFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let addr = Signer::address_of(sender); + if (!is_accept(addr)) { + move_to(sender, IdentifierNFT { + nft: Option::none(), + }); + } + } + + /// Destroy the empty IdentifierNFT + public entry fun destroy_empty_entry(sender: signer) acquires IdentifierNFT { + destroy_empty(&sender); + } + + public fun destroy_empty(sender: &signer) acquires IdentifierNFT { + let addr = Signer::address_of(sender); + if (exists>(addr)) { + let id_nft = move_from>(addr); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_none(nft); + } + } + + /// Grant nft as IdentifierNFT to `sender` with MintCapability, sender will auto accept the NFT. + public fun grant( + cap: &mut MintCapability, + sender: &signer, + nft: NFT + ) acquires IdentifierNFT { + Self::accept(sender); + Self::grant_to(cap, Signer::address_of(sender), nft); + } + + /// Grant nft as IdentifierNFT to `receiver` with MintCapability, the receiver should accept the NFT first. + public fun grant_to( + _cap: &mut MintCapability, + receiver: address, + nft: NFT + ) acquires IdentifierNFT { + assert!(exists>(receiver), Errors::not_published(ERR_NFT_NOT_ACCEPT)); + let id_nft = borrow_global_mut>(receiver); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + Option::fill(&mut id_nft.nft, nft); + } + + /// Revoke the NFT from owner. + public fun revoke( + _cap: &mut BurnCapability, + owner: address + ): NFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = move_from>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_some(nft) + } + + /// borrow_out the NFT from owner. + public fun borrow_out( + _cap: &mut UpdateCapability, + owner: address + ): BorrowNFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let id_nft = borrow_global_mut>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let nft = Option::extract(&mut id_nft.nft); + + BorrowNFT{ + nft : nft, + addr: owner + } + } + + /// return_back the NFT to owner. + public fun return_back( + borrownft: BorrowNFT, + ) acquires IdentifierNFT { + + let BorrowNFT{ + nft: nft, + addr: owner + } = borrownft ; + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = borrow_global_mut>(owner); + + Option::fill(&mut id_nft.nft , nft) + } + + public fun borrow_nft( + borrownft:&BorrowNFT + ) : & NFT { + & borrownft.nft + } + + public fun borrow_nft_mut ( + borrownft:&mut BorrowNFT + ) : &mut NFT { + &mut borrownft.nft + } + + /// Check `owner` is owns the IdentifierNFT + public fun owns(owner: address): bool acquires IdentifierNFT { + if (!exists>(owner)) { + return false + }; + let id_nft = borrow_global>(owner); + Option::is_some(&id_nft.nft) + } + /// deprecated. Use `owns()` instead. + public fun is_owns(owner: address): bool acquires IdentifierNFT { + owns(owner) + } + + public fun get_nft_info( + owner: address + ): Option> acquires IdentifierNFT { + if (!exists>(owner)) { + return Option::none>() + }; + let id_nft = borrow_global>(owner); + let info = if (Option::is_some(&id_nft.nft)) { + let nft = Option::borrow(&id_nft.nft); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + info + } +} + +module IdentifierNFTScripts { + use StarcoinFramework::IdentifierNFT; + spec module { + pragma verify = false; + } + + /// Init IdentifierNFT for accept NFT as Identifier. + public entry fun accept(sender: signer) { + IdentifierNFT::accept_entry(sender); + } + + /// Destroy empty IdentifierNFT + public entry fun destroy_empty(sender: signer) { + IdentifierNFT::destroy_empty_entry(sender); + } +} + +/// NFTGallery is user collection of NFT. +module NFTGallery { + use StarcoinFramework::Signer; + use StarcoinFramework::NFT::{Self, NFT}; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::Event; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + + const ERR_NFT_NOT_EXISTS: u64 = 101; + + const ERR_NFTGALLERY_NOT_EXISTS:u64 = 102; + + spec module { + pragma verify = false; + } + + struct WithdrawEvent has drop, store { + owner: address, + id: u64, + } + + struct DepositEvent has drop, store { + owner: address, + id: u64, + } + + struct NFTGallery has key, store { + withdraw_events: Event::EventHandle>, + deposit_events: Event::EventHandle>, + items: vector>, + } + + /// Check the `owner` is prepared with NFTGallery for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Init a NFTGallery to accept NFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let sender_addr = Signer::address_of(sender); + if (!is_accept(sender_addr)) { + let gallery = NFTGallery { + withdraw_events: Event::new_event_handle>(sender), + deposit_events: Event::new_event_handle>(sender), + items: Vector::empty>(), + }; + move_to(sender, gallery); + } + } + + /// Transfer NFT from `sender` to `receiver` + public entry fun transfer_entry( + sender: signer, + id: u64, receiver: address + ) acquires NFTGallery { + transfer(&sender, id, receiver); + } + + public fun transfer( + sender: &signer, + id: u64, + receiver: address + ) acquires NFTGallery { + let nft = withdraw(sender, id); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let nft = Option::destroy_some(nft); + deposit_to(receiver, nft) + } + + /// Get the NFT info by the NFT id. + public fun get_nft_info_by_id( + owner: address, + id: u64 + ): Option> acquires NFTGallery { + if(!is_accept(owner)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(owner); + let idx = find_by_id(&gallery.items, id); + + let info = if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::borrow>(&gallery.items, i); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + return info + } + + /// Get the NFT info by the NFT idx in NFTGallery + public fun get_nft_info_by_idx( + owner: address, + idx: u64 + ): NFT::NFTInfo acquires NFTGallery { + assert!(exists>(owner), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(owner); + let nft = Vector::borrow>(&gallery.items, idx); + NFT::get_info(nft) + } + + /// Get the all NFT info + public fun get_nft_infos( + owner: address + ): vector> acquires NFTGallery { + if(!is_accept(owner)){ + return Vector::empty>() + }; + let gallery = borrow_global_mut>(owner); + let infos = Vector::empty(); + let len = Vector::length(&gallery.items); + let idx = 0; + while (len > idx) { + let nft = Vector::borrow>(&gallery.items, idx); + Vector::push_back(&mut infos, NFT::get_info(nft)); + idx = idx + 1; + }; + infos + } + + /// Deposit nft to `sender` NFTGallery + public fun deposit( + sender: &signer, + nft: NFT + ) acquires NFTGallery { + Self::accept(sender); + let sender_addr = Signer::address_of(sender); + deposit_to(sender_addr, nft) + } + + /// Deposit nft to `receiver` NFTGallery + public fun deposit_to( + receiver: address, + nft: NFT + ) acquires NFTGallery { + assert!(exists>(receiver), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(receiver); + Event::emit_event(&mut gallery.deposit_events, DepositEvent { id: NFT::get_id(&nft), owner: receiver }); + Vector::push_back(&mut gallery.items, nft); + } + + /// Withdraw one nft of NFTMeta from `sender`, caller should ensure at least one NFT in the Gallery. + public fun withdraw_one( + sender: &signer + ): NFT acquires NFTGallery { + let nft = do_withdraw(sender, Option::none()); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + Option::destroy_some(nft) + } + + /// Withdraw nft of NFTMeta and id from `sender` + public fun withdraw( + sender: &signer, + id: u64 + ): Option> acquires NFTGallery { + do_withdraw(sender, Option::some(id)) + } + + /// Withdraw nft of NFTMeta and id from `sender` + fun do_withdraw( + sender: &signer, + id: Option + ): Option> acquires NFTGallery { + let sender_addr = Signer::address_of(sender); + if(!is_accept(sender_addr)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(sender_addr); + let len = Vector::length(&gallery.items); + let nft = if (len == 0) { + Option::none() + } else { + let idx = if (Option::is_some(&id)) { + let id = Option::extract(&mut id); + find_by_id(&gallery.items, id) + } else { + //default withdraw the last nft. + Option::some(len - 1) + }; + + if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::remove>(&mut gallery.items, i); + Event::emit_event( + &mut gallery.withdraw_events, + WithdrawEvent { id: NFT::get_id(&nft), owner: sender_addr } + ); + Option::some(nft) + } else { + Option::none() + } + }; + nft + } + + fun find_by_id( + c: &vector>, + id: u64 + ): Option { + let len = Vector::length(c); + if (len == 0) { + return Option::none() + }; + let idx = len - 1; + loop { + let nft = Vector::borrow(c, idx); + if (NFT::get_id(nft) == id) { + return Option::some(idx) + }; + if (idx == 0) { + return Option::none() + }; + idx = idx - 1; + } + } + + /// Count all NFTs assigned to an owner + public fun count_of(owner: address): u64 acquires NFTGallery { + if(!is_accept(owner)){ + return 0 + }; + let gallery = borrow_global_mut>(owner); + Vector::length(&gallery.items) + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery_entry(sender: signer) acquires NFTGallery { + remove_empty_gallery(&sender); + } + + public fun remove_empty_gallery(sender: &signer) acquires NFTGallery{ + let sender_addr = Signer::address_of(sender); + assert!(exists>(sender_addr), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let NFTGallery {withdraw_events, deposit_events, items} = move_from>(sender_addr); + + Event::destroy_handle>(withdraw_events); + Event::destroy_handle>(deposit_events); + Vector::destroy_empty>(items); + } + + spec remove_empty_gallery { + let sender_addr = Signer::address_of(sender); + aborts_if !exists>(sender_addr); + + let gallery = global>(sender_addr); + aborts_if Vector::length>(gallery.items) > 0; + + ensures !exists>(sender_addr); + } + +} + +module NFTGalleryScripts { + use StarcoinFramework::NFTGallery; + + spec module { + pragma verify = false; + } + + /// Init a NFTGallery for accept NFT + public entry fun accept(sender: signer) { + NFTGallery::accept_entry(sender); + } + /// Transfer NFT with `id` from `sender` to `receiver` + public entry fun transfer( + sender: signer, + id: u64, receiver: address + ) { + NFTGallery::transfer_entry(sender, id, receiver); + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery(sender: signer) { + NFTGallery::remove_empty_gallery_entry(sender); + } +} +} diff --git a/release/v13/sources/LanguageVersion.move b/release/v13/sources/LanguageVersion.move new file mode 100644 index 00000000..82dc1d5b --- /dev/null +++ b/release/v13/sources/LanguageVersion.move @@ -0,0 +1,22 @@ +address StarcoinFramework { +module LanguageVersion { + struct LanguageVersion has copy, drop, store { + major: u64, + } + + public fun new(version: u64): LanguageVersion { + LanguageVersion {major: version} + } + + public fun version(version: &LanguageVersion): u64 { + version.major + } + + spec new { + pragma verify = false; + } + spec version { + pragma verify = false; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Math.move b/release/v13/sources/Math.move new file mode 100644 index 00000000..ccd25206 --- /dev/null +++ b/release/v13/sources/Math.move @@ -0,0 +1,155 @@ +address StarcoinFramework { +/// The module provide some improved math calculations. +module Math { + use StarcoinFramework::Vector; + + // TODO: verify the module. + spec module { + pragma verify = false; + pragma aborts_if_is_strict; + } + + const U64_MAX:u64 = 18446744073709551615; + const U128_MAX:u128 = 340282366920938463463374607431768211455; + + /// u64::MAX + public fun u64_max(): u64 { + U64_MAX + } + + /// u128::MAX + public fun u128_max(): u128 { + U128_MAX + } + + /// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) + public fun sqrt(y: u128): u64 { + if (y < 4) { + if (y == 0) { + 0u64 + } else { + 1u64 + } + } else { + let z = y; + let x = y / 2 + 1; + while (x < z) { + z = x; + x = (y / x + x) / 2; + }; + (z as u64) + } + } + + spec sqrt { + pragma opaque = true; + pragma verify = false; //while loop + aborts_if [abstract] false; + ensures [abstract] result == spec_sqrt(); + } + + /// We use an uninterpreted function to represent the result of sqrt. The actual value + /// does not matter for the verification of callers. + spec fun spec_sqrt(): u128; + + /// calculate the `y` pow of `x`. + public fun pow(x: u64, y: u64): u128 { + let result = 1u128; + let z = y; + let u = (x as u128); + while (z > 0) { + if (z % 2 == 1) { + result = (u * result as u128); + }; + u = (u * u as u128); + z = z / 2; + }; + result + } + + spec pow { + pragma opaque = true; + pragma verify = false; //while loop + aborts_if [abstract] false; + ensures [abstract] result == spec_pow(); + } + + /// We use an uninterpreted function to represent the result of pow. The actual value + /// does not matter for the verification of callers. + spec fun spec_pow(): u128; + + /// https://medium.com/coinmonks/math-in-solidity-part-3-percents-and-proportions-4db014e080b1 + /// calculate x * y /z with as little loss of precision as possible and avoid overflow + public fun mul_div(x: u128, y: u128, z: u128): u128 { + if (y == z) { + return x + }; + if (x == z) { + return y + }; + let a = x / z; + let b = x % z; + //x = a * z + b; + let c = y / z; + let d = y % z; + //y = c * z + d; + a * c * z + a * d + b * c + b * d / z + } + + spec mul_div { + pragma opaque = true; + include MulDivAbortsIf; + aborts_if [abstract] false; + ensures [abstract] result == spec_mul_div(); + } + + spec schema MulDivAbortsIf { + x: u128; + y: u128; + z: u128; + aborts_if y != z && x > z && z == 0; + aborts_if y != z && x > z && z!=0 && x/z*y > MAX_U128; + aborts_if y != z && x <= z && z == 0; + //a * b overflow + aborts_if y != z && x <= z && x / z * (x % z) > MAX_U128; + //a * b * z overflow + aborts_if y != z && x <= z && x / z * (x % z) * z > MAX_U128; + //a * d overflow + aborts_if y != z && x <= z && x / z * (y % z) > MAX_U128; + //a * b * z + a * d overflow + aborts_if y != z && x <= z && x / z * (x % z) * z + x / z * (y % z) > MAX_U128; + //b * c overflow + aborts_if y != z && x <= z && x % z * (y / z) > MAX_U128; + //b * d overflow + aborts_if y != z && x <= z && x % z * (y % z) > MAX_U128; + //b * d / z overflow + aborts_if y != z && x <= z && x % z * (y % z) / z > MAX_U128; + //a * b * z + a * d + b * c overflow + aborts_if y != z && x <= z && x / z * (x % z) * z + x / z * (y % z) + x % z * (y / z) > MAX_U128; + //a * b * z + a * d + b * c + b * d / z overflow + aborts_if y != z && x <= z && x / z * (x % z) * z + x / z * (y % z) + x % z * (y / z) + x % z * (y % z) / z > MAX_U128; + + } + + spec fun spec_mul_div(): u128; + + /// calculate sum of nums + public fun sum(nums: &vector): u128 { + let len = Vector::length(nums); + let i = 0; + let sum = 0; + while (i < len){ + sum = sum + *Vector::borrow(nums, i); + i = i + 1; + }; + sum + } + + /// calculate average of nums + public fun avg(nums: &vector): u128{ + let len = Vector::length(nums); + let sum = sum(nums); + sum/(len as u128) + } +} +} \ No newline at end of file diff --git a/release/v13/sources/MerkleNFTDistributor.move b/release/v13/sources/MerkleNFTDistributor.move new file mode 100644 index 00000000..be992e67 --- /dev/null +++ b/release/v13/sources/MerkleNFTDistributor.move @@ -0,0 +1,131 @@ +module StarcoinFramework::MerkleProof { + use StarcoinFramework::Hash; + use StarcoinFramework::Vector; + use StarcoinFramework::Compare; + + /// verify leaf node with hash of `leaf` with `proof` againest merkle `root`. + public fun verify(proof: &vector>, root: &vector, leaf: vector): bool { + let computed_hash = leaf; + let i = 0; + let proof_length = Vector::length(proof); + while (i < proof_length) { + let sibling = Vector::borrow(proof, i); + // computed_hash is left. + if (Compare::cmp_bytes( &computed_hash, sibling) < 2) { + let concated = concat(computed_hash, * sibling); + computed_hash = Hash::sha3_256(concated); + } else { + let concated = concat(*sibling, computed_hash); + computed_hash = Hash::sha3_256(concated); + }; + + i = i + 1; + }; + &computed_hash == root + } + + fun concat(v1: vector, v2: vector): vector { + Vector::append( &mut v1, v2); + v1 + } +} + +module StarcoinFramework::MerkleNFTDistributor { + use StarcoinFramework::Vector; + use StarcoinFramework::NFT::{Self, NFT, Metadata, MintCapability}; + use StarcoinFramework::Hash; + use StarcoinFramework::BCS; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::MerkleProof; + const ALREADY_MINTED: u64 = 1000; + const INVALID_PROOF: u64 = 1001; + const ERR_NO_MINT_CAPABILITY: u64 = 1002; + + struct MerkleNFTDistribution has key { + merkle_root: vector, + claimed_bitmap: vector, + } + + public fun register(signer: &signer, merkle_root: vector, leafs: u64, info: Info, meta: Metadata): MintCapability { + let bitmap_count = leafs / 128; + if (bitmap_count * 128 < leafs) { + bitmap_count = bitmap_count + 1; + }; + let claimed_bitmap = Vector::empty(); + let j = 0; + while (j < bitmap_count) { + Vector::push_back( &mut claimed_bitmap, 0u128); + j = j + 1; + }; + let distribution = MerkleNFTDistribution{ + merkle_root, + claimed_bitmap + }; + NFT::register(signer, info, meta); + move_to(signer, distribution); + NFT::remove_mint_capability(signer) + } + + public fun mint_with_cap(sender: &signer, cap:&mut MintCapability, creator: address, index: u64, base_meta: Metadata, type_meta: NFTMeta, body: NFTBody, merkle_proof:vector>): NFT + acquires MerkleNFTDistribution { + let addr = Signer::address_of(sender); + let distribution = borrow_global_mut>(creator); + let minted = is_minted_(distribution, index); + assert!(!minted, Errors::custom(ALREADY_MINTED)); + let leaf_data = encode_leaf(&index, &addr); + let verified = MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data)); + assert!(verified, Errors::custom(INVALID_PROOF)); + set_minted_(distribution, index); + let nft = NFT::mint_with_cap(creator, cap, base_meta, type_meta, body); + return nft + } + + fun encode_leaf(index: &u64, account: &address): vector { + let leaf = Vector::empty(); + Vector::append(&mut leaf, BCS::to_bytes(index)); + Vector::append(&mut leaf, BCS::to_bytes(account)); + leaf + } + + fun set_minted_(distribution: &mut MerkleNFTDistribution, index: u64) { + let claimed_word_index = index / 128; + let claimed_bit_index = ((index % 128) as u8); + let word = Vector::borrow_mut(&mut distribution.claimed_bitmap, claimed_word_index); + // word | (1 << bit_index) + let mask = 1u128 << claimed_bit_index; + *word = (*word | mask); + } + + spec set_minted_ { + pragma verify = false; // Bitwise operator + pragma opaque; + } + + public fun verify_proof(account: address, creator: address, index: u64, merkle_proof:vector>): bool + acquires MerkleNFTDistribution { + let distribution = borrow_global_mut>(creator); + let leaf_data = encode_leaf(&index, &account); + MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data)) + } + + public fun is_minted(creator: address, index: u64): bool + acquires MerkleNFTDistribution { + let distribution = borrow_global_mut>(creator); + is_minted_(distribution, index) + } + + fun is_minted_(distribution: &MerkleNFTDistribution, index: u64): bool { + let claimed_word_index = index / 128; + let claimed_bit_index = ((index % 128) as u8); + let word = Vector::borrow( &distribution.claimed_bitmap, claimed_word_index); + let mask = 1u128 << claimed_bit_index; + (*word & mask) == mask + } + + spec is_minted_ { + pragma verify = false; // Bitwise operator + pragma opaque; + } + +} diff --git a/release/v13/sources/MerkleProof.move b/release/v13/sources/MerkleProof.move new file mode 100644 index 00000000..be992e67 --- /dev/null +++ b/release/v13/sources/MerkleProof.move @@ -0,0 +1,131 @@ +module StarcoinFramework::MerkleProof { + use StarcoinFramework::Hash; + use StarcoinFramework::Vector; + use StarcoinFramework::Compare; + + /// verify leaf node with hash of `leaf` with `proof` againest merkle `root`. + public fun verify(proof: &vector>, root: &vector, leaf: vector): bool { + let computed_hash = leaf; + let i = 0; + let proof_length = Vector::length(proof); + while (i < proof_length) { + let sibling = Vector::borrow(proof, i); + // computed_hash is left. + if (Compare::cmp_bytes( &computed_hash, sibling) < 2) { + let concated = concat(computed_hash, * sibling); + computed_hash = Hash::sha3_256(concated); + } else { + let concated = concat(*sibling, computed_hash); + computed_hash = Hash::sha3_256(concated); + }; + + i = i + 1; + }; + &computed_hash == root + } + + fun concat(v1: vector, v2: vector): vector { + Vector::append( &mut v1, v2); + v1 + } +} + +module StarcoinFramework::MerkleNFTDistributor { + use StarcoinFramework::Vector; + use StarcoinFramework::NFT::{Self, NFT, Metadata, MintCapability}; + use StarcoinFramework::Hash; + use StarcoinFramework::BCS; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::MerkleProof; + const ALREADY_MINTED: u64 = 1000; + const INVALID_PROOF: u64 = 1001; + const ERR_NO_MINT_CAPABILITY: u64 = 1002; + + struct MerkleNFTDistribution has key { + merkle_root: vector, + claimed_bitmap: vector, + } + + public fun register(signer: &signer, merkle_root: vector, leafs: u64, info: Info, meta: Metadata): MintCapability { + let bitmap_count = leafs / 128; + if (bitmap_count * 128 < leafs) { + bitmap_count = bitmap_count + 1; + }; + let claimed_bitmap = Vector::empty(); + let j = 0; + while (j < bitmap_count) { + Vector::push_back( &mut claimed_bitmap, 0u128); + j = j + 1; + }; + let distribution = MerkleNFTDistribution{ + merkle_root, + claimed_bitmap + }; + NFT::register(signer, info, meta); + move_to(signer, distribution); + NFT::remove_mint_capability(signer) + } + + public fun mint_with_cap(sender: &signer, cap:&mut MintCapability, creator: address, index: u64, base_meta: Metadata, type_meta: NFTMeta, body: NFTBody, merkle_proof:vector>): NFT + acquires MerkleNFTDistribution { + let addr = Signer::address_of(sender); + let distribution = borrow_global_mut>(creator); + let minted = is_minted_(distribution, index); + assert!(!minted, Errors::custom(ALREADY_MINTED)); + let leaf_data = encode_leaf(&index, &addr); + let verified = MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data)); + assert!(verified, Errors::custom(INVALID_PROOF)); + set_minted_(distribution, index); + let nft = NFT::mint_with_cap(creator, cap, base_meta, type_meta, body); + return nft + } + + fun encode_leaf(index: &u64, account: &address): vector { + let leaf = Vector::empty(); + Vector::append(&mut leaf, BCS::to_bytes(index)); + Vector::append(&mut leaf, BCS::to_bytes(account)); + leaf + } + + fun set_minted_(distribution: &mut MerkleNFTDistribution, index: u64) { + let claimed_word_index = index / 128; + let claimed_bit_index = ((index % 128) as u8); + let word = Vector::borrow_mut(&mut distribution.claimed_bitmap, claimed_word_index); + // word | (1 << bit_index) + let mask = 1u128 << claimed_bit_index; + *word = (*word | mask); + } + + spec set_minted_ { + pragma verify = false; // Bitwise operator + pragma opaque; + } + + public fun verify_proof(account: address, creator: address, index: u64, merkle_proof:vector>): bool + acquires MerkleNFTDistribution { + let distribution = borrow_global_mut>(creator); + let leaf_data = encode_leaf(&index, &account); + MerkleProof::verify(&merkle_proof, &distribution.merkle_root, Hash::sha3_256(leaf_data)) + } + + public fun is_minted(creator: address, index: u64): bool + acquires MerkleNFTDistribution { + let distribution = borrow_global_mut>(creator); + is_minted_(distribution, index) + } + + fun is_minted_(distribution: &MerkleNFTDistribution, index: u64): bool { + let claimed_word_index = index / 128; + let claimed_bit_index = ((index % 128) as u8); + let word = Vector::borrow( &distribution.claimed_bitmap, claimed_word_index); + let mask = 1u128 << claimed_bit_index; + (*word & mask) == mask + } + + spec is_minted_ { + pragma verify = false; // Bitwise operator + pragma opaque; + } + +} diff --git a/release/v13/sources/MintDaoProposal.move b/release/v13/sources/MintDaoProposal.move new file mode 100644 index 00000000..18ff68cc --- /dev/null +++ b/release/v13/sources/MintDaoProposal.move @@ -0,0 +1,98 @@ +address StarcoinFramework { +/// MintDaoProposal is a dao proposal for mint extra tokens. +module MintDaoProposal { + use StarcoinFramework::Token; + use StarcoinFramework::Signer; + use StarcoinFramework::Dao; + use StarcoinFramework::Account; + use StarcoinFramework::Errors; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_strict; + pragma aborts_if_is_partial; + } + + /// A wrapper of Token MintCapability. + struct WrappedMintCapability has key { + cap: Token::MintCapability, + } + + /// MintToken request. + struct MintToken has copy, drop, store { + /// the receiver of minted tokens. + receiver: address, + /// how many tokens to mint. + amount: u128, + } + + const ERR_NOT_AUTHORIZED: u64 = 401; + + /// Plugin method of the module. + /// Should be called by token issuer. + public fun plugin(signer: &signer) { + let token_issuer = Token::token_address(); + assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + let mint_cap = Token::remove_mint_capability(signer); + move_to(signer, WrappedMintCapability { cap: mint_cap }); + } + spec plugin { + pragma aborts_if_is_partial = false; + let sender = Signer::address_of(signer); + aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if !exists>(sender); + aborts_if exists>(sender); + + ensures !exists>(sender); + ensures exists>(sender); + } + + + /// Entrypoint for the proposal. + public fun propose_mint_to(signer: &signer, receiver: address, amount: u128, exec_delay: u64) { + Dao::propose( + signer, + MintToken { receiver, amount }, + exec_delay, + ); + } + spec propose_mint_to { + use StarcoinFramework::Timestamp; + use StarcoinFramework::CoreAddresses; + pragma aborts_if_is_partial = false; + + // copy from Dao::propose spec. + include Dao::AbortIfDaoConfigNotExist; + include Dao::AbortIfDaoInfoNotExist; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config().min_action_delay; + include Dao::CheckQuorumVotes; + let sender = Signer::address_of(signer); + aborts_if exists>(sender); + } + + /// Once the proposal is agreed, anyone can call the method to make the proposal happen. + public fun execute_mint_proposal( + proposer_address: address, + proposal_id: u64, + ) acquires WrappedMintCapability { + let MintToken { receiver, amount } = Dao::extract_proposal_action( + proposer_address, + proposal_id, + ); + let cap = borrow_global>(Token::token_address()); + let tokens = Token::mint_with_capability(&cap.cap, amount); + Account::deposit(receiver, tokens); + } + + spec execute_mint_proposal { + use StarcoinFramework::Option; + pragma aborts_if_is_partial = true; + let expected_states = vec(6); + include Dao::CheckProposalStates{expected_states}; + let proposal = global>(proposer_address); + aborts_if Option::is_none(proposal.action); + aborts_if !exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/MintScripts.move b/release/v13/sources/MintScripts.move new file mode 100644 index 00000000..cdfb599e --- /dev/null +++ b/release/v13/sources/MintScripts.move @@ -0,0 +1,4 @@ +address StarcoinFramework { +module MintScripts { +} +} \ No newline at end of file diff --git a/release/v13/sources/ModifyDaoConfigProposal.move b/release/v13/sources/ModifyDaoConfigProposal.move new file mode 100644 index 00000000..65c3cfb1 --- /dev/null +++ b/release/v13/sources/ModifyDaoConfigProposal.move @@ -0,0 +1,125 @@ +address StarcoinFramework { +/// A proposal module which is used to modify Token's DAO configuration. +module ModifyDaoConfigProposal { + // use StarcoinFramework::Config; + use StarcoinFramework::Token; + use StarcoinFramework::Signer; + use StarcoinFramework::Config; + use StarcoinFramework::Dao; + use StarcoinFramework::Errors; + use StarcoinFramework::Option; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_strict; + pragma aborts_if_is_partial; + } + + /// A wrapper of `Config::ModifyConfigCapability>`. + struct DaoConfigModifyCapability has key { + cap: Config::ModifyConfigCapability>, + } + + const ERR_NOT_AUTHORIZED: u64 = 401; + const ERR_QUORUM_RATE_INVALID: u64 = 402; + + /// a proposal action to update dao config. + /// if any field is `0`, that means the proposal want to update. + struct DaoConfigUpdate has copy, drop, store { + /// new voting delay setting. + voting_delay: u64, + /// new voting period setting. + voting_period: u64, + /// new voting quorum rate setting. + voting_quorum_rate: u8, + /// new min action delay setting. + min_action_delay: u64, + } + + /// Plugin method of the module. + /// Should be called by token issuer. + public fun plugin(signer: &signer) { + let token_issuer = Token::token_address(); + assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + let dao_config_modify_cap = Config::extract_modify_config_capability< + Dao::DaoConfig, + >(signer); + assert!(Config::account_address(&dao_config_modify_cap) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + let cap = DaoConfigModifyCapability { cap: dao_config_modify_cap }; + move_to(signer, cap); + } + + spec plugin { + pragma aborts_if_is_partial = false; + let sender = Signer::address_of(signer); + aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS(); + include Config::AbortsIfCapNotExist>{address: sender}; + let config_cap = Config::spec_cap>(sender); + aborts_if Option::is_none(config_cap); + aborts_if Option::borrow(config_cap).account_address != sender; + aborts_if exists>(sender); + ensures exists>(sender); + } + + /// Entrypoint for the proposal. + public entry fun propose( + signer: signer, + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + exec_delay: u64, + ) { + assert!(voting_quorum_rate <= 100, Errors::invalid_argument(ERR_QUORUM_RATE_INVALID)); + let action = DaoConfigUpdate { + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + }; + Dao::propose(&signer, action, exec_delay); + } + spec propose { + use StarcoinFramework::Timestamp; + use StarcoinFramework::CoreAddresses; + pragma aborts_if_is_partial = false; + aborts_if voting_quorum_rate > 100; + + // copy from Dao::propose spec. + include Dao::AbortIfDaoConfigNotExist; + include Dao::AbortIfDaoInfoNotExist; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config().min_action_delay; + include Dao::CheckQuorumVotes; + let sender = Signer::address_of(signer); + aborts_if exists>(sender); + + } + /// Once the proposal is agreed, anyone can call the method to make the proposal happen. + public entry fun execute(proposer_address: address, proposal_id: u64) + acquires DaoConfigModifyCapability { + let DaoConfigUpdate { + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + } = Dao::extract_proposal_action(proposer_address, proposal_id); + let cap = borrow_global_mut>( + Token::token_address(), + ); + Dao::modify_dao_config( + &mut cap.cap, + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + ); + } + spec execute { + pragma aborts_if_is_partial = true; + // let expected_states = vec(6); + // include Dao::CheckProposalStates{expected_states}; + aborts_if !exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/ModuleUpgradeScripts.move b/release/v13/sources/ModuleUpgradeScripts.move new file mode 100644 index 00000000..3d28887b --- /dev/null +++ b/release/v13/sources/ModuleUpgradeScripts.move @@ -0,0 +1,116 @@ +address StarcoinFramework { +module ModuleUpgradeScripts { + use StarcoinFramework::PackageTxnManager; + use StarcoinFramework::Config; + use StarcoinFramework::Signer; + use StarcoinFramework::Version; + use StarcoinFramework::Option; + use StarcoinFramework::UpgradeModuleDaoProposal; + use StarcoinFramework::Errors; + + const ERR_WRONG_UPGRADE_STRATEGY: u64 = 100; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_partial = false; + pragma aborts_if_is_strict = true; + } + + public entry fun propose_module_upgrade_v2( + signer: signer, + module_address: address, + package_hash: vector, + version: u64, + exec_delay: u64, + enforced: bool, + ) { + UpgradeModuleDaoProposal::propose_module_upgrade_v2( + &signer, + module_address, + package_hash, + version, + exec_delay, + enforced + ); + } + + ///Update `sender`'s module upgrade strategy to `strategy` + public entry fun update_module_upgrade_strategy( + sender: signer, + strategy: u8, + ) { + // 1. check version + if (strategy == PackageTxnManager::get_strategy_two_phase()) { + if (!Config::config_exist_by_address(Signer::address_of(&sender))) { + Config::publish_new_config(&sender, Version::new_version(1)); + } + }; + + // 2. update strategy + PackageTxnManager::update_module_upgrade_strategy( + &sender, + strategy, + Option::none(), + ); + } + + /// Update `sender`'s module upgrade strategy to `strategy` with min_time_limit. + /// This can only be invoked when strategy is STRATEGY_TWO_PHASE. + public entry fun update_module_upgrade_strategy_with_min_time( + sender: signer, + strategy: u8, + min_time_limit: u64, + ){ + // 1. check version + assert!(strategy == PackageTxnManager::get_strategy_two_phase(), Errors::invalid_argument(ERR_WRONG_UPGRADE_STRATEGY)); + // 2. update strategy + PackageTxnManager::update_module_upgrade_strategy( + &sender, + strategy, + Option::some(min_time_limit), + ); + } + + /// a alias of execute_module_upgrade_plan_propose, will deprecated in the future. + public entry fun submit_module_upgrade_plan( + sender: signer, + proposer_address: address, + proposal_id: u64, + ) { + Self::execute_module_upgrade_plan_propose(sender, proposer_address, proposal_id); + } + + ///Execute module upgrade plan propose by submit module upgrade plan, the propose must been agreed, and anyone can execute this function. + public entry fun execute_module_upgrade_plan_propose( + _sender: signer, + proposer_address: address, + proposal_id: u64, + ) { + UpgradeModuleDaoProposal::submit_module_upgrade_plan(proposer_address, proposal_id); + } + + spec execute_module_upgrade_plan_propose { + pragma verify = false; + } + + ///Directly submit a upgrade plan, the `sender`'s module upgrade plan must been PackageTxnManager::STRATEGY_TWO_PHASE and have UpgradePlanCapability + public entry fun submit_upgrade_plan(sender: signer, package_hash: vector, version:u64, enforced: bool) { + PackageTxnManager::submit_upgrade_plan_v2(&sender, package_hash, version, enforced); + } + + spec submit_upgrade_plan { + pragma verify = false; + } + + ///Cancel current upgrade plan, the `sender` must have `UpgradePlanCapability`. + public entry fun cancel_upgrade_plan( + signer: signer, + ) { + PackageTxnManager::cancel_upgrade_plan(&signer); + } + + spec cancel_upgrade_plan { + pragma verify = false; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/NFT.move b/release/v13/sources/NFT.move new file mode 100644 index 00000000..d1ac25f8 --- /dev/null +++ b/release/v13/sources/NFT.move @@ -0,0 +1,1023 @@ +address StarcoinFramework { +/// Non-fungible token standard and implementations. +module NFT { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Account; + use StarcoinFramework::Vector; + use StarcoinFramework::Event; + use StarcoinFramework::GenesisSignerCapability; + + const ERR_NO_MINT_CAPABILITY: u64 = 101; + const ERR_NO_BURN_CAPABILITY: u64 = 102; + const ERR_NO_UPDATE_CAPABILITY: u64 = 103; + const ERR_CANOT_EMPTY: u64 = 104; + const ERR_NFT_TYPE_ALREADY_REGISTERED: u64 = 105; + const ERR_NFT_TYPE_NO_REGISTERED: u64 = 106; + + spec module { + pragma verify = false; + } + + struct MintEvent has drop, store { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + struct BurnEvent has drop, store { + id: u64, + } + + /// The info of NFT type, this type is deprecated, please use NFTTypeInfoV2 + struct NFTTypeInfo has key, store { + counter: u64, + meta: Metadata, + info: NFTTypeInfoExt, + mint_events: Event::EventHandle>, + } + + /// The info of NFT type + struct NFTTypeInfoV2 has key, store { + register: address, + counter: u64, + meta: Metadata, + mint_events: Event::EventHandle>, + burn_events: Event::EventHandle>, + } + + struct NFTTypeInfoCompat has key { + info: NFTTypeInfoExt, + } + + /// Deprecated. Use `new_nft_type_info_v2` instead. + fun new_nft_type_info( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ): NFTTypeInfo { + NFTTypeInfo { + counter: 0, + info, + meta, + mint_events: Event::new_event_handle>(sender), + } + } + + fun new_nft_type_info_v2(sender: &signer, meta: Metadata): NFTTypeInfoV2 { + NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter: 0, + meta, + mint_events: Event::new_event_handle>(sender), + burn_events: Event::new_event_handle>(sender), + } + } + + /// Note: this function is deprecated + public fun nft_type_info_ex_info( + ): NFTTypeInfoExt acquires NFTTypeInfo, NFTTypeInfoCompat { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } + } + + /// Note: this function is deprecated, please use nft_type_info_counter_v2 + public fun nft_type_info_counter( + ): u64 acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + Self::nft_type_info_counter_v2() + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + } + + public fun nft_type_info_counter_v2(): u64 acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + + public fun nft_type_info_meta(): Metadata acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.meta + } + + public fun upgrade_nft_type_info_from_v1_to_v2( + sender: &signer, + _cap: &mut MintCapability + ) acquires NFTTypeInfo { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfo { counter, meta, info, mint_events } = nft_type_info; + + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let nft_type_info_v2 = NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter, + meta, + mint_events, + burn_events: Event::new_event_handle>(sender), + }; + move_to(&genesis_account, nft_type_info_v2); + move_to(&genesis_account, NFTTypeInfoCompat { info }); + } + } + + public fun remove_compat_info( + _cap: &mut MintCapability + ): NFTTypeInfoExt acquires NFTTypeInfoCompat { + let compat_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfoCompat{info} = compat_info; + info + } + /// deprecated. + struct GenesisSignerCapability has key { + cap: Account::SignerCapability, + } + /// The capability to mint the nft. + struct MintCapability has key, store {} + /// The Capability to burn the nft. + struct BurnCapability has key, store {} + /// The Capability to update the nft metadata. + struct UpdateCapability has key, store {} + + struct Metadata has copy, store, drop { + /// NFT name's utf8 bytes. + name: vector, + /// Image link, such as ipfs://xxxx + image: vector, + /// Image bytes data, image or image_data can not empty for both. + image_data: vector, + /// NFT description utf8 bytes. + description: vector, + } + + public fun empty_meta(): Metadata { + Metadata { + name: Vector::empty(), + image: Vector::empty(), + image_data: Vector::empty(), + description: Vector::empty(), + } + } + + public fun new_meta(name: vector, description: vector): Metadata { + Metadata { + name, + image: Vector::empty(), + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image(name: vector, image: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image, + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image_data(name: vector, image_data: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image_data), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image: Vector::empty(), + image_data, + description, + } + } + + public fun meta_name(metadata: &Metadata): vector { + *&metadata.name + } + + public fun meta_image(metadata: &Metadata): vector { + *&metadata.image + } + + public fun meta_image_data(metadata: &Metadata): vector { + *&metadata.image_data + } + + public fun meta_description(metadata: &Metadata): vector { + *&metadata.description + } + + struct NFT has store { + /// The creator of NFT + creator: address, + /// The unique id of NFT under NFTMeta type + id: u64, + /// The metadata of NFT + base_meta: Metadata, + /// The extension metadata of NFT + type_meta: NFTMeta, + /// The body of NFT, NFT is a box for NFTBody + body: NFTBody, + } + + /// The information of NFT instance return by get_nft_info + struct NFTInfo has copy, store, drop { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + public fun get_info(nft: &NFT): NFTInfo { + NFTInfo { + id: nft.id, + creator: nft.creator, + base_meta: *&nft.base_meta, + type_meta: *&nft.type_meta, + } + } + + public fun unpack_info( + nft_info: NFTInfo + ): (u64, address, Metadata, NFTMeta) { + let NFTInfo { id, creator, base_meta, type_meta } = nft_info; + (id, creator, base_meta, type_meta) + } + + public fun get_id(nft: &NFT): u64 { + return nft.id + } + + public fun get_base_meta(nft: &NFT): &Metadata { + return &nft.base_meta + } + + public fun get_type_meta(nft: &NFT): &NFTMeta { + return &nft.type_meta + } + + public fun get_creator(nft: &NFT): address { + return nft.creator + } + + /// deprecated. + public fun initialize(_signer: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, + /// in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability { + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register a NFT type to genesis + /// Note: this function is deprecated, please use `register_v2` + public fun register( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ) acquires NFTTypeInfo { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let type_info = new_nft_type_info(sender, info, meta); + move_to>(&genesis_account, type_info); + let mint_cap = MintCapability {}; + + Self::upgrade_nft_type_info_from_v1_to_v2(sender, &mut mint_cap); + + move_to>(sender, mint_cap); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Register a NFT type to genesis + public fun register_v2(sender: &signer, meta: Metadata) { + assert!(!is_registered(), Errors::invalid_argument(ERR_NFT_TYPE_ALREADY_REGISTERED)); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let type_info = new_nft_type_info_v2(sender, meta); + move_to>(&genesis_account, type_info); + move_to>(sender, MintCapability {}); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Check the NFTMeta is register + public fun is_registered(): bool { + exists>(CoreAddresses::GENESIS_ADDRESS()) + } + + /// deprecated. Use "is_registered" instead. + public fun is_register(): bool { + is_registered() + } + + /// Add MintCapability to `sender` + public fun add_mint_capability(sender: &signer, cap: MintCapability) { + move_to(sender, cap); + } + + /// Remove the MintCapability from `sender` + public fun remove_mint_capability( + sender: &signer + ): MintCapability acquires MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the MintCapability + public fun destroy_mint_capability(cap: MintCapability) { + let MintCapability {} = cap; + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + /// Note: this function is deprecated, please use `mint_with_cap_v2` + public fun mint_with_cap( + creator: address, + cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + mint_with_cap_v2(creator, cap, base_meta, type_meta, body) + } else { + let nft_type_info = + borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id: id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + public fun mint_with_cap_v2( + creator: address, + _cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2 { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + + /// Mint nft, the `sender` must have MintCapability + /// Note: this function is deprecated, please use `mint_v2` + public fun mint( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap(addr, cap, base_meta, type_meta, body) + } + + /// Mint nft, the `sender` must have MintCapability + public fun mint_v2( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap_v2(addr, cap, base_meta, type_meta, body) + } + + /// Add BurnCapability to `sender` + public fun add_burn_capability(sender: &signer, cap: BurnCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_burn_capability( + sender: &signer + ): BurnCapability acquires BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the BurnCapability + public fun destroy_burn_capability(cap: BurnCapability) { + let BurnCapability {} = cap; + } + + /// Burn nft with BurnCapability + public fun burn_with_cap( + _cap: &mut BurnCapability, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2 { + let NFT { creator: _, id: id, base_meta: _, type_meta: _, body } = nft; + // only NFTTypeInfoV2 has burn_events EventHandle + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + Event::emit_event(&mut nft_type_info.burn_events, BurnEvent { + id, + }); + }; + body + } + + /// Burn nft, the `sender` must have BurnCapability + public fun burn( + sender: &signer, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2, BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + let cap = borrow_global_mut>(addr); + burn_with_cap(cap, nft) + } + + /// Add UpdateCapability to `sender` + public fun add_update_capability(sender: &signer, cap: UpdateCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_update_capability( + sender: &signer + ): UpdateCapability acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the UpdateCapability + public fun destroy_update_capability(cap: UpdateCapability) { + let UpdateCapability {} = cap; + } + + /// Update the NFTTypeInfoV2 metadata with UpdateCapability + public fun update_nft_type_info_meta_with_cap( + _cap: &mut UpdateCapability, + new_meta: Metadata + ) acquires NFTTypeInfoV2{ + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + info.meta = new_meta; + } + + /// Update the NFTTypeInfoV2 metadata, the `sender` must have UpdateCapability + public fun update_nft_type_info_meta( + sender: &signer, + new_meta: Metadata + ) acquires UpdateCapability, NFTTypeInfoV2 { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_nft_type_info_meta_with_cap(cap, new_meta) + } + + /// Update the nft's base_meta and type_meta with UpdateCapability + public fun update_meta_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT, + base_meta: Metadata, type_meta: NFTMeta + ) { + nft.base_meta = base_meta; + nft.type_meta = type_meta; + } + + /// Update the nft's base_meta and type_meta, the `sender` must have UpdateCapability + public fun update_meta( + sender: &signer, + nft: &mut NFT, + base_meta: Metadata, + type_meta: NFTMeta + ) acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_meta_with_cap(cap, nft, base_meta, type_meta) + } + + /// Borrow NFTBody ref + public fun borrow_body(nft: &NFT): &NFTBody { + &nft.body + } + + /// Borrow NFTBody mut ref for update body with UpdateCapability + public fun borrow_body_mut_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT + ): &mut NFTBody { + &mut nft.body + } +} + +/// IdentifierNFT using NFT as identifier for an on chain account +/// The NFT can not been transfer by owner. +module IdentifierNFT { + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::NFT::{Self, NFT, MintCapability, BurnCapability, UpdateCapability}; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + const ERR_NFT_EXISTS: u64 = 101; + const ERR_NFT_NOT_EXISTS: u64 = 102; + const ERR_NFT_NOT_ACCEPT: u64 = 103; + const ERR_BORROW_ADDR_NOT_SAME: u64 = 104; + + spec module { + pragma verify = false; + } + + struct IdentifierNFT has key { + nft: Option>, + } + + //Used when borrowing or returning NFT, note: there is no drop ability, it must be returned after borrowing + struct BorrowNFT { + nft: NFT, + addr:address + } + + /// Check the `owner` is prepared with IdentifierNFT for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Accept NFT, prepare an empty IdentifierNFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let addr = Signer::address_of(sender); + if (!is_accept(addr)) { + move_to(sender, IdentifierNFT { + nft: Option::none(), + }); + } + } + + /// Destroy the empty IdentifierNFT + public entry fun destroy_empty_entry(sender: signer) acquires IdentifierNFT { + destroy_empty(&sender); + } + + public fun destroy_empty(sender: &signer) acquires IdentifierNFT { + let addr = Signer::address_of(sender); + if (exists>(addr)) { + let id_nft = move_from>(addr); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_none(nft); + } + } + + /// Grant nft as IdentifierNFT to `sender` with MintCapability, sender will auto accept the NFT. + public fun grant( + cap: &mut MintCapability, + sender: &signer, + nft: NFT + ) acquires IdentifierNFT { + Self::accept(sender); + Self::grant_to(cap, Signer::address_of(sender), nft); + } + + /// Grant nft as IdentifierNFT to `receiver` with MintCapability, the receiver should accept the NFT first. + public fun grant_to( + _cap: &mut MintCapability, + receiver: address, + nft: NFT + ) acquires IdentifierNFT { + assert!(exists>(receiver), Errors::not_published(ERR_NFT_NOT_ACCEPT)); + let id_nft = borrow_global_mut>(receiver); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + Option::fill(&mut id_nft.nft, nft); + } + + /// Revoke the NFT from owner. + public fun revoke( + _cap: &mut BurnCapability, + owner: address + ): NFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = move_from>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_some(nft) + } + + /// borrow_out the NFT from owner. + public fun borrow_out( + _cap: &mut UpdateCapability, + owner: address + ): BorrowNFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let id_nft = borrow_global_mut>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let nft = Option::extract(&mut id_nft.nft); + + BorrowNFT{ + nft : nft, + addr: owner + } + } + + /// return_back the NFT to owner. + public fun return_back( + borrownft: BorrowNFT, + ) acquires IdentifierNFT { + + let BorrowNFT{ + nft: nft, + addr: owner + } = borrownft ; + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = borrow_global_mut>(owner); + + Option::fill(&mut id_nft.nft , nft) + } + + public fun borrow_nft( + borrownft:&BorrowNFT + ) : & NFT { + & borrownft.nft + } + + public fun borrow_nft_mut ( + borrownft:&mut BorrowNFT + ) : &mut NFT { + &mut borrownft.nft + } + + /// Check `owner` is owns the IdentifierNFT + public fun owns(owner: address): bool acquires IdentifierNFT { + if (!exists>(owner)) { + return false + }; + let id_nft = borrow_global>(owner); + Option::is_some(&id_nft.nft) + } + /// deprecated. Use `owns()` instead. + public fun is_owns(owner: address): bool acquires IdentifierNFT { + owns(owner) + } + + public fun get_nft_info( + owner: address + ): Option> acquires IdentifierNFT { + if (!exists>(owner)) { + return Option::none>() + }; + let id_nft = borrow_global>(owner); + let info = if (Option::is_some(&id_nft.nft)) { + let nft = Option::borrow(&id_nft.nft); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + info + } +} + +module IdentifierNFTScripts { + use StarcoinFramework::IdentifierNFT; + spec module { + pragma verify = false; + } + + /// Init IdentifierNFT for accept NFT as Identifier. + public entry fun accept(sender: signer) { + IdentifierNFT::accept_entry(sender); + } + + /// Destroy empty IdentifierNFT + public entry fun destroy_empty(sender: signer) { + IdentifierNFT::destroy_empty_entry(sender); + } +} + +/// NFTGallery is user collection of NFT. +module NFTGallery { + use StarcoinFramework::Signer; + use StarcoinFramework::NFT::{Self, NFT}; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::Event; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + + const ERR_NFT_NOT_EXISTS: u64 = 101; + + const ERR_NFTGALLERY_NOT_EXISTS:u64 = 102; + + spec module { + pragma verify = false; + } + + struct WithdrawEvent has drop, store { + owner: address, + id: u64, + } + + struct DepositEvent has drop, store { + owner: address, + id: u64, + } + + struct NFTGallery has key, store { + withdraw_events: Event::EventHandle>, + deposit_events: Event::EventHandle>, + items: vector>, + } + + /// Check the `owner` is prepared with NFTGallery for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Init a NFTGallery to accept NFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let sender_addr = Signer::address_of(sender); + if (!is_accept(sender_addr)) { + let gallery = NFTGallery { + withdraw_events: Event::new_event_handle>(sender), + deposit_events: Event::new_event_handle>(sender), + items: Vector::empty>(), + }; + move_to(sender, gallery); + } + } + + /// Transfer NFT from `sender` to `receiver` + public entry fun transfer_entry( + sender: signer, + id: u64, receiver: address + ) acquires NFTGallery { + transfer(&sender, id, receiver); + } + + public fun transfer( + sender: &signer, + id: u64, + receiver: address + ) acquires NFTGallery { + let nft = withdraw(sender, id); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let nft = Option::destroy_some(nft); + deposit_to(receiver, nft) + } + + /// Get the NFT info by the NFT id. + public fun get_nft_info_by_id( + owner: address, + id: u64 + ): Option> acquires NFTGallery { + if(!is_accept(owner)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(owner); + let idx = find_by_id(&gallery.items, id); + + let info = if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::borrow>(&gallery.items, i); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + return info + } + + /// Get the NFT info by the NFT idx in NFTGallery + public fun get_nft_info_by_idx( + owner: address, + idx: u64 + ): NFT::NFTInfo acquires NFTGallery { + assert!(exists>(owner), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(owner); + let nft = Vector::borrow>(&gallery.items, idx); + NFT::get_info(nft) + } + + /// Get the all NFT info + public fun get_nft_infos( + owner: address + ): vector> acquires NFTGallery { + if(!is_accept(owner)){ + return Vector::empty>() + }; + let gallery = borrow_global_mut>(owner); + let infos = Vector::empty(); + let len = Vector::length(&gallery.items); + let idx = 0; + while (len > idx) { + let nft = Vector::borrow>(&gallery.items, idx); + Vector::push_back(&mut infos, NFT::get_info(nft)); + idx = idx + 1; + }; + infos + } + + /// Deposit nft to `sender` NFTGallery + public fun deposit( + sender: &signer, + nft: NFT + ) acquires NFTGallery { + Self::accept(sender); + let sender_addr = Signer::address_of(sender); + deposit_to(sender_addr, nft) + } + + /// Deposit nft to `receiver` NFTGallery + public fun deposit_to( + receiver: address, + nft: NFT + ) acquires NFTGallery { + assert!(exists>(receiver), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(receiver); + Event::emit_event(&mut gallery.deposit_events, DepositEvent { id: NFT::get_id(&nft), owner: receiver }); + Vector::push_back(&mut gallery.items, nft); + } + + /// Withdraw one nft of NFTMeta from `sender`, caller should ensure at least one NFT in the Gallery. + public fun withdraw_one( + sender: &signer + ): NFT acquires NFTGallery { + let nft = do_withdraw(sender, Option::none()); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + Option::destroy_some(nft) + } + + /// Withdraw nft of NFTMeta and id from `sender` + public fun withdraw( + sender: &signer, + id: u64 + ): Option> acquires NFTGallery { + do_withdraw(sender, Option::some(id)) + } + + /// Withdraw nft of NFTMeta and id from `sender` + fun do_withdraw( + sender: &signer, + id: Option + ): Option> acquires NFTGallery { + let sender_addr = Signer::address_of(sender); + if(!is_accept(sender_addr)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(sender_addr); + let len = Vector::length(&gallery.items); + let nft = if (len == 0) { + Option::none() + } else { + let idx = if (Option::is_some(&id)) { + let id = Option::extract(&mut id); + find_by_id(&gallery.items, id) + } else { + //default withdraw the last nft. + Option::some(len - 1) + }; + + if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::remove>(&mut gallery.items, i); + Event::emit_event( + &mut gallery.withdraw_events, + WithdrawEvent { id: NFT::get_id(&nft), owner: sender_addr } + ); + Option::some(nft) + } else { + Option::none() + } + }; + nft + } + + fun find_by_id( + c: &vector>, + id: u64 + ): Option { + let len = Vector::length(c); + if (len == 0) { + return Option::none() + }; + let idx = len - 1; + loop { + let nft = Vector::borrow(c, idx); + if (NFT::get_id(nft) == id) { + return Option::some(idx) + }; + if (idx == 0) { + return Option::none() + }; + idx = idx - 1; + } + } + + /// Count all NFTs assigned to an owner + public fun count_of(owner: address): u64 acquires NFTGallery { + if(!is_accept(owner)){ + return 0 + }; + let gallery = borrow_global_mut>(owner); + Vector::length(&gallery.items) + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery_entry(sender: signer) acquires NFTGallery { + remove_empty_gallery(&sender); + } + + public fun remove_empty_gallery(sender: &signer) acquires NFTGallery{ + let sender_addr = Signer::address_of(sender); + assert!(exists>(sender_addr), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let NFTGallery {withdraw_events, deposit_events, items} = move_from>(sender_addr); + + Event::destroy_handle>(withdraw_events); + Event::destroy_handle>(deposit_events); + Vector::destroy_empty>(items); + } + + spec remove_empty_gallery { + let sender_addr = Signer::address_of(sender); + aborts_if !exists>(sender_addr); + + let gallery = global>(sender_addr); + aborts_if Vector::length>(gallery.items) > 0; + + ensures !exists>(sender_addr); + } + +} + +module NFTGalleryScripts { + use StarcoinFramework::NFTGallery; + + spec module { + pragma verify = false; + } + + /// Init a NFTGallery for accept NFT + public entry fun accept(sender: signer) { + NFTGallery::accept_entry(sender); + } + /// Transfer NFT with `id` from `sender` to `receiver` + public entry fun transfer( + sender: signer, + id: u64, receiver: address + ) { + NFTGallery::transfer_entry(sender, id, receiver); + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery(sender: signer) { + NFTGallery::remove_empty_gallery_entry(sender); + } +} +} diff --git a/release/v13/sources/NFTGallery.move b/release/v13/sources/NFTGallery.move new file mode 100644 index 00000000..d1ac25f8 --- /dev/null +++ b/release/v13/sources/NFTGallery.move @@ -0,0 +1,1023 @@ +address StarcoinFramework { +/// Non-fungible token standard and implementations. +module NFT { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Account; + use StarcoinFramework::Vector; + use StarcoinFramework::Event; + use StarcoinFramework::GenesisSignerCapability; + + const ERR_NO_MINT_CAPABILITY: u64 = 101; + const ERR_NO_BURN_CAPABILITY: u64 = 102; + const ERR_NO_UPDATE_CAPABILITY: u64 = 103; + const ERR_CANOT_EMPTY: u64 = 104; + const ERR_NFT_TYPE_ALREADY_REGISTERED: u64 = 105; + const ERR_NFT_TYPE_NO_REGISTERED: u64 = 106; + + spec module { + pragma verify = false; + } + + struct MintEvent has drop, store { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + struct BurnEvent has drop, store { + id: u64, + } + + /// The info of NFT type, this type is deprecated, please use NFTTypeInfoV2 + struct NFTTypeInfo has key, store { + counter: u64, + meta: Metadata, + info: NFTTypeInfoExt, + mint_events: Event::EventHandle>, + } + + /// The info of NFT type + struct NFTTypeInfoV2 has key, store { + register: address, + counter: u64, + meta: Metadata, + mint_events: Event::EventHandle>, + burn_events: Event::EventHandle>, + } + + struct NFTTypeInfoCompat has key { + info: NFTTypeInfoExt, + } + + /// Deprecated. Use `new_nft_type_info_v2` instead. + fun new_nft_type_info( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ): NFTTypeInfo { + NFTTypeInfo { + counter: 0, + info, + meta, + mint_events: Event::new_event_handle>(sender), + } + } + + fun new_nft_type_info_v2(sender: &signer, meta: Metadata): NFTTypeInfoV2 { + NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter: 0, + meta, + mint_events: Event::new_event_handle>(sender), + burn_events: Event::new_event_handle>(sender), + } + } + + /// Note: this function is deprecated + public fun nft_type_info_ex_info( + ): NFTTypeInfoExt acquires NFTTypeInfo, NFTTypeInfoCompat { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } + } + + /// Note: this function is deprecated, please use nft_type_info_counter_v2 + public fun nft_type_info_counter( + ): u64 acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + Self::nft_type_info_counter_v2() + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + } + + public fun nft_type_info_counter_v2(): u64 acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + + public fun nft_type_info_meta(): Metadata acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.meta + } + + public fun upgrade_nft_type_info_from_v1_to_v2( + sender: &signer, + _cap: &mut MintCapability + ) acquires NFTTypeInfo { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfo { counter, meta, info, mint_events } = nft_type_info; + + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let nft_type_info_v2 = NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter, + meta, + mint_events, + burn_events: Event::new_event_handle>(sender), + }; + move_to(&genesis_account, nft_type_info_v2); + move_to(&genesis_account, NFTTypeInfoCompat { info }); + } + } + + public fun remove_compat_info( + _cap: &mut MintCapability + ): NFTTypeInfoExt acquires NFTTypeInfoCompat { + let compat_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfoCompat{info} = compat_info; + info + } + /// deprecated. + struct GenesisSignerCapability has key { + cap: Account::SignerCapability, + } + /// The capability to mint the nft. + struct MintCapability has key, store {} + /// The Capability to burn the nft. + struct BurnCapability has key, store {} + /// The Capability to update the nft metadata. + struct UpdateCapability has key, store {} + + struct Metadata has copy, store, drop { + /// NFT name's utf8 bytes. + name: vector, + /// Image link, such as ipfs://xxxx + image: vector, + /// Image bytes data, image or image_data can not empty for both. + image_data: vector, + /// NFT description utf8 bytes. + description: vector, + } + + public fun empty_meta(): Metadata { + Metadata { + name: Vector::empty(), + image: Vector::empty(), + image_data: Vector::empty(), + description: Vector::empty(), + } + } + + public fun new_meta(name: vector, description: vector): Metadata { + Metadata { + name, + image: Vector::empty(), + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image(name: vector, image: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image, + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image_data(name: vector, image_data: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image_data), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image: Vector::empty(), + image_data, + description, + } + } + + public fun meta_name(metadata: &Metadata): vector { + *&metadata.name + } + + public fun meta_image(metadata: &Metadata): vector { + *&metadata.image + } + + public fun meta_image_data(metadata: &Metadata): vector { + *&metadata.image_data + } + + public fun meta_description(metadata: &Metadata): vector { + *&metadata.description + } + + struct NFT has store { + /// The creator of NFT + creator: address, + /// The unique id of NFT under NFTMeta type + id: u64, + /// The metadata of NFT + base_meta: Metadata, + /// The extension metadata of NFT + type_meta: NFTMeta, + /// The body of NFT, NFT is a box for NFTBody + body: NFTBody, + } + + /// The information of NFT instance return by get_nft_info + struct NFTInfo has copy, store, drop { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + public fun get_info(nft: &NFT): NFTInfo { + NFTInfo { + id: nft.id, + creator: nft.creator, + base_meta: *&nft.base_meta, + type_meta: *&nft.type_meta, + } + } + + public fun unpack_info( + nft_info: NFTInfo + ): (u64, address, Metadata, NFTMeta) { + let NFTInfo { id, creator, base_meta, type_meta } = nft_info; + (id, creator, base_meta, type_meta) + } + + public fun get_id(nft: &NFT): u64 { + return nft.id + } + + public fun get_base_meta(nft: &NFT): &Metadata { + return &nft.base_meta + } + + public fun get_type_meta(nft: &NFT): &NFTMeta { + return &nft.type_meta + } + + public fun get_creator(nft: &NFT): address { + return nft.creator + } + + /// deprecated. + public fun initialize(_signer: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, + /// in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability { + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register a NFT type to genesis + /// Note: this function is deprecated, please use `register_v2` + public fun register( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ) acquires NFTTypeInfo { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let type_info = new_nft_type_info(sender, info, meta); + move_to>(&genesis_account, type_info); + let mint_cap = MintCapability {}; + + Self::upgrade_nft_type_info_from_v1_to_v2(sender, &mut mint_cap); + + move_to>(sender, mint_cap); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Register a NFT type to genesis + public fun register_v2(sender: &signer, meta: Metadata) { + assert!(!is_registered(), Errors::invalid_argument(ERR_NFT_TYPE_ALREADY_REGISTERED)); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let type_info = new_nft_type_info_v2(sender, meta); + move_to>(&genesis_account, type_info); + move_to>(sender, MintCapability {}); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Check the NFTMeta is register + public fun is_registered(): bool { + exists>(CoreAddresses::GENESIS_ADDRESS()) + } + + /// deprecated. Use "is_registered" instead. + public fun is_register(): bool { + is_registered() + } + + /// Add MintCapability to `sender` + public fun add_mint_capability(sender: &signer, cap: MintCapability) { + move_to(sender, cap); + } + + /// Remove the MintCapability from `sender` + public fun remove_mint_capability( + sender: &signer + ): MintCapability acquires MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the MintCapability + public fun destroy_mint_capability(cap: MintCapability) { + let MintCapability {} = cap; + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + /// Note: this function is deprecated, please use `mint_with_cap_v2` + public fun mint_with_cap( + creator: address, + cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + mint_with_cap_v2(creator, cap, base_meta, type_meta, body) + } else { + let nft_type_info = + borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id: id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + public fun mint_with_cap_v2( + creator: address, + _cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2 { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + + /// Mint nft, the `sender` must have MintCapability + /// Note: this function is deprecated, please use `mint_v2` + public fun mint( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap(addr, cap, base_meta, type_meta, body) + } + + /// Mint nft, the `sender` must have MintCapability + public fun mint_v2( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap_v2(addr, cap, base_meta, type_meta, body) + } + + /// Add BurnCapability to `sender` + public fun add_burn_capability(sender: &signer, cap: BurnCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_burn_capability( + sender: &signer + ): BurnCapability acquires BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the BurnCapability + public fun destroy_burn_capability(cap: BurnCapability) { + let BurnCapability {} = cap; + } + + /// Burn nft with BurnCapability + public fun burn_with_cap( + _cap: &mut BurnCapability, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2 { + let NFT { creator: _, id: id, base_meta: _, type_meta: _, body } = nft; + // only NFTTypeInfoV2 has burn_events EventHandle + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + Event::emit_event(&mut nft_type_info.burn_events, BurnEvent { + id, + }); + }; + body + } + + /// Burn nft, the `sender` must have BurnCapability + public fun burn( + sender: &signer, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2, BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + let cap = borrow_global_mut>(addr); + burn_with_cap(cap, nft) + } + + /// Add UpdateCapability to `sender` + public fun add_update_capability(sender: &signer, cap: UpdateCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_update_capability( + sender: &signer + ): UpdateCapability acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the UpdateCapability + public fun destroy_update_capability(cap: UpdateCapability) { + let UpdateCapability {} = cap; + } + + /// Update the NFTTypeInfoV2 metadata with UpdateCapability + public fun update_nft_type_info_meta_with_cap( + _cap: &mut UpdateCapability, + new_meta: Metadata + ) acquires NFTTypeInfoV2{ + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + info.meta = new_meta; + } + + /// Update the NFTTypeInfoV2 metadata, the `sender` must have UpdateCapability + public fun update_nft_type_info_meta( + sender: &signer, + new_meta: Metadata + ) acquires UpdateCapability, NFTTypeInfoV2 { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_nft_type_info_meta_with_cap(cap, new_meta) + } + + /// Update the nft's base_meta and type_meta with UpdateCapability + public fun update_meta_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT, + base_meta: Metadata, type_meta: NFTMeta + ) { + nft.base_meta = base_meta; + nft.type_meta = type_meta; + } + + /// Update the nft's base_meta and type_meta, the `sender` must have UpdateCapability + public fun update_meta( + sender: &signer, + nft: &mut NFT, + base_meta: Metadata, + type_meta: NFTMeta + ) acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_meta_with_cap(cap, nft, base_meta, type_meta) + } + + /// Borrow NFTBody ref + public fun borrow_body(nft: &NFT): &NFTBody { + &nft.body + } + + /// Borrow NFTBody mut ref for update body with UpdateCapability + public fun borrow_body_mut_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT + ): &mut NFTBody { + &mut nft.body + } +} + +/// IdentifierNFT using NFT as identifier for an on chain account +/// The NFT can not been transfer by owner. +module IdentifierNFT { + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::NFT::{Self, NFT, MintCapability, BurnCapability, UpdateCapability}; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + const ERR_NFT_EXISTS: u64 = 101; + const ERR_NFT_NOT_EXISTS: u64 = 102; + const ERR_NFT_NOT_ACCEPT: u64 = 103; + const ERR_BORROW_ADDR_NOT_SAME: u64 = 104; + + spec module { + pragma verify = false; + } + + struct IdentifierNFT has key { + nft: Option>, + } + + //Used when borrowing or returning NFT, note: there is no drop ability, it must be returned after borrowing + struct BorrowNFT { + nft: NFT, + addr:address + } + + /// Check the `owner` is prepared with IdentifierNFT for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Accept NFT, prepare an empty IdentifierNFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let addr = Signer::address_of(sender); + if (!is_accept(addr)) { + move_to(sender, IdentifierNFT { + nft: Option::none(), + }); + } + } + + /// Destroy the empty IdentifierNFT + public entry fun destroy_empty_entry(sender: signer) acquires IdentifierNFT { + destroy_empty(&sender); + } + + public fun destroy_empty(sender: &signer) acquires IdentifierNFT { + let addr = Signer::address_of(sender); + if (exists>(addr)) { + let id_nft = move_from>(addr); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_none(nft); + } + } + + /// Grant nft as IdentifierNFT to `sender` with MintCapability, sender will auto accept the NFT. + public fun grant( + cap: &mut MintCapability, + sender: &signer, + nft: NFT + ) acquires IdentifierNFT { + Self::accept(sender); + Self::grant_to(cap, Signer::address_of(sender), nft); + } + + /// Grant nft as IdentifierNFT to `receiver` with MintCapability, the receiver should accept the NFT first. + public fun grant_to( + _cap: &mut MintCapability, + receiver: address, + nft: NFT + ) acquires IdentifierNFT { + assert!(exists>(receiver), Errors::not_published(ERR_NFT_NOT_ACCEPT)); + let id_nft = borrow_global_mut>(receiver); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + Option::fill(&mut id_nft.nft, nft); + } + + /// Revoke the NFT from owner. + public fun revoke( + _cap: &mut BurnCapability, + owner: address + ): NFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = move_from>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_some(nft) + } + + /// borrow_out the NFT from owner. + public fun borrow_out( + _cap: &mut UpdateCapability, + owner: address + ): BorrowNFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let id_nft = borrow_global_mut>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let nft = Option::extract(&mut id_nft.nft); + + BorrowNFT{ + nft : nft, + addr: owner + } + } + + /// return_back the NFT to owner. + public fun return_back( + borrownft: BorrowNFT, + ) acquires IdentifierNFT { + + let BorrowNFT{ + nft: nft, + addr: owner + } = borrownft ; + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = borrow_global_mut>(owner); + + Option::fill(&mut id_nft.nft , nft) + } + + public fun borrow_nft( + borrownft:&BorrowNFT + ) : & NFT { + & borrownft.nft + } + + public fun borrow_nft_mut ( + borrownft:&mut BorrowNFT + ) : &mut NFT { + &mut borrownft.nft + } + + /// Check `owner` is owns the IdentifierNFT + public fun owns(owner: address): bool acquires IdentifierNFT { + if (!exists>(owner)) { + return false + }; + let id_nft = borrow_global>(owner); + Option::is_some(&id_nft.nft) + } + /// deprecated. Use `owns()` instead. + public fun is_owns(owner: address): bool acquires IdentifierNFT { + owns(owner) + } + + public fun get_nft_info( + owner: address + ): Option> acquires IdentifierNFT { + if (!exists>(owner)) { + return Option::none>() + }; + let id_nft = borrow_global>(owner); + let info = if (Option::is_some(&id_nft.nft)) { + let nft = Option::borrow(&id_nft.nft); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + info + } +} + +module IdentifierNFTScripts { + use StarcoinFramework::IdentifierNFT; + spec module { + pragma verify = false; + } + + /// Init IdentifierNFT for accept NFT as Identifier. + public entry fun accept(sender: signer) { + IdentifierNFT::accept_entry(sender); + } + + /// Destroy empty IdentifierNFT + public entry fun destroy_empty(sender: signer) { + IdentifierNFT::destroy_empty_entry(sender); + } +} + +/// NFTGallery is user collection of NFT. +module NFTGallery { + use StarcoinFramework::Signer; + use StarcoinFramework::NFT::{Self, NFT}; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::Event; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + + const ERR_NFT_NOT_EXISTS: u64 = 101; + + const ERR_NFTGALLERY_NOT_EXISTS:u64 = 102; + + spec module { + pragma verify = false; + } + + struct WithdrawEvent has drop, store { + owner: address, + id: u64, + } + + struct DepositEvent has drop, store { + owner: address, + id: u64, + } + + struct NFTGallery has key, store { + withdraw_events: Event::EventHandle>, + deposit_events: Event::EventHandle>, + items: vector>, + } + + /// Check the `owner` is prepared with NFTGallery for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Init a NFTGallery to accept NFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let sender_addr = Signer::address_of(sender); + if (!is_accept(sender_addr)) { + let gallery = NFTGallery { + withdraw_events: Event::new_event_handle>(sender), + deposit_events: Event::new_event_handle>(sender), + items: Vector::empty>(), + }; + move_to(sender, gallery); + } + } + + /// Transfer NFT from `sender` to `receiver` + public entry fun transfer_entry( + sender: signer, + id: u64, receiver: address + ) acquires NFTGallery { + transfer(&sender, id, receiver); + } + + public fun transfer( + sender: &signer, + id: u64, + receiver: address + ) acquires NFTGallery { + let nft = withdraw(sender, id); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let nft = Option::destroy_some(nft); + deposit_to(receiver, nft) + } + + /// Get the NFT info by the NFT id. + public fun get_nft_info_by_id( + owner: address, + id: u64 + ): Option> acquires NFTGallery { + if(!is_accept(owner)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(owner); + let idx = find_by_id(&gallery.items, id); + + let info = if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::borrow>(&gallery.items, i); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + return info + } + + /// Get the NFT info by the NFT idx in NFTGallery + public fun get_nft_info_by_idx( + owner: address, + idx: u64 + ): NFT::NFTInfo acquires NFTGallery { + assert!(exists>(owner), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(owner); + let nft = Vector::borrow>(&gallery.items, idx); + NFT::get_info(nft) + } + + /// Get the all NFT info + public fun get_nft_infos( + owner: address + ): vector> acquires NFTGallery { + if(!is_accept(owner)){ + return Vector::empty>() + }; + let gallery = borrow_global_mut>(owner); + let infos = Vector::empty(); + let len = Vector::length(&gallery.items); + let idx = 0; + while (len > idx) { + let nft = Vector::borrow>(&gallery.items, idx); + Vector::push_back(&mut infos, NFT::get_info(nft)); + idx = idx + 1; + }; + infos + } + + /// Deposit nft to `sender` NFTGallery + public fun deposit( + sender: &signer, + nft: NFT + ) acquires NFTGallery { + Self::accept(sender); + let sender_addr = Signer::address_of(sender); + deposit_to(sender_addr, nft) + } + + /// Deposit nft to `receiver` NFTGallery + public fun deposit_to( + receiver: address, + nft: NFT + ) acquires NFTGallery { + assert!(exists>(receiver), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(receiver); + Event::emit_event(&mut gallery.deposit_events, DepositEvent { id: NFT::get_id(&nft), owner: receiver }); + Vector::push_back(&mut gallery.items, nft); + } + + /// Withdraw one nft of NFTMeta from `sender`, caller should ensure at least one NFT in the Gallery. + public fun withdraw_one( + sender: &signer + ): NFT acquires NFTGallery { + let nft = do_withdraw(sender, Option::none()); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + Option::destroy_some(nft) + } + + /// Withdraw nft of NFTMeta and id from `sender` + public fun withdraw( + sender: &signer, + id: u64 + ): Option> acquires NFTGallery { + do_withdraw(sender, Option::some(id)) + } + + /// Withdraw nft of NFTMeta and id from `sender` + fun do_withdraw( + sender: &signer, + id: Option + ): Option> acquires NFTGallery { + let sender_addr = Signer::address_of(sender); + if(!is_accept(sender_addr)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(sender_addr); + let len = Vector::length(&gallery.items); + let nft = if (len == 0) { + Option::none() + } else { + let idx = if (Option::is_some(&id)) { + let id = Option::extract(&mut id); + find_by_id(&gallery.items, id) + } else { + //default withdraw the last nft. + Option::some(len - 1) + }; + + if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::remove>(&mut gallery.items, i); + Event::emit_event( + &mut gallery.withdraw_events, + WithdrawEvent { id: NFT::get_id(&nft), owner: sender_addr } + ); + Option::some(nft) + } else { + Option::none() + } + }; + nft + } + + fun find_by_id( + c: &vector>, + id: u64 + ): Option { + let len = Vector::length(c); + if (len == 0) { + return Option::none() + }; + let idx = len - 1; + loop { + let nft = Vector::borrow(c, idx); + if (NFT::get_id(nft) == id) { + return Option::some(idx) + }; + if (idx == 0) { + return Option::none() + }; + idx = idx - 1; + } + } + + /// Count all NFTs assigned to an owner + public fun count_of(owner: address): u64 acquires NFTGallery { + if(!is_accept(owner)){ + return 0 + }; + let gallery = borrow_global_mut>(owner); + Vector::length(&gallery.items) + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery_entry(sender: signer) acquires NFTGallery { + remove_empty_gallery(&sender); + } + + public fun remove_empty_gallery(sender: &signer) acquires NFTGallery{ + let sender_addr = Signer::address_of(sender); + assert!(exists>(sender_addr), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let NFTGallery {withdraw_events, deposit_events, items} = move_from>(sender_addr); + + Event::destroy_handle>(withdraw_events); + Event::destroy_handle>(deposit_events); + Vector::destroy_empty>(items); + } + + spec remove_empty_gallery { + let sender_addr = Signer::address_of(sender); + aborts_if !exists>(sender_addr); + + let gallery = global>(sender_addr); + aborts_if Vector::length>(gallery.items) > 0; + + ensures !exists>(sender_addr); + } + +} + +module NFTGalleryScripts { + use StarcoinFramework::NFTGallery; + + spec module { + pragma verify = false; + } + + /// Init a NFTGallery for accept NFT + public entry fun accept(sender: signer) { + NFTGallery::accept_entry(sender); + } + /// Transfer NFT with `id` from `sender` to `receiver` + public entry fun transfer( + sender: signer, + id: u64, receiver: address + ) { + NFTGallery::transfer_entry(sender, id, receiver); + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery(sender: signer) { + NFTGallery::remove_empty_gallery_entry(sender); + } +} +} diff --git a/release/v13/sources/NFTGalleryScripts.move b/release/v13/sources/NFTGalleryScripts.move new file mode 100644 index 00000000..d1ac25f8 --- /dev/null +++ b/release/v13/sources/NFTGalleryScripts.move @@ -0,0 +1,1023 @@ +address StarcoinFramework { +/// Non-fungible token standard and implementations. +module NFT { + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Account; + use StarcoinFramework::Vector; + use StarcoinFramework::Event; + use StarcoinFramework::GenesisSignerCapability; + + const ERR_NO_MINT_CAPABILITY: u64 = 101; + const ERR_NO_BURN_CAPABILITY: u64 = 102; + const ERR_NO_UPDATE_CAPABILITY: u64 = 103; + const ERR_CANOT_EMPTY: u64 = 104; + const ERR_NFT_TYPE_ALREADY_REGISTERED: u64 = 105; + const ERR_NFT_TYPE_NO_REGISTERED: u64 = 106; + + spec module { + pragma verify = false; + } + + struct MintEvent has drop, store { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + struct BurnEvent has drop, store { + id: u64, + } + + /// The info of NFT type, this type is deprecated, please use NFTTypeInfoV2 + struct NFTTypeInfo has key, store { + counter: u64, + meta: Metadata, + info: NFTTypeInfoExt, + mint_events: Event::EventHandle>, + } + + /// The info of NFT type + struct NFTTypeInfoV2 has key, store { + register: address, + counter: u64, + meta: Metadata, + mint_events: Event::EventHandle>, + burn_events: Event::EventHandle>, + } + + struct NFTTypeInfoCompat has key { + info: NFTTypeInfoExt, + } + + /// Deprecated. Use `new_nft_type_info_v2` instead. + fun new_nft_type_info( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ): NFTTypeInfo { + NFTTypeInfo { + counter: 0, + info, + meta, + mint_events: Event::new_event_handle>(sender), + } + } + + fun new_nft_type_info_v2(sender: &signer, meta: Metadata): NFTTypeInfoV2 { + NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter: 0, + meta, + mint_events: Event::new_event_handle>(sender), + burn_events: Event::new_event_handle>(sender), + } + } + + /// Note: this function is deprecated + public fun nft_type_info_ex_info( + ): NFTTypeInfoExt acquires NFTTypeInfo, NFTTypeInfoCompat { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.info + } + } + + /// Note: this function is deprecated, please use nft_type_info_counter_v2 + public fun nft_type_info_counter( + ): u64 acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + Self::nft_type_info_counter_v2() + } else { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + } + + public fun nft_type_info_counter_v2(): u64 acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.counter + } + + public fun nft_type_info_meta(): Metadata acquires NFTTypeInfoV2 { + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&info.meta + } + + public fun upgrade_nft_type_info_from_v1_to_v2( + sender: &signer, + _cap: &mut MintCapability + ) acquires NFTTypeInfo { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfo { counter, meta, info, mint_events } = nft_type_info; + + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let nft_type_info_v2 = NFTTypeInfoV2 { + register: Signer::address_of(sender), + counter, + meta, + mint_events, + burn_events: Event::new_event_handle>(sender), + }; + move_to(&genesis_account, nft_type_info_v2); + move_to(&genesis_account, NFTTypeInfoCompat { info }); + } + } + + public fun remove_compat_info( + _cap: &mut MintCapability + ): NFTTypeInfoExt acquires NFTTypeInfoCompat { + let compat_info = move_from>(CoreAddresses::GENESIS_ADDRESS()); + let NFTTypeInfoCompat{info} = compat_info; + info + } + /// deprecated. + struct GenesisSignerCapability has key { + cap: Account::SignerCapability, + } + /// The capability to mint the nft. + struct MintCapability has key, store {} + /// The Capability to burn the nft. + struct BurnCapability has key, store {} + /// The Capability to update the nft metadata. + struct UpdateCapability has key, store {} + + struct Metadata has copy, store, drop { + /// NFT name's utf8 bytes. + name: vector, + /// Image link, such as ipfs://xxxx + image: vector, + /// Image bytes data, image or image_data can not empty for both. + image_data: vector, + /// NFT description utf8 bytes. + description: vector, + } + + public fun empty_meta(): Metadata { + Metadata { + name: Vector::empty(), + image: Vector::empty(), + image_data: Vector::empty(), + description: Vector::empty(), + } + } + + public fun new_meta(name: vector, description: vector): Metadata { + Metadata { + name, + image: Vector::empty(), + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image(name: vector, image: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image, + image_data: Vector::empty(), + description, + } + } + + public fun new_meta_with_image_data(name: vector, image_data: vector, description: vector): Metadata { + assert!(!Vector::is_empty(&name), Errors::invalid_argument(ERR_CANOT_EMPTY)); + assert!(!Vector::is_empty(&image_data), Errors::invalid_argument(ERR_CANOT_EMPTY)); + Metadata { + name, + image: Vector::empty(), + image_data, + description, + } + } + + public fun meta_name(metadata: &Metadata): vector { + *&metadata.name + } + + public fun meta_image(metadata: &Metadata): vector { + *&metadata.image + } + + public fun meta_image_data(metadata: &Metadata): vector { + *&metadata.image_data + } + + public fun meta_description(metadata: &Metadata): vector { + *&metadata.description + } + + struct NFT has store { + /// The creator of NFT + creator: address, + /// The unique id of NFT under NFTMeta type + id: u64, + /// The metadata of NFT + base_meta: Metadata, + /// The extension metadata of NFT + type_meta: NFTMeta, + /// The body of NFT, NFT is a box for NFTBody + body: NFTBody, + } + + /// The information of NFT instance return by get_nft_info + struct NFTInfo has copy, store, drop { + id: u64, + creator: address, + base_meta: Metadata, + type_meta: NFTMeta, + } + + public fun get_info(nft: &NFT): NFTInfo { + NFTInfo { + id: nft.id, + creator: nft.creator, + base_meta: *&nft.base_meta, + type_meta: *&nft.type_meta, + } + } + + public fun unpack_info( + nft_info: NFTInfo + ): (u64, address, Metadata, NFTMeta) { + let NFTInfo { id, creator, base_meta, type_meta } = nft_info; + (id, creator, base_meta, type_meta) + } + + public fun get_id(nft: &NFT): u64 { + return nft.id + } + + public fun get_base_meta(nft: &NFT): &Metadata { + return &nft.base_meta + } + + public fun get_type_meta(nft: &NFT): &NFTMeta { + return &nft.type_meta + } + + public fun get_creator(nft: &NFT): address { + return nft.creator + } + + /// deprecated. + public fun initialize(_signer: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, + /// in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability { + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register a NFT type to genesis + /// Note: this function is deprecated, please use `register_v2` + public fun register( + sender: &signer, + info: NFTTypeInfoExt, + meta: Metadata + ) acquires NFTTypeInfo { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + let type_info = new_nft_type_info(sender, info, meta); + move_to>(&genesis_account, type_info); + let mint_cap = MintCapability {}; + + Self::upgrade_nft_type_info_from_v1_to_v2(sender, &mut mint_cap); + + move_to>(sender, mint_cap); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Register a NFT type to genesis + public fun register_v2(sender: &signer, meta: Metadata) { + assert!(!is_registered(), Errors::invalid_argument(ERR_NFT_TYPE_ALREADY_REGISTERED)); + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + + let type_info = new_nft_type_info_v2(sender, meta); + move_to>(&genesis_account, type_info); + move_to>(sender, MintCapability {}); + move_to>(sender, BurnCapability {}); + move_to>(sender, UpdateCapability {}); + } + + /// Check the NFTMeta is register + public fun is_registered(): bool { + exists>(CoreAddresses::GENESIS_ADDRESS()) + } + + /// deprecated. Use "is_registered" instead. + public fun is_register(): bool { + is_registered() + } + + /// Add MintCapability to `sender` + public fun add_mint_capability(sender: &signer, cap: MintCapability) { + move_to(sender, cap); + } + + /// Remove the MintCapability from `sender` + public fun remove_mint_capability( + sender: &signer + ): MintCapability acquires MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the MintCapability + public fun destroy_mint_capability(cap: MintCapability) { + let MintCapability {} = cap; + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + /// Note: this function is deprecated, please use `mint_with_cap_v2` + public fun mint_with_cap( + creator: address, + cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2 { + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + mint_with_cap_v2(creator, cap, base_meta, type_meta, body) + } else { + let nft_type_info = + borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id: id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + } + + /// Mint nft with MintCapability, `creator` will been the NFT's creator. + public fun mint_with_cap_v2( + creator: address, + _cap: &mut MintCapability, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2 { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + nft_type_info.counter = nft_type_info.counter + 1; + let id = nft_type_info.counter; + let nft = NFT { + id, + creator, + base_meta: copy base_meta, + type_meta: copy type_meta, + body, + }; + Event::emit_event(&mut nft_type_info.mint_events, MintEvent { + id, + creator, + base_meta, + type_meta, + }); + nft + } + + /// Mint nft, the `sender` must have MintCapability + /// Note: this function is deprecated, please use `mint_v2` + public fun mint( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfo, NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap(addr, cap, base_meta, type_meta, body) + } + + /// Mint nft, the `sender` must have MintCapability + public fun mint_v2( + sender: &signer, + base_meta: Metadata, + type_meta: NFTMeta, + body: NFTBody + ): NFT acquires NFTTypeInfoV2, MintCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_MINT_CAPABILITY)); + let cap = borrow_global_mut>(addr); + mint_with_cap_v2(addr, cap, base_meta, type_meta, body) + } + + /// Add BurnCapability to `sender` + public fun add_burn_capability(sender: &signer, cap: BurnCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_burn_capability( + sender: &signer + ): BurnCapability acquires BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the BurnCapability + public fun destroy_burn_capability(cap: BurnCapability) { + let BurnCapability {} = cap; + } + + /// Burn nft with BurnCapability + public fun burn_with_cap( + _cap: &mut BurnCapability, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2 { + let NFT { creator: _, id: id, base_meta: _, type_meta: _, body } = nft; + // only NFTTypeInfoV2 has burn_events EventHandle + if (exists>(CoreAddresses::GENESIS_ADDRESS())) { + let nft_type_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + Event::emit_event(&mut nft_type_info.burn_events, BurnEvent { + id, + }); + }; + body + } + + /// Burn nft, the `sender` must have BurnCapability + public fun burn( + sender: &signer, + nft: NFT + ): NFTBody acquires NFTTypeInfoV2, BurnCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_BURN_CAPABILITY)); + let cap = borrow_global_mut>(addr); + burn_with_cap(cap, nft) + } + + /// Add UpdateCapability to `sender` + public fun add_update_capability(sender: &signer, cap: UpdateCapability) { + move_to(sender, cap); + } + + /// Remove the BurnCapability from `sender` + public fun remove_update_capability( + sender: &signer + ): UpdateCapability acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(addr) + } + + /// Destroy the UpdateCapability + public fun destroy_update_capability(cap: UpdateCapability) { + let UpdateCapability {} = cap; + } + + /// Update the NFTTypeInfoV2 metadata with UpdateCapability + public fun update_nft_type_info_meta_with_cap( + _cap: &mut UpdateCapability, + new_meta: Metadata + ) acquires NFTTypeInfoV2{ + let info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + info.meta = new_meta; + } + + /// Update the NFTTypeInfoV2 metadata, the `sender` must have UpdateCapability + public fun update_nft_type_info_meta( + sender: &signer, + new_meta: Metadata + ) acquires UpdateCapability, NFTTypeInfoV2 { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_nft_type_info_meta_with_cap(cap, new_meta) + } + + /// Update the nft's base_meta and type_meta with UpdateCapability + public fun update_meta_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT, + base_meta: Metadata, type_meta: NFTMeta + ) { + nft.base_meta = base_meta; + nft.type_meta = type_meta; + } + + /// Update the nft's base_meta and type_meta, the `sender` must have UpdateCapability + public fun update_meta( + sender: &signer, + nft: &mut NFT, + base_meta: Metadata, + type_meta: NFTMeta + ) acquires UpdateCapability { + let addr = Signer::address_of(sender); + assert!(exists>(addr), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(addr); + update_meta_with_cap(cap, nft, base_meta, type_meta) + } + + /// Borrow NFTBody ref + public fun borrow_body(nft: &NFT): &NFTBody { + &nft.body + } + + /// Borrow NFTBody mut ref for update body with UpdateCapability + public fun borrow_body_mut_with_cap( + _cap: &mut UpdateCapability, + nft: &mut NFT + ): &mut NFTBody { + &mut nft.body + } +} + +/// IdentifierNFT using NFT as identifier for an on chain account +/// The NFT can not been transfer by owner. +module IdentifierNFT { + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::NFT::{Self, NFT, MintCapability, BurnCapability, UpdateCapability}; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + const ERR_NFT_EXISTS: u64 = 101; + const ERR_NFT_NOT_EXISTS: u64 = 102; + const ERR_NFT_NOT_ACCEPT: u64 = 103; + const ERR_BORROW_ADDR_NOT_SAME: u64 = 104; + + spec module { + pragma verify = false; + } + + struct IdentifierNFT has key { + nft: Option>, + } + + //Used when borrowing or returning NFT, note: there is no drop ability, it must be returned after borrowing + struct BorrowNFT { + nft: NFT, + addr:address + } + + /// Check the `owner` is prepared with IdentifierNFT for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Accept NFT, prepare an empty IdentifierNFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let addr = Signer::address_of(sender); + if (!is_accept(addr)) { + move_to(sender, IdentifierNFT { + nft: Option::none(), + }); + } + } + + /// Destroy the empty IdentifierNFT + public entry fun destroy_empty_entry(sender: signer) acquires IdentifierNFT { + destroy_empty(&sender); + } + + public fun destroy_empty(sender: &signer) acquires IdentifierNFT { + let addr = Signer::address_of(sender); + if (exists>(addr)) { + let id_nft = move_from>(addr); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_none(nft); + } + } + + /// Grant nft as IdentifierNFT to `sender` with MintCapability, sender will auto accept the NFT. + public fun grant( + cap: &mut MintCapability, + sender: &signer, + nft: NFT + ) acquires IdentifierNFT { + Self::accept(sender); + Self::grant_to(cap, Signer::address_of(sender), nft); + } + + /// Grant nft as IdentifierNFT to `receiver` with MintCapability, the receiver should accept the NFT first. + public fun grant_to( + _cap: &mut MintCapability, + receiver: address, + nft: NFT + ) acquires IdentifierNFT { + assert!(exists>(receiver), Errors::not_published(ERR_NFT_NOT_ACCEPT)); + let id_nft = borrow_global_mut>(receiver); + assert!(Option::is_none(&id_nft.nft), Errors::already_published(ERR_NFT_EXISTS)); + Option::fill(&mut id_nft.nft, nft); + } + + /// Revoke the NFT from owner. + public fun revoke( + _cap: &mut BurnCapability, + owner: address + ): NFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = move_from>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let IdentifierNFT { nft } = id_nft; + Option::destroy_some(nft) + } + + /// borrow_out the NFT from owner. + public fun borrow_out( + _cap: &mut UpdateCapability, + owner: address + ): BorrowNFT acquires IdentifierNFT { + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let id_nft = borrow_global_mut>(owner); + assert!(Option::is_some(&id_nft.nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + + let nft = Option::extract(&mut id_nft.nft); + + BorrowNFT{ + nft : nft, + addr: owner + } + } + + /// return_back the NFT to owner. + public fun return_back( + borrownft: BorrowNFT, + ) acquires IdentifierNFT { + + let BorrowNFT{ + nft: nft, + addr: owner + } = borrownft ; + assert!(exists>(owner), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let id_nft = borrow_global_mut>(owner); + + Option::fill(&mut id_nft.nft , nft) + } + + public fun borrow_nft( + borrownft:&BorrowNFT + ) : & NFT { + & borrownft.nft + } + + public fun borrow_nft_mut ( + borrownft:&mut BorrowNFT + ) : &mut NFT { + &mut borrownft.nft + } + + /// Check `owner` is owns the IdentifierNFT + public fun owns(owner: address): bool acquires IdentifierNFT { + if (!exists>(owner)) { + return false + }; + let id_nft = borrow_global>(owner); + Option::is_some(&id_nft.nft) + } + /// deprecated. Use `owns()` instead. + public fun is_owns(owner: address): bool acquires IdentifierNFT { + owns(owner) + } + + public fun get_nft_info( + owner: address + ): Option> acquires IdentifierNFT { + if (!exists>(owner)) { + return Option::none>() + }; + let id_nft = borrow_global>(owner); + let info = if (Option::is_some(&id_nft.nft)) { + let nft = Option::borrow(&id_nft.nft); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + info + } +} + +module IdentifierNFTScripts { + use StarcoinFramework::IdentifierNFT; + spec module { + pragma verify = false; + } + + /// Init IdentifierNFT for accept NFT as Identifier. + public entry fun accept(sender: signer) { + IdentifierNFT::accept_entry(sender); + } + + /// Destroy empty IdentifierNFT + public entry fun destroy_empty(sender: signer) { + IdentifierNFT::destroy_empty_entry(sender); + } +} + +/// NFTGallery is user collection of NFT. +module NFTGallery { + use StarcoinFramework::Signer; + use StarcoinFramework::NFT::{Self, NFT}; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::Event; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + + const ERR_NFT_NOT_EXISTS: u64 = 101; + + const ERR_NFTGALLERY_NOT_EXISTS:u64 = 102; + + spec module { + pragma verify = false; + } + + struct WithdrawEvent has drop, store { + owner: address, + id: u64, + } + + struct DepositEvent has drop, store { + owner: address, + id: u64, + } + + struct NFTGallery has key, store { + withdraw_events: Event::EventHandle>, + deposit_events: Event::EventHandle>, + items: vector>, + } + + /// Check the `owner` is prepared with NFTGallery for accept the NFT + public fun is_accept(owner: address): bool { + exists>(owner) + } + + /// Init a NFTGallery to accept NFT for `sender` + public entry fun accept_entry(sender: signer) { + accept(&sender); + } + + public fun accept(sender: &signer) { + let sender_addr = Signer::address_of(sender); + if (!is_accept(sender_addr)) { + let gallery = NFTGallery { + withdraw_events: Event::new_event_handle>(sender), + deposit_events: Event::new_event_handle>(sender), + items: Vector::empty>(), + }; + move_to(sender, gallery); + } + } + + /// Transfer NFT from `sender` to `receiver` + public entry fun transfer_entry( + sender: signer, + id: u64, receiver: address + ) acquires NFTGallery { + transfer(&sender, id, receiver); + } + + public fun transfer( + sender: &signer, + id: u64, + receiver: address + ) acquires NFTGallery { + let nft = withdraw(sender, id); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + let nft = Option::destroy_some(nft); + deposit_to(receiver, nft) + } + + /// Get the NFT info by the NFT id. + public fun get_nft_info_by_id( + owner: address, + id: u64 + ): Option> acquires NFTGallery { + if(!is_accept(owner)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(owner); + let idx = find_by_id(&gallery.items, id); + + let info = if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::borrow>(&gallery.items, i); + Option::some(NFT::get_info(nft)) + } else { + Option::none>() + }; + return info + } + + /// Get the NFT info by the NFT idx in NFTGallery + public fun get_nft_info_by_idx( + owner: address, + idx: u64 + ): NFT::NFTInfo acquires NFTGallery { + assert!(exists>(owner), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(owner); + let nft = Vector::borrow>(&gallery.items, idx); + NFT::get_info(nft) + } + + /// Get the all NFT info + public fun get_nft_infos( + owner: address + ): vector> acquires NFTGallery { + if(!is_accept(owner)){ + return Vector::empty>() + }; + let gallery = borrow_global_mut>(owner); + let infos = Vector::empty(); + let len = Vector::length(&gallery.items); + let idx = 0; + while (len > idx) { + let nft = Vector::borrow>(&gallery.items, idx); + Vector::push_back(&mut infos, NFT::get_info(nft)); + idx = idx + 1; + }; + infos + } + + /// Deposit nft to `sender` NFTGallery + public fun deposit( + sender: &signer, + nft: NFT + ) acquires NFTGallery { + Self::accept(sender); + let sender_addr = Signer::address_of(sender); + deposit_to(sender_addr, nft) + } + + /// Deposit nft to `receiver` NFTGallery + public fun deposit_to( + receiver: address, + nft: NFT + ) acquires NFTGallery { + assert!(exists>(receiver), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let gallery = borrow_global_mut>(receiver); + Event::emit_event(&mut gallery.deposit_events, DepositEvent { id: NFT::get_id(&nft), owner: receiver }); + Vector::push_back(&mut gallery.items, nft); + } + + /// Withdraw one nft of NFTMeta from `sender`, caller should ensure at least one NFT in the Gallery. + public fun withdraw_one( + sender: &signer + ): NFT acquires NFTGallery { + let nft = do_withdraw(sender, Option::none()); + assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_NOT_EXISTS)); + Option::destroy_some(nft) + } + + /// Withdraw nft of NFTMeta and id from `sender` + public fun withdraw( + sender: &signer, + id: u64 + ): Option> acquires NFTGallery { + do_withdraw(sender, Option::some(id)) + } + + /// Withdraw nft of NFTMeta and id from `sender` + fun do_withdraw( + sender: &signer, + id: Option + ): Option> acquires NFTGallery { + let sender_addr = Signer::address_of(sender); + if(!is_accept(sender_addr)){ + return Option::none>() + }; + let gallery = borrow_global_mut>(sender_addr); + let len = Vector::length(&gallery.items); + let nft = if (len == 0) { + Option::none() + } else { + let idx = if (Option::is_some(&id)) { + let id = Option::extract(&mut id); + find_by_id(&gallery.items, id) + } else { + //default withdraw the last nft. + Option::some(len - 1) + }; + + if (Option::is_some(&idx)) { + let i = Option::extract(&mut idx); + let nft = Vector::remove>(&mut gallery.items, i); + Event::emit_event( + &mut gallery.withdraw_events, + WithdrawEvent { id: NFT::get_id(&nft), owner: sender_addr } + ); + Option::some(nft) + } else { + Option::none() + } + }; + nft + } + + fun find_by_id( + c: &vector>, + id: u64 + ): Option { + let len = Vector::length(c); + if (len == 0) { + return Option::none() + }; + let idx = len - 1; + loop { + let nft = Vector::borrow(c, idx); + if (NFT::get_id(nft) == id) { + return Option::some(idx) + }; + if (idx == 0) { + return Option::none() + }; + idx = idx - 1; + } + } + + /// Count all NFTs assigned to an owner + public fun count_of(owner: address): u64 acquires NFTGallery { + if(!is_accept(owner)){ + return 0 + }; + let gallery = borrow_global_mut>(owner); + Vector::length(&gallery.items) + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery_entry(sender: signer) acquires NFTGallery { + remove_empty_gallery(&sender); + } + + public fun remove_empty_gallery(sender: &signer) acquires NFTGallery{ + let sender_addr = Signer::address_of(sender); + assert!(exists>(sender_addr), Errors::not_published(ERR_NFTGALLERY_NOT_EXISTS)); + let NFTGallery {withdraw_events, deposit_events, items} = move_from>(sender_addr); + + Event::destroy_handle>(withdraw_events); + Event::destroy_handle>(deposit_events); + Vector::destroy_empty>(items); + } + + spec remove_empty_gallery { + let sender_addr = Signer::address_of(sender); + aborts_if !exists>(sender_addr); + + let gallery = global>(sender_addr); + aborts_if Vector::length>(gallery.items) > 0; + + ensures !exists>(sender_addr); + } + +} + +module NFTGalleryScripts { + use StarcoinFramework::NFTGallery; + + spec module { + pragma verify = false; + } + + /// Init a NFTGallery for accept NFT + public entry fun accept(sender: signer) { + NFTGallery::accept_entry(sender); + } + /// Transfer NFT with `id` from `sender` to `receiver` + public entry fun transfer( + sender: signer, + id: u64, receiver: address + ) { + NFTGallery::transfer_entry(sender, id, receiver); + } + + /// Remove empty NFTGallery. + public entry fun remove_empty_gallery(sender: signer) { + NFTGallery::remove_empty_gallery_entry(sender); + } +} +} diff --git a/release/v13/sources/Offer.move b/release/v13/sources/Offer.move new file mode 100644 index 00000000..cb406860 --- /dev/null +++ b/release/v13/sources/Offer.move @@ -0,0 +1,84 @@ +address StarcoinFramework { +module Offer { + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::Collection2; + + spec module { + pragma verify = true; + pragma aborts_if_is_strict = true; + } + + /// A wrapper around value `offered` that can be claimed by the address stored in `for` when after lock time. + struct Offer has key { offered: Offered, for: address, time_lock: u64 } + + /// An offer of the specified type for the account does not match + const EOFFER_DNE_FOR_ACCOUNT: u64 = 101; + + /// Offer is not unlocked yet. + const EOFFER_NOT_UNLOCKED: u64 = 102; + + /// Publish a value of type `Offered` under the sender's account. The value can be claimed by + /// either the `for` address or the transaction sender. + public fun create(account: &signer, offered: Offered, for: address, lock_period: u64) { + let time_lock = Timestamp::now_seconds() + lock_period; + //TODO should support multi Offer? + move_to(account, Offer { offered, for, time_lock }); + } + + spec create { + include Timestamp::AbortsIfTimestampNotExists; + aborts_if Timestamp::now_seconds() + lock_period > max_u64(); + aborts_if exists>(Signer::address_of(account)); + } + + /// Claim the value of type `Offered` published at `offer_address`. + /// Only succeeds if the sender is the intended recipient stored in `for` or the original + /// publisher `offer_address`, and now >= time_lock + /// Also fails if no such value exists. + public fun redeem(account: &signer, offer_address: address): Offered acquires Offer { + let Offer { offered, for, time_lock } = move_from>(offer_address); + let sender = Signer::address_of(account); + let now = Timestamp::now_seconds(); + assert!(sender == for || sender == offer_address, Errors::invalid_argument(EOFFER_DNE_FOR_ACCOUNT)); + assert!(now >= time_lock, Errors::not_published(EOFFER_NOT_UNLOCKED)); + offered + } + + spec redeem { + aborts_if !exists>(offer_address); + aborts_if Signer::address_of(account) != global>(offer_address).for && Signer::address_of(account) != offer_address; + aborts_if Timestamp::now_seconds() < global>(offer_address).time_lock; + include Timestamp::AbortsIfTimestampNotExists; + } + + /// Returns true if an offer of type `Offered` exists at `offer_address`. + public fun exists_at(offer_address: address): bool { + exists>(offer_address) + } + + spec exists_at {aborts_if false;} + + /// Returns the address of the `Offered` type stored at `offer_address`. + /// Fails if no such `Offer` exists. + public fun address_of(offer_address: address): address acquires Offer { + borrow_global>(offer_address).for + } + + spec address_of {aborts_if !exists>(offer_address);} + + /// Take Offer and put to signer's Collection. + public entry fun take_offer( + signer: signer, + offer_address: address, + ) acquires Offer { + let offered = redeem(&signer, offer_address); + Collection2::put(&signer, Signer::address_of(&signer), offered); + } + + spec take_offer { + pragma verify = false; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/OnChainConfigDao.move b/release/v13/sources/OnChainConfigDao.move new file mode 100644 index 00000000..a92e9889 --- /dev/null +++ b/release/v13/sources/OnChainConfigDao.move @@ -0,0 +1,97 @@ +address StarcoinFramework { +/// OnChainConfigDao is a DAO proposal for modify onchain configuration. +module OnChainConfigDao { + use StarcoinFramework::Token; + use StarcoinFramework::Signer; + use StarcoinFramework::Config; + use StarcoinFramework::Dao; + use StarcoinFramework::Errors; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_strict; + pragma aborts_if_is_partial; + } + + /// A wrapper of `Config::ModifyConfigCapability`. + struct WrappedConfigModifyCapability has key { + cap: Config::ModifyConfigCapability, + } + + /// request of updating configuration. + struct OnChainConfigUpdate has copy, drop, store { + value: ConfigT, + } + + const ERR_NOT_AUTHORIZED: u64 = 401; + + /// Plugin method of the module. + /// Should be called by token issuer. + public fun plugin(signer: &signer) { + let token_issuer = Token::token_address(); + assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + let config_modify_cap = Config::extract_modify_config_capability(signer); + let cap = WrappedConfigModifyCapability { cap: config_modify_cap }; + move_to(signer, cap); + } + spec plugin { + pragma aborts_if_is_partial = false; + let sender = Signer::address_of(signer); + aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS(); + include Config::AbortsIfCapNotExist{address: sender}; + aborts_if exists>(sender); + ensures exists>(sender); + } + + /// issue a proposal to update config of ConfigT goved by TokenT + public fun propose_update( + signer: &signer, + new_config: ConfigT, + exec_delay: u64, + ) { + Dao::propose>( + signer, + OnChainConfigUpdate { value: new_config }, + exec_delay, + ); + } + + spec propose_update { + use StarcoinFramework::Timestamp; + use StarcoinFramework::CoreAddresses; + pragma aborts_if_is_partial = false; + + // copy from Dao::propose spec. + include Dao::AbortIfDaoConfigNotExist; + include Dao::AbortIfDaoInfoNotExist; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config().min_action_delay; + include Dao::CheckQuorumVotes; + let sender = Signer::address_of(signer); + aborts_if exists>>(sender); + } + + /// Once the proposal is agreed, anyone can call the method to make the proposal happen. + /// Caller need to make sure that the proposal of `proposal_id` under `proposal_address` is + /// the kind of this proposal module. + public fun execute( + proposer_address: address, + proposal_id: u64, + ) acquires WrappedConfigModifyCapability { + let OnChainConfigUpdate { value } = Dao::extract_proposal_action< + TokenT, + OnChainConfigUpdate, + >(proposer_address, proposal_id); + let cap = borrow_global_mut>( + Token::token_address(), + ); + Config::set_with_capability(&mut cap.cap, value); + } + spec execute { + pragma aborts_if_is_partial = true; + let expected_states = vec(6); + include Dao::CheckProposalStates>{expected_states}; + aborts_if !exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/OnChainConfigScripts.move b/release/v13/sources/OnChainConfigScripts.move new file mode 100644 index 00000000..7bc0bc29 --- /dev/null +++ b/release/v13/sources/OnChainConfigScripts.move @@ -0,0 +1,148 @@ +address StarcoinFramework { +module OnChainConfigScripts { + use StarcoinFramework::FlexiDagConfig; + use StarcoinFramework::ConsensusConfig; + use StarcoinFramework::OnChainConfigDao; + use StarcoinFramework::STC; + use StarcoinFramework::RewardConfig; + use StarcoinFramework::TransactionPublishOption; + use StarcoinFramework::TransactionTimeoutConfig; + use StarcoinFramework::VMConfig; + use StarcoinFramework::Signer; + use StarcoinFramework::LanguageVersion; + + public entry fun propose_update_consensus_config(account: signer, + uncle_rate_target: u64, + base_block_time_target: u64, + base_reward_per_block: u128, + base_reward_per_uncle_percent: u64, + epoch_block_count: u64, + base_block_difficulty_window: u64, + min_block_time_target: u64, + max_block_time_target: u64, + base_max_uncles_per_block: u64, + base_block_gas_limit: u64, + strategy: u8, + exec_delay: u64) { + let consensus_config = ConsensusConfig::new_consensus_config(uncle_rate_target, + base_block_time_target, + base_reward_per_block, + base_reward_per_uncle_percent, + epoch_block_count, + base_block_difficulty_window, + min_block_time_target, + max_block_time_target, + base_max_uncles_per_block, + base_block_gas_limit, + strategy); + OnChainConfigDao::propose_update(&account, consensus_config, exec_delay); + } + + spec propose_update_consensus_config { + pragma verify = false; + } + + public entry fun propose_update_reward_config(account: signer, + reward_delay: u64, + exec_delay: u64) { + let reward_config = RewardConfig::new_reward_config(reward_delay); + OnChainConfigDao::propose_update(&account, reward_config, exec_delay); + } + + spec propose_update_reward_config { + pragma verify = false; + } + + public entry fun propose_update_txn_publish_option(account: signer, + script_allowed: bool, + module_publishing_allowed: bool, + exec_delay: u64) { + let txn_publish_option = TransactionPublishOption::new_transaction_publish_option(script_allowed, module_publishing_allowed); + OnChainConfigDao::propose_update(&account, txn_publish_option, exec_delay); + } + + spec propose_update_txn_publish_option { + pragma verify = false; + } + + public entry fun propose_update_txn_timeout_config(account: signer, + duration_seconds: u64, + exec_delay: u64) { + let txn_timeout_config = TransactionTimeoutConfig::new_transaction_timeout_config(duration_seconds); + OnChainConfigDao::propose_update(&account, txn_timeout_config, exec_delay); + } + + spec propose_update_txn_timeout_config { + pragma verify = false; + } + + public entry fun propose_update_vm_config(account: signer, + instruction_schedule: vector, + native_schedule: vector, + global_memory_per_byte_cost: u64, + global_memory_per_byte_write_cost: u64, + min_transaction_gas_units: u64, + large_transaction_cutoff: u64, + instrinsic_gas_per_byte: u64, + maximum_number_of_gas_units: u64, + min_price_per_gas_unit: u64, + max_price_per_gas_unit: u64, + max_transaction_size_in_bytes: u64, + gas_unit_scaling_factor: u64, + default_account_size: u64, + exec_delay: u64, ) { + let vm_config = VMConfig::new_vm_config(instruction_schedule, + native_schedule, + global_memory_per_byte_cost, + global_memory_per_byte_write_cost, + min_transaction_gas_units, + large_transaction_cutoff, + instrinsic_gas_per_byte, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit, + max_transaction_size_in_bytes, + gas_unit_scaling_factor, + default_account_size); + OnChainConfigDao::propose_update(&account, vm_config, exec_delay); + } + + spec propose_update_vm_config { + pragma verify = false; + } + + public entry fun propose_update_move_language_version(account: signer, new_version: u64, exec_delay: u64) { + let lang_version = LanguageVersion::new(new_version); + OnChainConfigDao::propose_update(&account, lang_version, exec_delay); + } + + spec propose_update_move_language_version { + pragma verify = false; + } + + public entry fun propose_update_flexi_dag_effective_height(account: signer, new_height: u64, exec_delay: u64) { + let config = FlexiDagConfig::new_flexidag_config(new_height); + OnChainConfigDao::propose_update(&account, config, exec_delay); + } + + spec propose_update_flexi_dag_effective_height { + pragma verify = false; + } + + public entry fun execute_on_chain_config_proposal(account: signer, proposal_id: u64) { + OnChainConfigDao::execute(Signer::address_of(&account), proposal_id); + } + + spec execute_on_chain_config_proposal { + pragma verify = false; + } + + public entry fun execute_on_chain_config_proposal_v2(proposer_address: address, proposal_id: u64) { + OnChainConfigDao::execute(proposer_address, proposal_id); + } + + spec execute_on_chain_config_proposal_v2 { + pragma verify = false; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Option.move b/release/v13/sources/Option.move new file mode 100644 index 00000000..8baad1e0 --- /dev/null +++ b/release/v13/sources/Option.move @@ -0,0 +1,235 @@ +address StarcoinFramework { + +/// This module defines the Option type and its methods to represent and handle an optional value. +module Option { + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + + /// Abstraction of a value that may or may not be present. Implemented with a vector of size + /// zero or one because Move bytecode does not have ADTs. + struct Option has copy, drop, store { + vec: vector + } + spec Option { + /// The size of vector is always less than equal to 1 + /// because it's 0 for "none" or 1 for "some". + invariant len(vec) <= 1; + } + + /// The `Option` is in an invalid state for the operation attempted. + /// The `Option` is `Some` while it should be `None`. + const EOPTION_IS_SET: u64 = 0; + /// The `Option` is in an invalid state for the operation attempted. + /// The `Option` is `None` while it should be `Some`. + const EOPTION_NOT_SET: u64 = 1; + + /// Return an empty `Option` + public fun none(): Option { + Option { vec: Vector::empty() } + } + spec none { + pragma opaque; + aborts_if false; + ensures result == spec_none(); + } + spec fun spec_none(): Option { + Option{ vec: vec() } + } + + /// Return an `Option` containing `e` + public fun some(e: Element): Option { + Option { vec: Vector::singleton(e) } + } + spec some { + pragma opaque; + aborts_if false; + ensures result == spec_some(e); + } + spec fun spec_some(e: Element): Option { + Option{ vec: vec(e) } + } + + /// Return true if `t` does not hold a value + public fun is_none(t: &Option): bool { + Vector::is_empty(&t.vec) + } + spec is_none { + pragma opaque; + aborts_if false; + ensures result == is_none(t); + } + + /// Return true if `t` holds a value + public fun is_some(t: &Option): bool { + !Vector::is_empty(&t.vec) + } + spec is_some { + pragma opaque; + aborts_if false; + ensures result == is_some(t); + } + + /// Return true if the value in `t` is equal to `e_ref` + /// Always returns `false` if `t` does not hold a value + public fun contains(t: &Option, e_ref: &Element): bool { + Vector::contains(&t.vec, e_ref) + } + spec contains { + pragma opaque; + aborts_if false; + ensures result == spec_contains(t, e_ref); + } + spec fun spec_contains(t: Option, e: Element): bool { + is_some(t) && borrow(t) == e + } + + /// Return an immutable reference to the value inside `t` + /// Aborts if `t` does not hold a value + public fun borrow(t: &Option): &Element { + assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET)); + Vector::borrow(&t.vec, 0) + } + spec borrow { + pragma opaque; + include AbortsIfNone; + ensures result == borrow(t); + } + + /// Return a reference to the value inside `t` if it holds one + /// Return `default_ref` if `t` does not hold a value + public fun borrow_with_default(t: &Option, default_ref: &Element): &Element { + let vec_ref = &t.vec; + if (Vector::is_empty(vec_ref)) default_ref + else Vector::borrow(vec_ref, 0) + } + spec borrow_with_default { + pragma opaque; + aborts_if false; + ensures result == (if (is_some(t)) borrow(t) else default_ref); + } + + /// Return the value inside `t` if it holds one + /// Return `default` if `t` does not hold a value + public fun get_with_default( + t: &Option, + default: Element, + ): Element { + let vec_ref = &t.vec; + if (Vector::is_empty(vec_ref)) default + else *Vector::borrow(vec_ref, 0) + } + spec get_with_default { + pragma opaque; + aborts_if false; + ensures result == (if (is_some(t)) borrow(t) else default); + } + + /// Convert the none option `t` to a some option by adding `e`. + /// Aborts if `t` already holds a value + public fun fill(t: &mut Option, e: Element) { + let vec_ref = &mut t.vec; + if (Vector::is_empty(vec_ref)) Vector::push_back(vec_ref, e) + else abort Errors::invalid_argument(EOPTION_IS_SET) + } + spec fill { + pragma opaque; + aborts_if is_some(t) with Errors::INVALID_ARGUMENT; + ensures is_some(t); + ensures borrow(t) == e; + } + + /// Convert a `some` option to a `none` by removing and returning the value stored inside `t` + /// Aborts if `t` does not hold a value + public fun extract(t: &mut Option): Element { + assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET)); + Vector::pop_back(&mut t.vec) + } + spec extract { + pragma opaque; + include AbortsIfNone; + ensures result == borrow(old(t)); + ensures is_none(t); + } + + /// Return a mutable reference to the value inside `t` + /// Aborts if `t` does not hold a value + public fun borrow_mut(t: &mut Option): &mut Element { + assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET)); + Vector::borrow_mut(&mut t.vec, 0) + } + spec borrow_mut { + pragma opaque; + include AbortsIfNone; + ensures result == borrow(t); + } + + /// Swap the old value inside `t` with `e` and return the old value + /// Aborts if `t` does not hold a value + public fun swap(t: &mut Option, e: Element): Element { + assert!(is_some(t), Errors::invalid_argument(EOPTION_NOT_SET)); + let vec_ref = &mut t.vec; + let old_value = Vector::pop_back(vec_ref); + Vector::push_back(vec_ref, e); + old_value + } + spec swap { + pragma opaque; + include AbortsIfNone; + ensures result == borrow(old(t)); + ensures is_some(t); + ensures borrow(t) == e; + } + + /// Destroys `t.` If `t` holds a value, return it. Returns `default` otherwise + public fun destroy_with_default(t: Option, default: Element): Element { + let Option { vec } = t; + if (Vector::is_empty(&mut vec)) default + else Vector::pop_back(&mut vec) + } + spec destroy_with_default { + pragma opaque; + aborts_if false; + ensures result == (if (is_some(t)) borrow(t) else default); + } + + /// Unpack `t` and return its contents + /// Aborts if `t` does not hold a value + public fun destroy_some(t: Option): Element { + assert!(is_some(&t), Errors::invalid_argument(EOPTION_NOT_SET)); + let Option { vec } = t; + let elem = Vector::pop_back(&mut vec); + Vector::destroy_empty(vec); + elem + } + spec destroy_some { + pragma opaque; + include AbortsIfNone; + ensures result == borrow(t); + } + + /// Unpack `t` + /// Aborts if `t` holds a value + public fun destroy_none(t: Option) { + assert!(is_none(&t), Errors::invalid_argument(EOPTION_IS_SET)); + let Option { vec } = t; + Vector::destroy_empty(vec) + } + spec destroy_none { + pragma opaque; + aborts_if is_some(t) with Errors::INVALID_ARGUMENT; + } + + spec module {} // switch documentation context back to module level + + spec module { + pragma aborts_if_is_strict; + } + + /// # Helper Schema + + spec schema AbortsIfNone { + t: Option; + aborts_if is_none(t) with Errors::INVALID_ARGUMENT; + } +} +} diff --git a/release/v13/sources/Oracle.move b/release/v13/sources/Oracle.move new file mode 100644 index 00000000..9241b263 --- /dev/null +++ b/release/v13/sources/Oracle.move @@ -0,0 +1,325 @@ +address StarcoinFramework { +module Oracle { + use StarcoinFramework::Event; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::Vector; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Account; + use StarcoinFramework::GenesisSignerCapability; + + struct OracleInfo has key { + ///The datasource counter + counter: u64, + ///Ext info + info: Info, + } + + struct DataRecord has copy, store, drop { + ///The data version + version: u64, + ///The record value + value: ValueT, + ///Update timestamp millisecond + updated_at: u64, + } + + struct OracleFeed has key { + record: DataRecord, + } + + struct OracleUpdateEvent has copy,store,drop { + source_id: u64, + record: DataRecord, + } + + struct DataSource has key { + /// the id of data source of ValueT + id: u64, + /// the data version counter. + counter: u64, + update_events: Event::EventHandle>, + } + + struct UpdateCapability has store, key { + account: address, + } + + struct GenesisSignerCapability has key{ + cap: Account::SignerCapability, + } + + /// The oracle type not register. + const ERR_ORACLE_TYPE_NOT_REGISTER:u64 = 101; + /// No capability to update the oracle value. + const ERR_NO_UPDATE_CAPABILITY: u64 = 102; + const ERR_NO_DATA_SOURCE: u64 = 103; + const ERR_CAPABILITY_ACCOUNT_MISS_MATCH: u64 = 104; + + /// deprecated. + public fun initialize(_sender: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability{ + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register `OracleT` as an oracle type. + public fun register_oracle(_sender: &signer, info: Info) { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + move_to(&genesis_account, OracleInfo { + counter: 0, + info, + }); + } + + /// Get the `OracleT` oracle's counter, the counter represent how many `OracleT` datasources + public fun get_oracle_counter() : u64 acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + oracle_info.counter + } + + public fun get_oracle_info() : Info acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&oracle_info.info + } + + /// Init a data source for type `OracleT` + public fun init_data_source(sender: &signer, init_value: ValueT) acquires OracleInfo{ + assert!(exists>(CoreAddresses::GENESIS_ADDRESS()), Errors::not_published(ERR_ORACLE_TYPE_NOT_REGISTER)); + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + let now = Timestamp::now_milliseconds(); + move_to(sender, OracleFeed { + record: DataRecord { + version: 0, + value: init_value, + updated_at: now, + } + }); + let sender_addr = Signer::address_of(sender); + move_to(sender, DataSource { + id: oracle_info.counter, + counter: 1, + update_events: Event::new_event_handle>(sender), + }); + move_to(sender, UpdateCapability{account: sender_addr}); + oracle_info.counter = oracle_info.counter + 1; + } + + /// Check the DataSource is initiailzed at ds_addr + public fun is_data_source_initialized(ds_addr: address): bool { + exists>(ds_addr) + } + + /// Update Oracle's record with new value, the `sender` must have UpdateCapability + public fun update(sender: &signer, value: ValueT) acquires UpdateCapability, DataSource, OracleFeed{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(account); + update_with_cap(cap,value); + } + + /// Update Oracle's record with new value and UpdateCapability + public fun update_with_cap(cap: &mut UpdateCapability, value: ValueT) acquires DataSource,OracleFeed { + let account = cap.account; + assert!(exists>(account), Errors::requires_capability(ERR_NO_DATA_SOURCE)); + let source = borrow_global_mut>(account); + let now = Timestamp::now_milliseconds(); + let oracle_feed = borrow_global_mut>(account); + oracle_feed.record.version = source.counter; + oracle_feed.record.value = value; + oracle_feed.record.updated_at = now; + source.counter = source.counter + 1; + Event::emit_event(&mut source.update_events,OracleUpdateEvent{ + source_id: source.id, + record: *&oracle_feed.record + }); + } + + /// Read the Oracle's value from `ds_addr` + public fun read(ds_addr: address): ValueT acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record.value + } + + /// Read the Oracle's DataRecord from `ds_addr` + public fun read_record(ds_addr: address): DataRecord acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record + } + + /// Batch read Oracle's DataRecord from `ds_addrs` + public fun read_records(ds_addrs: &vector
): vector> acquires OracleFeed{ + let len = Vector::length(ds_addrs); + let results = Vector::empty(); + let i = 0; + while (i < len){ + let addr = *Vector::borrow(ds_addrs, i); + let record = Self::read_record(addr); + Vector::push_back(&mut results, record); + i = i + 1; + }; + results + } + + /// Remove UpdateCapability from current sender. + public fun remove_update_capability(sender: &signer):UpdateCapability acquires UpdateCapability{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(account) + } + + /// Add UpdateCapability to current sender + public fun add_update_capability(sender: &signer, update_cap: UpdateCapability){ + assert!(Signer::address_of(sender) == update_cap.account, Errors::invalid_argument(ERR_CAPABILITY_ACCOUNT_MISS_MATCH)); + move_to(sender, update_cap); + } + + /// Unpack Record to fields: version, oracle, updated_at. + public fun unpack_record(record: DataRecord):(u64, ValueT, u64) { + (record.version,*&record.value,record.updated_at) + } +} +module PriceOracle{ + use StarcoinFramework::Math; + use StarcoinFramework::Oracle::{Self, DataRecord, UpdateCapability}; + + struct PriceOracleInfo has copy,store,drop{ + scaling_factor: u128, + } + + public entry fun register_oracle_entry(sender: signer, precision: u8){ + register_oracle(&sender, precision); + } + + public fun register_oracle(sender: &signer, precision: u8){ + let scaling_factor = Math::pow(10, (precision as u64)); + Oracle::register_oracle(sender, PriceOracleInfo{ + scaling_factor, + }); + } + + + public entry fun init_data_source_entry(sender: signer, init_value: u128){ + init_data_source(&sender, init_value); + } + + public fun init_data_source(sender: &signer, init_value: u128){ + Oracle::init_data_source(sender, init_value); + } + + public fun is_data_source_initialized(ds_addr: address): bool{ + Oracle::is_data_source_initialized(ds_addr) + } + + public fun get_scaling_factor() : u128 { + let info = Oracle::get_oracle_info(); + info.scaling_factor + } + + public entry fun update_entry(sender: signer, value: u128){ + update(&sender, value); + } + + public fun update(sender: &signer, value: u128){ + Oracle::update(sender, value); + } + + public fun update_with_cap(cap: &mut UpdateCapability, value: u128) { + Oracle::update_with_cap(cap, value); + } + + public fun read(addr: address) : u128{ + Oracle::read(addr) + } + + public fun read_record(addr: address): DataRecord{ + Oracle::read_record(addr) + } + + public fun read_records(addrs: &vector
): vector>{ + Oracle::read_records(addrs) + } + +} + +module STCUSDOracle{ + use StarcoinFramework::Oracle::{DataRecord}; + use StarcoinFramework::PriceOracle::{Self}; + + /// The STC to USD price oracle + struct STCUSD has copy,store,drop {} + + public fun register(sender: &signer){ + PriceOracle::register_oracle(sender, 6); + } + + public fun read(ds_addr: address) : u128{ + PriceOracle::read(ds_addr) + } + + public fun read_record(ds_addr: address): DataRecord{ + PriceOracle::read_record(ds_addr) + } + + public fun read_records(ds_addrs: &vector
): vector>{ + PriceOracle::read_records(ds_addrs) + } +} + +module PriceOracleScripts{ + use StarcoinFramework::PriceOracle; + + public entry fun register_oracle(sender: signer, precision: u8){ + PriceOracle::register_oracle_entry(sender, precision); + } + + public entry fun init_data_source(sender: signer, init_value: u128){ + PriceOracle::init_data_source_entry(sender, init_value); + } + + public entry fun update(sender: signer, value: u128){ + PriceOracle::update_entry(sender, value); + } +} + +module PriceOracleAggregator{ + use StarcoinFramework::Vector; + use StarcoinFramework::Oracle; + use StarcoinFramework::PriceOracle; + use StarcoinFramework::Math; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Errors; + + /// No price data match requirement condition. + const ERR_NO_PRICE_DATA_AVIABLE:u64 = 101; + + /// Get latest price from datasources and calculate avg. + /// `addrs`: the datasource's addr, `updated_in`: the datasource should updated in x millseoconds. + public fun latest_price_average_aggregator(addrs: &vector
, updated_in: u64): u128 { + let len = Vector::length(addrs); + let price_records = PriceOracle::read_records(addrs); + let prices = Vector::empty(); + let i = 0; + let expect_updated_after = Timestamp::now_milliseconds() - updated_in; + while (i < len){ + let record = Vector::pop_back(&mut price_records); + let (_version, price, updated_at) = Oracle::unpack_record(record); + if (updated_at >= expect_updated_after) { + Vector::push_back(&mut prices, price); + }; + i = i + 1; + }; + // if all price data not match the update_in filter, abort. + assert!(!Vector::is_empty(&prices), Errors::invalid_state(ERR_NO_PRICE_DATA_AVIABLE)); + Math::avg(&prices) + } +} + + +} \ No newline at end of file diff --git a/release/v13/sources/PackageTxnManager.move b/release/v13/sources/PackageTxnManager.move new file mode 100644 index 00000000..0fb41ee4 --- /dev/null +++ b/release/v13/sources/PackageTxnManager.move @@ -0,0 +1,469 @@ +address StarcoinFramework { + /// The module provides strategies for module upgrading. + module PackageTxnManager { + use StarcoinFramework::Option::{Self,Option}; + use StarcoinFramework::Signer; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Version; + use StarcoinFramework::Event; + use StarcoinFramework::Config; + use StarcoinFramework::Timestamp; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + /// module upgrade plan + struct UpgradePlan has copy, drop, store { + package_hash: vector, + active_after_time: u64, + version: u64, + } + + /// The holder of UpgradePlanCapability for account_address can submit UpgradePlan for account_address. + struct UpgradePlanCapability has key, store { + account_address: address, + } + + const STRATEGY_ARBITRARY: u8 = 0; + const STRATEGY_TWO_PHASE: u8 = 1; + const STRATEGY_NEW_MODULE: u8 = 2; + const STRATEGY_FREEZE: u8 = 3; + const DEFAULT_MIN_TIME_LIMIT: u64 = 86400000;// one day + + /// arbitary stragegy + public fun get_strategy_arbitrary(): u8 { STRATEGY_ARBITRARY } + /// two phase stragegy + public fun get_strategy_two_phase(): u8 { STRATEGY_TWO_PHASE } + /// new module strategy + public fun get_strategy_new_module(): u8 { STRATEGY_NEW_MODULE } + /// freezed strategy + public fun get_strategy_freeze(): u8 { STRATEGY_FREEZE } + /// default min time limit + public fun get_default_min_time_limit(): u64 { DEFAULT_MIN_TIME_LIMIT } + + const EUPGRADE_PLAN_IS_NONE: u64 = 102; + const EPACKAGE_HASH_INCORRECT: u64 = 103; + const EACTIVE_TIME_INCORRECT: u64 = 104; + const ESTRATEGY_FREEZED: u64 = 105; + const ESTRATEGY_INCORRECT: u64 = 106; + const ESTRATEGY_NOT_TWO_PHASE: u64 = 107; + const EUNKNOWN_STRATEGY: u64 = 108; + const ESENDER_AND_PACKAGE_ADDRESS_MISMATCH: u64 = 109; + + struct UpgradePlanV2 has copy, drop, store { + package_hash: vector, + active_after_time: u64, + version: u64, + enforced: bool, + } + + /// module upgrade strategy + struct ModuleUpgradeStrategy has key, store { + /// 0 arbitrary + /// 1 two phase upgrade + /// 2 only new module + /// 3 freeze + strategy: u8, + } + + /// data of two phase upgrade strategy. + struct TwoPhaseUpgrade has key { + config: TwoPhaseUpgradeConfig, + plan: Option, + version_cap: Config::ModifyConfigCapability, + upgrade_event: Event::EventHandle, + } + + /// config of two phase upgrade strategy. + struct TwoPhaseUpgradeConfig has copy, drop, store { + min_time_limit: u64, + } + + /// data of two phase upgrade strategy. + struct TwoPhaseUpgradeV2 has key { + config: TwoPhaseUpgradeConfig, + plan: Option, + version_cap: Config::ModifyConfigCapability, + upgrade_event: Event::EventHandle, + } + + /// module upgrade event. + struct UpgradeEvent has drop, store { + package_address: address, + package_hash: vector, + version: u64, + } + + /// Update account's ModuleUpgradeStrategy + public fun update_module_upgrade_strategy(account: &signer, strategy: u8, min_time: Option) acquires ModuleUpgradeStrategy, TwoPhaseUpgrade, TwoPhaseUpgradeV2, UpgradePlanCapability{ + assert!(strategy == STRATEGY_ARBITRARY || strategy == STRATEGY_TWO_PHASE || strategy == STRATEGY_NEW_MODULE || strategy == STRATEGY_FREEZE, Errors::invalid_argument(EUNKNOWN_STRATEGY)); + let account_address = Signer::address_of(account); + let previous_strategy = get_module_upgrade_strategy(account_address); + assert!(strategy > previous_strategy, Errors::invalid_argument(ESTRATEGY_INCORRECT)); + if (exists(account_address)) { + borrow_global_mut(account_address).strategy = strategy; + }else{ + move_to(account, ModuleUpgradeStrategy{ strategy: strategy}); + }; + if (strategy == STRATEGY_TWO_PHASE){ + let version_cap = Config::extract_modify_config_capability(account); + let min_time_limit = Option::get_with_default(&min_time, DEFAULT_MIN_TIME_LIMIT); + move_to(account, UpgradePlanCapability{ account_address: account_address}); + move_to(account, TwoPhaseUpgradeV2{ + config: TwoPhaseUpgradeConfig{min_time_limit: min_time_limit}, + plan: Option::none(), + version_cap: version_cap, + upgrade_event: Event::new_event_handle(account)} + ); + }; + //clean two phase upgrade resource + if (previous_strategy == STRATEGY_TWO_PHASE){ + if (exists(account_address)) { + let tpu = move_from(account_address); + let TwoPhaseUpgrade{plan:_, version_cap, upgrade_event, config: _} = tpu; + Event::destroy_handle(upgrade_event); + Config::destroy_modify_config_capability(version_cap); + }; + if (exists(account_address)) { + let tpu = move_from(account_address); + let TwoPhaseUpgradeV2{plan:_, version_cap, upgrade_event, config: _} = tpu; + Event::destroy_handle(upgrade_event); + Config::destroy_modify_config_capability(version_cap); + }; + // UpgradePlanCapability may be extracted + if (exists(account_address)) { + let cap = move_from(account_address); + destroy_upgrade_plan_cap(cap); + }; + }; + } + + spec update_module_upgrade_strategy { + pragma verify = false; + aborts_if strategy != 0 && strategy != 1 && strategy != 2 && strategy != 3; + aborts_if exists(Signer::address_of(account)) && strategy <= global(Signer::address_of(account)).strategy; + aborts_if !exists(Signer::address_of(account)) && strategy == 0; + + aborts_if strategy == 1 && exists(Signer::address_of(account)); + aborts_if strategy == 1 && !exists>(Signer::address_of(account)); + let holder = global>(Signer::address_of(account)); + aborts_if strategy == 1 && Option::is_none>(holder.cap); + aborts_if strategy == 1 && exists(Signer::address_of(account)); + + aborts_if exists(Signer::address_of(account)) && global(Signer::address_of(account)).strategy == 1 + && !exists(Signer::address_of(account)); + } + + /// Get account address of UpgradePlanCapability + public fun account_address(cap: &UpgradePlanCapability): address { + cap.account_address + } + + /// destroy the given UpgradePlanCapability + public fun destroy_upgrade_plan_cap(cap: UpgradePlanCapability){ + let UpgradePlanCapability{account_address:_} = cap; + } + + spec destroy_upgrade_plan_cap { + aborts_if false; + } + + /// extract out UpgradePlanCapability from `signer`. + public fun extract_submit_upgrade_plan_cap(account: &signer): UpgradePlanCapability acquires ModuleUpgradeStrategy, UpgradePlanCapability{ + let account_address = Signer::address_of(account); + assert!(get_module_upgrade_strategy(account_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE)); + move_from(account_address) + } + + spec extract_submit_upgrade_plan_cap { + aborts_if !exists(Signer::address_of(account)); + aborts_if global(Signer::address_of(account)).strategy != 1; + aborts_if !exists(Signer::address_of(account)); + } + + public entry fun convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2(account: signer, package_address: address) acquires TwoPhaseUpgrade { + let account_address = Signer::address_of(&account); + // sender should be package owner + assert!(account_address == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH)); + let tpu = move_from(account_address); + let TwoPhaseUpgrade{config, plan, version_cap, upgrade_event} = tpu; + if (Option::is_some(&plan)) { + let old_plan = Option::borrow(&plan); + move_to(&account, TwoPhaseUpgradeV2{ + config: config, + plan: Option::some(UpgradePlanV2 { + package_hash: *&old_plan.package_hash, + active_after_time: old_plan.active_after_time, + version: old_plan.version, + enforced: false }), + version_cap: version_cap, + upgrade_event: upgrade_event + }); + } else { + move_to(&account, TwoPhaseUpgradeV2{ + config: config, + plan: Option::none(), + version_cap: version_cap, + upgrade_event: upgrade_event + }); + }; + } + + spec convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2 { + pragma verify = false; + } + + public fun submit_upgrade_plan_v2(account: &signer, package_hash: vector, version:u64, enforced: bool) acquires TwoPhaseUpgradeV2,UpgradePlanCapability,ModuleUpgradeStrategy{ + let account_address = Signer::address_of(account); + let cap = borrow_global(account_address); + submit_upgrade_plan_with_cap_v2(cap, package_hash, version, enforced); + } + + spec submit_upgrade_plan_v2 { + pragma verify = false; + aborts_if !exists(Signer::address_of(account)); + include SubmitUpgradePlanWithCapAbortsIf{account: global(Signer::address_of(account)).account_address}; + ensures Option::is_some(global(global(Signer::address_of(account)).account_address).plan); + } + public fun submit_upgrade_plan_with_cap_v2(cap: &UpgradePlanCapability, package_hash: vector, version: u64, enforced: bool) acquires TwoPhaseUpgradeV2,ModuleUpgradeStrategy{ + let package_address = cap.account_address; + assert!(get_module_upgrade_strategy(package_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE)); + let tpu = borrow_global_mut(package_address); + let active_after_time = Timestamp::now_milliseconds() + tpu.config.min_time_limit; + tpu.plan = Option::some(UpgradePlanV2 { package_hash, active_after_time, version, enforced }); + } + spec submit_upgrade_plan_with_cap_v2 { + pragma verify = false; + include SubmitUpgradePlanWithCapAbortsIf{account: cap.account_address}; + ensures Option::is_some(global(cap.account_address).plan); + } + + spec schema SubmitUpgradePlanWithCapAbortsIf { + account: address; + aborts_if !exists(account); + aborts_if global(account).strategy != 1; + aborts_if !exists(account); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if Timestamp::now_milliseconds() + global(account).config.min_time_limit > max_u64(); + } + + /// Cancel a module upgrade plan. + public fun cancel_upgrade_plan(account: &signer) acquires TwoPhaseUpgradeV2,UpgradePlanCapability,ModuleUpgradeStrategy{ + let account_address = Signer::address_of(account); + let cap = borrow_global(account_address); + cancel_upgrade_plan_with_cap(cap); + } + + spec cancel_upgrade_plan { + aborts_if !exists(Signer::address_of(account)); + include CancelUpgradePlanWithCapAbortsIf{account: global(Signer::address_of(account)).account_address}; + ensures Option::is_none(global(global(Signer::address_of(account)).account_address).plan); + } + + /// Cancel a module upgrade plan with given cap. + public fun cancel_upgrade_plan_with_cap(cap: &UpgradePlanCapability) acquires TwoPhaseUpgradeV2,ModuleUpgradeStrategy{ + let package_address = cap.account_address; + assert!(get_module_upgrade_strategy(package_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE)); + let tpu = borrow_global_mut(package_address); + assert!(Option::is_some(&tpu.plan), Errors::invalid_state(EUPGRADE_PLAN_IS_NONE)); + tpu.plan = Option::none(); + } + + spec cancel_upgrade_plan_with_cap { + include CancelUpgradePlanWithCapAbortsIf{account: cap.account_address}; + ensures Option::is_none(global(cap.account_address).plan); + } + + spec schema CancelUpgradePlanWithCapAbortsIf { + account: address; + aborts_if !exists(account); + aborts_if global(account).strategy != 1; + aborts_if !exists(account); + aborts_if !Option::is_some(global(account).plan); + } + + /// Get module upgrade strategy of an module address. + public fun get_module_upgrade_strategy(module_address: address): u8 acquires ModuleUpgradeStrategy { + if (exists(module_address)) { + borrow_global(module_address).strategy + }else{ + 0 + } + } + + spec get_module_upgrade_strategy { + aborts_if false; + } + + spec fun spec_get_module_upgrade_strategy(module_address: address): u8 { + if (exists(module_address)) { + global(module_address).strategy + }else{ + 0 + } + } + + /// Get module upgrade plan of an address. + public fun get_upgrade_plan(_module_address: address): Option { + // DEPRECATED_CODE + Option::none() + } + + spec get_upgrade_plan { + aborts_if false; + } + + /// Get module upgrade plan of an address. + public fun get_upgrade_plan_v2(module_address: address): Option acquires TwoPhaseUpgradeV2 { + if (exists(module_address)) { + *&borrow_global(module_address).plan + } else { + Option::none() + } + } + + spec get_upgrade_plan_v2 { + pragma verify = false; + aborts_if false; + } + spec fun spec_get_upgrade_plan_v2(module_address: address): Option { + if (exists(module_address)) { + global(module_address).plan + }else{ + Option::spec_none() + } + } + + /// Check againest on the given package data. + public fun check_package_txn(package_address: address, package_hash: vector) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy{ + let strategy = get_module_upgrade_strategy(package_address); + if (strategy == STRATEGY_ARBITRARY){ + //do nothing + }else if(strategy == STRATEGY_TWO_PHASE){ + let plan_opt = get_upgrade_plan_v2(package_address); + assert!(Option::is_some(&plan_opt), Errors::invalid_argument(EUPGRADE_PLAN_IS_NONE)); + let plan = Option::borrow(&plan_opt); + assert!(*&plan.package_hash == package_hash, Errors::invalid_argument(EPACKAGE_HASH_INCORRECT)); + assert!(plan.active_after_time <= Timestamp::now_milliseconds(), Errors::invalid_argument(EACTIVE_TIME_INCORRECT)); + }else if(strategy == STRATEGY_NEW_MODULE){ + //do check at VM runtime. + }else if(strategy == STRATEGY_FREEZE){ + Errors::invalid_argument(ESTRATEGY_FREEZED); + }; + } + + spec check_package_txn { + pragma verify = false; + include CheckPackageTxnAbortsIf; + } + + public fun check_package_txn_v2(txn_sender: address, package_address: address, package_hash: vector) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy{ + let strategy = get_module_upgrade_strategy(package_address); + if (strategy == STRATEGY_ARBITRARY){ + assert!(txn_sender == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH)); + }else if(strategy == STRATEGY_TWO_PHASE){ + let plan_opt = get_upgrade_plan_v2(package_address); + assert!(Option::is_some(&plan_opt), Errors::invalid_argument(EUPGRADE_PLAN_IS_NONE)); + let plan = Option::borrow(&plan_opt); + assert!(*&plan.package_hash == package_hash, Errors::invalid_argument(EPACKAGE_HASH_INCORRECT)); + assert!(plan.active_after_time <= Timestamp::now_milliseconds(), Errors::invalid_argument(EACTIVE_TIME_INCORRECT)); + }else if(strategy == STRATEGY_NEW_MODULE){ + //do check at VM runtime. + assert!(txn_sender == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH)); + }else if(strategy == STRATEGY_FREEZE){ + Errors::invalid_argument(ESTRATEGY_FREEZED); + }; + } + + spec schema CheckPackageTxnAbortsIf { + package_address: address; + package_hash: vector; + aborts_if spec_get_module_upgrade_strategy(package_address) == 3; + aborts_if spec_get_module_upgrade_strategy(package_address) == 1 && Option::is_none(spec_get_upgrade_plan_v2(package_address)); + aborts_if spec_get_module_upgrade_strategy(package_address) == 1 && Option::borrow(spec_get_upgrade_plan_v2(package_address)).package_hash != package_hash; + aborts_if spec_get_module_upgrade_strategy(package_address) == 1 && !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if spec_get_module_upgrade_strategy(package_address) == 1 && Option::borrow(spec_get_upgrade_plan_v2(package_address)).active_after_time > Timestamp::now_milliseconds(); + } + + spec schema CheckPackageTxnAbortsIfWithType { + is_package: bool; + sender: address; + package_address: address; + package_hash: vector; + aborts_if is_package && spec_get_module_upgrade_strategy(package_address) == 3; + aborts_if is_package && spec_get_module_upgrade_strategy(package_address) == 1 && Option::is_none(spec_get_upgrade_plan_v2(package_address)); + aborts_if is_package && spec_get_module_upgrade_strategy(package_address) == 1 && Option::borrow(spec_get_upgrade_plan_v2(package_address)).package_hash != package_hash; + aborts_if is_package && spec_get_module_upgrade_strategy(package_address) == 1 && !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if is_package && spec_get_module_upgrade_strategy(package_address) == 1 && Option::borrow(spec_get_upgrade_plan_v2(package_address)).active_after_time > Timestamp::now_milliseconds(); + } + + fun finish_upgrade_plan(package_address: address) acquires TwoPhaseUpgradeV2 { + let tpu = borrow_global_mut(package_address); + if (Option::is_some(&tpu.plan)) { + let plan = Option::borrow(&tpu.plan); + Config::set_with_capability(&mut tpu.version_cap, Version::new_version(plan.version)); + Event::emit_event(&mut tpu.upgrade_event, UpgradeEvent { + package_address: package_address, + package_hash: *&plan.package_hash, + version: plan.version}); + }; + tpu.plan = Option::none(); + } + + spec finish_upgrade_plan { + pragma verify = false; + aborts_if !exists(package_address); + let tpu = global(package_address); + aborts_if Option::is_some(tpu.plan) && !exists>(tpu.version_cap.account_address); + } + + /// Prologue of package transaction. + public fun package_txn_prologue(account: &signer, package_address: address, package_hash: vector) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy { + // Can only be invoked by genesis account + CoreAddresses::assert_genesis_address(account); + check_package_txn(package_address, package_hash); + } + + spec package_txn_prologue { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + include CheckPackageTxnAbortsIf{}; + } + + public fun package_txn_prologue_v2(account: &signer, txn_sender: address, package_address: address, package_hash: vector) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy { + // Can only be invoked by genesis account + CoreAddresses::assert_genesis_address(account); + check_package_txn_v2(txn_sender, package_address, package_hash); + } + + /// Package txn finished, and clean UpgradePlan + public fun package_txn_epilogue(account: &signer, _txn_sender: address, package_address: address, success: bool) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy { + // Can only be invoked by genesis account + CoreAddresses::assert_genesis_address(account); + let strategy = get_module_upgrade_strategy(package_address); + if(strategy == STRATEGY_TWO_PHASE){ + if (success) { + finish_upgrade_plan(package_address); + }; + }; + } + + spec schema AbortsIfPackageTxnEpilogue { + is_package: bool; + package_address: address; + success: bool; + aborts_if is_package && get_module_upgrade_strategy(package_address) == STRATEGY_TWO_PHASE && success && !exists(package_address); + } + + spec package_txn_epilogue { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if spec_get_module_upgrade_strategy(package_address) == 1 + && success && !exists(package_address); + aborts_if spec_get_module_upgrade_strategy(package_address) == 1 + && success && Option::is_some(global(package_address).plan) + && !exists>(global(package_address).version_cap.account_address); + } + + } +} \ No newline at end of file diff --git a/release/v13/sources/PriceOracle.move b/release/v13/sources/PriceOracle.move new file mode 100644 index 00000000..9241b263 --- /dev/null +++ b/release/v13/sources/PriceOracle.move @@ -0,0 +1,325 @@ +address StarcoinFramework { +module Oracle { + use StarcoinFramework::Event; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::Vector; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Account; + use StarcoinFramework::GenesisSignerCapability; + + struct OracleInfo has key { + ///The datasource counter + counter: u64, + ///Ext info + info: Info, + } + + struct DataRecord has copy, store, drop { + ///The data version + version: u64, + ///The record value + value: ValueT, + ///Update timestamp millisecond + updated_at: u64, + } + + struct OracleFeed has key { + record: DataRecord, + } + + struct OracleUpdateEvent has copy,store,drop { + source_id: u64, + record: DataRecord, + } + + struct DataSource has key { + /// the id of data source of ValueT + id: u64, + /// the data version counter. + counter: u64, + update_events: Event::EventHandle>, + } + + struct UpdateCapability has store, key { + account: address, + } + + struct GenesisSignerCapability has key{ + cap: Account::SignerCapability, + } + + /// The oracle type not register. + const ERR_ORACLE_TYPE_NOT_REGISTER:u64 = 101; + /// No capability to update the oracle value. + const ERR_NO_UPDATE_CAPABILITY: u64 = 102; + const ERR_NO_DATA_SOURCE: u64 = 103; + const ERR_CAPABILITY_ACCOUNT_MISS_MATCH: u64 = 104; + + /// deprecated. + public fun initialize(_sender: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability{ + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register `OracleT` as an oracle type. + public fun register_oracle(_sender: &signer, info: Info) { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + move_to(&genesis_account, OracleInfo { + counter: 0, + info, + }); + } + + /// Get the `OracleT` oracle's counter, the counter represent how many `OracleT` datasources + public fun get_oracle_counter() : u64 acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + oracle_info.counter + } + + public fun get_oracle_info() : Info acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&oracle_info.info + } + + /// Init a data source for type `OracleT` + public fun init_data_source(sender: &signer, init_value: ValueT) acquires OracleInfo{ + assert!(exists>(CoreAddresses::GENESIS_ADDRESS()), Errors::not_published(ERR_ORACLE_TYPE_NOT_REGISTER)); + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + let now = Timestamp::now_milliseconds(); + move_to(sender, OracleFeed { + record: DataRecord { + version: 0, + value: init_value, + updated_at: now, + } + }); + let sender_addr = Signer::address_of(sender); + move_to(sender, DataSource { + id: oracle_info.counter, + counter: 1, + update_events: Event::new_event_handle>(sender), + }); + move_to(sender, UpdateCapability{account: sender_addr}); + oracle_info.counter = oracle_info.counter + 1; + } + + /// Check the DataSource is initiailzed at ds_addr + public fun is_data_source_initialized(ds_addr: address): bool { + exists>(ds_addr) + } + + /// Update Oracle's record with new value, the `sender` must have UpdateCapability + public fun update(sender: &signer, value: ValueT) acquires UpdateCapability, DataSource, OracleFeed{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(account); + update_with_cap(cap,value); + } + + /// Update Oracle's record with new value and UpdateCapability + public fun update_with_cap(cap: &mut UpdateCapability, value: ValueT) acquires DataSource,OracleFeed { + let account = cap.account; + assert!(exists>(account), Errors::requires_capability(ERR_NO_DATA_SOURCE)); + let source = borrow_global_mut>(account); + let now = Timestamp::now_milliseconds(); + let oracle_feed = borrow_global_mut>(account); + oracle_feed.record.version = source.counter; + oracle_feed.record.value = value; + oracle_feed.record.updated_at = now; + source.counter = source.counter + 1; + Event::emit_event(&mut source.update_events,OracleUpdateEvent{ + source_id: source.id, + record: *&oracle_feed.record + }); + } + + /// Read the Oracle's value from `ds_addr` + public fun read(ds_addr: address): ValueT acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record.value + } + + /// Read the Oracle's DataRecord from `ds_addr` + public fun read_record(ds_addr: address): DataRecord acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record + } + + /// Batch read Oracle's DataRecord from `ds_addrs` + public fun read_records(ds_addrs: &vector
): vector> acquires OracleFeed{ + let len = Vector::length(ds_addrs); + let results = Vector::empty(); + let i = 0; + while (i < len){ + let addr = *Vector::borrow(ds_addrs, i); + let record = Self::read_record(addr); + Vector::push_back(&mut results, record); + i = i + 1; + }; + results + } + + /// Remove UpdateCapability from current sender. + public fun remove_update_capability(sender: &signer):UpdateCapability acquires UpdateCapability{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(account) + } + + /// Add UpdateCapability to current sender + public fun add_update_capability(sender: &signer, update_cap: UpdateCapability){ + assert!(Signer::address_of(sender) == update_cap.account, Errors::invalid_argument(ERR_CAPABILITY_ACCOUNT_MISS_MATCH)); + move_to(sender, update_cap); + } + + /// Unpack Record to fields: version, oracle, updated_at. + public fun unpack_record(record: DataRecord):(u64, ValueT, u64) { + (record.version,*&record.value,record.updated_at) + } +} +module PriceOracle{ + use StarcoinFramework::Math; + use StarcoinFramework::Oracle::{Self, DataRecord, UpdateCapability}; + + struct PriceOracleInfo has copy,store,drop{ + scaling_factor: u128, + } + + public entry fun register_oracle_entry(sender: signer, precision: u8){ + register_oracle(&sender, precision); + } + + public fun register_oracle(sender: &signer, precision: u8){ + let scaling_factor = Math::pow(10, (precision as u64)); + Oracle::register_oracle(sender, PriceOracleInfo{ + scaling_factor, + }); + } + + + public entry fun init_data_source_entry(sender: signer, init_value: u128){ + init_data_source(&sender, init_value); + } + + public fun init_data_source(sender: &signer, init_value: u128){ + Oracle::init_data_source(sender, init_value); + } + + public fun is_data_source_initialized(ds_addr: address): bool{ + Oracle::is_data_source_initialized(ds_addr) + } + + public fun get_scaling_factor() : u128 { + let info = Oracle::get_oracle_info(); + info.scaling_factor + } + + public entry fun update_entry(sender: signer, value: u128){ + update(&sender, value); + } + + public fun update(sender: &signer, value: u128){ + Oracle::update(sender, value); + } + + public fun update_with_cap(cap: &mut UpdateCapability, value: u128) { + Oracle::update_with_cap(cap, value); + } + + public fun read(addr: address) : u128{ + Oracle::read(addr) + } + + public fun read_record(addr: address): DataRecord{ + Oracle::read_record(addr) + } + + public fun read_records(addrs: &vector
): vector>{ + Oracle::read_records(addrs) + } + +} + +module STCUSDOracle{ + use StarcoinFramework::Oracle::{DataRecord}; + use StarcoinFramework::PriceOracle::{Self}; + + /// The STC to USD price oracle + struct STCUSD has copy,store,drop {} + + public fun register(sender: &signer){ + PriceOracle::register_oracle(sender, 6); + } + + public fun read(ds_addr: address) : u128{ + PriceOracle::read(ds_addr) + } + + public fun read_record(ds_addr: address): DataRecord{ + PriceOracle::read_record(ds_addr) + } + + public fun read_records(ds_addrs: &vector
): vector>{ + PriceOracle::read_records(ds_addrs) + } +} + +module PriceOracleScripts{ + use StarcoinFramework::PriceOracle; + + public entry fun register_oracle(sender: signer, precision: u8){ + PriceOracle::register_oracle_entry(sender, precision); + } + + public entry fun init_data_source(sender: signer, init_value: u128){ + PriceOracle::init_data_source_entry(sender, init_value); + } + + public entry fun update(sender: signer, value: u128){ + PriceOracle::update_entry(sender, value); + } +} + +module PriceOracleAggregator{ + use StarcoinFramework::Vector; + use StarcoinFramework::Oracle; + use StarcoinFramework::PriceOracle; + use StarcoinFramework::Math; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Errors; + + /// No price data match requirement condition. + const ERR_NO_PRICE_DATA_AVIABLE:u64 = 101; + + /// Get latest price from datasources and calculate avg. + /// `addrs`: the datasource's addr, `updated_in`: the datasource should updated in x millseoconds. + public fun latest_price_average_aggregator(addrs: &vector
, updated_in: u64): u128 { + let len = Vector::length(addrs); + let price_records = PriceOracle::read_records(addrs); + let prices = Vector::empty(); + let i = 0; + let expect_updated_after = Timestamp::now_milliseconds() - updated_in; + while (i < len){ + let record = Vector::pop_back(&mut price_records); + let (_version, price, updated_at) = Oracle::unpack_record(record); + if (updated_at >= expect_updated_after) { + Vector::push_back(&mut prices, price); + }; + i = i + 1; + }; + // if all price data not match the update_in filter, abort. + assert!(!Vector::is_empty(&prices), Errors::invalid_state(ERR_NO_PRICE_DATA_AVIABLE)); + Math::avg(&prices) + } +} + + +} \ No newline at end of file diff --git a/release/v13/sources/PriceOracleAggregator.move b/release/v13/sources/PriceOracleAggregator.move new file mode 100644 index 00000000..9241b263 --- /dev/null +++ b/release/v13/sources/PriceOracleAggregator.move @@ -0,0 +1,325 @@ +address StarcoinFramework { +module Oracle { + use StarcoinFramework::Event; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::Vector; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Account; + use StarcoinFramework::GenesisSignerCapability; + + struct OracleInfo has key { + ///The datasource counter + counter: u64, + ///Ext info + info: Info, + } + + struct DataRecord has copy, store, drop { + ///The data version + version: u64, + ///The record value + value: ValueT, + ///Update timestamp millisecond + updated_at: u64, + } + + struct OracleFeed has key { + record: DataRecord, + } + + struct OracleUpdateEvent has copy,store,drop { + source_id: u64, + record: DataRecord, + } + + struct DataSource has key { + /// the id of data source of ValueT + id: u64, + /// the data version counter. + counter: u64, + update_events: Event::EventHandle>, + } + + struct UpdateCapability has store, key { + account: address, + } + + struct GenesisSignerCapability has key{ + cap: Account::SignerCapability, + } + + /// The oracle type not register. + const ERR_ORACLE_TYPE_NOT_REGISTER:u64 = 101; + /// No capability to update the oracle value. + const ERR_NO_UPDATE_CAPABILITY: u64 = 102; + const ERR_NO_DATA_SOURCE: u64 = 103; + const ERR_CAPABILITY_ACCOUNT_MISS_MATCH: u64 = 104; + + /// deprecated. + public fun initialize(_sender: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability{ + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register `OracleT` as an oracle type. + public fun register_oracle(_sender: &signer, info: Info) { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + move_to(&genesis_account, OracleInfo { + counter: 0, + info, + }); + } + + /// Get the `OracleT` oracle's counter, the counter represent how many `OracleT` datasources + public fun get_oracle_counter() : u64 acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + oracle_info.counter + } + + public fun get_oracle_info() : Info acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&oracle_info.info + } + + /// Init a data source for type `OracleT` + public fun init_data_source(sender: &signer, init_value: ValueT) acquires OracleInfo{ + assert!(exists>(CoreAddresses::GENESIS_ADDRESS()), Errors::not_published(ERR_ORACLE_TYPE_NOT_REGISTER)); + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + let now = Timestamp::now_milliseconds(); + move_to(sender, OracleFeed { + record: DataRecord { + version: 0, + value: init_value, + updated_at: now, + } + }); + let sender_addr = Signer::address_of(sender); + move_to(sender, DataSource { + id: oracle_info.counter, + counter: 1, + update_events: Event::new_event_handle>(sender), + }); + move_to(sender, UpdateCapability{account: sender_addr}); + oracle_info.counter = oracle_info.counter + 1; + } + + /// Check the DataSource is initiailzed at ds_addr + public fun is_data_source_initialized(ds_addr: address): bool { + exists>(ds_addr) + } + + /// Update Oracle's record with new value, the `sender` must have UpdateCapability + public fun update(sender: &signer, value: ValueT) acquires UpdateCapability, DataSource, OracleFeed{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(account); + update_with_cap(cap,value); + } + + /// Update Oracle's record with new value and UpdateCapability + public fun update_with_cap(cap: &mut UpdateCapability, value: ValueT) acquires DataSource,OracleFeed { + let account = cap.account; + assert!(exists>(account), Errors::requires_capability(ERR_NO_DATA_SOURCE)); + let source = borrow_global_mut>(account); + let now = Timestamp::now_milliseconds(); + let oracle_feed = borrow_global_mut>(account); + oracle_feed.record.version = source.counter; + oracle_feed.record.value = value; + oracle_feed.record.updated_at = now; + source.counter = source.counter + 1; + Event::emit_event(&mut source.update_events,OracleUpdateEvent{ + source_id: source.id, + record: *&oracle_feed.record + }); + } + + /// Read the Oracle's value from `ds_addr` + public fun read(ds_addr: address): ValueT acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record.value + } + + /// Read the Oracle's DataRecord from `ds_addr` + public fun read_record(ds_addr: address): DataRecord acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record + } + + /// Batch read Oracle's DataRecord from `ds_addrs` + public fun read_records(ds_addrs: &vector
): vector> acquires OracleFeed{ + let len = Vector::length(ds_addrs); + let results = Vector::empty(); + let i = 0; + while (i < len){ + let addr = *Vector::borrow(ds_addrs, i); + let record = Self::read_record(addr); + Vector::push_back(&mut results, record); + i = i + 1; + }; + results + } + + /// Remove UpdateCapability from current sender. + public fun remove_update_capability(sender: &signer):UpdateCapability acquires UpdateCapability{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(account) + } + + /// Add UpdateCapability to current sender + public fun add_update_capability(sender: &signer, update_cap: UpdateCapability){ + assert!(Signer::address_of(sender) == update_cap.account, Errors::invalid_argument(ERR_CAPABILITY_ACCOUNT_MISS_MATCH)); + move_to(sender, update_cap); + } + + /// Unpack Record to fields: version, oracle, updated_at. + public fun unpack_record(record: DataRecord):(u64, ValueT, u64) { + (record.version,*&record.value,record.updated_at) + } +} +module PriceOracle{ + use StarcoinFramework::Math; + use StarcoinFramework::Oracle::{Self, DataRecord, UpdateCapability}; + + struct PriceOracleInfo has copy,store,drop{ + scaling_factor: u128, + } + + public entry fun register_oracle_entry(sender: signer, precision: u8){ + register_oracle(&sender, precision); + } + + public fun register_oracle(sender: &signer, precision: u8){ + let scaling_factor = Math::pow(10, (precision as u64)); + Oracle::register_oracle(sender, PriceOracleInfo{ + scaling_factor, + }); + } + + + public entry fun init_data_source_entry(sender: signer, init_value: u128){ + init_data_source(&sender, init_value); + } + + public fun init_data_source(sender: &signer, init_value: u128){ + Oracle::init_data_source(sender, init_value); + } + + public fun is_data_source_initialized(ds_addr: address): bool{ + Oracle::is_data_source_initialized(ds_addr) + } + + public fun get_scaling_factor() : u128 { + let info = Oracle::get_oracle_info(); + info.scaling_factor + } + + public entry fun update_entry(sender: signer, value: u128){ + update(&sender, value); + } + + public fun update(sender: &signer, value: u128){ + Oracle::update(sender, value); + } + + public fun update_with_cap(cap: &mut UpdateCapability, value: u128) { + Oracle::update_with_cap(cap, value); + } + + public fun read(addr: address) : u128{ + Oracle::read(addr) + } + + public fun read_record(addr: address): DataRecord{ + Oracle::read_record(addr) + } + + public fun read_records(addrs: &vector
): vector>{ + Oracle::read_records(addrs) + } + +} + +module STCUSDOracle{ + use StarcoinFramework::Oracle::{DataRecord}; + use StarcoinFramework::PriceOracle::{Self}; + + /// The STC to USD price oracle + struct STCUSD has copy,store,drop {} + + public fun register(sender: &signer){ + PriceOracle::register_oracle(sender, 6); + } + + public fun read(ds_addr: address) : u128{ + PriceOracle::read(ds_addr) + } + + public fun read_record(ds_addr: address): DataRecord{ + PriceOracle::read_record(ds_addr) + } + + public fun read_records(ds_addrs: &vector
): vector>{ + PriceOracle::read_records(ds_addrs) + } +} + +module PriceOracleScripts{ + use StarcoinFramework::PriceOracle; + + public entry fun register_oracle(sender: signer, precision: u8){ + PriceOracle::register_oracle_entry(sender, precision); + } + + public entry fun init_data_source(sender: signer, init_value: u128){ + PriceOracle::init_data_source_entry(sender, init_value); + } + + public entry fun update(sender: signer, value: u128){ + PriceOracle::update_entry(sender, value); + } +} + +module PriceOracleAggregator{ + use StarcoinFramework::Vector; + use StarcoinFramework::Oracle; + use StarcoinFramework::PriceOracle; + use StarcoinFramework::Math; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Errors; + + /// No price data match requirement condition. + const ERR_NO_PRICE_DATA_AVIABLE:u64 = 101; + + /// Get latest price from datasources and calculate avg. + /// `addrs`: the datasource's addr, `updated_in`: the datasource should updated in x millseoconds. + public fun latest_price_average_aggregator(addrs: &vector
, updated_in: u64): u128 { + let len = Vector::length(addrs); + let price_records = PriceOracle::read_records(addrs); + let prices = Vector::empty(); + let i = 0; + let expect_updated_after = Timestamp::now_milliseconds() - updated_in; + while (i < len){ + let record = Vector::pop_back(&mut price_records); + let (_version, price, updated_at) = Oracle::unpack_record(record); + if (updated_at >= expect_updated_after) { + Vector::push_back(&mut prices, price); + }; + i = i + 1; + }; + // if all price data not match the update_in filter, abort. + assert!(!Vector::is_empty(&prices), Errors::invalid_state(ERR_NO_PRICE_DATA_AVIABLE)); + Math::avg(&prices) + } +} + + +} \ No newline at end of file diff --git a/release/v13/sources/PriceOracleScripts.move b/release/v13/sources/PriceOracleScripts.move new file mode 100644 index 00000000..9241b263 --- /dev/null +++ b/release/v13/sources/PriceOracleScripts.move @@ -0,0 +1,325 @@ +address StarcoinFramework { +module Oracle { + use StarcoinFramework::Event; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::Vector; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Account; + use StarcoinFramework::GenesisSignerCapability; + + struct OracleInfo has key { + ///The datasource counter + counter: u64, + ///Ext info + info: Info, + } + + struct DataRecord has copy, store, drop { + ///The data version + version: u64, + ///The record value + value: ValueT, + ///Update timestamp millisecond + updated_at: u64, + } + + struct OracleFeed has key { + record: DataRecord, + } + + struct OracleUpdateEvent has copy,store,drop { + source_id: u64, + record: DataRecord, + } + + struct DataSource has key { + /// the id of data source of ValueT + id: u64, + /// the data version counter. + counter: u64, + update_events: Event::EventHandle>, + } + + struct UpdateCapability has store, key { + account: address, + } + + struct GenesisSignerCapability has key{ + cap: Account::SignerCapability, + } + + /// The oracle type not register. + const ERR_ORACLE_TYPE_NOT_REGISTER:u64 = 101; + /// No capability to update the oracle value. + const ERR_NO_UPDATE_CAPABILITY: u64 = 102; + const ERR_NO_DATA_SOURCE: u64 = 103; + const ERR_CAPABILITY_ACCOUNT_MISS_MATCH: u64 = 104; + + /// deprecated. + public fun initialize(_sender: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability{ + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register `OracleT` as an oracle type. + public fun register_oracle(_sender: &signer, info: Info) { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + move_to(&genesis_account, OracleInfo { + counter: 0, + info, + }); + } + + /// Get the `OracleT` oracle's counter, the counter represent how many `OracleT` datasources + public fun get_oracle_counter() : u64 acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + oracle_info.counter + } + + public fun get_oracle_info() : Info acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&oracle_info.info + } + + /// Init a data source for type `OracleT` + public fun init_data_source(sender: &signer, init_value: ValueT) acquires OracleInfo{ + assert!(exists>(CoreAddresses::GENESIS_ADDRESS()), Errors::not_published(ERR_ORACLE_TYPE_NOT_REGISTER)); + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + let now = Timestamp::now_milliseconds(); + move_to(sender, OracleFeed { + record: DataRecord { + version: 0, + value: init_value, + updated_at: now, + } + }); + let sender_addr = Signer::address_of(sender); + move_to(sender, DataSource { + id: oracle_info.counter, + counter: 1, + update_events: Event::new_event_handle>(sender), + }); + move_to(sender, UpdateCapability{account: sender_addr}); + oracle_info.counter = oracle_info.counter + 1; + } + + /// Check the DataSource is initiailzed at ds_addr + public fun is_data_source_initialized(ds_addr: address): bool { + exists>(ds_addr) + } + + /// Update Oracle's record with new value, the `sender` must have UpdateCapability + public fun update(sender: &signer, value: ValueT) acquires UpdateCapability, DataSource, OracleFeed{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(account); + update_with_cap(cap,value); + } + + /// Update Oracle's record with new value and UpdateCapability + public fun update_with_cap(cap: &mut UpdateCapability, value: ValueT) acquires DataSource,OracleFeed { + let account = cap.account; + assert!(exists>(account), Errors::requires_capability(ERR_NO_DATA_SOURCE)); + let source = borrow_global_mut>(account); + let now = Timestamp::now_milliseconds(); + let oracle_feed = borrow_global_mut>(account); + oracle_feed.record.version = source.counter; + oracle_feed.record.value = value; + oracle_feed.record.updated_at = now; + source.counter = source.counter + 1; + Event::emit_event(&mut source.update_events,OracleUpdateEvent{ + source_id: source.id, + record: *&oracle_feed.record + }); + } + + /// Read the Oracle's value from `ds_addr` + public fun read(ds_addr: address): ValueT acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record.value + } + + /// Read the Oracle's DataRecord from `ds_addr` + public fun read_record(ds_addr: address): DataRecord acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record + } + + /// Batch read Oracle's DataRecord from `ds_addrs` + public fun read_records(ds_addrs: &vector
): vector> acquires OracleFeed{ + let len = Vector::length(ds_addrs); + let results = Vector::empty(); + let i = 0; + while (i < len){ + let addr = *Vector::borrow(ds_addrs, i); + let record = Self::read_record(addr); + Vector::push_back(&mut results, record); + i = i + 1; + }; + results + } + + /// Remove UpdateCapability from current sender. + public fun remove_update_capability(sender: &signer):UpdateCapability acquires UpdateCapability{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(account) + } + + /// Add UpdateCapability to current sender + public fun add_update_capability(sender: &signer, update_cap: UpdateCapability){ + assert!(Signer::address_of(sender) == update_cap.account, Errors::invalid_argument(ERR_CAPABILITY_ACCOUNT_MISS_MATCH)); + move_to(sender, update_cap); + } + + /// Unpack Record to fields: version, oracle, updated_at. + public fun unpack_record(record: DataRecord):(u64, ValueT, u64) { + (record.version,*&record.value,record.updated_at) + } +} +module PriceOracle{ + use StarcoinFramework::Math; + use StarcoinFramework::Oracle::{Self, DataRecord, UpdateCapability}; + + struct PriceOracleInfo has copy,store,drop{ + scaling_factor: u128, + } + + public entry fun register_oracle_entry(sender: signer, precision: u8){ + register_oracle(&sender, precision); + } + + public fun register_oracle(sender: &signer, precision: u8){ + let scaling_factor = Math::pow(10, (precision as u64)); + Oracle::register_oracle(sender, PriceOracleInfo{ + scaling_factor, + }); + } + + + public entry fun init_data_source_entry(sender: signer, init_value: u128){ + init_data_source(&sender, init_value); + } + + public fun init_data_source(sender: &signer, init_value: u128){ + Oracle::init_data_source(sender, init_value); + } + + public fun is_data_source_initialized(ds_addr: address): bool{ + Oracle::is_data_source_initialized(ds_addr) + } + + public fun get_scaling_factor() : u128 { + let info = Oracle::get_oracle_info(); + info.scaling_factor + } + + public entry fun update_entry(sender: signer, value: u128){ + update(&sender, value); + } + + public fun update(sender: &signer, value: u128){ + Oracle::update(sender, value); + } + + public fun update_with_cap(cap: &mut UpdateCapability, value: u128) { + Oracle::update_with_cap(cap, value); + } + + public fun read(addr: address) : u128{ + Oracle::read(addr) + } + + public fun read_record(addr: address): DataRecord{ + Oracle::read_record(addr) + } + + public fun read_records(addrs: &vector
): vector>{ + Oracle::read_records(addrs) + } + +} + +module STCUSDOracle{ + use StarcoinFramework::Oracle::{DataRecord}; + use StarcoinFramework::PriceOracle::{Self}; + + /// The STC to USD price oracle + struct STCUSD has copy,store,drop {} + + public fun register(sender: &signer){ + PriceOracle::register_oracle(sender, 6); + } + + public fun read(ds_addr: address) : u128{ + PriceOracle::read(ds_addr) + } + + public fun read_record(ds_addr: address): DataRecord{ + PriceOracle::read_record(ds_addr) + } + + public fun read_records(ds_addrs: &vector
): vector>{ + PriceOracle::read_records(ds_addrs) + } +} + +module PriceOracleScripts{ + use StarcoinFramework::PriceOracle; + + public entry fun register_oracle(sender: signer, precision: u8){ + PriceOracle::register_oracle_entry(sender, precision); + } + + public entry fun init_data_source(sender: signer, init_value: u128){ + PriceOracle::init_data_source_entry(sender, init_value); + } + + public entry fun update(sender: signer, value: u128){ + PriceOracle::update_entry(sender, value); + } +} + +module PriceOracleAggregator{ + use StarcoinFramework::Vector; + use StarcoinFramework::Oracle; + use StarcoinFramework::PriceOracle; + use StarcoinFramework::Math; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Errors; + + /// No price data match requirement condition. + const ERR_NO_PRICE_DATA_AVIABLE:u64 = 101; + + /// Get latest price from datasources and calculate avg. + /// `addrs`: the datasource's addr, `updated_in`: the datasource should updated in x millseoconds. + public fun latest_price_average_aggregator(addrs: &vector
, updated_in: u64): u128 { + let len = Vector::length(addrs); + let price_records = PriceOracle::read_records(addrs); + let prices = Vector::empty(); + let i = 0; + let expect_updated_after = Timestamp::now_milliseconds() - updated_in; + while (i < len){ + let record = Vector::pop_back(&mut price_records); + let (_version, price, updated_at) = Oracle::unpack_record(record); + if (updated_at >= expect_updated_after) { + Vector::push_back(&mut prices, price); + }; + i = i + 1; + }; + // if all price data not match the update_in filter, abort. + assert!(!Vector::is_empty(&prices), Errors::invalid_state(ERR_NO_PRICE_DATA_AVIABLE)); + Math::avg(&prices) + } +} + + +} \ No newline at end of file diff --git a/release/v13/sources/RewardConfig.move b/release/v13/sources/RewardConfig.move new file mode 100644 index 00000000..07179d9c --- /dev/null +++ b/release/v13/sources/RewardConfig.move @@ -0,0 +1,71 @@ +address StarcoinFramework { +/// The module provide configuration for block reward. +module RewardConfig { + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Config; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + /// Reward configuration + struct RewardConfig has copy, drop, store { + /// how many blocks delay reward distribution. + reward_delay: u64, + } + + const EINVALID_ARGUMENT: u64 = 18; + + /// Module initialization. + public fun initialize(account: &signer, reward_delay: u64) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + + Config::publish_new_config( + account, + new_reward_config(reward_delay) + ); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists>(Signer::address_of(account)); + include Config::PublishNewConfigAbortsIf; + include Config::PublishNewConfigEnsures; + } + + /// Create a new reward config mainly used in DAO. + public fun new_reward_config(reward_delay: u64) : RewardConfig { + RewardConfig {reward_delay: reward_delay} + } + + spec new_reward_config {} + + /// Get reward configuration. + public fun get_reward_config(): RewardConfig { + Config::get_by_address(CoreAddresses::GENESIS_ADDRESS()) + } + + spec get_reward_config { + include GetRewardConfigAbortsIf; + } + + spec schema GetRewardConfigAbortsIf { + aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Get reward delay. + public fun reward_delay() :u64 { + let reward_config = get_reward_config(); + reward_config.reward_delay + } + + spec reward_delay { + aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Ring.move b/release/v13/sources/Ring.move new file mode 100644 index 00000000..1ad9f86b --- /dev/null +++ b/release/v13/sources/Ring.move @@ -0,0 +1,152 @@ +address StarcoinFramework { + +/// A ring-shaped container that can hold any type, indexed from 0 +/// The capacity is fixed at creation time, and the accessible index is constantly growing +module Ring { + + use StarcoinFramework::Vector; + use StarcoinFramework::Option; + use StarcoinFramework::Errors; + + + /// The index into the vector is out of bounds + const ERROR_RING_INDEX_OUT_OF_BOUNDS:u64 = 101; + + struct Ring has store{ + data : vector>, + insertion_index : u64, + external_index : u64 + } + + /// Create a Ring with capacity. + public fun create_with_capacity( len: u64 ):Ring{ + let data = Vector::empty>(); + let i = 0; + while(i < len){ + Vector::push_back(&mut data , Option::none()); + i = i + 1; + }; + Ring { + data : data, + insertion_index : 0, + external_index : 0, + } + } + + spec create_with_capacity{ + pragma intrinsic = true; + } + + ///is Ring full + public fun is_full(r: &Ring):bool{ + Option::is_some(Vector::borrow(&r.data, r.insertion_index)) + } + + spec is_full{ + pragma intrinsic = true; + } + + ///Return the capacity of the Ring. + public fun capacity(r: &Ring): u64{ + Vector::length( &r.data ) + } + + spec capacity{ + } + + /// Add element `e` to the insertion_index of the Ring `r`. + public fun push (r: &mut Ring , e: Element):Option::Option{ + let op_e = Vector::borrow_mut>(&mut r.data, r.insertion_index); + let res = if( Option::is_none(op_e) ){ + Option::fill( op_e, e); + Option::none() + }else{ + Option::some( Option::swap( op_e, e) ) + }; + r.insertion_index = ( r.insertion_index + 1 ) % Vector::length(&r.data); + r.external_index = r.external_index + 1; + res + } + + spec push{ + pragma intrinsic = true; + } + + /// Return a reference to the `i`th element in the Ring `r`. + public fun borrow(r:& Ring, i: u64):&Option::Option{ + let len = capacity(r); + if( r.external_index > len - 1) { + assert!( i >= r.external_index - len && i < r.external_index , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS)); + Vector::borrow(&r.data, i % len) + }else { + assert!( i < len , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS)); + Vector::borrow(&r.data, i ) + } + } + + spec borrow{ + pragma intrinsic = true; + } + + /// Return a mutable reference to the `i`th element in the Ring `r`. + public fun borrow_mut(r: &mut Ring, i: u64):&mut Option::Option{ + let len = capacity(r); + if( r.external_index > len - 1) { + assert!( i >= r.external_index - len && i < r.external_index , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS)); + Vector::borrow_mut(&mut r.data, i % len) + }else { + assert!( i < len , Errors::invalid_argument(ERROR_RING_INDEX_OUT_OF_BOUNDS)); + Vector::borrow_mut(&mut r.data, i ) + } + + } + + spec borrow_mut { + } + + + /// Return `Option::Option` if `e` is in the Ring `r` at index `i`. + /// Otherwise, returns `Option::none`. + public fun index_of(r: &Ring, e: &Element):Option::Option{ + let i = 0; + let len = capacity(r); + while ( i < len ) { + if ( Option::borrow(Vector::borrow( &r.data, i )) == e) return Option::some(i + r.external_index - len); + i = i + 1; + }; + Option::none() + } + + spec index_of{ + pragma intrinsic = true; + } + + /// Destroy the Ring `r`. + /// Returns the vector saved by ring + public fun destroy(r: Ring):vector{ + let Ring { + data : data , + insertion_index : _, + external_index : _, + } = r ; + let len = Vector::length(&data); + let i = 0; + let vec = Vector::empty(); + while ( i < len ) { + let op_e = Vector::pop_back( &mut data ); + if ( Option::is_some(&op_e) ) { + Vector::push_back(&mut vec, Option::destroy_some(op_e)) + }else { + Option::destroy_none(op_e) + }; + i = i + 1; + }; + Vector::destroy_empty(data); + vec + } + + spec destroy{ + pragma intrinsic = true; + } +} +} diff --git a/release/v13/sources/SIP_2.move b/release/v13/sources/SIP_2.move new file mode 100644 index 00000000..effeb896 --- /dev/null +++ b/release/v13/sources/SIP_2.move @@ -0,0 +1,15 @@ +address StarcoinFramework { +/// The modules define SIP. +/// Only the SIP that needs to do hard forked needs to be defined here, every SIP as a module, and can be a feature flag. + +/// https://github.com/starcoinorg/SIPs/tree/master/sip-2 +module SIP_2 { + +} + +/// https://github.com/starcoinorg/SIPs/tree/master/sip-3 +module SIP_3 { + +} + +} diff --git a/release/v13/sources/SIP_3.move b/release/v13/sources/SIP_3.move new file mode 100644 index 00000000..effeb896 --- /dev/null +++ b/release/v13/sources/SIP_3.move @@ -0,0 +1,15 @@ +address StarcoinFramework { +/// The modules define SIP. +/// Only the SIP that needs to do hard forked needs to be defined here, every SIP as a module, and can be a feature flag. + +/// https://github.com/starcoinorg/SIPs/tree/master/sip-2 +module SIP_2 { + +} + +/// https://github.com/starcoinorg/SIPs/tree/master/sip-3 +module SIP_3 { + +} + +} diff --git a/release/v13/sources/STC.move b/release/v13/sources/STC.move new file mode 100644 index 00000000..92fc837d --- /dev/null +++ b/release/v13/sources/STC.move @@ -0,0 +1,160 @@ +address StarcoinFramework { +/// STC is the token of Starcoin blockchain. +/// It uses apis defined in the `Token` module. +module STC { + use StarcoinFramework::Token::{Self, Token}; + use StarcoinFramework::Dao; + use StarcoinFramework::ModifyDaoConfigProposal; + use StarcoinFramework::UpgradeModuleDaoProposal; + use StarcoinFramework::PackageTxnManager; + use StarcoinFramework::OnChainConfigDao; + use StarcoinFramework::TransactionPublishOption; + use StarcoinFramework::VMConfig; + use StarcoinFramework::ConsensusConfig; + use StarcoinFramework::RewardConfig; + use StarcoinFramework::TransactionTimeoutConfig; + use StarcoinFramework::Treasury; + use StarcoinFramework::CoreAddresses; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + /// STC token marker. + struct STC has copy, drop, store { } + + /// precision of STC token. + const PRECISION: u8 = 9; + + /// Burn capability of STC. + struct SharedBurnCapability has key, store { + cap: Token::BurnCapability, + } + + /// STC initialization. + public fun initialize( + account: &signer, + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + ) { + Token::register_token(account, PRECISION); + let burn_cap = Token::remove_burn_capability(account); + move_to(account, SharedBurnCapability { cap: burn_cap }); + Dao::plugin( + account, + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + ); + ModifyDaoConfigProposal::plugin(account); + let upgrade_plan_cap = PackageTxnManager::extract_submit_upgrade_plan_cap(account); + UpgradeModuleDaoProposal::plugin( + account, + upgrade_plan_cap, + ); + // the following configurations are gov-ed by Dao. + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + } + + spec initialize { + include Token::RegisterTokenAbortsIf{precision: PRECISION}; + } + + public fun upgrade_from_v1_to_v2(account: &signer,total_amount: u128,): Treasury::WithdrawCapability { + CoreAddresses::assert_genesis_address(account); + + // Mint all stc, and destroy mint capability + let total_stc = Token::mint(account, total_amount-Token::market_cap()); + let withdraw_cap = Treasury::initialize(account, total_stc); + let mint_cap = Token::remove_mint_capability(account); + Token::destroy_mint_capability(mint_cap); + withdraw_cap + } + + spec upgrade_from_v1_to_v2 { + pragma verify = false; + } + + /// STC initialization. + public fun initialize_v2( + account: &signer, + total_amount: u128, + voting_delay: u64, + voting_period: u64, + voting_quorum_rate: u8, + min_action_delay: u64, + ): Treasury::WithdrawCapability { + Token::register_token(account, PRECISION); + + // Mint all stc, and destroy mint capability + + let total_stc = Token::mint(account, total_amount); + let withdraw_cap = Treasury::initialize(account, total_stc); + let mint_cap = Token::remove_mint_capability(account); + Token::destroy_mint_capability(mint_cap); + + let burn_cap = Token::remove_burn_capability(account); + move_to(account, SharedBurnCapability { cap: burn_cap }); + Dao::plugin( + account, + voting_delay, + voting_period, + voting_quorum_rate, + min_action_delay, + ); + ModifyDaoConfigProposal::plugin(account); + let upgrade_plan_cap = PackageTxnManager::extract_submit_upgrade_plan_cap(account); + UpgradeModuleDaoProposal::plugin( + account, + upgrade_plan_cap, + ); + // the following configurations are gov-ed by Dao. + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + OnChainConfigDao::plugin(account); + withdraw_cap + } + + spec initialize_v2 { + include Token::RegisterTokenAbortsIf{precision: PRECISION}; + } + + /// Returns true if `TokenType` is `STC::STC` + public fun is_stc(): bool { + Token::is_same_token() + } + + spec is_stc { + } + + /// Burn STC tokens. + /// It can be called by anyone. + public fun burn(token: Token) acquires SharedBurnCapability { + let cap = borrow_global(token_address()); + Token::burn_with_capability(&cap.cap, token); + } + + spec burn { + aborts_if Token::spec_abstract_total_value() - token.value < 0; + aborts_if !exists(Token::SPEC_TOKEN_TEST_ADDRESS()); + } + + /// Return STC token address. + public fun token_address(): address { + Token::token_address() + } + + spec token_address { + } +} +} \ No newline at end of file diff --git a/release/v13/sources/STCUSDOracle.move b/release/v13/sources/STCUSDOracle.move new file mode 100644 index 00000000..9241b263 --- /dev/null +++ b/release/v13/sources/STCUSDOracle.move @@ -0,0 +1,325 @@ +address StarcoinFramework { +module Oracle { + use StarcoinFramework::Event; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Signer; + use StarcoinFramework::Vector; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Account; + use StarcoinFramework::GenesisSignerCapability; + + struct OracleInfo has key { + ///The datasource counter + counter: u64, + ///Ext info + info: Info, + } + + struct DataRecord has copy, store, drop { + ///The data version + version: u64, + ///The record value + value: ValueT, + ///Update timestamp millisecond + updated_at: u64, + } + + struct OracleFeed has key { + record: DataRecord, + } + + struct OracleUpdateEvent has copy,store,drop { + source_id: u64, + record: DataRecord, + } + + struct DataSource has key { + /// the id of data source of ValueT + id: u64, + /// the data version counter. + counter: u64, + update_events: Event::EventHandle>, + } + + struct UpdateCapability has store, key { + account: address, + } + + struct GenesisSignerCapability has key{ + cap: Account::SignerCapability, + } + + /// The oracle type not register. + const ERR_ORACLE_TYPE_NOT_REGISTER:u64 = 101; + /// No capability to update the oracle value. + const ERR_NO_UPDATE_CAPABILITY: u64 = 102; + const ERR_NO_DATA_SOURCE: u64 = 103; + const ERR_CAPABILITY_ACCOUNT_MISS_MATCH: u64 = 104; + + /// deprecated. + public fun initialize(_sender: &signer) { + } + + /// Used in v7->v8 upgrade. struct `GenesisSignerCapability` is deprecated, in favor of module `StarcoinFramework::GenesisSignerCapability`. + public fun extract_signer_cap(signer: &signer): Account::SignerCapability acquires GenesisSignerCapability{ + CoreAddresses::assert_genesis_address(signer); + let cap = move_from(Signer::address_of(signer)); + let GenesisSignerCapability {cap} = cap; + cap + } + + /// Register `OracleT` as an oracle type. + public fun register_oracle(_sender: &signer, info: Info) { + let genesis_account = GenesisSignerCapability::get_genesis_signer(); + move_to(&genesis_account, OracleInfo { + counter: 0, + info, + }); + } + + /// Get the `OracleT` oracle's counter, the counter represent how many `OracleT` datasources + public fun get_oracle_counter() : u64 acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + oracle_info.counter + } + + public fun get_oracle_info() : Info acquires OracleInfo { + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + *&oracle_info.info + } + + /// Init a data source for type `OracleT` + public fun init_data_source(sender: &signer, init_value: ValueT) acquires OracleInfo{ + assert!(exists>(CoreAddresses::GENESIS_ADDRESS()), Errors::not_published(ERR_ORACLE_TYPE_NOT_REGISTER)); + let oracle_info = borrow_global_mut>(CoreAddresses::GENESIS_ADDRESS()); + let now = Timestamp::now_milliseconds(); + move_to(sender, OracleFeed { + record: DataRecord { + version: 0, + value: init_value, + updated_at: now, + } + }); + let sender_addr = Signer::address_of(sender); + move_to(sender, DataSource { + id: oracle_info.counter, + counter: 1, + update_events: Event::new_event_handle>(sender), + }); + move_to(sender, UpdateCapability{account: sender_addr}); + oracle_info.counter = oracle_info.counter + 1; + } + + /// Check the DataSource is initiailzed at ds_addr + public fun is_data_source_initialized(ds_addr: address): bool { + exists>(ds_addr) + } + + /// Update Oracle's record with new value, the `sender` must have UpdateCapability + public fun update(sender: &signer, value: ValueT) acquires UpdateCapability, DataSource, OracleFeed{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + let cap = borrow_global_mut>(account); + update_with_cap(cap,value); + } + + /// Update Oracle's record with new value and UpdateCapability + public fun update_with_cap(cap: &mut UpdateCapability, value: ValueT) acquires DataSource,OracleFeed { + let account = cap.account; + assert!(exists>(account), Errors::requires_capability(ERR_NO_DATA_SOURCE)); + let source = borrow_global_mut>(account); + let now = Timestamp::now_milliseconds(); + let oracle_feed = borrow_global_mut>(account); + oracle_feed.record.version = source.counter; + oracle_feed.record.value = value; + oracle_feed.record.updated_at = now; + source.counter = source.counter + 1; + Event::emit_event(&mut source.update_events,OracleUpdateEvent{ + source_id: source.id, + record: *&oracle_feed.record + }); + } + + /// Read the Oracle's value from `ds_addr` + public fun read(ds_addr: address): ValueT acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record.value + } + + /// Read the Oracle's DataRecord from `ds_addr` + public fun read_record(ds_addr: address): DataRecord acquires OracleFeed{ + let oracle_feed = borrow_global>(ds_addr); + *&oracle_feed.record + } + + /// Batch read Oracle's DataRecord from `ds_addrs` + public fun read_records(ds_addrs: &vector
): vector> acquires OracleFeed{ + let len = Vector::length(ds_addrs); + let results = Vector::empty(); + let i = 0; + while (i < len){ + let addr = *Vector::borrow(ds_addrs, i); + let record = Self::read_record(addr); + Vector::push_back(&mut results, record); + i = i + 1; + }; + results + } + + /// Remove UpdateCapability from current sender. + public fun remove_update_capability(sender: &signer):UpdateCapability acquires UpdateCapability{ + let account = Signer::address_of(sender); + assert!(exists>(account), Errors::requires_capability(ERR_NO_UPDATE_CAPABILITY)); + move_from>(account) + } + + /// Add UpdateCapability to current sender + public fun add_update_capability(sender: &signer, update_cap: UpdateCapability){ + assert!(Signer::address_of(sender) == update_cap.account, Errors::invalid_argument(ERR_CAPABILITY_ACCOUNT_MISS_MATCH)); + move_to(sender, update_cap); + } + + /// Unpack Record to fields: version, oracle, updated_at. + public fun unpack_record(record: DataRecord):(u64, ValueT, u64) { + (record.version,*&record.value,record.updated_at) + } +} +module PriceOracle{ + use StarcoinFramework::Math; + use StarcoinFramework::Oracle::{Self, DataRecord, UpdateCapability}; + + struct PriceOracleInfo has copy,store,drop{ + scaling_factor: u128, + } + + public entry fun register_oracle_entry(sender: signer, precision: u8){ + register_oracle(&sender, precision); + } + + public fun register_oracle(sender: &signer, precision: u8){ + let scaling_factor = Math::pow(10, (precision as u64)); + Oracle::register_oracle(sender, PriceOracleInfo{ + scaling_factor, + }); + } + + + public entry fun init_data_source_entry(sender: signer, init_value: u128){ + init_data_source(&sender, init_value); + } + + public fun init_data_source(sender: &signer, init_value: u128){ + Oracle::init_data_source(sender, init_value); + } + + public fun is_data_source_initialized(ds_addr: address): bool{ + Oracle::is_data_source_initialized(ds_addr) + } + + public fun get_scaling_factor() : u128 { + let info = Oracle::get_oracle_info(); + info.scaling_factor + } + + public entry fun update_entry(sender: signer, value: u128){ + update(&sender, value); + } + + public fun update(sender: &signer, value: u128){ + Oracle::update(sender, value); + } + + public fun update_with_cap(cap: &mut UpdateCapability, value: u128) { + Oracle::update_with_cap(cap, value); + } + + public fun read(addr: address) : u128{ + Oracle::read(addr) + } + + public fun read_record(addr: address): DataRecord{ + Oracle::read_record(addr) + } + + public fun read_records(addrs: &vector
): vector>{ + Oracle::read_records(addrs) + } + +} + +module STCUSDOracle{ + use StarcoinFramework::Oracle::{DataRecord}; + use StarcoinFramework::PriceOracle::{Self}; + + /// The STC to USD price oracle + struct STCUSD has copy,store,drop {} + + public fun register(sender: &signer){ + PriceOracle::register_oracle(sender, 6); + } + + public fun read(ds_addr: address) : u128{ + PriceOracle::read(ds_addr) + } + + public fun read_record(ds_addr: address): DataRecord{ + PriceOracle::read_record(ds_addr) + } + + public fun read_records(ds_addrs: &vector
): vector>{ + PriceOracle::read_records(ds_addrs) + } +} + +module PriceOracleScripts{ + use StarcoinFramework::PriceOracle; + + public entry fun register_oracle(sender: signer, precision: u8){ + PriceOracle::register_oracle_entry(sender, precision); + } + + public entry fun init_data_source(sender: signer, init_value: u128){ + PriceOracle::init_data_source_entry(sender, init_value); + } + + public entry fun update(sender: signer, value: u128){ + PriceOracle::update_entry(sender, value); + } +} + +module PriceOracleAggregator{ + use StarcoinFramework::Vector; + use StarcoinFramework::Oracle; + use StarcoinFramework::PriceOracle; + use StarcoinFramework::Math; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Errors; + + /// No price data match requirement condition. + const ERR_NO_PRICE_DATA_AVIABLE:u64 = 101; + + /// Get latest price from datasources and calculate avg. + /// `addrs`: the datasource's addr, `updated_in`: the datasource should updated in x millseoconds. + public fun latest_price_average_aggregator(addrs: &vector
, updated_in: u64): u128 { + let len = Vector::length(addrs); + let price_records = PriceOracle::read_records(addrs); + let prices = Vector::empty(); + let i = 0; + let expect_updated_after = Timestamp::now_milliseconds() - updated_in; + while (i < len){ + let record = Vector::pop_back(&mut price_records); + let (_version, price, updated_at) = Oracle::unpack_record(record); + if (updated_at >= expect_updated_after) { + Vector::push_back(&mut prices, price); + }; + i = i + 1; + }; + // if all price data not match the update_in filter, abort. + assert!(!Vector::is_empty(&prices), Errors::invalid_state(ERR_NO_PRICE_DATA_AVIABLE)); + Math::avg(&prices) + } +} + + +} \ No newline at end of file diff --git a/release/v13/sources/Secp256k1.move b/release/v13/sources/Secp256k1.move new file mode 100644 index 00000000..98a12c44 --- /dev/null +++ b/release/v13/sources/Secp256k1.move @@ -0,0 +1,132 @@ +/// This module implements ECDSA signatures based on the prime-order secp256k1 ellptic curve (i.e., cofactor is 1). + +module StarcoinFramework::Secp256k1 { + + use StarcoinFramework::Option::Option; + use StarcoinFramework::Option; + use StarcoinFramework::Vector; + use StarcoinFramework::Errors; + + /// An error occurred while deserializing, for example due to wrong input size. + const E_DESERIALIZE: u64 = 1; // This code must be the same, if ever returned from the native Rust implementation. + + /// The size of a secp256k1-based ECDSA public key, in bytes. + const RAW_PUBLIC_KEY_NUM_BYTES: u64 = 64; + //const COMPRESSED_PUBLIC_KEY_SIZE: u64 = 33; + + /// The size of a secp256k1-based ECDSA signature, in bytes. + const SIGNATURE_NUM_BYTES: u64 = 64; + + /// A 64-byte ECDSA public key. + struct ECDSARawPublicKey has copy, drop, store { + bytes: vector + } + + /// A 64-byte ECDSA signature. + struct ECDSASignature has copy, drop, store { + bytes: vector + } + + /// Constructs an ECDSASignature struct from the given 64 bytes. + public fun ecdsa_signature_from_bytes(bytes: vector): ECDSASignature { + assert!(Vector::length(&bytes) == SIGNATURE_NUM_BYTES, Errors::invalid_argument(E_DESERIALIZE)); + ECDSASignature { bytes } + } + + /// Constructs an ECDSARawPublicKey struct, given a 64-byte raw representation. + public fun ecdsa_raw_public_key_from_64_bytes(bytes: vector): ECDSARawPublicKey { + assert!(Vector::length(&bytes) == RAW_PUBLIC_KEY_NUM_BYTES, Errors::invalid_argument(E_DESERIALIZE)); + ECDSARawPublicKey { bytes } + } + + /// Serializes an ECDSARawPublicKey struct to 64-bytes. + public fun ecdsa_raw_public_key_to_bytes(pk: &ECDSARawPublicKey): vector { + *&pk.bytes + } + + /// Serializes an ECDSASignature struct to 64-bytes. + public fun ecdsa_signature_to_bytes(sig: &ECDSASignature): vector { + *&sig.bytes + } + + /// Recovers the signer's raw (64-byte) public key from a secp256k1 ECDSA `signature` given the `recovery_id` and the signed + /// `message` (32 byte digest). + /// + /// Note that an invalid signature, or a signature from a different message, will result in the recovery of an + /// incorrect public key. This recovery algorithm can only be used to check validity of a signature if the signer's + /// public key (or its hash) is known beforehand. + public fun ecdsa_recover( + message: vector, + recovery_id: u8, + signature: &ECDSASignature, + ): Option { + let (pk, success) = ecdsa_recover_internal(message, recovery_id, *&signature.bytes); + if (success) { + Option::some(ecdsa_raw_public_key_from_64_bytes(pk)) + } else { + Option::none() + } + } + + // + // Native functions + // + + /// Returns `(public_key, true)` if `signature` verifies on `message` under the recovered `public_key` + /// and returns `([], false)` otherwise. + native fun ecdsa_recover_internal( + message: vector, + recovery_id: u8, + signature: vector + ): (vector, bool); + + spec ecdsa_recover_internal { + pragma opaque; // Native + } + + // + // Tests + // + + #[test] + /// Test on a valid secp256k1 ECDSA signature created using sk = x"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + fun test_ecdsa_recover() { + use StarcoinFramework::Hash; + + let pk = ecdsa_recover( + Hash::sha2_256(b"test aptos secp256k1"), + 0, + &ECDSASignature { bytes: x"f7ad936da03f948c14c542020e3c5f4e02aaacd1f20427c11aa6e2fbf8776477646bba0e1a37f9e7c777c423a1d2849baafd7ff6a9930814a43c3f80d59db56f" }, + ); + assert!(Option::is_some(&pk), 1); + assert!( + *&Option::extract( + &mut pk + ).bytes == x"4646ae5047316b4230d0086c8acec687f00b1cd9d1dc634f6cb358ac0a9a8ffffe77b4dd0a4bfb95851f3b7355c781dd60f8418fc8a65d14907aff47c903a559", + 1 + ); + + // Flipped bits; Signature stays valid + let pk = ecdsa_recover( + Hash::sha2_256(b"test aptos secp256k1"), + 0, + // NOTE: A '7' was flipped to an 'f' here + &ECDSASignature { bytes: x"f7ad936da03f948c14c542020e3c5f4e02aaacd1f20427c11aa6e2fbf8776477646bba0e1a37f9e7c7f7c423a1d2849baafd7ff6a9930814a43c3f80d59db56f" }, + ); + assert!(Option::is_some(&pk), 1); + assert!( + *&Option::extract( + &mut pk + ).bytes != x"4646ae5047316b4230d0086c8acec687f00b1cd9d1dc634f6cb358ac0a9a8ffffe77b4dd0a4bfb95851f3b7355c781dd60f8418fc8a65d14907aff47c903a559", + 1 + ); + + // Flipped bits; Signature becomes invalid + let pk = ecdsa_recover( + Hash::sha2_256(b"test aptos secp256k1"), + 0, + &ECDSASignature { bytes: x"ffad936da03f948c14c542020e3c5f4e02aaacd1f20427c11aa6e2fbf8776477646bba0e1a37f9e7c7f7c423a1d2849baafd7ff6a9930814a43c3f80d59db56f" }, + ); + assert!(Option::is_none(&pk), 1); + } +} diff --git a/release/v13/sources/SharedEd25519PublicKey.move b/release/v13/sources/SharedEd25519PublicKey.move new file mode 100644 index 00000000..9b513d06 --- /dev/null +++ b/release/v13/sources/SharedEd25519PublicKey.move @@ -0,0 +1,110 @@ +address StarcoinFramework { +/// Each address that holds a `SharedEd25519PublicKey` resource can rotate the public key stored in +/// this resource, but the account's authentication key will be updated in lockstep. This ensures +/// that the two keys always stay in sync. +module SharedEd25519PublicKey { + use StarcoinFramework::Authenticator; + use StarcoinFramework::Account; + use StarcoinFramework::Signature; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// A resource that forces the account associated with `rotation_cap` to use a ed25519 + /// authentication key derived from `key` + struct SharedEd25519PublicKey has key { + /// 32 byte ed25519 public key + key: vector, + /// rotation capability for an account whose authentication key is always derived from `key` + rotation_cap: Account::KeyRotationCapability, + } + + const EMALFORMED_PUBLIC_KEY: u64 = 101; + + /// (1) Rotate the authentication key of the sender to `key` + /// (2) Publish a resource containing a 32-byte ed25519 public key and the rotation capability + /// of the sender under the `account`'s address. + /// Aborts if the sender already has a `SharedEd25519PublicKey` resource. + /// Aborts if the length of `new_public_key` is not 32. + public fun publish(account: &signer, key: vector) { + let t = SharedEd25519PublicKey { + key: x"", + rotation_cap: Account::extract_key_rotation_capability(account) + }; + rotate_key_(&mut t, key); + move_to(account, t); + } + + spec publish { + aborts_if !exists(Signer::address_of(account)); + aborts_if StarcoinFramework::Option::is_none(global(Signer::address_of(account)).key_rotation_capability); + aborts_if !exists( + StarcoinFramework::Option::borrow( + global(Signer::address_of(account)) + .key_rotation_capability + ).account_address); + aborts_if !Signature::ed25519_validate_pubkey(key); + aborts_if exists(Signer::address_of(account)); + aborts_if len(Authenticator::spec_ed25519_authentication_key(key)) != 32; + } + + fun rotate_key_(shared_key: &mut SharedEd25519PublicKey, new_public_key: vector) { + // Cryptographic check of public key validity + assert!( + Signature::ed25519_validate_pubkey(copy new_public_key), + Errors::invalid_argument(EMALFORMED_PUBLIC_KEY) + ); + Account::rotate_authentication_key_with_capability( + &shared_key.rotation_cap, + Authenticator::ed25519_authentication_key(copy new_public_key) + ); + shared_key.key = new_public_key; + } + + spec rotate_key_ { + aborts_if !exists(shared_key.rotation_cap.account_address); + aborts_if !Signature::ed25519_validate_pubkey(new_public_key); + aborts_if len(Authenticator::spec_ed25519_authentication_key(new_public_key)) != 32; + } + + /// (1) rotate the public key stored `account`'s `SharedEd25519PublicKey` resource to + /// `new_public_key` + /// (2) rotate the authentication key using the capability stored in the `account`'s + /// `SharedEd25519PublicKey` to a new value derived from `new_public_key` + /// Aborts if the sender does not have a `SharedEd25519PublicKey` resource. + /// Aborts if the length of `new_public_key` is not 32. + public fun rotate_key(account: &signer, new_public_key: vector) acquires SharedEd25519PublicKey { + rotate_key_(borrow_global_mut(Signer::address_of(account)), new_public_key); + } + + spec rotate_key { + aborts_if !exists(Signer::address_of(account)); + aborts_if !exists(global(Signer::address_of(account)).rotation_cap.account_address); + aborts_if !Signature::ed25519_validate_pubkey(new_public_key); + aborts_if len(Authenticator::spec_ed25519_authentication_key(new_public_key)) != 32; + } + + /// Return the public key stored under `addr`. + /// Aborts if `addr` does not hold a `SharedEd25519PublicKey` resource. + public fun key(addr: address): vector acquires SharedEd25519PublicKey { + *&borrow_global(addr).key + } + + spec key { + aborts_if !exists(addr); + } + + /// Returns true if `addr` holds a `SharedEd25519PublicKey` resource. + public fun exists_at(addr: address): bool { + exists(addr) + } + + spec exists_at { + aborts_if false; + } +} +} diff --git a/release/v13/sources/Signature.move b/release/v13/sources/Signature.move new file mode 100644 index 00000000..044636e4 --- /dev/null +++ b/release/v13/sources/Signature.move @@ -0,0 +1,131 @@ +address StarcoinFramework { + +/// Contains functions for [ed25519](https://en.wikipedia.org/wiki/EdDSA) digital signatures. +module Signature { + + use StarcoinFramework::Vector; + use StarcoinFramework::Option::{Self, Option}; + use StarcoinFramework::EVMAddress::{Self, EVMAddress}; + + native public fun ed25519_validate_pubkey(public_key: vector): bool; + native public fun ed25519_verify(signature: vector, public_key: vector, message: vector): bool; + + /// recover address from ECDSA signature, if recover fail, return an empty vector + native fun native_ecrecover(hash: vector, signature: vector): vector; + + /// recover address from ECDSA signature, if recover fail, return None + public fun ecrecover(hash: vector, signature: vector):Option{ + let bytes = native_ecrecover(hash, signature); + if (Vector::is_empty(&bytes)){ + Option::none() + }else{ + Option::some(EVMAddress::new(bytes)) + } + } + + // verify eth secp256k1 sign and compare addr, if add equal return true + public fun secp256k1_verify(signature: vector, addr: vector, message: vector) : bool{ + let receover_address_opt:Option = ecrecover(message, signature); + let expect_address = EVMAddress::new(addr); + &Option::destroy_some(receover_address_opt) == &expect_address + } + + spec module { + pragma intrinsic = true; + } + + #[test] + fun test_ecrecover_invalid(){ + let h = b"00"; + let s = b"00"; + let addr = ecrecover(h, s); + assert!(Option::is_none(&addr), 1001); + } +} + +module EVMAddress{ + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + use StarcoinFramework::Vector; + + const EVM_ADDR_LENGTH:u64 = 20; + + struct EVMAddress has copy, store, drop{ + bytes: vector, + } + + /// Create EVMAddress from bytes, If bytes is larger than EVM_ADDR_LENGTH(20), bytes will be cropped from the left. + /// keep same as https://github.com/ethereum/go-ethereum/blob/master/common/types.go#L302 + public fun new(bytes: vector): EVMAddress{ + let len = Vector::length(&bytes); + let bytes = if (len > EVM_ADDR_LENGTH){ + let new_bytes = Vector::empty(); + let i = 0; + while (i < EVM_ADDR_LENGTH) { + Vector::push_back(&mut new_bytes, *Vector::borrow(&bytes, i)); + i = i + 1; + }; + new_bytes + }else if (len == EVM_ADDR_LENGTH){ + bytes + }else{ + let i = 0; + let new_bytes = Vector::empty(); + while (i < EVM_ADDR_LENGTH - len) { + // pad zero to address + Vector::push_back(&mut new_bytes, 0); + i = i + 1; + }; + Vector::append(&mut new_bytes, bytes); + new_bytes + }; + EVMAddress{ + bytes + } + } + + spec new { + pragma verify = false; + //TODO + } + + /// Get the inner bytes of the `addr` as a reference + public fun as_bytes(addr: &EVMAddress): &vector { + &addr.bytes + } + + spec as_bytes { + pragma verify = false; + //TODO + } + + /// Unpack the `addr` to get its backing bytes + public fun into_bytes(addr: EVMAddress): vector { + let EVMAddress { bytes } = addr; + bytes + } + + spec into_bytes { + pragma verify = false; + //TODO + } + + #[test] + fun test_evm_address_padding(){ + let addr1 = new(x"00"); + let addr2 = new(x"0000"); + assert!(&addr1.bytes == &addr2.bytes, 1001); + } + + #[test] + fun test_evm_address_crop(){ + let addr1 = new(x"01234567890123456789012345678901234567891111"); + let addr2 = new(x"01234567890123456789012345678901234567892222"); + assert!(&addr1.bytes == &addr2.bytes, 1001); + } +} +} diff --git a/release/v13/sources/SignedInteger64.move b/release/v13/sources/SignedInteger64.move new file mode 100644 index 00000000..5258cc78 --- /dev/null +++ b/release/v13/sources/SignedInteger64.move @@ -0,0 +1,109 @@ +address StarcoinFramework { +/// Implementation of i64. +module SignedInteger64 { + + /// Define a signed integer type with two 32 bits. + struct SignedInteger64 has copy, drop, store { + value: u64, + is_negative: bool, + } + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// Multiply a u64 integer by a signed integer number. + public fun multiply_u64(num: u64, multiplier: SignedInteger64): SignedInteger64 { + let product = multiplier.value * num; + SignedInteger64 { value: product, is_negative: multiplier.is_negative } + } + + /// Divide a u64 integer by a signed integer number. + public fun divide_u64(num: u64, divisor: SignedInteger64): SignedInteger64 { + let quotient = num / divisor.value; + SignedInteger64 { value: quotient, is_negative: divisor.is_negative } + } + + /// Sub: `num - minus` + public fun sub_u64(num: u64, minus: SignedInteger64): SignedInteger64 { + if (minus.is_negative) { + let result = num + minus.value; + SignedInteger64 { value: result, is_negative: false } + } else { + if (num >= minus.value) { + let result = num - minus.value; + SignedInteger64 { value: result, is_negative: false } + }else { + let result = minus.value - num; + SignedInteger64 { value: result, is_negative: true } + } + } + } + + /// Add: `num + addend` + public fun add_u64(num: u64, addend: SignedInteger64): SignedInteger64 { + if (addend.is_negative) { + if (num >= addend.value) { + let result = num - addend.value; + SignedInteger64 { value: result, is_negative: false } + }else { + let result = addend.value - num; + SignedInteger64 { value: result, is_negative: true } + } + } else { + let result = num + addend.value; + SignedInteger64 { value: result, is_negative: false } + } + } + + /// Create a signed integer value from a unsigned integer + public fun create_from_raw_value(value: u64, is_negative: bool): SignedInteger64 { + SignedInteger64 { value, is_negative } + } + + /// Get value part of i64 ignore sign part. + public fun get_value(num: SignedInteger64): u64 { + num.value + } + + /// Check if the given num is negative. + public fun is_negative(num: SignedInteger64): bool { + num.is_negative + } + + // **************** SPECIFICATIONS **************** + + spec multiply_u64 { + aborts_if multiplier.value * num > max_u64(); + } + + spec divide_u64 { + aborts_if divisor.value == 0; + } + + spec sub_u64 { + aborts_if minus.is_negative && num + minus.value > max_u64(); + } + + spec add_u64 { + aborts_if !addend.is_negative && num + addend.value > max_u64(); + } + + spec create_from_raw_value { + aborts_if false; + ensures result == SignedInteger64 { value, is_negative }; + } + + spec get_value { + aborts_if false; + ensures result == num.value; + } + + spec is_negative { + aborts_if false; + ensures result == num.is_negative; + } + +} +} diff --git a/release/v13/sources/Signer.move b/release/v13/sources/Signer.move new file mode 100644 index 00000000..47ec53be --- /dev/null +++ b/release/v13/sources/Signer.move @@ -0,0 +1,28 @@ +address StarcoinFramework { +/// Provide access methods for Signer. +module Signer { + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + /// Borrows the address of the signer + /// Conceptually, you can think of the `signer` as being a resource struct wrapper around an + /// address + /// ``` + /// resource struct Signer has key, store { addr: address } + /// ``` + /// `borrow_address` borrows this inner field + native public fun borrow_address(s: &signer): &address; + + /// Copies the address of the signer + public fun address_of(s: &signer): address { + *borrow_address(s) + } + + spec address_of { + pragma opaque = true; + aborts_if false; + ensures result == address_of(s); + } +} +} diff --git a/release/v13/sources/SimpleMap.move b/release/v13/sources/SimpleMap.move new file mode 100644 index 00000000..0b7127af --- /dev/null +++ b/release/v13/sources/SimpleMap.move @@ -0,0 +1,289 @@ +address StarcoinFramework { +/// This module provides a solution for sorted maps, that is it has the properties that +/// 1) Keys point to Values +/// 2) Each Key must be unique +/// 3) A Key can be found within O(N) time +/// 4) The keys are unsorted. +/// 5) Adds and removals take O(N) time +module SimpleMap { + use StarcoinFramework::Errors; + use StarcoinFramework::Option; + use StarcoinFramework::Vector; + + /// Map key already exists + const EKEY_ALREADY_EXISTS: u64 = 1; + /// Map key is not found + const EKEY_NOT_FOUND: u64 = 2; + + struct SimpleMap has copy, drop, store { + data: vector>, + } + + struct Element has copy, drop, store { + key: Key, + value: Value, + } + + public fun length(map: &SimpleMap): u64 { + Vector::length(&map.data) + } + + spec length { + pragma intrinsic = true; + } + + public fun create(): SimpleMap { + SimpleMap { + data: Vector::empty(), + } + } + + spec create { + pragma intrinsic = true; + } + + public fun borrow( + map: &SimpleMap, + key: &Key, + ): &Value { + let maybe_idx = find(map, key); + assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND)); + let idx = Option::extract(&mut maybe_idx); + &Vector::borrow(&map.data, idx).value + } + + spec borrow { + pragma intrinsic = true; + } + + + public fun borrow_mut( + map: &mut SimpleMap, + key: &Key, + ): &mut Value { + let maybe_idx = find(map, key); + assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND)); + let idx = Option::extract(&mut maybe_idx); + &mut Vector::borrow_mut(&mut map.data, idx).value + } + + spec borrow_mut { + pragma intrinsic = true; + } + + + public fun contains_key( + map: &SimpleMap, + key: &Key, + ): bool { + let maybe_idx = find(map, key); + Option::is_some(&maybe_idx) + } + + spec contains_key { + pragma intrinsic = true; + } + + + public fun destroy_empty(map: SimpleMap) { + let SimpleMap { data } = map; + Vector::destroy_empty(data); + } + + spec destroy_empty { + pragma intrinsic = true; + } + + public fun add( + map: &mut SimpleMap, + key: Key, + value: Value, + ) { + let maybe_idx = find(map, &key); + assert!(Option::is_none(&maybe_idx), Errors::invalid_argument(EKEY_ALREADY_EXISTS)); + + Vector::push_back(&mut map.data, Element { key, value }); + } + + spec add { + pragma intrinsic = true; + } + + + /// Insert key/value pair or update an existing key to a new value + public fun upsert( + map: &mut SimpleMap, + key: Key, + value: Value + ): (Option::Option, Option::Option) { + let data = &mut map.data; + let len = Vector::length(data); + let i = 0; + while (i < len) { + let element = Vector::borrow(data, i); + if (&element.key == &key) { + Vector::push_back(data, Element { key, value }); + Vector::swap(data, i, len); + let Element { key, value } = Vector::pop_back(data); + return (Option::some(key), Option::some(value)) + }; + i = i + 1; + }; + Vector::push_back(&mut map.data, Element { key, value }); + (Option::none(), Option::none()) + } + + spec upsert { + pragma verify=false; + } + + + public fun remove( + map: &mut SimpleMap, + key: &Key, + ): (Key, Value) { + let maybe_idx = find(map, key); + assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND)); + let placement = Option::extract(&mut maybe_idx); + let Element { key, value } = Vector::swap_remove(&mut map.data, placement); + (key, value) + } + + spec remove { + pragma intrinsic = true; + } + + fun find( + map: &SimpleMap, + key: &Key, + ): Option::Option { + let leng = Vector::length(&map.data); + let i = 0; + while (i < leng) { + let element = Vector::borrow(&map.data, i); + if (&element.key == key) { + return Option::some(i) + }; + i = i + 1; + }; + Option::none() + } + + spec find { + pragma verify=false; + } + + + #[test] + public fun add_remove_many() { + let map = create(); + + assert!(length(&map) == 0, 0); + assert!(!contains_key(&map, &3), 1); + add(&mut map, 3, 1); + assert!(length(&map) == 1, 2); + assert!(contains_key(&map, &3), 3); + assert!(borrow(&map, &3) == &1, 4); + *borrow_mut(&mut map, &3) = 2; + assert!(borrow(&map, &3) == &2, 5); + + assert!(!contains_key(&map, &2), 6); + add(&mut map, 2, 5); + assert!(length(&map) == 2, 7); + assert!(contains_key(&map, &2), 8); + assert!(borrow(&map, &2) == &5, 9); + *borrow_mut(&mut map, &2) = 9; + assert!(borrow(&map, &2) == &9, 10); + + remove(&mut map, &2); + assert!(length(&map) == 1, 11); + assert!(!contains_key(&map, &2), 12); + assert!(borrow(&map, &3) == &2, 13); + + remove(&mut map, &3); + assert!(length(&map) == 0, 14); + assert!(!contains_key(&map, &3), 15); + + destroy_empty(map); + } + + #[test] + public fun test_several() { + let map = create(); + add(&mut map, 6, 6); + add(&mut map, 1, 1); + add(&mut map, 5, 5); + add(&mut map, 2, 2); + add(&mut map, 3, 3); + add(&mut map, 0, 0); + add(&mut map, 7, 7); + add(&mut map, 4, 4); + + assert!(Option::destroy_some(find(&map, &6)) == 0, 100); + assert!(Option::destroy_some(find(&map, &1)) == 1, 101); + assert!(Option::destroy_some(find(&map, &5)) == 2, 102); + assert!(Option::destroy_some(find(&map, &2)) == 3, 103); + assert!(Option::destroy_some(find(&map, &3)) == 4, 104); + assert!(Option::destroy_some(find(&map, &0)) == 5, 105); + assert!(Option::destroy_some(find(&map, &7)) == 6, 106); + assert!(Option::destroy_some(find(&map, &4)) == 7, 107); + + remove(&mut map, &0); + remove(&mut map, &1); + remove(&mut map, &2); + remove(&mut map, &3); + remove(&mut map, &4); + remove(&mut map, &5); + remove(&mut map, &6); + remove(&mut map, &7); + + destroy_empty(map); + } + + #[test] + #[expected_failure] + public fun add_twice() { + let map = create(); + add(&mut map, 3, 1); + add(&mut map, 3, 1); + + remove(&mut map, &3); + destroy_empty(map); + } + + #[test] + #[expected_failure] + public fun remove_twice() { + let map = create(); + add(&mut map, 3, 1); + remove(&mut map, &3); + remove(&mut map, &3); + + destroy_empty(map); + } + + #[test] + public fun upsert_test() { + let map = create(); + // test adding 3 elements using upsert + upsert(&mut map, 1, 1); + upsert(&mut map, 2, 2); + upsert(&mut map, 3, 3); + + assert!(length(&map) == 3, 0); + assert!(contains_key(&map, &1), 1); + assert!(contains_key(&map, &2), 2); + assert!(contains_key(&map, &3), 3); + assert!(borrow(&map, &1) == &1, 4); + assert!(borrow(&map, &2) == &2, 5); + assert!(borrow(&map, &3) == &3, 6); + + // change mapping 1->1 to 1->4 + upsert(&mut map, 1, 4); + + assert!(length(&map) == 3, 7); + assert!(contains_key(&map, &1), 8); + assert!(borrow(&map, &1) == &4, 9); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/StarcoinVerifier.move b/release/v13/sources/StarcoinVerifier.move new file mode 100644 index 00000000..ea985cf1 --- /dev/null +++ b/release/v13/sources/StarcoinVerifier.move @@ -0,0 +1,356 @@ +address StarcoinFramework { +module StarcoinVerifier { + use StarcoinFramework::Vector; + use StarcoinFramework::Option; + use StarcoinFramework::BCS; + use StarcoinFramework::StructuredHash; + use StarcoinFramework::Hash; + + const HASH_LEN_IN_BITS: u64 = 32 * 8; + const SPARSE_MERKLE_LEAF_NODE: vector = b"SparseMerkleLeafNode"; + const SPARSE_MERKLE_INTERNAL_NODE: vector = b"SparseMerkleInternalNode"; + const BLOB_HASH_PREFIX: vector = b"Blob"; + const DEFAULT_VALUE: vector = x""; + const ACCOUNT_STORAGE_INDEX_RESOURCE: u64 = 1; + const ERROR_ACCOUNT_STORAGE_ROOTS: u64 = 101; + const ERROR_LITERAL_HASH_WRONG_LENGTH: u64 = 102; + const SPARSE_MERKLE_PLACEHOLDER_HASH_LITERAL: vector = b"SPARSE_MERKLE_PLACEHOLDER_HASH"; + + + struct AccountState has store, drop, copy { + storage_roots: vector>>, + } + + public fun bcs_deserialize_account_state(data: &vector): AccountState { + let (vec, _) = BCS::deserialize_option_bytes_vector(data, 0); + AccountState{ + storage_roots: vec + } + } + + struct StateProof has store, drop, copy { + /** + * Account state's proof for global state root. + */ + account_proof: SparseMerkleProof, + /** + * Account state including storage roots. + */ + account_state: vector, + /** + * State's proof for account storage root. + */ + proof: SparseMerkleProof, + } + + public fun new_state_proof(account_proof: SparseMerkleProof, account_state: vector, proof: SparseMerkleProof): StateProof { + StateProof{ + account_proof, + account_state, + proof, + } + } + + struct SparseMerkleProof has store, drop, copy { + siblings: vector>, + leaf: SMTNode, + } + + public fun new_sparse_merkle_proof(siblings: vector>, leaf: SMTNode): SparseMerkleProof { + SparseMerkleProof{ + siblings, + leaf, + } + } + + struct SMTNode has store, drop, copy { + hash1: vector, + hash2: vector, + } + + public fun new_smt_node(hash1: vector, hash2: vector): SMTNode { + SMTNode{ + hash1, + hash2, + } + } + + public fun empty_smt_node(): SMTNode { + SMTNode{ + hash1: Vector::empty(), + hash2: Vector::empty(), + } + } + + public fun verify_state_proof(state_proof: &StateProof, state_root: &vector, + account_address: address, resource_struct_tag: &vector, + state: &vector): bool { + let accountState: AccountState = bcs_deserialize_account_state(&state_proof.account_state); + assert!(Vector::length(&accountState.storage_roots) > ACCOUNT_STORAGE_INDEX_RESOURCE, ERROR_ACCOUNT_STORAGE_ROOTS); + + // First, verify state for storage root. + let storageRoot = Option::borrow(Vector::borrow(&accountState.storage_roots, ACCOUNT_STORAGE_INDEX_RESOURCE)); + let ok: bool = verify_smp(&state_proof.proof.siblings, + &state_proof.proof.leaf, + storageRoot, + resource_struct_tag, // resource struct tag BCS serialized as key + state); + if (!ok) { + return false + }; + + // Then, verify account state for global state root. + ok = verify_smp(&state_proof.account_proof.siblings, + &state_proof.account_proof.leaf, + state_root, + &BCS::to_bytes
(&account_address), // account address as key + &state_proof.account_state, + ); + ok + } + + #[test] + fun test_verify_state_proof() { + // miannet, block number 6495396 + let state_root = x"d337896a5cd8bae3d0130e09409c0f5eede159d93af38a642528acb15c1204b8"; + let account_address = @0x47d36856884d7fb9e91a475ea3472341; +// let state = x""; + let state = x"00000000000000000000000000000000"; + // 0x00000000000000000000000000000001::Account::Balance<0x8c109349c6bd91411d6bc962e080c4a3::STAR::STAR> + let resource_struct_tag = x"00000000000000000000000000000001074163636f756e740742616c616e636501078c109349c6bd91411d6bc962e080c4a30453544152045354415200"; + + let account_proof_sibling_nodes = Vector::empty>(); + Vector::push_back(&mut account_proof_sibling_nodes, x"b81050a469dbe041f915cda6942143c691b1735599815142c895f77cf088a656"); + Vector::push_back(&mut account_proof_sibling_nodes, x"5350415253455f4d45524b4c455f504c414345484f4c4445525f484153480000"); + Vector::push_back(&mut account_proof_sibling_nodes, x"1c0cbf70e7474e739db9ca9958470f605f37a5a0f322c7c854b24866c4330577"); + Vector::push_back(&mut account_proof_sibling_nodes, x"68dde7ba4f4a9cf6329675e598a1ab7b545f8de36e5cb8151be8ff167e479c26"); + Vector::push_back(&mut account_proof_sibling_nodes, x"3c59423b2956d25cdacf638540e39dbc53238d36ca1420d31c4321580aeff633"); + Vector::push_back(&mut account_proof_sibling_nodes, x"5befec9a99ad40f1cb6d5b44c87ca6ad26841535cd7fac27f2f88205088b55e3"); + Vector::push_back(&mut account_proof_sibling_nodes, x"32f4d82b78b1339fb5d9c60ac2e49780c87b9e9c675bee9d1cb3ace3f34a63d6"); + Vector::push_back(&mut account_proof_sibling_nodes, x"a60429524592cac0170763196269c4997e08b6a09b2ad45647775486a81559af"); + Vector::push_back(&mut account_proof_sibling_nodes, x"6b6bc4e16bad1fbc6c6df21a923a9be06ae8508827cdd3dbdd4e0e6607abdb6d"); + Vector::push_back(&mut account_proof_sibling_nodes, x"5e6165bc60b30f46611d52f9779668e0fa79eeb60bf3a0d90346b33331156155"); + Vector::push_back(&mut account_proof_sibling_nodes, x"dfc42240b0d542457748e873e3ab0ee362c68dedd91df13532cd85a1a6ea6f00"); + Vector::push_back(&mut account_proof_sibling_nodes, x"610cba3b3c467b137ddbda0f5783ef269357ca4e30aaa2cfecb96e2ee8b2c5e7"); + Vector::push_back(&mut account_proof_sibling_nodes, x"179235bb93b8ece25921f7405a2f797f80630d90f71e763e8a700734d6945b99"); + Vector::push_back(&mut account_proof_sibling_nodes, x"85d80d435c4cb8b8034d32aff6b05230efc43b583154428a0990e1f752adcf3a"); + Vector::push_back(&mut account_proof_sibling_nodes, x"a2f4a3e6f11e6b42d700c920c06a89ffe6d086d5411aa578d56d8de000704669"); + Vector::push_back(&mut account_proof_sibling_nodes, x"ee57409d642877366ac26b2bf8948f7daae3c45a545b931b28a6d902e01bdc1f"); + + let state_proof_sibling_nodes = Vector::empty>(); + Vector::push_back(&mut state_proof_sibling_nodes, x"2a3c2096fbd5a1a2e81077e4b2156c7232b9291ad3d85ea7451eb8b7cda828fd"); + Vector::push_back(&mut state_proof_sibling_nodes, x"4cc4f038091aba95645a5b8153dc088ffdbfb7c7e82dd6b166f187b33eea7432"); + Vector::push_back(&mut state_proof_sibling_nodes, x"aa140a1627b5385e108f6580062110463d09768bce681bbb88c3d9c59680d75d"); + Vector::push_back(&mut state_proof_sibling_nodes, x"7c8f59d557168dd5667fcc950ed444cfa0cbce778e032c918a8b2a2084c48c03"); + Vector::push_back(&mut state_proof_sibling_nodes, x"a0718e77be611f67f880f5fc4d7c801940aae65890f02b38a663458ed924c2f9"); + + let proof = new_state_proof( + new_sparse_merkle_proof( + account_proof_sibling_nodes, + new_smt_node( + x"3fe0547cb3576cad025fb5cfa98b85a3545a41e5b14e844fd8cad5edaa619c05", + x"ed0e07d03371a130b84bea8245f84bf546955e5a190afc20ef34f310614c6d10", + ), + ), + x"02000120ceaafd667b54252bba61993770d87bbb997b1a689b0e08899543e3c8f82adca7", + new_sparse_merkle_proof( + state_proof_sibling_nodes, + new_smt_node( + x"1de0d92e4e770fa53ceaa12c83edb8c0e51c1d19499d769c572ffd9d38cef40f", + x"6493204f1b87055adb8937a385daa238b3c08c491026f2ac50ebe4dab9133030", + ), + ), + ); + + let b = verify_state_proof( + &proof, + &state_root, + account_address, + &resource_struct_tag, + &state, + ); + assert!(b, 1110); + } + + /// Verify sparse merkle proof by key and value. + public fun verify_smp(sibling_nodes: &vector>, leaf_data: &SMTNode, expected_root: &vector, key: &vector, value: &vector): bool { + let path = hash_key(key); + let current_hash: vector; + if (*value == DEFAULT_VALUE) { + // Non-membership proof. + if (empty_smt_node() == *leaf_data) { + current_hash = placeholder(); + } else { + if (*&leaf_data.hash1 == *&path) { + return false + }; + if (!(count_common_prefix(&leaf_data.hash1, &path) >= Vector::length(sibling_nodes))) { + return false + }; + current_hash = StructuredHash::hash(SPARSE_MERKLE_LEAF_NODE, leaf_data); + }; + } else { + // Membership proof. + if (empty_smt_node() == *leaf_data) { + return false + }; + if (*&leaf_data.hash1 != *&path) { + return false + }; + let value_hash = hash_value(value); + if (*&leaf_data.hash2 != value_hash) { + return false + }; + current_hash = StructuredHash::hash(SPARSE_MERKLE_LEAF_NODE, leaf_data); + }; + + current_hash = compute_smp_root_by_path_and_node_hash(sibling_nodes, &path, ¤t_hash); + current_hash == *expected_root + } + +// #[test] +// fun test_print_storage_root(){ +// let account_state = x"02000120ceaafd667b54252bba61993770d87bbb997b1a689b0e08899543e3c8f82adca7"; +// let accountState: AccountState = bcs_deserialize_account_state(&account_state); +// let storageRoot = Option::borrow(Vector::borrow(&accountState.storage_roots, ACCOUNT_STORAGE_INDEX_RESOURCE)); +// Debug::print(storageRoot); +// } + + #[test] + fun test_verify_smp() { + let sibling_nodes = Vector::empty>(); + Vector::push_back(&mut sibling_nodes, x"2a3c2096fbd5a1a2e81077e4b2156c7232b9291ad3d85ea7451eb8b7cda828fd"); + Vector::push_back(&mut sibling_nodes, x"4cc4f038091aba95645a5b8153dc088ffdbfb7c7e82dd6b166f187b33eea7432"); + Vector::push_back(&mut sibling_nodes, x"aa140a1627b5385e108f6580062110463d09768bce681bbb88c3d9c59680d75d"); + Vector::push_back(&mut sibling_nodes, x"7c8f59d557168dd5667fcc950ed444cfa0cbce778e032c918a8b2a2084c48c03"); + Vector::push_back(&mut sibling_nodes, x"a0718e77be611f67f880f5fc4d7c801940aae65890f02b38a663458ed924c2f9"); + + let leaf_node = new_smt_node( + x"1de0d92e4e770fa53ceaa12c83edb8c0e51c1d19499d769c572ffd9d38cef40f", + x"6493204f1b87055adb8937a385daa238b3c08c491026f2ac50ebe4dab9133030", + ); + let account_state = x"02000120ceaafd667b54252bba61993770d87bbb997b1a689b0e08899543e3c8f82adca7"; + let accountState: AccountState = bcs_deserialize_account_state(&account_state); + let expected_root = Option::borrow(Vector::borrow(&accountState.storage_roots, ACCOUNT_STORAGE_INDEX_RESOURCE)); +// let expected_root = x"0f30a41872208c6324fa842889315b14f9be6f3dd0d5050686317adfdd0cda60"; + let key = x"00000000000000000000000000000001074163636f756e740742616c616e636501078c109349c6bd91411d6bc962e080c4a30453544152045354415200"; + // Above key is this StructTag BCS serialized bytes: + let value = x"00000000000000000000000000000000"; + + let b = verify_smp(&sibling_nodes, &leaf_node, expected_root, &key, &value); + assert!(b, 1112) + } + + #[test] + fun test_verify_smp_2() { + let sibling_nodes: vector> = Vector::empty(); + let leaf_data: SMTNode = empty_smt_node(); + let expected_root: vector = placeholder(); + let key: vector = b"random key"; + let value: vector = Vector::empty(); //x"" + let b = verify_smp(&sibling_nodes, &leaf_data, &expected_root, &key, &value); + assert!(b, 1113); + + value = b"random value"; + b = verify_smp(&sibling_nodes, &leaf_data, &expected_root, &key, &value); + assert!(!b, 1114); + } + + public fun compute_smp_root_by_path_and_node_hash(sibling_nodes: &vector>, path: &vector, node_hash: &vector): vector { + let current_hash = *node_hash; + let i = 0; + let proof_length = Vector::length(sibling_nodes); + while (i < proof_length) { + let sibling = *Vector::borrow(sibling_nodes, i); + let bit = get_bit_at_from_msb(path, proof_length - i - 1); + let internal_node = if (bit) { + SMTNode{ hash1: sibling, hash2: current_hash } + } else { + SMTNode{ hash1: current_hash, hash2: sibling } + }; + current_hash = StructuredHash::hash(SPARSE_MERKLE_INTERNAL_NODE, &internal_node); + i = i + 1; + }; + current_hash + } + + public fun placeholder(): vector { + create_literal_hash(&SPARSE_MERKLE_PLACEHOLDER_HASH_LITERAL) + } + + public fun create_literal_hash(word: &vector): vector { + if (Vector::length(word) <= 32) { + let lenZero = 32 - Vector::length(word); + let i = 0; + let r = *word; + while (i < lenZero) { + Vector::push_back(&mut r, 0); + i = i + 1; + }; + return r + }; + abort ERROR_LITERAL_HASH_WRONG_LENGTH + } + + #[test] + fun test_create_literal_hash() { + let word = b"SPARSE_MERKLE_PLACEHOLDER_HASH"; + let r = create_literal_hash(&word); + assert!(r == x"5350415253455f4d45524b4c455f504c414345484f4c4445525f484153480000", 1115); + } + + fun hash_key(key: &vector): vector { + Hash::sha3_256(*key) + } + + fun hash_value(value: &vector): vector { + StructuredHash::hash(BLOB_HASH_PREFIX, value) + } + + fun count_common_prefix(data1: &vector, data2: &vector): u64 { + let count = 0; + let i = 0; + while ( i < Vector::length(data1) * 8) { + if (get_bit_at_from_msb(data1, i) == get_bit_at_from_msb(data2, i)) { + count = count + 1; + } else { + break + }; + i = i + 1; + }; + count + } + + fun get_bit_at_from_msb(data: &vector, index: u64): bool { + let pos = index / 8; + let bit = (7 - index % 8); + (*Vector::borrow(data, pos) >> (bit as u8)) & 1u8 != 0 + } + + spec get_bit_at_from_msb { + pragma verify = false; // Bitwise operator + pragma opaque; + } +} + +module StructuredHash { + use StarcoinFramework::Hash; + use StarcoinFramework::Vector; + use StarcoinFramework::BCS; + + const STARCOIN_HASH_PREFIX: vector = b"STARCOIN::"; + + public fun hash(structure: vector, data: &MoveValue): vector { + let prefix_hash = Hash::sha3_256(concat(&STARCOIN_HASH_PREFIX, structure)); + let bcs_bytes = BCS::to_bytes(data); + Hash::sha3_256(concat(&prefix_hash, bcs_bytes)) + } + + fun concat(v1: &vector, v2: vector): vector { + let data = *v1; + Vector::append(&mut data, v2); + data + } +} + +} diff --git a/release/v13/sources/StdlibUpgradeScripts.move b/release/v13/sources/StdlibUpgradeScripts.move new file mode 100644 index 00000000..8b7cd6bf --- /dev/null +++ b/release/v13/sources/StdlibUpgradeScripts.move @@ -0,0 +1,133 @@ +address StarcoinFramework { +/// The module for StdlibUpgrade init scripts +module StdlibUpgradeScripts { + + use StarcoinFramework::Math::u64_max; + use StarcoinFramework::FlexiDagConfig; + use StarcoinFramework::EasyGas; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::STC::{Self, STC}; + use StarcoinFramework::Token::{Self, LinearTimeMintKey}; + use StarcoinFramework::TreasuryWithdrawDaoProposal; + use StarcoinFramework::Treasury::{Self, LinearWithdrawCapability}; + use StarcoinFramework::Offer; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Collection; + use StarcoinFramework::Oracle; + use StarcoinFramework::STCUSDOracle; + use StarcoinFramework::NFT; + use StarcoinFramework::GenesisNFT; + use StarcoinFramework::LanguageVersion; + use StarcoinFramework::OnChainConfigDao; + use StarcoinFramework::Config; + use StarcoinFramework::GenesisSignerCapability; + use StarcoinFramework::Account; + use StarcoinFramework::Block; + use StarcoinFramework::GasSchedule; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + /// Stdlib upgrade script from v2 to v3 + public entry fun upgrade_from_v2_to_v3(account: signer, total_stc_amount: u128 ) { + CoreAddresses::assert_genesis_address(&account); + + let withdraw_cap = STC::upgrade_from_v1_to_v2(&account, total_stc_amount); + + let mint_keys = Collection::borrow_collection>(CoreAddresses::ASSOCIATION_ROOT_ADDRESS()); + let mint_key = Collection::borrow(&mint_keys, 0); + let (total, minted, start_time, period) = Token::read_linear_time_key(mint_key); + Collection::return_collection(mint_keys); + + let now = Timestamp::now_seconds(); + let linear_withdraw_cap = Treasury::issue_linear_withdraw_capability(&mut withdraw_cap, total-minted, period - (now - start_time)); + // Lock the TreasuryWithdrawCapability to Dao + TreasuryWithdrawDaoProposal::plugin(&account, withdraw_cap); + // Give a LinearWithdrawCapability Offer to association, association need to take the offer, and destroy old LinearTimeMintKey. + Offer::create(&account, linear_withdraw_cap, CoreAddresses::ASSOCIATION_ROOT_ADDRESS(), 0); + } + + /// association account should call this script after upgrade from v2 to v3. + public entry fun take_linear_withdraw_capability(signer: signer){ + let offered = Offer::redeem>(&signer, CoreAddresses::GENESIS_ADDRESS()); + Treasury::add_linear_withdraw_capability(&signer, offered); + let mint_key = Collection::take>(&signer); + Token::destroy_linear_time_key(mint_key); + } + + public fun do_upgrade_from_v5_to_v6(sender: &signer) { + CoreAddresses::assert_genesis_address(sender); + Oracle::initialize(sender); + //register oracle + STCUSDOracle::register(sender); + NFT::initialize(sender); + let merkle_root = x"5969f0e8e19f8769276fb638e6060d5c02e40088f5fde70a6778dd69d659ee6d"; + let image = b"ipfs://QmSPcvcXgdtHHiVTAAarzTeubk5X3iWymPAoKBfiRFjPMY"; + GenesisNFT::initialize(sender, merkle_root, 1639u64, image); + } + + public entry fun upgrade_from_v5_to_v6(sender: signer) { + Self::do_upgrade_from_v5_to_v6(&sender) + } + + public entry fun upgrade_from_v6_to_v7(sender: signer) { + Self::do_upgrade_from_v6_to_v7_with_language_version(&sender, 2); + } + + /// deprecated, use `do_upgrade_from_v6_to_v7_with_language_version`. + public fun do_upgrade_from_v6_to_v7(sender: &signer) { + do_upgrade_from_v6_to_v7_with_language_version(sender, 2); + } + + public fun do_upgrade_from_v6_to_v7_with_language_version(sender: &signer, language_version: u64) { + // initialize the language version config. + Config::publish_new_config(sender, LanguageVersion::new(language_version)); + // use STC Dao to upgrade onchain's move-language-version configuration. + OnChainConfigDao::plugin(sender); + // upgrade genesis NFT + GenesisNFT::upgrade_to_nft_type_info_v2(sender); + } + + public entry fun upgrade_from_v7_to_v8(sender: signer) { + do_upgrade_from_v7_to_v8(&sender); + } + public fun do_upgrade_from_v7_to_v8(sender: &signer) { + { + let cap = Oracle::extract_signer_cap(sender); + GenesisSignerCapability::initialize(sender, cap); + }; + + { + let cap = NFT::extract_signer_cap(sender); + Account::destroy_signer_cap(cap); + }; + } + + public entry fun upgrade_from_v11_to_v12(sender: signer) { + do_upgrade_from_v11_to_v12(&sender); + } + public fun do_upgrade_from_v11_to_v12(sender: &signer) { + { + GasSchedule::initialize(sender,GasSchedule::new_gas_schedule()); + let address = @0x8c109349c6bd91411d6bc962e080c4a3; + EasyGas::initialize(sender, + address, + b"STAR",b"STAR", + address); + Block::checkpoints_init(sender); + }; + } + + public entry fun upgrade_from_v12_to_v13(sender: signer) { + do_upgrade_from_v12_to_v13(&sender); + } + public fun do_upgrade_from_v12_to_v13(sender: &signer) { + { + FlexiDagConfig::initialize(sender, u64_max()); + OnChainConfigDao::plugin(sender); + }; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/String.move b/release/v13/sources/String.move new file mode 100644 index 00000000..12c98456 --- /dev/null +++ b/release/v13/sources/String.move @@ -0,0 +1,128 @@ +/// The `string` module defines the `String` type which represents UTF8 encoded strings. +module StarcoinFramework::String { + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + use StarcoinFramework::Option::{Self, Option}; + + /// An invalid UTF8 encoding. + const EINVALID_UTF8: u64 = 1; + + /// Index out of range. + const EINVALID_INDEX: u64 = 2; + + /// A `String` holds a sequence of bytes which is guaranteed to be in utf8 format. + struct String has copy, drop, store { + bytes: vector, + } + + /// Creates a new string from a sequence of bytes. Aborts if the bytes do not represent valid utf8. + public fun utf8(bytes: vector): String { + assert!(internal_check_utf8(&bytes), Errors::invalid_state(EINVALID_UTF8)); + String{bytes} + } + + spec fun spec_utf8(bytes: vector): String { + String{bytes} + } + + /// Tries to create a new string from a sequence of bytes. + public fun try_utf8(bytes: vector): Option { + if (internal_check_utf8(&bytes)) { + Option::some(String{bytes}) + } else { + Option::none() + } + } + + /// Returns a reference to the underlying byte vector. + public fun bytes(s: &String): &vector { + &s.bytes + } + + /// Checks whether this string is empty. + public fun is_empty(s: &String): bool { + Vector::is_empty(&s.bytes) + } + + /// Returns the length of this string, in bytes. + public fun length(s: &String): u64 { + Vector::length(&s.bytes) + } + + /// Appends a string. + public fun append(s: &mut String, r: String) { + Vector::append(&mut s.bytes, *&r.bytes) + } + + /// Appends bytes which must be in valid utf8 format. + public fun append_utf8(s: &mut String, bytes: vector) { + append(s, utf8(bytes)) + } + + /// Insert the other string at the byte index in given string. The index must be at a valid utf8 char + /// boundary. + public fun insert(s: &mut String, at: u64, o: String) { + let bytes = &s.bytes; + assert!(at <= Vector::length(bytes) && internal_is_char_boundary(bytes, at), Errors::invalid_state(EINVALID_INDEX)); + let l = length(s); + let front = sub_string(s, 0, at); + let end = sub_string(s, at, l); + append(&mut front, o); + append(&mut front, end); + *s = front; + } + + /// Returns a sub-string using the given byte indices, where `i` is the first byte position and `j` is the start + /// of the first byte not included (or the length of the string). The indices must be at valid utf8 char boundaries, + /// guaranteeing that the result is valid utf8. + public fun sub_string(s: &String, i: u64, j: u64): String { + let bytes = &s.bytes; + let l = Vector::length(bytes); + assert!( + j <= l && i <= j && internal_is_char_boundary(bytes, i) && internal_is_char_boundary(bytes, j), + Errors::invalid_state(EINVALID_INDEX) + ); + String{bytes: internal_sub_string(bytes, i, j)} + } + + /// Computes the index of the first occurrence of a string. Returns `length(s)` if no occurrence found. + public fun index_of(s: &String, r: &String): u64 { + internal_index_of(&s.bytes, &r.bytes) + } + + + // Native API + native fun internal_check_utf8(v: &vector): bool; + native fun internal_is_char_boundary(v: &vector, i: u64): bool; + native fun internal_sub_string(v: &vector, i: u64, j: u64): vector; + native fun internal_index_of(v: &vector, r: &vector): u64; + + spec internal_check_utf8(v: &vector): bool { + pragma opaque; + aborts_if [abstract] false; + ensures [abstract] result == spec_internal_check_utf8(v); + } + + spec internal_is_char_boundary(v: &vector, i: u64): bool { + pragma opaque; + aborts_if [abstract] false; + ensures [abstract] result == spec_internal_is_char_boundary(v, i); + } + spec internal_sub_string(v: &vector, i: u64, j: u64): vector { + pragma opaque; + aborts_if [abstract] false; + ensures [abstract] result == spec_internal_sub_string(v, i, j); + } + spec internal_index_of(v: &vector, r: &vector): u64 { + pragma opaque; + aborts_if [abstract] false; + ensures [abstract] result == spec_internal_index_of(v, r); + } + + spec module { + fun spec_internal_check_utf8(v: vector): bool; + fun spec_internal_is_char_boundary(v: vector, i: u64): bool; + fun spec_internal_sub_string(v: vector, i: u64, j: u64): vector; + fun spec_internal_index_of(v: vector, r: vector): u64; + } +} \ No newline at end of file diff --git a/release/v13/sources/StructuredHash.move b/release/v13/sources/StructuredHash.move new file mode 100644 index 00000000..ea985cf1 --- /dev/null +++ b/release/v13/sources/StructuredHash.move @@ -0,0 +1,356 @@ +address StarcoinFramework { +module StarcoinVerifier { + use StarcoinFramework::Vector; + use StarcoinFramework::Option; + use StarcoinFramework::BCS; + use StarcoinFramework::StructuredHash; + use StarcoinFramework::Hash; + + const HASH_LEN_IN_BITS: u64 = 32 * 8; + const SPARSE_MERKLE_LEAF_NODE: vector = b"SparseMerkleLeafNode"; + const SPARSE_MERKLE_INTERNAL_NODE: vector = b"SparseMerkleInternalNode"; + const BLOB_HASH_PREFIX: vector = b"Blob"; + const DEFAULT_VALUE: vector = x""; + const ACCOUNT_STORAGE_INDEX_RESOURCE: u64 = 1; + const ERROR_ACCOUNT_STORAGE_ROOTS: u64 = 101; + const ERROR_LITERAL_HASH_WRONG_LENGTH: u64 = 102; + const SPARSE_MERKLE_PLACEHOLDER_HASH_LITERAL: vector = b"SPARSE_MERKLE_PLACEHOLDER_HASH"; + + + struct AccountState has store, drop, copy { + storage_roots: vector>>, + } + + public fun bcs_deserialize_account_state(data: &vector): AccountState { + let (vec, _) = BCS::deserialize_option_bytes_vector(data, 0); + AccountState{ + storage_roots: vec + } + } + + struct StateProof has store, drop, copy { + /** + * Account state's proof for global state root. + */ + account_proof: SparseMerkleProof, + /** + * Account state including storage roots. + */ + account_state: vector, + /** + * State's proof for account storage root. + */ + proof: SparseMerkleProof, + } + + public fun new_state_proof(account_proof: SparseMerkleProof, account_state: vector, proof: SparseMerkleProof): StateProof { + StateProof{ + account_proof, + account_state, + proof, + } + } + + struct SparseMerkleProof has store, drop, copy { + siblings: vector>, + leaf: SMTNode, + } + + public fun new_sparse_merkle_proof(siblings: vector>, leaf: SMTNode): SparseMerkleProof { + SparseMerkleProof{ + siblings, + leaf, + } + } + + struct SMTNode has store, drop, copy { + hash1: vector, + hash2: vector, + } + + public fun new_smt_node(hash1: vector, hash2: vector): SMTNode { + SMTNode{ + hash1, + hash2, + } + } + + public fun empty_smt_node(): SMTNode { + SMTNode{ + hash1: Vector::empty(), + hash2: Vector::empty(), + } + } + + public fun verify_state_proof(state_proof: &StateProof, state_root: &vector, + account_address: address, resource_struct_tag: &vector, + state: &vector): bool { + let accountState: AccountState = bcs_deserialize_account_state(&state_proof.account_state); + assert!(Vector::length(&accountState.storage_roots) > ACCOUNT_STORAGE_INDEX_RESOURCE, ERROR_ACCOUNT_STORAGE_ROOTS); + + // First, verify state for storage root. + let storageRoot = Option::borrow(Vector::borrow(&accountState.storage_roots, ACCOUNT_STORAGE_INDEX_RESOURCE)); + let ok: bool = verify_smp(&state_proof.proof.siblings, + &state_proof.proof.leaf, + storageRoot, + resource_struct_tag, // resource struct tag BCS serialized as key + state); + if (!ok) { + return false + }; + + // Then, verify account state for global state root. + ok = verify_smp(&state_proof.account_proof.siblings, + &state_proof.account_proof.leaf, + state_root, + &BCS::to_bytes
(&account_address), // account address as key + &state_proof.account_state, + ); + ok + } + + #[test] + fun test_verify_state_proof() { + // miannet, block number 6495396 + let state_root = x"d337896a5cd8bae3d0130e09409c0f5eede159d93af38a642528acb15c1204b8"; + let account_address = @0x47d36856884d7fb9e91a475ea3472341; +// let state = x""; + let state = x"00000000000000000000000000000000"; + // 0x00000000000000000000000000000001::Account::Balance<0x8c109349c6bd91411d6bc962e080c4a3::STAR::STAR> + let resource_struct_tag = x"00000000000000000000000000000001074163636f756e740742616c616e636501078c109349c6bd91411d6bc962e080c4a30453544152045354415200"; + + let account_proof_sibling_nodes = Vector::empty>(); + Vector::push_back(&mut account_proof_sibling_nodes, x"b81050a469dbe041f915cda6942143c691b1735599815142c895f77cf088a656"); + Vector::push_back(&mut account_proof_sibling_nodes, x"5350415253455f4d45524b4c455f504c414345484f4c4445525f484153480000"); + Vector::push_back(&mut account_proof_sibling_nodes, x"1c0cbf70e7474e739db9ca9958470f605f37a5a0f322c7c854b24866c4330577"); + Vector::push_back(&mut account_proof_sibling_nodes, x"68dde7ba4f4a9cf6329675e598a1ab7b545f8de36e5cb8151be8ff167e479c26"); + Vector::push_back(&mut account_proof_sibling_nodes, x"3c59423b2956d25cdacf638540e39dbc53238d36ca1420d31c4321580aeff633"); + Vector::push_back(&mut account_proof_sibling_nodes, x"5befec9a99ad40f1cb6d5b44c87ca6ad26841535cd7fac27f2f88205088b55e3"); + Vector::push_back(&mut account_proof_sibling_nodes, x"32f4d82b78b1339fb5d9c60ac2e49780c87b9e9c675bee9d1cb3ace3f34a63d6"); + Vector::push_back(&mut account_proof_sibling_nodes, x"a60429524592cac0170763196269c4997e08b6a09b2ad45647775486a81559af"); + Vector::push_back(&mut account_proof_sibling_nodes, x"6b6bc4e16bad1fbc6c6df21a923a9be06ae8508827cdd3dbdd4e0e6607abdb6d"); + Vector::push_back(&mut account_proof_sibling_nodes, x"5e6165bc60b30f46611d52f9779668e0fa79eeb60bf3a0d90346b33331156155"); + Vector::push_back(&mut account_proof_sibling_nodes, x"dfc42240b0d542457748e873e3ab0ee362c68dedd91df13532cd85a1a6ea6f00"); + Vector::push_back(&mut account_proof_sibling_nodes, x"610cba3b3c467b137ddbda0f5783ef269357ca4e30aaa2cfecb96e2ee8b2c5e7"); + Vector::push_back(&mut account_proof_sibling_nodes, x"179235bb93b8ece25921f7405a2f797f80630d90f71e763e8a700734d6945b99"); + Vector::push_back(&mut account_proof_sibling_nodes, x"85d80d435c4cb8b8034d32aff6b05230efc43b583154428a0990e1f752adcf3a"); + Vector::push_back(&mut account_proof_sibling_nodes, x"a2f4a3e6f11e6b42d700c920c06a89ffe6d086d5411aa578d56d8de000704669"); + Vector::push_back(&mut account_proof_sibling_nodes, x"ee57409d642877366ac26b2bf8948f7daae3c45a545b931b28a6d902e01bdc1f"); + + let state_proof_sibling_nodes = Vector::empty>(); + Vector::push_back(&mut state_proof_sibling_nodes, x"2a3c2096fbd5a1a2e81077e4b2156c7232b9291ad3d85ea7451eb8b7cda828fd"); + Vector::push_back(&mut state_proof_sibling_nodes, x"4cc4f038091aba95645a5b8153dc088ffdbfb7c7e82dd6b166f187b33eea7432"); + Vector::push_back(&mut state_proof_sibling_nodes, x"aa140a1627b5385e108f6580062110463d09768bce681bbb88c3d9c59680d75d"); + Vector::push_back(&mut state_proof_sibling_nodes, x"7c8f59d557168dd5667fcc950ed444cfa0cbce778e032c918a8b2a2084c48c03"); + Vector::push_back(&mut state_proof_sibling_nodes, x"a0718e77be611f67f880f5fc4d7c801940aae65890f02b38a663458ed924c2f9"); + + let proof = new_state_proof( + new_sparse_merkle_proof( + account_proof_sibling_nodes, + new_smt_node( + x"3fe0547cb3576cad025fb5cfa98b85a3545a41e5b14e844fd8cad5edaa619c05", + x"ed0e07d03371a130b84bea8245f84bf546955e5a190afc20ef34f310614c6d10", + ), + ), + x"02000120ceaafd667b54252bba61993770d87bbb997b1a689b0e08899543e3c8f82adca7", + new_sparse_merkle_proof( + state_proof_sibling_nodes, + new_smt_node( + x"1de0d92e4e770fa53ceaa12c83edb8c0e51c1d19499d769c572ffd9d38cef40f", + x"6493204f1b87055adb8937a385daa238b3c08c491026f2ac50ebe4dab9133030", + ), + ), + ); + + let b = verify_state_proof( + &proof, + &state_root, + account_address, + &resource_struct_tag, + &state, + ); + assert!(b, 1110); + } + + /// Verify sparse merkle proof by key and value. + public fun verify_smp(sibling_nodes: &vector>, leaf_data: &SMTNode, expected_root: &vector, key: &vector, value: &vector): bool { + let path = hash_key(key); + let current_hash: vector; + if (*value == DEFAULT_VALUE) { + // Non-membership proof. + if (empty_smt_node() == *leaf_data) { + current_hash = placeholder(); + } else { + if (*&leaf_data.hash1 == *&path) { + return false + }; + if (!(count_common_prefix(&leaf_data.hash1, &path) >= Vector::length(sibling_nodes))) { + return false + }; + current_hash = StructuredHash::hash(SPARSE_MERKLE_LEAF_NODE, leaf_data); + }; + } else { + // Membership proof. + if (empty_smt_node() == *leaf_data) { + return false + }; + if (*&leaf_data.hash1 != *&path) { + return false + }; + let value_hash = hash_value(value); + if (*&leaf_data.hash2 != value_hash) { + return false + }; + current_hash = StructuredHash::hash(SPARSE_MERKLE_LEAF_NODE, leaf_data); + }; + + current_hash = compute_smp_root_by_path_and_node_hash(sibling_nodes, &path, ¤t_hash); + current_hash == *expected_root + } + +// #[test] +// fun test_print_storage_root(){ +// let account_state = x"02000120ceaafd667b54252bba61993770d87bbb997b1a689b0e08899543e3c8f82adca7"; +// let accountState: AccountState = bcs_deserialize_account_state(&account_state); +// let storageRoot = Option::borrow(Vector::borrow(&accountState.storage_roots, ACCOUNT_STORAGE_INDEX_RESOURCE)); +// Debug::print(storageRoot); +// } + + #[test] + fun test_verify_smp() { + let sibling_nodes = Vector::empty>(); + Vector::push_back(&mut sibling_nodes, x"2a3c2096fbd5a1a2e81077e4b2156c7232b9291ad3d85ea7451eb8b7cda828fd"); + Vector::push_back(&mut sibling_nodes, x"4cc4f038091aba95645a5b8153dc088ffdbfb7c7e82dd6b166f187b33eea7432"); + Vector::push_back(&mut sibling_nodes, x"aa140a1627b5385e108f6580062110463d09768bce681bbb88c3d9c59680d75d"); + Vector::push_back(&mut sibling_nodes, x"7c8f59d557168dd5667fcc950ed444cfa0cbce778e032c918a8b2a2084c48c03"); + Vector::push_back(&mut sibling_nodes, x"a0718e77be611f67f880f5fc4d7c801940aae65890f02b38a663458ed924c2f9"); + + let leaf_node = new_smt_node( + x"1de0d92e4e770fa53ceaa12c83edb8c0e51c1d19499d769c572ffd9d38cef40f", + x"6493204f1b87055adb8937a385daa238b3c08c491026f2ac50ebe4dab9133030", + ); + let account_state = x"02000120ceaafd667b54252bba61993770d87bbb997b1a689b0e08899543e3c8f82adca7"; + let accountState: AccountState = bcs_deserialize_account_state(&account_state); + let expected_root = Option::borrow(Vector::borrow(&accountState.storage_roots, ACCOUNT_STORAGE_INDEX_RESOURCE)); +// let expected_root = x"0f30a41872208c6324fa842889315b14f9be6f3dd0d5050686317adfdd0cda60"; + let key = x"00000000000000000000000000000001074163636f756e740742616c616e636501078c109349c6bd91411d6bc962e080c4a30453544152045354415200"; + // Above key is this StructTag BCS serialized bytes: + let value = x"00000000000000000000000000000000"; + + let b = verify_smp(&sibling_nodes, &leaf_node, expected_root, &key, &value); + assert!(b, 1112) + } + + #[test] + fun test_verify_smp_2() { + let sibling_nodes: vector> = Vector::empty(); + let leaf_data: SMTNode = empty_smt_node(); + let expected_root: vector = placeholder(); + let key: vector = b"random key"; + let value: vector = Vector::empty(); //x"" + let b = verify_smp(&sibling_nodes, &leaf_data, &expected_root, &key, &value); + assert!(b, 1113); + + value = b"random value"; + b = verify_smp(&sibling_nodes, &leaf_data, &expected_root, &key, &value); + assert!(!b, 1114); + } + + public fun compute_smp_root_by_path_and_node_hash(sibling_nodes: &vector>, path: &vector, node_hash: &vector): vector { + let current_hash = *node_hash; + let i = 0; + let proof_length = Vector::length(sibling_nodes); + while (i < proof_length) { + let sibling = *Vector::borrow(sibling_nodes, i); + let bit = get_bit_at_from_msb(path, proof_length - i - 1); + let internal_node = if (bit) { + SMTNode{ hash1: sibling, hash2: current_hash } + } else { + SMTNode{ hash1: current_hash, hash2: sibling } + }; + current_hash = StructuredHash::hash(SPARSE_MERKLE_INTERNAL_NODE, &internal_node); + i = i + 1; + }; + current_hash + } + + public fun placeholder(): vector { + create_literal_hash(&SPARSE_MERKLE_PLACEHOLDER_HASH_LITERAL) + } + + public fun create_literal_hash(word: &vector): vector { + if (Vector::length(word) <= 32) { + let lenZero = 32 - Vector::length(word); + let i = 0; + let r = *word; + while (i < lenZero) { + Vector::push_back(&mut r, 0); + i = i + 1; + }; + return r + }; + abort ERROR_LITERAL_HASH_WRONG_LENGTH + } + + #[test] + fun test_create_literal_hash() { + let word = b"SPARSE_MERKLE_PLACEHOLDER_HASH"; + let r = create_literal_hash(&word); + assert!(r == x"5350415253455f4d45524b4c455f504c414345484f4c4445525f484153480000", 1115); + } + + fun hash_key(key: &vector): vector { + Hash::sha3_256(*key) + } + + fun hash_value(value: &vector): vector { + StructuredHash::hash(BLOB_HASH_PREFIX, value) + } + + fun count_common_prefix(data1: &vector, data2: &vector): u64 { + let count = 0; + let i = 0; + while ( i < Vector::length(data1) * 8) { + if (get_bit_at_from_msb(data1, i) == get_bit_at_from_msb(data2, i)) { + count = count + 1; + } else { + break + }; + i = i + 1; + }; + count + } + + fun get_bit_at_from_msb(data: &vector, index: u64): bool { + let pos = index / 8; + let bit = (7 - index % 8); + (*Vector::borrow(data, pos) >> (bit as u8)) & 1u8 != 0 + } + + spec get_bit_at_from_msb { + pragma verify = false; // Bitwise operator + pragma opaque; + } +} + +module StructuredHash { + use StarcoinFramework::Hash; + use StarcoinFramework::Vector; + use StarcoinFramework::BCS; + + const STARCOIN_HASH_PREFIX: vector = b"STARCOIN::"; + + public fun hash(structure: vector, data: &MoveValue): vector { + let prefix_hash = Hash::sha3_256(concat(&STARCOIN_HASH_PREFIX, structure)); + let bcs_bytes = BCS::to_bytes(data); + Hash::sha3_256(concat(&prefix_hash, bcs_bytes)) + } + + fun concat(v1: &vector, v2: vector): vector { + let data = *v1; + Vector::append(&mut data, v2); + data + } +} + +} diff --git a/release/v13/sources/Table.move b/release/v13/sources/Table.move new file mode 100644 index 00000000..8993f0c5 --- /dev/null +++ b/release/v13/sources/Table.move @@ -0,0 +1,235 @@ +address StarcoinFramework { +/// Type of large-scale storage tables. +module Table { + use StarcoinFramework::Errors; + + // native code raises this with Errors::invalid_arguments() + const EALREADY_EXISTS: u64 = 100; + // native code raises this with Errors::invalid_arguments() + const ENOT_FOUND: u64 = 101; + const ENOT_EMPTY: u64 = 102; + + /// Type of tables + struct Table has store { + handle: address, + length: u64, + } + + spec Table { + pragma intrinsic = map, + map_new = new, + map_destroy_empty = destroy_empty, + map_len = length, + map_is_empty = empty, + map_has_key = contains, + map_add_no_override = add, + map_del_must_exist = remove, + map_borrow = borrow, + map_borrow_mut = borrow_mut, + map_spec_get = spec_get, + map_spec_set = spec_set, + map_spec_del = spec_remove, + map_spec_len = spec_len, + map_spec_has_key = spec_contains; + } + + /// Create a new Table. + public fun new(): Table { + Table{ + handle: new_table_handle(), + length: 0, + } + } + + spec new { + pragma intrinsic; + } + + + /// Destroy a table. The table must be empty to succeed. + public fun destroy_empty(table: Table) { + assert!(table.length == 0, Errors::invalid_state(ENOT_EMPTY)); + destroy_empty_box>(&table); + drop_unchecked_box>(table) + } + + spec destroy_empty { + pragma intrinsic; + } + + /// Add a new entry to the table. Aborts if an entry for this + /// key already exists. The entry itself is not stored in the + /// table, and cannot be discovered from it. + public fun add(table: &mut Table, key: K, val: V) { + add_box>(table, key, Box{val}); + table.length = table.length + 1 + } + + spec add { + pragma intrinsic; + } + + /// Acquire an immutable reference to the value which `key` maps to. + /// Aborts if there is no entry for `key`. + public fun borrow(table: &Table, key: K): &V { + &borrow_box>(table, key).val + } + + spec borrow { + pragma intrinsic; + } + + /// Acquire an immutable reference to the value which `key` maps to. + /// Returns specified default value if there is no entry for `key`. + public fun borrow_with_default(table: &Table, key: K, default: &V): &V { + if (!contains(table, copy key)) { + default + } else { + borrow(table, copy key) + } + } + + /// Acquire a mutable reference to the value which `key` maps to. + /// Aborts if there is no entry for `key`. + public fun borrow_mut(table: &mut Table, key: K): &mut V { + &mut borrow_box_mut>(table, key).val + } + + spec borrow_mut { + pragma intrinsic; + } + + /// Returns the length of the table, i.e. the number of entries. + public fun length(table: &Table): u64 { + table.length + } + + spec length { + pragma intrinsic; + } + + /// Returns true if this table is empty. + public fun empty(table: &Table): bool { + table.length == 0 + } + + spec empty { + pragma intrinsic; + } + + /// Acquire a mutable reference to the value which `key` maps to. + /// Insert the pair (`key`, `default`) first if there is no entry for `key`. + public fun borrow_mut_with_default(table: &mut Table, key: K, default: V): &mut V { + if (!contains(table, copy key)) { + add(table, copy key, default) + }; + borrow_mut(table, key) + } + + spec borrow_mut_with_default { + pragma opaque, verify=false; + aborts_if false; + // TODO: Prover need to extend with supporting the `borrow_mut_with_default` pragma. + // `table.length` cannot be accessed because the struct is intrinsic. + // It seems not possible to write an abstract postcondition for this function. + } + + /// Insert the pair (`key`, `value`) if there is no entry for `key`. + /// update the value of the entry for `key` to `value` otherwise + public fun upsert(table: &mut Table, key: K, value: V) { + if (!contains(table, copy key)) { + add(table, copy key, value) + } else { + let ref = borrow_mut(table, key); + *ref = value; + }; + } + + /// Remove from `table` and return the value which `key` maps to. + /// Aborts if there is no entry for `key`. + public fun remove(table: &mut Table, key: K): V { + let Box{val} = remove_box>(table, key); + table.length = table.length - 1; + val + } + + spec remove { + pragma intrinsic; + } + + /// Returns true iff `table` contains an entry for `key`. + public fun contains(table: &Table, key: K): bool { + contains_box>(table, key) + } + + spec contains { + pragma intrinsic; + } + + #[test_only] + /// Testing only: allows to drop a table even if it is not empty. + public fun drop_unchecked(table: Table) { + drop_unchecked_box>(table) + } + + #[test_only] + struct TableHolder has key { + t: Table + } + + #[test(account = @0x1)] + fun test_upsert(account: signer) { + let t = new(); + let key: u64 = 111; + let error_code: u64 = 1; + assert!(!contains(&t, key), error_code); + upsert(&mut t, key, 12); + assert!(*borrow(&t, key) == 12, error_code); + upsert(&mut t, key, 23); + assert!(*borrow(&t, key) == 23, error_code); + + move_to(&account, TableHolder { t }); + } + + #[test(account = @0x1)] + fun test_borrow_with_default(account: signer) { + let t = new(); + let key: u64 = 100; + let error_code: u64 = 1; + assert!(!contains(&t, key), error_code); + assert!(*borrow_with_default(&t, key, &12) == 12, error_code); + add(&mut t, key, 1); + assert!(*borrow_with_default(&t, key, &12) == 1, error_code); + + move_to(&account, TableHolder{ t }); + } + + + // ====================================================================================================== + // Internal API + + /// Wrapper for values. Required for making values appear as resources in the implementation. + struct Box has key, drop, store { + val: V + } + + // Primitives which take as an additional type parameter `Box`, so the implementation + // can use this to determine serialization layout. + native fun new_table_handle(): address; + native fun add_box(table: &mut Table, key: K, val: Box); + native fun borrow_box(table: &Table, key: K): &Box; + native fun borrow_box_mut(table: &mut Table, key: K): &mut Box; + native fun contains_box(table: &Table, key: K): bool; + native fun remove_box(table: &mut Table, key: K): Box; + native fun destroy_empty_box(table: &Table); + native fun drop_unchecked_box(table: Table); + + // Specification functions for tables + + spec native fun spec_len(t: Table): num; + spec native fun spec_contains(t: Table, k: K): bool; + spec native fun spec_set(t: Table, k: K, v: V): Table; + spec native fun spec_remove(t: Table, k: K): Table; + spec native fun spec_get(t: Table, k: K): V; +} +} diff --git a/release/v13/sources/Timestamp.move b/release/v13/sources/Timestamp.move new file mode 100644 index 00000000..0dd5da96 --- /dev/null +++ b/release/v13/sources/Timestamp.move @@ -0,0 +1,131 @@ +address StarcoinFramework { +/// The module implements onchain timestamp oracle. +/// Timestamp is updated on each block. It always steps forward, and never come backward. +module Timestamp { + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + + // A singleton resource holding the current Unix time in milliseconds + struct CurrentTimeMilliseconds has key { + milliseconds: u64, + } + + /// A singleton resource used to determine whether time has started. This + /// is called at the end of genesis. + struct TimeHasStarted has key {} + + /// Conversion factor between seconds and milliseconds + const MILLI_CONVERSION_FACTOR: u64 = 1000; + + const ENOT_GENESIS: u64 = 12; + const EINVALID_TIMESTAMP: u64 = 14; + const ENOT_INITIALIZED: u64 = 101; + // Initialize the global wall clock time resource. + public fun initialize(account: &signer, genesis_timestamp: u64) { + // Only callable by the Genesis address + CoreAddresses::assert_genesis_address(account); + let milli_timer = CurrentTimeMilliseconds {milliseconds: genesis_timestamp}; + move_to(account, milli_timer); + } + spec initialize { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists(Signer::address_of(account)); + ensures exists(Signer::address_of(account)); + } + + // Update the wall clock time by consensus. Requires VM privilege and will be invoked during block prologue. + public fun update_global_time(account: &signer, timestamp: u64) acquires CurrentTimeMilliseconds { + CoreAddresses::assert_genesis_address(account); + //Do not update time before time start. + let global_milli_timer = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + assert!(timestamp > global_milli_timer.milliseconds, Errors::invalid_argument(EINVALID_TIMESTAMP)); + global_milli_timer.milliseconds = timestamp; + } + spec update_global_time { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if timestamp <= global(CoreAddresses::GENESIS_ADDRESS()).milliseconds; + ensures global(CoreAddresses::GENESIS_ADDRESS()).milliseconds == timestamp; + } + + // Get the timestamp representing `now` in seconds. + public fun now_seconds(): u64 acquires CurrentTimeMilliseconds { + now_milliseconds() / MILLI_CONVERSION_FACTOR + } + spec now_seconds { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures result == now_milliseconds() / MILLI_CONVERSION_FACTOR; + } + spec fun spec_now_seconds(): u64 { + global(CoreAddresses::GENESIS_ADDRESS()).milliseconds / MILLI_CONVERSION_FACTOR + } + + // Get the timestamp representing `now` in milliseconds. + public fun now_milliseconds(): u64 acquires CurrentTimeMilliseconds { + borrow_global(CoreAddresses::GENESIS_ADDRESS()).milliseconds + } + + spec now_milliseconds { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + ensures result == global(CoreAddresses::GENESIS_ADDRESS()).milliseconds; + } + + spec fun spec_now_millseconds(): u64 { + global(CoreAddresses::GENESIS_ADDRESS()).milliseconds + } + + /// Marks that time has started and genesis has finished. This can only be called from genesis. + public fun set_time_has_started(account: &signer) { + CoreAddresses::assert_genesis_address(account); + + // Current time must have been initialized. + assert!( + exists(CoreAddresses::GENESIS_ADDRESS()), + Errors::invalid_state(ENOT_INITIALIZED) + ); + move_to(account, TimeHasStarted{}); + } + + spec set_time_has_started { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(Signer::address_of(account)); + aborts_if exists(Signer::address_of(account)); + ensures exists(Signer::address_of(account)); + } + + /// Helper function to determine if the blockchain is in genesis state. + public fun is_genesis(): bool { + !exists(CoreAddresses::GENESIS_ADDRESS()) + } + + spec is_genesis { + aborts_if false; + ensures result == !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + /// Helper function to assert genesis state. + public fun assert_genesis() { + assert!(is_genesis(), Errors::invalid_state(ENOT_GENESIS)); + } + spec assert_genesis { + pragma opaque = true; + include AbortsIfNotGenesis; + } + + /// Helper schema to specify that a function aborts if not in genesis. + spec schema AbortsIfNotGenesis { + aborts_if !is_genesis(); + } + + spec schema AbortsIfTimestampNotExists { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } +} +} diff --git a/release/v13/sources/Token.move b/release/v13/sources/Token.move new file mode 100644 index 00000000..290b9843 --- /dev/null +++ b/release/v13/sources/Token.move @@ -0,0 +1,545 @@ +address StarcoinFramework { +/// Token implementation of Starcoin. +module Token { + use StarcoinFramework::Event; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::Math; + + friend StarcoinFramework::TypeInfo; + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// The token has a `TokenType` color that tells us what token the + /// `value` inside represents. + struct Token has store { + value: u128, + } + + /// Token Code which identify a unique Token. + struct TokenCode has copy, drop, store { + /// address who define the module contains the Token Type. + addr: address, + /// module which contains the Token Type. + module_name: vector, + /// name of the token. may nested if the token is an instantiated generic token type. + name: vector, + } + + /// A minting capability allows tokens of type `TokenType` to be minted + struct MintCapability has key, store {} + + /// A fixed time mint key which can mint token until global time > end_time + struct FixedTimeMintKey has key, store { + total: u128, + end_time: u64, + } + + /// A linear time mint key which can mint token in a period by time-based linear release. + struct LinearTimeMintKey has key, store { + total: u128, + minted: u128, + start_time: u64, + period: u64, + } + + /// A burn capability allows tokens of type `TokenType` to be burned. + struct BurnCapability has key, store {} + + + /// Event emitted when token minted. + struct MintEvent has drop, store { + /// funds added to the system + amount: u128, + /// full info of Token. + token_code: TokenCode, + } + + /// Event emitted when token burned. + struct BurnEvent has drop, store { + /// funds removed from the system + amount: u128, + /// full info of Token + token_code: TokenCode, + } + + /// Token information. + struct TokenInfo has key { + /// The total value for the token represented by + /// `TokenType`. Mutable. + total_value: u128, + /// The scaling factor for the coin (i.e. the amount to divide by + /// to get to the human-readable representation for this currency). + /// e.g. 10^6 for `Coin1` + scaling_factor: u128, + /// event stream for minting + mint_events: Event::EventHandle, + /// event stream for burning + burn_events: Event::EventHandle, + } + + const EDEPRECATED_FUNCTION: u64 = 19; + + const EDESTROY_TOKEN_NON_ZERO: u64 = 16; + const EINVALID_ARGUMENT: u64 = 18; + + /// Token register's address should same as TokenType's address. + const ETOKEN_REGISTER: u64 = 101; + + const EAMOUNT_EXCEEDS_COIN_VALUE: u64 = 102; + // Mint key time limit + const EMINT_KEY_TIME_LIMIT: u64 = 103; + + const EDESTROY_KEY_NOT_EMPTY: u64 = 104; + const EPRECISION_TOO_LARGE: u64 = 105; + const EEMPTY_KEY: u64 = 106; + const ESPLIT: u64 = 107; + const EPERIOD_NEW: u64 = 108; + const EMINT_AMOUNT_EQUAL_ZERO: u64 = 109; + + /// 2^128 < 10**39 + const MAX_PRECISION: u8 = 38; + + /// Register the type `TokenType` as a Token and got MintCapability and BurnCapability. + public fun register_token( + account: &signer, + precision: u8, + ) { + assert!(precision <= MAX_PRECISION, Errors::invalid_argument(EPRECISION_TOO_LARGE)); + let scaling_factor = Math::pow(10, (precision as u64)); + let token_address = token_address(); + assert!(Signer::address_of(account) == token_address, Errors::requires_address(ETOKEN_REGISTER)); + move_to(account, MintCapability {}); + move_to(account, BurnCapability {}); + move_to( + account, + TokenInfo { + total_value: 0, + scaling_factor, + mint_events: Event::new_event_handle(account), + burn_events: Event::new_event_handle(account), + }, + ); + } + + spec register_token { + include RegisterTokenAbortsIf; + include RegisterTokenEnsures; + } + + spec schema RegisterTokenAbortsIf { + precision: u8; + account: signer; + aborts_if precision > MAX_PRECISION; + aborts_if Signer::address_of(account) != SPEC_TOKEN_TEST_ADDRESS(); + aborts_if exists>(Signer::address_of(account)); + aborts_if exists>(Signer::address_of(account)); + aborts_if exists>(Signer::address_of(account)); + } + + spec schema RegisterTokenEnsures { + account: signer; + ensures exists>(Signer::address_of(account)); + ensures exists>(Signer::address_of(account)); + ensures exists>(Signer::address_of(account)); + } + + /// Remove mint capability from `signer`. + public fun remove_mint_capability(signer: &signer): MintCapability + acquires MintCapability { + move_from>(Signer::address_of(signer)) + } + + spec remove_mint_capability { + aborts_if !exists>(Signer::address_of(signer)); + ensures !exists>(Signer::address_of(signer)); + } + + /// Add mint capability to `signer`. + public fun add_mint_capability(signer: &signer, cap: MintCapability) { + move_to(signer, cap) + } + + spec add_mint_capability { + aborts_if exists>(Signer::address_of(signer)); + ensures exists>(Signer::address_of(signer)); + } + + /// Destroy the given mint capability. + public fun destroy_mint_capability(cap: MintCapability) { + let MintCapability {} = cap; + } + + spec destroy_mint_capability { + } + + /// remove the token burn capability from `signer`. + public fun remove_burn_capability(signer: &signer): BurnCapability + acquires BurnCapability { + move_from>(Signer::address_of(signer)) + } + + spec remove_burn_capability { + aborts_if !exists>(Signer::address_of(signer)); + ensures !exists>(Signer::address_of(signer)); + } + + /// Add token burn capability to `signer`. + public fun add_burn_capability(signer: &signer, cap: BurnCapability) { + move_to(signer, cap) + } + + spec add_burn_capability { + aborts_if exists>(Signer::address_of(signer)); + ensures exists>(Signer::address_of(signer)); + } + + /// Destroy the given burn capability. + public fun destroy_burn_capability(cap: BurnCapability) { + let BurnCapability {} = cap; + } + + spec destroy_burn_capability { + } + + /// Return `amount` tokens. + /// Fails if the sender does not have a published MintCapability. + public fun mint(account: &signer, amount: u128): Token + acquires TokenInfo, MintCapability { + mint_with_capability( + borrow_global>(Signer::address_of(account)), + amount, + ) + } + + spec mint { + aborts_if spec_abstract_total_value() + amount > MAX_U128; + aborts_if !exists>(Signer::address_of(account)); + } + + /// Mint a new Token::Token worth `amount`. + /// The caller must have a reference to a MintCapability. + /// Only the Association account can acquire such a reference, and it can do so only via + /// `borrow_sender_mint_capability` + public fun mint_with_capability( + _capability: &MintCapability, + amount: u128, + ): Token acquires TokenInfo { + do_mint(amount) + } + + spec mint_with_capability { + aborts_if spec_abstract_total_value() + amount > MAX_U128; + ensures spec_abstract_total_value() == + old(global>(SPEC_TOKEN_TEST_ADDRESS()).total_value) + amount; + } + + fun do_mint(amount: u128): Token acquires TokenInfo { + // update market cap resource to reflect minting + let (token_address, module_name, token_name) = name_of_token(); + let info = borrow_global_mut>(token_address); + info.total_value = info.total_value + amount; + Event::emit_event( + &mut info.mint_events, + MintEvent { + amount, + token_code: TokenCode { addr: token_address, module_name, name: token_name }, + }, + ); + Token { value: amount } + } + + spec do_mint { + aborts_if !exists>(SPEC_TOKEN_TEST_ADDRESS()); + aborts_if spec_abstract_total_value() + amount > MAX_U128; + } + + /// Deprecated since @v3 + /// Issue a `FixedTimeMintKey` with given `MintCapability`. + public fun issue_fixed_mint_key( + _capability: &MintCapability, + _amount: u128, + _period: u64, + ): FixedTimeMintKey { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + spec issue_fixed_mint_key { + aborts_if true; + } + + /// Deprecated since @v3 + /// Issue a `LinearTimeMintKey` with given `MintCapability`. + public fun issue_linear_mint_key( + _capability: &MintCapability, + _amount: u128, + _period: u64, + ): LinearTimeMintKey { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + spec issue_linear_mint_key { + aborts_if true; + } + + /// Destroy `LinearTimeMintKey`, for deprecated + public fun destroy_linear_time_key(key: LinearTimeMintKey): (u128, u128, u64, u64) { + let LinearTimeMintKey { total, minted, start_time, period } = key; + (total, minted, start_time, period) + } + + public fun read_linear_time_key(key: &LinearTimeMintKey): (u128, u128, u64, u64) { + (key.total, key.minted, key.start_time, key.period) + } + + /// Burn some tokens of `signer`. + public fun burn(account: &signer, tokens: Token) + acquires TokenInfo, BurnCapability { + burn_with_capability( + borrow_global>(Signer::address_of(account)), + tokens, + ) + } + + spec burn { + aborts_if spec_abstract_total_value() - tokens.value < 0; + aborts_if !exists>(Signer::address_of(account)); + } + + /// Burn tokens with the given `BurnCapability`. + public fun burn_with_capability( + _capability: &BurnCapability, + tokens: Token, + ) acquires TokenInfo { + let (token_address, module_name, token_name) = name_of_token(); + let info = borrow_global_mut>(token_address); + let Token { value } = tokens; + info.total_value = info.total_value - value; + Event::emit_event( + &mut info.burn_events, + BurnEvent { + amount: value, + token_code: TokenCode { addr: token_address, module_name, name: token_name }, + }, + ); + } + + spec burn_with_capability { + aborts_if spec_abstract_total_value() - tokens.value < 0; + ensures spec_abstract_total_value() == + old(global>(SPEC_TOKEN_TEST_ADDRESS()).total_value) - tokens.value; + } + + /// Create a new Token::Token with a value of 0 + public fun zero(): Token { + Token { value: 0 } + } + + spec zero { + ensures result.value == 0; + } + + + /// Public accessor for the value of a token + public fun value(token: &Token): u128 { + token.value + } + + spec value { + aborts_if false; + ensures result == token.value; + } + + /// Splits the given token into two and returns them both + public fun split( + token: Token, + value: u128, + ): (Token, Token) { + let rest = withdraw(&mut token, value); + (token, rest) + } + + spec split { + aborts_if token.value < value; + // N.B. spec translator regards `token` as a pure expression + ensures token.value == result_1.value + result_2.value; + } + + /// "Divides" the given token into two, where the original token is modified in place. + /// The original token will have value = original value - `value` + /// The new token will have a value = `value` + /// Fails if the tokens value is less than `value` + public fun withdraw( + token: &mut Token, + value: u128, + ): Token { + // Check that `value` is less than the token's value + assert!(token.value >= value, Errors::limit_exceeded(EAMOUNT_EXCEEDS_COIN_VALUE)); + token.value = token.value - value; + Token { value: value } + } + + spec withdraw { + aborts_if token.value < value; + ensures result.value == value; + ensures token.value == old(token).value - value; + } + + /// Merges two tokens of the same token and returns a new token whose + /// value is equal to the sum of the two inputs + public fun join( + token1: Token, + token2: Token, + ): Token { + deposit(&mut token1, token2); + token1 + } + + spec join { + aborts_if token1.value + token2.value > max_u128(); + ensures token1.value + token2.value == result.value; + } + + /// "Merges" the two tokens + /// The token passed in by reference will have a value equal to the sum of the two tokens + /// The `check` token is consumed in the process + public fun deposit(token: &mut Token, check: Token) { + let Token { value } = check; + token.value = token.value + value; + } + + spec deposit { + aborts_if token.value + check.value > max_u128(); + ensures old(token).value + check.value == token.value; + } + + /// Destroy a token + /// Fails if the value is non-zero + /// The amount of Token in the system is a tightly controlled property, + /// so you cannot "burn" any non-zero amount of Token + public fun destroy_zero(token: Token) { + let Token { value } = token; + assert!(value == 0, Errors::invalid_state(EDESTROY_TOKEN_NON_ZERO)) + } + + spec destroy_zero { + aborts_if token.value > 0; + } + + /// Returns the scaling_factor for the `TokenType` token. + public fun scaling_factor(): u128 acquires TokenInfo { + let token_address = token_address(); + borrow_global>(token_address).scaling_factor + } + + spec scaling_factor { + aborts_if false; + ensures result == global>(SPEC_TOKEN_TEST_ADDRESS()).scaling_factor; + } + + /// Return the total amount of token of type `TokenType`. + public fun market_cap(): u128 acquires TokenInfo { + let token_address = token_address(); + borrow_global>(token_address).total_value + } + + spec market_cap { + aborts_if false; + ensures result == global>(SPEC_TOKEN_TEST_ADDRESS()).total_value; + } + + /// Return true if the type `TokenType` is a registered in `token_address`. + public fun is_registered_in(token_address: address): bool { + exists>(token_address) + } + + spec is_registered_in { + aborts_if false; + ensures result == exists>(token_address); + } + + /// Return true if the type `TokenType1` is same with `TokenType2` + public fun is_same_token(): bool { + return token_code() == token_code() + } + + spec is_same_token { + aborts_if false; + } + + /// Return the TokenType's address + public fun token_address(): address { + let (addr, _, _) = name_of(); + addr + } + + // The specification of this function is abstracted to avoid the complexity to + // return a real address to caller + spec token_address { + pragma opaque = true; + aborts_if false; + ensures [abstract] exists>(result); + ensures [abstract] result == SPEC_TOKEN_TEST_ADDRESS(); + ensures [abstract] global>(result).total_value == 100000000u128; + } + + /// Return the token code for the registered token. + public fun token_code(): TokenCode { + let (addr, module_name, name) = name_of(); + TokenCode { + addr, + module_name, + name + } + } + + spec token_code { + pragma opaque = true; + aborts_if false; + // ensures [abstract] result == spec_token_code(); + } + + /// We use an uninterpreted function to represent the result of derived address. The actual value + /// does not matter for the verification of callers. + spec fun spec_token_code(): TokenCode; + + public (friend) fun type_of(): (address, vector, vector){ + name_of() + } + + /// Return Token's module address, module name, and type name of `TokenType`. + native fun name_of(): (address, vector, vector); + + spec name_of { + pragma opaque = true; + aborts_if false; + } + + fun name_of_token(): (address, vector, vector) { + name_of() + } + + // The specification of this function is abstracted to avoid the complexity to + // return a real address to caller + spec name_of_token { + pragma opaque = true; + aborts_if false; + ensures [abstract] exists>(result_1); + ensures [abstract] result_1 == SPEC_TOKEN_TEST_ADDRESS(); + ensures [abstract] global>(result_1).total_value == 100000000u128; + } + + + spec fun SPEC_TOKEN_TEST_ADDRESS(): address { + @0x2 + } + + spec fun spec_abstract_total_value(): num { + global>(SPEC_TOKEN_TEST_ADDRESS()).total_value + } + + +} +} \ No newline at end of file diff --git a/release/v13/sources/TransactionFee.move b/release/v13/sources/TransactionFee.move new file mode 100644 index 00000000..47a2fb5c --- /dev/null +++ b/release/v13/sources/TransactionFee.move @@ -0,0 +1,95 @@ +address StarcoinFramework { +/// `TransactionFee` collect gas fees used by transactions in blocks temporarily. +/// Then they are distributed in `TransactionManager`. +module TransactionFee { + use StarcoinFramework::Token::{Self, Token}; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Signer; + use StarcoinFramework::STC::{STC}; + use StarcoinFramework::Timestamp; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// The `TransactionFee` resource holds a preburn resource for each + /// fiat `TokenType` that can be collected as a transaction fee. + struct TransactionFee has key { + fee: Token, + } + + /// Called in genesis. Sets up the needed resources to collect transaction fees from the + /// `TransactionFee` resource with the TreasuryCompliance account. + public fun initialize( + account: &signer, + ) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + + // accept fees in all the currencies + add_txn_fee_token(account); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists>(Signer::address_of(account)); + } + + /// publishing a wrapper of the `Preburn` resource under `fee_account` + fun add_txn_fee_token( + account: &signer, + ) { + move_to( + account, + TransactionFee { + fee: Token::zero(), + } + ) + } + + spec add_txn_fee_token { + aborts_if exists>(Signer::address_of(account)); + } + + /// Deposit `token` into the transaction fees bucket + public fun pay_fee(token: Token) acquires TransactionFee { + let txn_fees = borrow_global_mut>( + CoreAddresses::GENESIS_ADDRESS() + ); + Token::deposit(&mut txn_fees.fee, token) + } + + spec pay_fee { + aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + aborts_if global>(CoreAddresses::GENESIS_ADDRESS()).fee.value + token.value > max_u128(); + } + + /// Distribute the transaction fees collected in the `TokenType` token. + /// If the `TokenType` is STC, it unpacks the token and preburns the + /// underlying fiat. + public fun distribute_transaction_fees( + account: &signer, + ): Token acquires TransactionFee { + let fee_address = CoreAddresses::GENESIS_ADDRESS(); + CoreAddresses::assert_genesis_address(account); + + // extract fees + let txn_fees = borrow_global_mut>(fee_address); + let value = Token::value(&txn_fees.fee); + if (value > 0) { + Token::withdraw(&mut txn_fees.fee, value) + }else { + Token::zero() + } + } + + spec distribute_transaction_fees { + pragma verify = false; +// aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); +// aborts_if !exists>(CoreAddresses::GENESIS_ADDRESS()); + + } + } +} diff --git a/release/v13/sources/TransactionManager.move b/release/v13/sources/TransactionManager.move new file mode 100644 index 00000000..d98607a8 --- /dev/null +++ b/release/v13/sources/TransactionManager.move @@ -0,0 +1,415 @@ +address StarcoinFramework { +/// `TransactionManager` manages: +/// 1. prologue and epilogue of transactions. +/// 2. prologue of blocks. +module TransactionManager { + use StarcoinFramework::Authenticator; + use StarcoinFramework::Account::{exists_at, is_signer_delegated, transaction_fee_simulate, + balance, Account, Balance + }; + use StarcoinFramework::TransactionTimeout; + use StarcoinFramework::Signer; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Account; + use StarcoinFramework::PackageTxnManager; + use StarcoinFramework::BlockReward; + use StarcoinFramework::Block; + use StarcoinFramework::STC::{STC, is_stc}; + use StarcoinFramework::TransactionFee; + use StarcoinFramework::Timestamp; + use StarcoinFramework::ChainId; + use StarcoinFramework::Errors; + use StarcoinFramework::TransactionPublishOption; + use StarcoinFramework::Epoch; + use StarcoinFramework::Hash; + use StarcoinFramework::Vector; + use StarcoinFramework::STC; + use StarcoinFramework::EasyGas; + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + const TXN_PAYLOAD_TYPE_SCRIPT: u8 = 0; + const TXN_PAYLOAD_TYPE_PACKAGE: u8 = 1; + const TXN_PAYLOAD_TYPE_SCRIPT_FUNCTION: u8 = 2; + + const EPROLOGUE_ACCOUNT_DOES_NOT_EXIST: u64 = 0; + const EPROLOGUE_TRANSACTION_EXPIRED: u64 = 5; + const EPROLOGUE_BAD_CHAIN_ID: u64 = 6; + const EPROLOGUE_MODULE_NOT_ALLOWED: u64 = 7; + const EPROLOGUE_SCRIPT_NOT_ALLOWED: u64 = 8; + + + /// The prologue is invoked at the beginning of every transaction + /// It verifies: + /// - The account's auth key matches the transaction's public key + /// - That the account has enough balance to pay for all of the gas + /// - That the sequence number matches the transaction's sequence key + public fun prologue( + account: signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + txn_expiration_time: u64, + chain_id: u8, + txn_payload_type: u8, + txn_script_or_package_hash: vector, + txn_package_address: address, + ) { + // Can only be invoked by genesis account + assert!( + Signer::address_of(&account) == CoreAddresses::GENESIS_ADDRESS(), + Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST), + ); + // Check that the chain ID stored on-chain matches the chain ID + // specified by the transaction + assert!(ChainId::get() == chain_id, Errors::invalid_argument(EPROLOGUE_BAD_CHAIN_ID)); + let (stc_price,scaling_factor)= if (!STC::is_stc()){ + (EasyGas::gas_oracle_read(), EasyGas::get_scaling_factor()) + }else{ + (1,1) + }; + + txn_prologue_v2( + &account, + txn_sender, + txn_sequence_number, + txn_authentication_key_preimage, + txn_gas_price, + txn_max_gas_units, + stc_price, + scaling_factor, + ); + assert!( + TransactionTimeout::is_valid_transaction_timestamp(txn_expiration_time), + Errors::invalid_argument(EPROLOGUE_TRANSACTION_EXPIRED), + ); + if (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE) { + // stdlib upgrade is not affected by PublishOption + if (txn_package_address != CoreAddresses::GENESIS_ADDRESS()) { + assert!( + TransactionPublishOption::is_module_allowed(Signer::address_of(&account)), + Errors::invalid_argument(EPROLOGUE_MODULE_NOT_ALLOWED), + ); + }; + PackageTxnManager::package_txn_prologue_v2( + &account, + txn_sender, + txn_package_address, + txn_script_or_package_hash, + ); + } else if (txn_payload_type == TXN_PAYLOAD_TYPE_SCRIPT) { + assert!( + TransactionPublishOption::is_script_allowed( + Signer::address_of(&account), + ), + Errors::invalid_argument(EPROLOGUE_SCRIPT_NOT_ALLOWED), + ); + }; + // do nothing for TXN_PAYLOAD_TYPE_SCRIPT_FUNCTION + } + + spec prologue { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if ChainId::get() != chain_id; + aborts_if !exists(txn_sender); + aborts_if Hash::sha3_256(txn_authentication_key_preimage) != global(txn_sender).authentication_key; + aborts_if txn_gas_price * txn_max_gas_units > max_u64(); + include Timestamp::AbortsIfTimestampNotExists; + include Block::AbortsIfBlockMetadataNotExist; + aborts_if txn_gas_price * txn_max_gas_units > 0 && !exists>(txn_sender); + aborts_if txn_gas_price * txn_max_gas_units > 0 && txn_sequence_number >= max_u64(); + aborts_if txn_sequence_number < global(txn_sender).sequence_number; + aborts_if txn_sequence_number != global(txn_sender).sequence_number; + include TransactionTimeout::AbortsIfTimestampNotValid; + aborts_if !TransactionTimeout::spec_is_valid_transaction_timestamp(txn_expiration_time); + include TransactionPublishOption::AbortsIfTxnPublishOptionNotExistWithBool { + is_script_or_package: (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE || txn_payload_type == TXN_PAYLOAD_TYPE_SCRIPT), + }; + aborts_if txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE && txn_package_address != CoreAddresses::GENESIS_ADDRESS() && !TransactionPublishOption::spec_is_module_allowed(Signer::address_of(account)); + aborts_if txn_payload_type == TXN_PAYLOAD_TYPE_SCRIPT && !TransactionPublishOption::spec_is_script_allowed(Signer::address_of(account)); + include PackageTxnManager::CheckPackageTxnAbortsIfWithType{is_package: (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE), sender:txn_sender, package_address: txn_package_address, package_hash: txn_script_or_package_hash}; + } + + /// The epilogue is invoked at the end of transactions. + /// It collects gas and bumps the sequence number + public fun epilogue( + account: signer, + txn_sender: address, + txn_sequence_number: u64, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + txn_payload_type: u8, + _txn_script_or_package_hash: vector, + txn_package_address: address, + // txn execute success or fail. + success: bool, + ) { + epilogue_v2(account, txn_sender, txn_sequence_number, Vector::empty(), txn_gas_price, txn_max_gas_units, gas_units_remaining, txn_payload_type, _txn_script_or_package_hash, txn_package_address, success) + } + + /// The epilogue is invoked at the end of transactions. + /// It collects gas and bumps the sequence number + public fun epilogue_v2( + account: signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + txn_payload_type: u8, + _txn_script_or_package_hash: vector, + txn_package_address: address, + // txn execute success or fail. + success: bool, + ) { + CoreAddresses::assert_genesis_address(&account); + let (stc_price,scaling_factor) = + if (!STC::is_stc()){ + (EasyGas::gas_oracle_read(), EasyGas::get_scaling_factor()) + }else{ + (1,1) + }; + txn_epilogue_v3( + &account, + txn_sender, + txn_sequence_number, + txn_authentication_key_preimage, + txn_gas_price, + txn_max_gas_units, + gas_units_remaining, + stc_price, + scaling_factor + ); + if (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE) { + PackageTxnManager::package_txn_epilogue( + &account, + txn_sender, + txn_package_address, + success, + ); + } + } + + spec epilogue { + pragma verify = false;//fixme : timeout + include CoreAddresses::AbortsIfNotGenesisAddress; + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(txn_sender); + aborts_if !exists>(txn_sender); + aborts_if txn_max_gas_units < gas_units_remaining; + aborts_if txn_sequence_number + 1 > max_u64(); + aborts_if txn_gas_price * (txn_max_gas_units - gas_units_remaining) > max_u64(); + include PackageTxnManager::AbortsIfPackageTxnEpilogue { + is_package: (txn_payload_type == TXN_PAYLOAD_TYPE_PACKAGE), + package_address: txn_package_address, + success: success, + }; + } + + /// Set the metadata for the current block and distribute transaction fees and block rewards. + /// The runtime always runs this before executing the transactions in a block. + public fun block_prologue( + account: signer, + parent_hash: vector, + timestamp: u64, + author: address, + auth_key_vec: vector, + uncles: u64, + number: u64, + chain_id: u8, + parent_gas_used: u64, + ) { + Self::block_prologue_v2(account, parent_hash, timestamp, author, auth_key_vec, uncles, number, chain_id, parent_gas_used, Vector::empty()) + } + + spec block_prologue { + pragma verify = false;//fixme : timeout + } + + /// Set the metadata for the current block and distribute transaction fees and block rewards. + /// The runtime always runs this before executing the transactions in a block. + /// For Flexidag block + public fun block_prologue_v2( + account: signer, + parent_hash: vector, + timestamp: u64, + author: address, + auth_key_vec: vector, + uncles: u64, + number: u64, + chain_id: u8, + parent_gas_used: u64, + parents_hash: vector, + ) { + // Can only be invoked by genesis account + CoreAddresses::assert_genesis_address(&account); + // Check that the chain ID stored on-chain matches the chain ID + // specified by the transaction + assert!(ChainId::get() == chain_id, Errors::invalid_argument(EPROLOGUE_BAD_CHAIN_ID)); + + // deal with previous block first. + let txn_fee = TransactionFee::distribute_transaction_fees(&account); + + // then deal with current block. + Timestamp::update_global_time(&account, timestamp); + Block::process_block_metadata_v2( + &account, + parent_hash, + author, + timestamp, + uncles, + number, + parents_hash, + ); + let reward = Epoch::adjust_epoch(&account, number, timestamp, uncles, parent_gas_used); + // pass in previous block gas fees. + BlockReward::process_block_reward(&account, number, reward, author, auth_key_vec, txn_fee); + } + + spec block_prologue_v2 { + pragma verify = false;//fixme : timeout + } + + const MAX_U64: u128 = 18446744073709551615; + const EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY: u64 = 1; + const EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD: u64 = 2; + const EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW: u64 = 3; + const EPROLOGUE_CANT_PAY_GAS_DEPOSIT: u64 = 4; + const EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG: u64 = 9; + const EINSUFFICIENT_BALANCE: u64 = 10; + const ECOIN_DEPOSIT_IS_ZERO: u64 = 15; + const EDEPRECATED_FUNCTION: u64 = 19; + const EPROLOGUE_SIGNER_ALREADY_DELEGATED: u64 = 200; + + public fun txn_prologue_v2( + account: &signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + stc_price: u128, + stc_price_scaling: u128 + ) { + CoreAddresses::assert_genesis_address(account); + + // Verify that the transaction sender's account exists + assert!(exists_at(txn_sender), Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST)); + // Verify the account has not delegate its signer cap. + assert!(!is_signer_delegated(txn_sender), Errors::invalid_state(EPROLOGUE_SIGNER_ALREADY_DELEGATED)); + + // Load the transaction sender's account + //let sender_account = borrow_global_mut(txn_sender); + if (Account::is_dummy_auth_key_v2(txn_sender)){ + // if sender's auth key is empty, use address as auth key for check transaction. + assert!( + Authenticator::derived_address(Hash::sha3_256(txn_authentication_key_preimage)) == txn_sender, + Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY) + ); + }else{ + // Check that the hash of the transaction's public key matches the account's auth key + assert!( + Hash::sha3_256(txn_authentication_key_preimage) == Account::authentication_key(txn_sender), + Errors::invalid_argument(EPROLOGUE_INVALID_ACCOUNT_AUTH_KEY) + ); + }; + // Check that the account has enough balance for all of the gas + let (max_transaction_fee_stc,max_transaction_fee_token) = transaction_fee_simulate(txn_gas_price,txn_max_gas_units,0, stc_price, stc_price_scaling); + assert!( + max_transaction_fee_stc <= MAX_U64, + Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT), + ); + if (max_transaction_fee_stc > 0) { + assert!( + (txn_sequence_number as u128) < MAX_U64, + Errors::limit_exceeded(EPROLOGUE_SEQUENCE_NUMBER_TOO_BIG) + ); + let balance_amount_token = balance(txn_sender); + assert!(balance_amount_token >= max_transaction_fee_token, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT)); + if (!is_stc()){ + let gas_fee_address = EasyGas::get_gas_fee_address(); + let balance_amount_stc= balance(gas_fee_address); + assert!(balance_amount_stc >= max_transaction_fee_stc, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT)); + } + }; + // Check that the transaction sequence number matches the sequence number of the account + assert!(txn_sequence_number >= Account::sequence_number(txn_sender), Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_OLD)); + assert!(txn_sequence_number == Account::sequence_number(txn_sender), Errors::invalid_argument(EPROLOGUE_SEQUENCE_NUMBER_TOO_NEW)); + + } + + /// The epilogue is invoked at the end of transactions. + /// It collects gas and bumps the sequence number + public fun txn_epilogue_v3( + account: &signer, + txn_sender: address, + txn_sequence_number: u64, + txn_authentication_key_preimage: vector, + txn_gas_price: u64, + txn_max_gas_units: u64, + gas_units_remaining: u64, + stc_price: u128, + stc_price_scaling: u128, + ) { + CoreAddresses::assert_genesis_address(account); + // Charge for gas + let (transaction_fee_amount_stc,transaction_fee_amount_token) = transaction_fee_simulate( + txn_gas_price, + txn_max_gas_units, + gas_units_remaining, + stc_price, + stc_price_scaling); + assert!( + balance(txn_sender) >= transaction_fee_amount_token, + Errors::limit_exceeded(EINSUFFICIENT_BALANCE) + ); + + if (!is_stc()){ + let gas_fee_address = EasyGas::get_gas_fee_address(); + let genesis_balance_amount_stc=balance(gas_fee_address); + assert!(genesis_balance_amount_stc >= transaction_fee_amount_stc, + Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT) + ); + }; + // Bump the sequence number + Account::set_sequence_number(txn_sender,txn_sequence_number+1); + // Set auth key when user send transaction first. + if (Account::is_dummy_auth_key_v2(txn_sender) && !Vector::is_empty(&txn_authentication_key_preimage)){ + Account::set_authentication_key(txn_sender, Hash::sha3_256(txn_authentication_key_preimage)); + }; + + if (transaction_fee_amount_stc > 0) { + let transaction_fee_token = Account::withdraw_from_balance_v2( + txn_sender, + transaction_fee_amount_token + ); + if(!is_stc()) { + let gas_fee_address = EasyGas::get_gas_fee_address(); + Account::deposit(gas_fee_address, transaction_fee_token); + let stc_fee_token = Account::withdraw_from_balance_v2(gas_fee_address, transaction_fee_amount_stc); + TransactionFee::pay_fee(stc_fee_token); + }else{ + TransactionFee::pay_fee(transaction_fee_token); + } + }; + } + + spec txn_epilogue_v3 { + + pragma verify = false; // Todo: fix me, cost too much time + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(txn_sender); + aborts_if !exists>(txn_sender); + aborts_if txn_sequence_number + 1 > max_u64(); + aborts_if !exists>(txn_sender); + aborts_if txn_max_gas_units < gas_units_remaining; + } +} +} diff --git a/release/v13/sources/TransactionPublishOption.move b/release/v13/sources/TransactionPublishOption.move new file mode 100644 index 00000000..285f55ec --- /dev/null +++ b/release/v13/sources/TransactionPublishOption.move @@ -0,0 +1,120 @@ +address StarcoinFramework { +/// `TransactionPublishOption` provide an option to limit: +/// - whether user can use script or publish custom modules on chain. +module TransactionPublishOption { + use StarcoinFramework::Config; + use StarcoinFramework::Timestamp; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Errors; + use StarcoinFramework::Signer; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_strict = true; + + } + spec fun spec_is_script_allowed(addr: address) : bool{ + let publish_option = Config::get_by_address(addr); + publish_option.script_allowed + } + + spec fun spec_is_module_allowed(addr: address) : bool{ + let publish_option = Config::get_by_address(addr); + publish_option.module_publishing_allowed + } + + const SCRIPT_HASH_LENGTH: u64 = 32; + + const EPROLOGUE_ACCOUNT_DOES_NOT_EXIST: u64 = 0; + const EINVALID_ARGUMENT: u64 = 18; + /// The script hash has an invalid length + const EINVALID_SCRIPT_HASH: u64 = 1001; + /// The script hash already exists in the allowlist + const EALLOWLIST_ALREADY_CONTAINS_SCRIPT: u64 = 1002; + + /// Defines and holds the publishing policies for the VM. There are three possible configurations: + /// 1. !script_allowed && !module_publishing_allowed No module publishing, only script function in module are allowed. + /// 2. script_allowed && !module_publishing_allowed No module publishing, custom scripts are allowed. + /// 3. script_allowed && module_publishing_allowed Both module publishing and custom scripts are allowed. + /// We represent these as the following resource. + struct TransactionPublishOption has copy, drop, store { + // Anyone can use script if this flag is set to true. + script_allowed: bool, + // Anyone can publish new module if this flag is set to true. + module_publishing_allowed: bool, + } + + /// Module initialization. + public fun initialize( + account: &signer, + script_allowed: bool, + module_publishing_allowed: bool, + ) { + Timestamp::assert_genesis(); + assert!( + Signer::address_of(account) == CoreAddresses::GENESIS_ADDRESS(), + Errors::requires_address(EPROLOGUE_ACCOUNT_DOES_NOT_EXIST), + ); + let transaction_publish_option = Self::new_transaction_publish_option(script_allowed, module_publishing_allowed); + Config::publish_new_config( + account, + transaction_publish_option, + ); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + include Config::PublishNewConfigAbortsIf; + include Config::PublishNewConfigEnsures; + } + + /// Create a new option. Mainly used in DAO. + public fun new_transaction_publish_option( + script_allowed: bool, + module_publishing_allowed: bool, + ): TransactionPublishOption { + TransactionPublishOption { script_allowed, module_publishing_allowed } + } + + spec new_transaction_publish_option { + aborts_if false; + } + + /// Check if sender can execute script with + public fun is_script_allowed(account: address): bool { + let publish_option = Config::get_by_address(account); + publish_option.script_allowed + } + + spec is_script_allowed { + include Config::AbortsIfConfigNotExist{ + addr: account + }; + } + + /// Check if a sender can publish a module + public fun is_module_allowed(account: address): bool { + let publish_option = Config::get_by_address(account); + publish_option.module_publishing_allowed + } + + spec is_module_allowed { + include Config::AbortsIfConfigNotExist{ + addr: account + }; + } + + spec schema AbortsIfTxnPublishOptionNotExist { + include Config::AbortsIfConfigNotExist{ + addr: CoreAddresses::GENESIS_ADDRESS() + }; + } + + spec schema AbortsIfTxnPublishOptionNotExistWithBool { + is_script_or_package : bool; + aborts_if is_script_or_package && !exists>(CoreAddresses::GENESIS_ADDRESS()); + } + +} +} \ No newline at end of file diff --git a/release/v13/sources/TransactionTimeout.move b/release/v13/sources/TransactionTimeout.move new file mode 100644 index 00000000..c9dfa68d --- /dev/null +++ b/release/v13/sources/TransactionTimeout.move @@ -0,0 +1,53 @@ +address StarcoinFramework { +/// A module used to check expiration time of transactions. +module TransactionTimeout { + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Block; + use StarcoinFramework::TransactionTimeoutConfig; + use StarcoinFramework::Config; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + + } + + spec fun spec_is_valid_transaction_timestamp(txn_timestamp: u64):bool { + if (Block::get_current_block_number() == 0) { + txn_timestamp > Timestamp::now_seconds() + } else { + Timestamp::now_seconds() < txn_timestamp && txn_timestamp < + (Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds()) + } + } + + /// Check whether the given timestamp is valid for transactions. + public fun is_valid_transaction_timestamp(txn_timestamp: u64): bool { + let current_block_time = Timestamp::now_seconds(); + let block_number = Block::get_current_block_number(); + // before first block, just require txn_timestamp > genesis timestamp. + if (block_number == 0) { + return txn_timestamp > current_block_time + }; + let timeout = TransactionTimeoutConfig::duration_seconds(); + let max_txn_time = current_block_time + timeout; + current_block_time < txn_timestamp && txn_timestamp < max_txn_time + } + spec is_valid_transaction_timestamp { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + include Timestamp::AbortsIfTimestampNotExists; + aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64(); + aborts_if Block::get_current_block_number() != 0 && !exists>(CoreAddresses::GENESIS_ADDRESS()); + } + + spec schema AbortsIfTimestampNotValid { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + include Timestamp::AbortsIfTimestampNotExists; + aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64(); + aborts_if Block::get_current_block_number() != 0 && !exists>(CoreAddresses::GENESIS_ADDRESS()); + } +} +} diff --git a/release/v13/sources/TransactionTimeoutConfig.move b/release/v13/sources/TransactionTimeoutConfig.move new file mode 100644 index 00000000..57ddf4b0 --- /dev/null +++ b/release/v13/sources/TransactionTimeoutConfig.move @@ -0,0 +1,76 @@ +address StarcoinFramework { +/// Onchain configuration for timeout setting of transaction. +module TransactionTimeoutConfig { + use StarcoinFramework::Timestamp; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Config; + use StarcoinFramework::Signer; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict = true; + } + + /// config structs. + struct TransactionTimeoutConfig has copy, drop, store { + /// timeout in second. + duration_seconds: u64, + } + + /// Initialize function. Should only be called in genesis. + public fun initialize(account: &signer, duration_seconds: u64) { + Timestamp::assert_genesis(); + CoreAddresses::assert_genesis_address(account); + + Config::publish_new_config( + account, + new_transaction_timeout_config(duration_seconds) + ); + } + + spec initialize { + aborts_if !Timestamp::is_genesis(); + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + include Config::PublishNewConfigAbortsIf; + include Config::PublishNewConfigEnsures; + } + + /// Create a new timeout config used in dao proposal. + public fun new_transaction_timeout_config(duration_seconds: u64) : TransactionTimeoutConfig { + TransactionTimeoutConfig {duration_seconds: duration_seconds} + } + + spec new_transaction_timeout_config { + aborts_if false; + } + + /// Get current timeout config. + public fun get_transaction_timeout_config(): TransactionTimeoutConfig { + Config::get_by_address(CoreAddresses::GENESIS_ADDRESS()) + } + + spec get_transaction_timeout_config { + include Config::AbortsIfConfigNotExist{ + addr: CoreAddresses::GENESIS_ADDRESS() + }; + } + + /// Get current txn timeout in seconds. + public fun duration_seconds() :u64 { + let config = get_transaction_timeout_config(); + config.duration_seconds + } + + spec duration_seconds { + include Config::AbortsIfConfigNotExist{ + addr: CoreAddresses::GENESIS_ADDRESS() + }; + } + + spec schema AbortsIfTxnTimeoutConfigNotExist { + include Config::AbortsIfConfigNotExist{ + addr: CoreAddresses::GENESIS_ADDRESS() + }; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/TransferScripts.move b/release/v13/sources/TransferScripts.move new file mode 100644 index 00000000..2c6c15ab --- /dev/null +++ b/release/v13/sources/TransferScripts.move @@ -0,0 +1,69 @@ +address StarcoinFramework { + +module TransferScripts { + use StarcoinFramework::Account; + use StarcoinFramework::Errors; + use StarcoinFramework::Vector; + const EADDRESS_AND_AUTH_KEY_MISMATCH: u64 = 101; + const ELENGTH_MISMATCH: u64 = 102; + const EDEPRECATED_FUNCTION: u64 = 19; + + public entry fun peer_to_peer(account: signer, payee: address, _payee_auth_key: vector, amount: u128) { + peer_to_peer_v2(account, payee, amount) + } + + public entry fun peer_to_peer_v2(account: signer, payee: address, amount: u128) { + if (!Account::exists_at(payee)) { + Account::create_account_with_address(payee); + }; + Account::pay_from(&account, payee, amount) + } + + /// Batch transfer token to others. + public entry fun batch_peer_to_peer(account: signer, payeees: vector
, _payee_auth_keys: vector>, amounts: vector) { + batch_peer_to_peer_v2(account, payeees, amounts) + } + + /// Batch transfer token to others. + public entry fun batch_peer_to_peer_v2(account: signer, payeees: vector
, amounts: vector) { + let len = Vector::length(&payeees); + assert!(len == Vector::length(&amounts), ELENGTH_MISMATCH); + let i = 0; + while (i < len){ + let payee = *Vector::borrow(&payeees, i); + if (!Account::exists_at(payee)) { + Account::create_account_with_address(payee); + }; + let amount = *Vector::borrow(&amounts, i); + Account::pay_from(&account, payee, amount); + i = i + 1; + } + } + + public entry fun peer_to_peer_batch(_account: signer, _payeees: vector, _payee_auth_keys: vector, _amount: u128) { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + public entry fun peer_to_peer_with_metadata( + account: signer, + payee: address, + _payee_auth_key: vector, + amount: u128, + metadata: vector, + ) { + peer_to_peer_with_metadata_v2(account, payee, amount, metadata) + } + + public entry fun peer_to_peer_with_metadata_v2( + account: signer, + payee: address, + amount: u128, + metadata: vector, + ) { + if (!Account::exists_at(payee)) { + Account::create_account_with_address(payee); + }; + Account::pay_from_with_metadata(&account,payee, amount, metadata) + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Treasury.move b/release/v13/sources/Treasury.move new file mode 100644 index 00000000..f2ca3e3e --- /dev/null +++ b/release/v13/sources/Treasury.move @@ -0,0 +1,388 @@ +address StarcoinFramework { +/// The module for the Treasury of DAO, which can hold the token of DAO. +module Treasury { + use StarcoinFramework::Event; + use StarcoinFramework::Signer; + use StarcoinFramework::Errors; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Math; + use StarcoinFramework::Token::{Self, Token}; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + struct Treasury has store, key { + balance: Token, + /// event handle for treasury withdraw event + withdraw_events: Event::EventHandle, + /// event handle for treasury deposit event + deposit_events: Event::EventHandle, + } + + spec Treasury { + // invariant [abstract] balance.value <= Token::spec_abstract_total_value(); + } + + /// A withdraw capability allows tokens of type `TokenT` to be withdraw from Treasury. + struct WithdrawCapability has key, store {} + + /// A linear time withdraw capability which can withdraw token from Treasury in a period by time-based linear release. + struct LinearWithdrawCapability has key, store { + /// The total amount of tokens that can be withdrawn by this capability + total: u128, + /// The amount of tokens that have been withdrawn by this capability + withdraw: u128, + /// The time-based linear release start time, timestamp in seconds. + start_time: u64, + /// The time-based linear release period in seconds + period: u64, + } + + /// Message for treasury withdraw event. + struct WithdrawEvent has drop, store { + amount: u128, + } + /// Message for treasury deposit event. + struct DepositEvent has drop, store { + amount: u128, + } + + const ERR_INVALID_PERIOD: u64 = 101; + const ERR_ZERO_AMOUNT: u64 = 102; + const ERR_TOO_BIG_AMOUNT: u64 = 103; + const ERR_NOT_AUTHORIZED: u64 = 104; + const ERR_TREASURY_NOT_EXIST: u64 = 105; + + + /// Init a Treasury for TokenT. Can only be called by token issuer. + public fun initialize(signer: &signer, init_token: Token): WithdrawCapability { + let token_issuer = Token::token_address(); + assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + let treasure = Treasury { + balance: init_token, + withdraw_events: Event::new_event_handle(signer), + deposit_events: Event::new_event_handle(signer), + }; + move_to(signer, treasure); + WithdrawCapability{} + } + + spec initialize { + aborts_if Signer::address_of(signer) != Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + ensures exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + ensures result == WithdrawCapability{}; + } + + /// Check the Treasury of TokenT is exists. + public fun exists_at(): bool { + let token_issuer = Token::token_address(); + exists>(token_issuer) + } + + spec exists_at { + aborts_if false; + ensures result == exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + } + + /// Get the balance of TokenT's Treasury + /// if the Treasury do not exists, return 0. + public fun balance(): u128 acquires Treasury { + let token_issuer = Token::token_address(); + if (!exists>(token_issuer)) { + return 0 + }; + let treasury = borrow_global>(token_issuer); + Token::value(&treasury.balance) + } + + spec balance { + aborts_if false; + ensures if (exists>(Token::SPEC_TOKEN_TEST_ADDRESS())) + result == spec_balance() + else + result == 0; + } + + public fun deposit(token: Token) acquires Treasury { + assert!(exists_at(), Errors::not_published(ERR_TREASURY_NOT_EXIST)); + let token_address = Token::token_address(); + let treasury = borrow_global_mut>(token_address); + let amount = Token::value(&token); + Event::emit_event( + &mut treasury.deposit_events, + DepositEvent { amount }, + ); + Token::deposit(&mut treasury.balance, token); + } + + spec deposit { + aborts_if !exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + aborts_if spec_balance() + token.value > MAX_U128; + ensures spec_balance() == old(spec_balance()) + token.value; + } + + fun do_withdraw(amount: u128): Token acquires Treasury { + assert!(amount > 0, Errors::invalid_argument(ERR_ZERO_AMOUNT)); + assert!(exists_at(), Errors::not_published(ERR_TREASURY_NOT_EXIST)); + let token_address = Token::token_address(); + let treasury = borrow_global_mut>(token_address); + assert!(amount <= Token::value(&treasury.balance) , Errors::invalid_argument(ERR_TOO_BIG_AMOUNT)); + Event::emit_event( + &mut treasury.withdraw_events, + WithdrawEvent { amount }, + ); + Token::withdraw(&mut treasury.balance, amount) + } + + spec do_withdraw { + include WithdrawSchema; + } + + spec schema WithdrawSchema { + amount: u64; + + aborts_if amount <= 0; + aborts_if !exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + aborts_if spec_balance() < amount; + ensures spec_balance() == + old(spec_balance()) - amount; + } + + /// Withdraw tokens with given `LinearWithdrawCapability`. + public fun withdraw_with_capability( + _cap: &mut WithdrawCapability, + amount: u128, + ): Token acquires Treasury { + do_withdraw(amount) + } + + spec withdraw_with_capability { + include WithdrawSchema; + } + + /// Withdraw from TokenT's treasury, the signer must have WithdrawCapability + public fun withdraw( + signer: &signer, + amount: u128 + ): Token acquires Treasury, WithdrawCapability { + let cap = borrow_global_mut>(Signer::address_of(signer)); + Self::withdraw_with_capability(cap, amount) + } + + spec withdraw { + aborts_if !exists>(Signer::address_of(signer)); + include WithdrawSchema; + } + + /// Issue a `LinearWithdrawCapability` with given `WithdrawCapability`. + public fun issue_linear_withdraw_capability( + _capability: &mut WithdrawCapability, + amount: u128, + period: u64 + ): LinearWithdrawCapability { + assert!(period > 0, Errors::invalid_argument(ERR_INVALID_PERIOD)); + assert!(amount > 0, Errors::invalid_argument(ERR_ZERO_AMOUNT)); + let start_time = Timestamp::now_seconds(); + LinearWithdrawCapability { + total: amount, + withdraw: 0, + start_time, + period, + } + } + + spec issue_linear_withdraw_capability { + aborts_if period == 0; + aborts_if amount == 0; + aborts_if !exists(StarcoinFramework::CoreAddresses::GENESIS_ADDRESS()); + } + + /// Withdraw tokens with given `LinearWithdrawCapability`. + public fun withdraw_with_linear_capability( + cap: &mut LinearWithdrawCapability, + ): Token acquires Treasury { + let amount = withdraw_amount_of_linear_cap(cap); + let token = do_withdraw(amount); + cap.withdraw = cap.withdraw + amount; + token + } + + spec withdraw_with_linear_capability { + pragma aborts_if_is_partial; + // TODO: See [MUL_DIV] + // include WithdrawSchema {amount: ?}; + } + + /// Withdraw from TokenT's treasury, the signer must have LinearWithdrawCapability + public fun withdraw_by_linear( + signer: &signer, + ): Token acquires Treasury, LinearWithdrawCapability { + let cap = borrow_global_mut>(Signer::address_of(signer)); + Self::withdraw_with_linear_capability(cap) + } + + spec withdraw_by_linear { + pragma aborts_if_is_partial; + aborts_if !exists>(Signer::address_of(signer)); + // TODO: See [MUL_DIV] + // include WithdrawSchema {amount: ?}; + } + + /// Split the given `LinearWithdrawCapability`. + public fun split_linear_withdraw_cap( + cap: &mut LinearWithdrawCapability, + amount: u128, + ): (Token, LinearWithdrawCapability) acquires Treasury { + assert!(amount > 0, Errors::invalid_argument(ERR_ZERO_AMOUNT)); + let token = Self::withdraw_with_linear_capability(cap); + assert!((cap.withdraw + amount) <= cap.total, Errors::invalid_argument(ERR_TOO_BIG_AMOUNT)); + cap.total = cap.total - amount; + let start_time = Timestamp::now_seconds(); + let new_period = cap.start_time + cap.period - start_time; + let new_key = LinearWithdrawCapability { + total: amount, + withdraw: 0, + start_time, + period: new_period + }; + (token, new_key) + } + + spec split_linear_withdraw_cap { + pragma aborts_if_is_partial; + ensures old(cap.total - cap.withdraw) == + result_1.value + (result_2.total - result_2.withdraw) + (cap.total - cap.withdraw); + } + + + /// Returns the amount of the LinearWithdrawCapability can mint now. + public fun withdraw_amount_of_linear_cap(cap: &LinearWithdrawCapability): u128 { + let now = Timestamp::now_seconds(); + let elapsed_time = now - cap.start_time; + if (elapsed_time >= cap.period) { + cap.total - cap.withdraw + } else { + Math::mul_div(cap.total, (elapsed_time as u128), (cap.period as u128)) - cap.withdraw + } + } + + spec withdraw_amount_of_linear_cap { + // TODO: [MUL_DIV] The most important property is the amount of value. + // However, Math::mul_div remains to be uninterpreted + pragma aborts_if_is_partial; + aborts_if !exists(StarcoinFramework::CoreAddresses::GENESIS_ADDRESS()); + aborts_if Timestamp::spec_now_seconds() < cap.start_time; + aborts_if Timestamp::spec_now_seconds() - cap.start_time >= cap.period && cap.total < cap.withdraw; + aborts_if [abstract] + Timestamp::spec_now_seconds() - cap.start_time < cap.period && Math::spec_mul_div() < cap.withdraw; + ensures [abstract] result <= cap.total - cap.withdraw; + } + + /// Check if the given `LinearWithdrawCapability` is empty. + public fun is_empty_linear_withdraw_cap(key: &LinearWithdrawCapability) : bool { + key.total == key.withdraw + } + + spec is_empty_linear_withdraw_cap { + aborts_if false; + ensures result == (key.total == key.withdraw); + } + + // Improvement: Make move prover support the following definition. + // Following specs contains lots of duplication + //spec schema AddCapability { + // signer: signer; + + // aborts_if exists(Signer::address_of(signer)); + // ensures exists(Signer::address_of(signer)); + //} + + /// Remove mint capability from `signer`. + public fun remove_withdraw_capability(signer: &signer): WithdrawCapability + acquires WithdrawCapability { + move_from>(Signer::address_of(signer)) + } + + spec remove_withdraw_capability { + aborts_if !exists>(Signer::address_of(signer)); + ensures !exists>(Signer::address_of(signer)); + } + + /// Save mint capability to `signer`. + public fun add_withdraw_capability(signer: &signer, cap: WithdrawCapability) { + move_to(signer, cap) + } + + spec add_withdraw_capability { + aborts_if exists>(Signer::address_of(signer)); + ensures exists>(Signer::address_of(signer)); + } + + /// Destroy the given mint capability. + public fun destroy_withdraw_capability(cap: WithdrawCapability) { + let WithdrawCapability {} = cap; + } + + spec destroy_withdraw_capability { + } + + /// Add LinearWithdrawCapability to `signer`, a address only can have one LinearWithdrawCapability + public fun add_linear_withdraw_capability(signer: &signer, cap: LinearWithdrawCapability) { + move_to(signer, cap) + } + + spec add_linear_withdraw_capability { + aborts_if exists>(Signer::address_of(signer)); + ensures exists>(Signer::address_of(signer)); + } + + /// Remove LinearWithdrawCapability from `signer`. + public fun remove_linear_withdraw_capability(signer: &signer): LinearWithdrawCapability + acquires LinearWithdrawCapability { + move_from>(Signer::address_of(signer)) + } + + spec remove_linear_withdraw_capability { + aborts_if !exists>(Signer::address_of(signer)); + ensures !exists>(Signer::address_of(signer)); + } + + /// Destroy LinearWithdrawCapability. + public fun destroy_linear_withdraw_capability(cap: LinearWithdrawCapability) { + let LinearWithdrawCapability{ total: _, withdraw: _, start_time: _, period: _ } = cap; + } + + public fun is_empty_linear_withdraw_capability(cap: &LinearWithdrawCapability): bool { + cap.total == cap.withdraw + } + + /// Get LinearWithdrawCapability total amount + public fun get_linear_withdraw_capability_total(cap: &LinearWithdrawCapability): u128 { + cap.total + } + + /// Get LinearWithdrawCapability withdraw amount + public fun get_linear_withdraw_capability_withdraw(cap: &LinearWithdrawCapability): u128 { + cap.withdraw + } + + /// Get LinearWithdrawCapability period in seconds + public fun get_linear_withdraw_capability_period(cap: &LinearWithdrawCapability): u64 { + cap.period + } + + /// Get LinearWithdrawCapability start_time in seconds + public fun get_linear_withdraw_capability_start_time(cap: &LinearWithdrawCapability): u64 { + cap.start_time + } + + + spec fun spec_balance(): num { + global>(Token::SPEC_TOKEN_TEST_ADDRESS()).balance.value + } + +} +} \ No newline at end of file diff --git a/release/v13/sources/TreasuryScripts.move b/release/v13/sources/TreasuryScripts.move new file mode 100644 index 00000000..34747f89 --- /dev/null +++ b/release/v13/sources/TreasuryScripts.move @@ -0,0 +1,79 @@ +address StarcoinFramework { +module TreasuryScripts { + use StarcoinFramework::Treasury; + use StarcoinFramework::Account; + use StarcoinFramework::Offer; + use StarcoinFramework::TreasuryWithdrawDaoProposal; + + public entry fun withdraw_and_split_lt_withdraw_cap( + signer: signer, + for_address: address, + amount: u128, + lock_period: u64, + ) { + // 1. take cap: LinearWithdrawCapability + let cap = Treasury::remove_linear_withdraw_capability(&signer); + + // 2. withdraw token and split + let (tokens, new_cap) = Treasury::split_linear_withdraw_cap(&mut cap, amount); + + // 3. deposit + Account::deposit_to_self(&signer, tokens); + + // 4. put or destroy key + if (Treasury::is_empty_linear_withdraw_capability(&cap)) { + Treasury::destroy_linear_withdraw_capability(cap); + } else { + Treasury::add_linear_withdraw_capability(&signer, cap); + }; + + // 5. offer + Offer::create(&signer, new_cap, for_address, lock_period); + } + + spec withdraw_and_split_lt_withdraw_cap { + pragma verify = false; + } + + public entry fun withdraw_token_with_linear_withdraw_capability( + signer: signer, + ) { + // 1. take cap + let cap = Treasury::remove_linear_withdraw_capability(&signer); + + // 2. withdraw token + let tokens = Treasury::withdraw_with_linear_capability(&mut cap); + + // 3. deposit + Account::deposit_to_self(&signer, tokens); + + // 4. put or destroy key + if (Treasury::is_empty_linear_withdraw_capability(&cap)) { + Treasury::destroy_linear_withdraw_capability(cap); + } else { + Treasury::add_linear_withdraw_capability(&signer, cap); + }; + } + + spec withdraw_token_with_linear_withdraw_capability { + pragma verify = false; + } + + public entry fun propose_withdraw(signer: signer, receiver: address, amount: u128, period: u64, exec_delay: u64){ + TreasuryWithdrawDaoProposal::propose_withdraw(&signer, receiver, amount, period, exec_delay) + } + + spec propose_withdraw { + pragma verify = false; + } + + public entry fun execute_withdraw_proposal(signer: signer, proposer_address: address, + proposal_id: u64,){ + TreasuryWithdrawDaoProposal::execute_withdraw_proposal(&signer, proposer_address, proposal_id); + } + + spec execute_withdraw_proposal { + pragma verify = false; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/TreasuryWithdrawDaoProposal.move b/release/v13/sources/TreasuryWithdrawDaoProposal.move new file mode 100644 index 00000000..5b3668c1 --- /dev/null +++ b/release/v13/sources/TreasuryWithdrawDaoProposal.move @@ -0,0 +1,119 @@ +address StarcoinFramework { +/// TreasuryWithdrawDaoProposal is a dao proposal for withdraw Token from Treasury. +module TreasuryWithdrawDaoProposal { + use StarcoinFramework::Token::{Self,Token}; + use StarcoinFramework::Signer; + use StarcoinFramework::Dao; + use StarcoinFramework::Errors; + use StarcoinFramework::Treasury; + use StarcoinFramework::CoreAddresses; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_strict; + pragma aborts_if_is_partial; + } + + /// A wrapper of Token MintCapability. + struct WrappedWithdrawCapability has key { + cap: Treasury::WithdrawCapability, + } + + /// WithdrawToken request. + struct WithdrawToken has copy, drop, store { + /// the receiver of withdraw tokens. + receiver: address, + /// how many tokens to mint. + amount: u128, + /// How long in milliseconds does it take for the token to be released + period: u64, + } + + const ERR_NOT_AUTHORIZED: u64 = 101; + /// Only receiver can execute TreasuryWithdrawDaoProposal + const ERR_NEED_RECEIVER_TO_EXECUTE: u64 = 102; + /// The withdraw amount of propose is too many. + const ERR_TOO_MANY_WITHDRAW_AMOUNT: u64 = 103; + + /// Plugin method of the module. + /// Should be called by token issuer. + public fun plugin(signer: &signer, cap: Treasury::WithdrawCapability) { + let token_issuer = Token::token_address(); + assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + move_to(signer, WrappedWithdrawCapability { cap: cap }); + } + + spec plugin { + pragma aborts_if_is_partial = false; + let sender = Signer::address_of(signer); + aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if !exists>(sender); + aborts_if exists>(sender); + + ensures !exists>(sender); + ensures exists>(sender); + } + + + /// Entrypoint for the proposal. + public fun propose_withdraw(signer: &signer, receiver: address, amount: u128, period: u64, exec_delay: u64) { + let quorum_votes = Dao::quorum_votes(); + assert!(amount <= quorum_votes, Errors::invalid_argument(ERR_TOO_MANY_WITHDRAW_AMOUNT)); + Dao::propose( + signer, + WithdrawToken { receiver, amount, period }, + exec_delay, + ); + } + spec propose_withdraw { + use StarcoinFramework::Timestamp; + use StarcoinFramework::CoreAddresses; + pragma aborts_if_is_partial = false; + let quorum_votes = Dao::spec_quorum_votes(); + aborts_if amount > quorum_votes; + // copy from Dao::propose spec. + include Dao::AbortIfDaoConfigNotExist; + include Dao::AbortIfDaoInfoNotExist; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if exec_delay > 0 && exec_delay < Dao::spec_dao_config().min_action_delay; + include Dao::CheckQuorumVotes; + let sender = Signer::address_of(signer); + aborts_if exists>(sender); + } + + /// Once the proposal is agreed, anyone can call the method to make the proposal happen. + public fun execute_withdraw_proposal( + signer: &signer, + proposer_address: address, + proposal_id: u64, + ) acquires WrappedWithdrawCapability { + let WithdrawToken { receiver, amount, period } = Dao::extract_proposal_action( + proposer_address, + proposal_id, + ); + assert!(receiver == Signer::address_of(signer), Errors::requires_address(ERR_NEED_RECEIVER_TO_EXECUTE)); + let cap = borrow_global_mut>(Token::token_address()); + let linear_cap = Treasury::issue_linear_withdraw_capability(&mut cap.cap, amount, period); + Treasury::add_linear_withdraw_capability(signer, linear_cap); + } + + spec execute_withdraw_proposal { + use StarcoinFramework::Option; + pragma aborts_if_is_partial = true; + let expected_states = vec(6); + include Dao::CheckProposalStates{expected_states}; + let proposal = global>(proposer_address); + aborts_if Option::is_none(proposal.action); + aborts_if !exists>(Token::SPEC_TOKEN_TEST_ADDRESS()); + } + + /// Provider a port for get block reward STC from Treasury, only genesis account can invoke this function. + /// The TreasuryWithdrawCapability is locked in TreasuryWithdrawDaoProposal, and only can withdraw by DAO proposal. + /// This approach is not graceful, but restricts the operation to genesis accounts only, so there are no security issues either. + public fun withdraw_for_block_reward(signer: &signer, reward: u128):Token acquires WrappedWithdrawCapability { + CoreAddresses::assert_genesis_address(signer); + let cap = borrow_global_mut>(Signer::address_of(signer)); + Treasury::withdraw_with_capability(&mut cap.cap, reward) + } +} +} \ No newline at end of file diff --git a/release/v13/sources/TypeInfo.move b/release/v13/sources/TypeInfo.move new file mode 100644 index 00000000..fa755c72 --- /dev/null +++ b/release/v13/sources/TypeInfo.move @@ -0,0 +1,44 @@ +module StarcoinFramework::TypeInfo { + use StarcoinFramework::BCS; + use StarcoinFramework::Token; + use StarcoinFramework::Vector; + + struct TypeInfo has copy, drop, store { + account_address: address, + module_name: vector, + struct_name: vector + } + + public fun account_address(type_info: &TypeInfo): address { + type_info.account_address + } + + public fun module_name(type_info: &TypeInfo): vector { + *&type_info.module_name + } + + public fun struct_name(type_info: &TypeInfo): vector { + *&type_info.struct_name + } + + public fun type_of(): TypeInfo { + let (account_address, module_name, struct_name) = Token::type_of(); + TypeInfo { + account_address, + module_name, + struct_name + } + } + + /// Return the BCS size, in bytes, of value at `val_ref`. + /// + /// See the [BCS spec](https://github.com/diem/bcs) + /// + /// See `test_size_of_val()` for an analysis of common types and + /// nesting patterns, as well as `test_size_of_val_vectors()` for an + /// analysis of vector size dynamism. + public fun size_of_val(val_ref: &T): u64 { + // Return vector length of vectorized BCS representation. + Vector::length(&BCS::to_bytes(val_ref)) + } +} \ No newline at end of file diff --git a/release/v13/sources/U256.move b/release/v13/sources/U256.move new file mode 100644 index 00000000..83cbdc27 --- /dev/null +++ b/release/v13/sources/U256.move @@ -0,0 +1,442 @@ +address StarcoinFramework { +/// Helper module to do u64 arith. +module Arith { + use StarcoinFramework::Errors; + const ERR_INVALID_CARRY: u64 = 301; + const ERR_INVALID_BORROW: u64 = 302; + + const P32: u64 = 0x100000000; + const P64: u128 = 0x10000000000000000; + + spec module { + pragma verify = true; + pragma aborts_if_is_strict; + } + + /// split u64 to (high, low) + public fun split_u64(i: u64): (u64, u64) { + (i >> 32, i & 0xFFFFFFFF) + } + + spec split_u64 { + pragma verify = false; + pragma opaque; // MVP cannot reason about bitwise operation + ensures [abstract] result_1 == i / P32; + ensures [abstract] result_2 == i % P32; + } + + /// combine (high, low) to u64, + /// any lower bits of `high` will be erased, any higher bits of `low` will be erased. + public fun combine_u64(hi: u64, lo: u64): u64 { + (hi << 32) | (lo & 0xFFFFFFFF) + } + + spec combine_u64 { + pragma verify = false; + pragma opaque = true; // MVP cannot reason about bitwise operation + let hi_32 = hi % P32; + let lo_32 = lo % P32; + ensures [abstract] result == hi_32 * P32 + lo_32; + } + + /// a + b, with carry + public fun adc(a: u64, b: u64, carry: &mut u64) : u64 { + assert!(*carry <= 1, Errors::invalid_argument(ERR_INVALID_CARRY)); + let (a1, a0) = split_u64(a); + let (b1, b0) = split_u64(b); + let (c, r0) = split_u64(a0 + b0 + *carry); + let (c, r1) = split_u64(a1 + b1 + c); + *carry = c; + combine_u64(r1, r0) + } + + spec adc { + // Carry has either to be 0 or 1 + aborts_if !(carry == 0 || carry == 1); + ensures carry == 0 || carry == 1; + // Result with or without carry + ensures carry == 0 ==> result == a + b + old(carry); + ensures carry == 1 ==> P64 + result == a + b + old(carry); + } + + /// a - b, with borrow + public fun sbb(a: u64, b: u64, borrow: &mut u64): u64 { + assert!(*borrow <= 1, Errors::invalid_argument(ERR_INVALID_BORROW)); + let (a1, a0) = split_u64(a); + let (b1, b0) = split_u64(b); + let (b, r0) = split_u64(P32 + a0 - b0 - *borrow); + let borrowed = 1 - b; + let (b, r1) = split_u64(P32 + a1 - b1 - borrowed); + *borrow = 1 - b; + + combine_u64(r1, r0) + } + + spec sbb { + // Borrow has either to be 0 or 1 + aborts_if !(borrow == 0 || borrow == 1); + ensures borrow == 0 || borrow == 1; + // Result with or without borrow + ensures borrow == 0 ==> result == a - b - old(borrow); + ensures borrow == 1 ==> result == P64 + a - b - old(borrow); + } +} + +/// Implementation u256. +module U256 { + + spec module { + pragma verify = true; + } + + use StarcoinFramework::Vector; + use StarcoinFramework::Errors; + + const WORD: u8 = 4; + const P32: u64 = 0x100000000; + const P64: u128 = 0x10000000000000000; + + const ERR_INVALID_LENGTH: u64 = 100; + const ERR_OVERFLOW: u64 = 200; + /// use vector to represent data. + /// so that we can use buildin vector ops later to construct U256. + /// vector should always has two elements. + struct U256 has copy, drop, store { + /// little endian representation + bits: vector, + } + + spec U256 { + invariant len(bits) == 4; + } + + spec fun value_of_U256(a: U256): num { + a.bits[0] + + a.bits[1] * P64 + + a.bits[2] * P64 * P64 + + a.bits[3] * P64 * P64 * P64 + } + + public fun zero(): U256 { + from_u128(0u128) + } + + public fun one(): U256 { + from_u128(1u128) + } + + public fun from_u64(v: u64): U256 { + from_u128((v as u128)) + } + + public fun from_u128(v: u128): U256 { + let low = ((v & 0xffffffffffffffff) as u64); + let high = ((v >> 64) as u64); + let bits = Vector::singleton(low); + Vector::push_back(&mut bits, high); + Vector::push_back(&mut bits, 0u64); + Vector::push_back(&mut bits, 0u64); + U256 { bits } + } + + spec from_u128 { + pragma verify = false; + pragma opaque; // Original function has bitwise operator + ensures value_of_U256(result) == v; + } + + #[test] + fun test_from_u128() { + // 2^64 + 1 + let v = from_u128(18446744073709551617u128); + assert!(*Vector::borrow(&v.bits, 0) == 1, 0); + assert!(*Vector::borrow(&v.bits, 1) == 1, 1); + assert!(*Vector::borrow(&v.bits, 2) == 0, 2); + assert!(*Vector::borrow(&v.bits, 3) == 0, 3); + } + + public fun from_big_endian(data: vector): U256 { + // TODO: define error code. + assert!(Vector::length(&data) <= 32, Errors::invalid_argument(ERR_INVALID_LENGTH)); + from_bytes(&data, true) + } + + spec from_big_endian { + pragma verify = false; // TODO: How to interpret the value of vector data of bytes + } + + public fun from_little_endian(data: vector): U256 { + // TODO: define error code. + assert!(Vector::length(&data) <= 32, Errors::invalid_argument(ERR_INVALID_LENGTH)); + from_bytes(&data, false) + } + + spec from_little_endian { + pragma verify = false; // TODO: How to interpret the value of vector data of bytes + } + + public fun to_u128(v: &U256): u128 { + assert!(*Vector::borrow(&v.bits, 3) == 0, Errors::invalid_state(ERR_OVERFLOW)); + assert!(*Vector::borrow(&v.bits, 2) == 0, Errors::invalid_state(ERR_OVERFLOW)); + ((*Vector::borrow(&v.bits, 1) as u128) << 64) | (*Vector::borrow(&v.bits, 0) as u128) + } + + spec to_u128 { + pragma verify = false; + pragma opaque; // Original function has bitwise operator + aborts_if value_of_U256(v) >= P64 * P64; + ensures value_of_U256(v) == result; + } + + #[test] + fun test_to_u128() { + // 2^^128 - 1 + let i = 340282366920938463463374607431768211455u128; + let v = from_u128(i); + assert!(to_u128(&v) == i, 128); + } + #[test] + #[expected_failure] + fun test_to_u128_overflow() { + // 2^^128 - 1 + let i = 340282366920938463463374607431768211455u128; + let v = from_u128(i); + let v = add(v, one()); + to_u128(&v); + } + + const EQUAL: u8 = 0; + const LESS_THAN: u8 = 1; + const GREATER_THAN: u8 = 2; + + public fun compare(a: &U256, b: &U256): u8 { + let i = (WORD as u64); + while (i > 0) { + i = i - 1; + let a_bits = *Vector::borrow(&a.bits, i); + let b_bits = *Vector::borrow(&b.bits, i); + if (a_bits != b_bits) { + if (a_bits < b_bits) { + return LESS_THAN + } else { + return GREATER_THAN + } + } + }; + return EQUAL + } + + // TODO: MVP interprets it wrong + // spec compare { + // let va = value_of_U256(a); + // let vb = value_of_U256(b); + // ensures (va > vb) ==> (result == GREATER_THAN); + // ensures (va < vb) ==> (result == LESS_THAN); + // ensures (va == vb) ==> (result == EQUAL); + // } + + #[test] + fun test_compare() { + let a = from_u64(111); + let b = from_u64(111); + let c = from_u64(112); + let d = from_u64(110); + assert!(compare(&a, &b) == EQUAL, 0); + assert!(compare(&a, &c) == LESS_THAN, 1); + assert!(compare(&a, &d) == GREATER_THAN, 2); + } + + + public fun add(a: U256, b: U256): U256 { + native_add(&mut a, &b); + a + } + + spec add { + aborts_if value_of_U256(a) + value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(result) == value_of_U256(a) + value_of_U256(b); + } + + #[test] + fun test_add() { + let a = Self::one(); + let b = Self::from_u128(10); + let ret = Self::add(a, b); + assert!(compare(&ret, &from_u64(11)) == EQUAL, 0); + } + + public fun sub(a: U256, b: U256): U256 { + native_sub(&mut a, &b); + a + } + + spec sub { + aborts_if value_of_U256(a) < value_of_U256(b); + ensures value_of_U256(result) == value_of_U256(a) - value_of_U256(b); + } + + #[test] + #[expected_failure] + fun test_sub_overflow() { + let a = Self::one(); + let b = Self::from_u128(10); + let _ = Self::sub(a, b); + } + + #[test] + fun test_sub_ok() { + let a = Self::from_u128(10); + let b = Self::one(); + let ret = Self::sub(a, b); + assert!(compare(&ret, &from_u64(9)) == EQUAL, 0); + } + + public fun mul(a: U256, b: U256): U256 { + native_mul(&mut a, &b); + a + } + + spec mul { + pragma verify = false; + pragma timeout = 200; // Take longer time + aborts_if value_of_U256(a) * value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(result) == value_of_U256(a) * value_of_U256(b); + } + + #[test] + fun test_mul() { + let a = Self::from_u128(10); + let b = Self::from_u64(10); + let ret = Self::mul(a, b); + assert!(compare(&ret, &from_u64(100)) == EQUAL, 0); + } + + public fun div(a: U256, b: U256): U256 { + native_div(&mut a, &b); + a + } + + spec div { + pragma verify = false; + pragma timeout = 160; // Might take longer time + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(result) == value_of_U256(a) / value_of_U256(b); + } + + #[test] + fun test_div() { + let a = Self::from_u128(10); + let b = Self::from_u64(2); + let c = Self::from_u64(3); + // as U256 cannot be implicitly copied, we need to add copy keyword. + assert!(compare(&Self::div(copy a, b), &from_u64(5)) == EQUAL, 0); + assert!(compare(&Self::div(copy a, c), &from_u64(3)) == EQUAL, 0); + } + + public fun rem(a: U256, b: U256): U256 { + native_rem(&mut a, &b); + a + } + + spec rem { + pragma verify = false; + pragma timeout = 160; // Might take longer time + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(result) == value_of_U256(a) % value_of_U256(b); + } + + #[test] + fun test_rem() { + let a = Self::from_u128(10); + let b = Self::from_u64(2); + let c = Self::from_u64(3); + assert!(compare(&Self::rem(copy a, b), &from_u64(0)) == EQUAL, 0); + assert!(compare(&Self::rem(copy a, c), &from_u64(1)) == EQUAL, 0); + } + + public fun pow(a: U256, b: U256): U256 { + native_pow(&mut a, &b); + a + } + + spec pow { + // Verfication of Pow takes enormous amount of time + // Don't verify it, and make it opaque so that the caller + // can make use of the properties listed here. + pragma verify = false; + pragma opaque; + pragma timeout = 600; + let p = pow_spec(value_of_U256(a), value_of_U256(b)); + aborts_if p >= P64 * P64 * P64 * P64; + ensures value_of_U256(result) == p; + } + + #[test] + fun test_pow() { + let a = Self::from_u128(10); + let b = Self::from_u64(1); + let c = Self::from_u64(2); + let d = Self::zero(); + assert!(compare(&Self::pow(copy a, b), &from_u64(10)) == EQUAL, 0); + assert!(compare(&Self::pow(copy a, c), &from_u64(100)) == EQUAL, 0); + assert!(compare(&Self::pow(copy a, d), &from_u64(1)) == EQUAL, 0); + } + + native fun from_bytes(data: &vector, be: bool): U256; + native fun native_add(a: &mut U256, b: &U256); + native fun native_sub(a: &mut U256, b: &U256); + native fun native_mul(a: &mut U256, b: &U256); + native fun native_div(a: &mut U256, b: &U256); + native fun native_rem(a: &mut U256, b: &U256); + native fun native_pow(a: &mut U256, b: &U256); + + spec native_add { + pragma opaque; + aborts_if value_of_U256(a) + value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(a) == value_of_U256(old(a)) + value_of_U256(b); + } + + spec native_sub { + pragma opaque; + aborts_if value_of_U256(a) - value_of_U256(b) < 0; + ensures value_of_U256(a) == value_of_U256(old(a)) - value_of_U256(b); + } + + spec native_mul { + pragma opaque; + aborts_if value_of_U256(a) * value_of_U256(b) >= P64 * P64 * P64 * P64; + ensures value_of_U256(a) == value_of_U256(old(a)) * value_of_U256(b); + } + + spec native_div { + pragma opaque; + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(a) == value_of_U256(old(a)) / value_of_U256(b); + } + + spec native_rem { + pragma opaque; + aborts_if value_of_U256(b) == 0; + ensures value_of_U256(a) == value_of_U256(old(a)) % value_of_U256(b); + } + + spec native_pow { + pragma opaque; + aborts_if pow_spec(value_of_U256(a), value_of_U256(b)) >= P64 * P64 * P64 * P64; + ensures value_of_U256(a) == pow_spec(value_of_U256(old(a)), value_of_U256(b)); + } + + spec fun pow_spec(base: num, expon: num): num { + // This actually doesn't follow a strict definition as 0^0 is undefined + // mathematically. But the U256::pow of Rust is defined to be like this: + // Link: https://docs.rs/uint/0.9.3/src/uint/uint.rs.html#1000-1003 + if (expon > 0) { + let x = pow_spec(base, expon / 2); + if (expon % 2 == 0) { x * x } else { x * x * base } + } else { + 1 + } + } + +} +} diff --git a/release/v13/sources/UpgradeModuleDaoProposal.move b/release/v13/sources/UpgradeModuleDaoProposal.move new file mode 100644 index 00000000..d4cb99c8 --- /dev/null +++ b/release/v13/sources/UpgradeModuleDaoProposal.move @@ -0,0 +1,117 @@ +address StarcoinFramework { +/// UpgradeModuleDaoProposal is a proposal moudle used to upgrade contract codes under a token. +module UpgradeModuleDaoProposal { + use StarcoinFramework::PackageTxnManager; + use StarcoinFramework::Token; + use StarcoinFramework::Signer; + use StarcoinFramework::Option; + use StarcoinFramework::Dao; + use StarcoinFramework::Errors; + + spec module { + pragma verify = false; // break after enabling v2 compilation scheme + pragma aborts_if_is_strict; + pragma aborts_if_is_partial; + } + + const ERR_UNABLE_TO_UPGRADE: u64 = 400; + const ERR_NOT_AUTHORIZED: u64 = 401; + const ERR_ADDRESS_MISSMATCH: u64 = 402; + + /// A wrapper of `PackageTxnManager::UpgradePlanCapability`. + struct UpgradeModuleCapability has key { + cap: PackageTxnManager::UpgradePlanCapability, + } + + /// request of upgrading module contract code. + struct UpgradeModule has copy, drop, store { + module_address: address, + package_hash: vector, + version: u64, + } + + struct UpgradeModuleV2 has copy, drop, store { + module_address: address, + package_hash: vector, + version: u64, + enforced: bool, + } + + /// If this goverment can upgrade module, call this to register capability. + public fun plugin( + signer: &signer, + cap: PackageTxnManager::UpgradePlanCapability, + ) { + let token_issuer = Token::token_address(); + assert!(Signer::address_of(signer) == token_issuer, Errors::requires_address(ERR_NOT_AUTHORIZED)); + move_to(signer, UpgradeModuleCapability { cap }) + } + + spec plugin { + pragma aborts_if_is_partial = false; + + let sender = Signer::address_of(signer); + aborts_if sender != Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if exists>(sender); + } + + spec schema AbortIfUnableUpgrade { + module_address: address; + let token_issuer = Token::SPEC_TOKEN_TEST_ADDRESS(); + aborts_if !exists>(token_issuer); + let cap = global>(token_issuer).cap; + aborts_if PackageTxnManager::account_address(cap) != module_address; + } + + public fun propose_module_upgrade_v2( + signer: &signer, + module_address: address, + package_hash: vector, + version: u64, + exec_delay: u64, + enforced: bool, + ) acquires UpgradeModuleCapability { + let cap = borrow_global>(Token::token_address()); + let account_address = PackageTxnManager::account_address(&cap.cap); + assert!(account_address == module_address, Errors::requires_capability(ERR_ADDRESS_MISSMATCH)); + Dao::propose( + signer, + UpgradeModuleV2 { module_address, package_hash, version, enforced }, + exec_delay, + ); + } + + spec propose_module_upgrade_v2 { + pragma aborts_if_is_partial = true; + include AbortIfUnableUpgrade; + } + + /// Once the proposal is agreed, anyone can call this method to generate the upgrading plan. + public fun submit_module_upgrade_plan( + proposer_address: address, + proposal_id: u64, + ) acquires UpgradeModuleCapability { + let UpgradeModuleV2 { module_address, package_hash, version, enforced } = Dao::extract_proposal_action< + TokenT, + UpgradeModuleV2, + >(proposer_address, proposal_id); + let cap = borrow_global>(Token::token_address()); + let account_address = PackageTxnManager::account_address(&cap.cap); + assert!(account_address == module_address, Errors::requires_capability(ERR_ADDRESS_MISSMATCH)); + PackageTxnManager::submit_upgrade_plan_with_cap_v2( + &cap.cap, + package_hash, + version, + enforced, + ); + } + spec submit_module_upgrade_plan { + let expected_states = vec(6); + include Dao::CheckProposalStates{expected_states}; + let proposal = global>(proposer_address); + aborts_if Option::is_none(proposal.action); + let action = proposal.action.vec[0]; + include AbortIfUnableUpgrade{module_address: action.module_address}; + } +} +} \ No newline at end of file diff --git a/release/v13/sources/VMConfig.move b/release/v13/sources/VMConfig.move new file mode 100644 index 00000000..3f962e83 --- /dev/null +++ b/release/v13/sources/VMConfig.move @@ -0,0 +1,442 @@ +address StarcoinFramework { +/// `VMConfig` keep track of VM related configuration, like gas schedule. +module VMConfig { + use StarcoinFramework::Config; + use StarcoinFramework::Signer; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Vector; + use StarcoinFramework::ChainId; + spec module { + pragma verify = false; + pragma aborts_if_is_strict; + } + + /// The struct to hold all config data needed to operate the VM. + /// * gas_schedule: Cost of running the VM. + struct VMConfig has copy, drop, store { + gas_schedule: GasSchedule, + } + + /// The gas schedule keeps two separate schedules for the gas: + /// * The instruction_schedule: This holds the gas for each bytecode instruction. + /// * The native_schedule: This holds the gas for used (per-byte operated over) for each native + /// function. + /// A couple notes: + /// 1. In the case that an instruction is deleted from the bytecode, that part of the cost schedule + /// still needs to remain the same; once a slot in the table is taken by an instruction, that is its + /// slot for the rest of time (since that instruction could already exist in a module on-chain). + /// 2. The initialization of the module will publish the instruction table to the genesis + /// address, and will preload the vector with the gas schedule for instructions. The VM will then + /// load this into memory at the startup of each block. + struct GasSchedule has copy, drop, store { + instruction_schedule: vector, + native_schedule: vector, + gas_constants: GasConstants, + } + + /// The gas constants contains all kind of constants used in gas calculation. + struct GasConstants has copy, drop, store { + /// The cost per-byte written to global storage. + global_memory_per_byte_cost: u64, + /// The cost per-byte written to storage. + global_memory_per_byte_write_cost: u64, + /// We charge one unit of gas per-byte for the first 600 bytes + min_transaction_gas_units: u64, + /// Any transaction over this size will be charged `INTRINSIC_GAS_PER_BYTE` per byte + large_transaction_cutoff: u64, + /// The units of gas that should be charged per byte for every transaction. + instrinsic_gas_per_byte: u64, + /// 1 nanosecond should equal one unit of computational gas. We bound the maximum + /// computational time of any given transaction at 10 milliseconds. We want this number and + /// `MAX_PRICE_PER_GAS_UNIT` to always satisfy the inequality that + /// MAXIMUM_NUMBER_OF_GAS_UNITS * MAX_PRICE_PER_GAS_UNIT < min(u64::MAX, GasUnits::MAX) + maximum_number_of_gas_units: u64, + /// The minimum gas price that a transaction can be submitted with. + min_price_per_gas_unit: u64, + /// The maximum gas unit price that a transaction can be submitted with. + max_price_per_gas_unit: u64, + /// The max transaction size in bytes that a transaction can have. + max_transaction_size_in_bytes: u64, + /// gas unit scaling factor. + gas_unit_scaling_factor: u64, + /// default account size. + default_account_size: u64, + } + + /// The `GasCost` tracks: + /// - instruction cost: how much time/computational power is needed to perform the instruction + /// - memory cost: how much memory is required for the instruction, and storage overhead + struct GasCost has copy, drop, store { + instruction_gas: u64, + memory_gas: u64, + } + + public fun instruction_schedule(): vector { + let table = Vector::empty(); + + // POP + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // RET + Vector::push_back(&mut table, new_gas_cost(638, 1)); + // BR_TRUE + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // BR_FALSE + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // BRANCH + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LD_U64 + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LD_CONST + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LD_TRUE + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LD_FALSE + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // COPY_LOC + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // MOVE_LOC + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // ST_LOC + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // MUT_BORROW_LOC + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // IMM_BORROW_LOC + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // MUT_BORROW_FIELD + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // IMM_BORROW_FIELD + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // CALL + Vector::push_back(&mut table, new_gas_cost(1132, 1)); + // PACK + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // UNPACK + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // READ_REF + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // WRITE_REF + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // ADD + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // SUB + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // MUL + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // MOD + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // DIV + Vector::push_back(&mut table, new_gas_cost(3, 1)); + // BIT_OR + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // BIT_AND + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // XOR + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // OR + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // AND + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // NOT + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // EQ + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // NEQ + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LT + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // GT + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LE + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // GE + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // ABORT + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // NOP + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // EXISTS + Vector::push_back(&mut table, new_gas_cost(41, 1)); + // MUT_BORROW_GLOBAL + Vector::push_back(&mut table, new_gas_cost(21, 1)); + // IML_BORROW_GLOBAL + Vector::push_back(&mut table, new_gas_cost(23, 1)); + // MOVE_FROM + Vector::push_back(&mut table, new_gas_cost(459, 1)); + // MOVE_TO + Vector::push_back(&mut table, new_gas_cost(13, 1)); + // FREEZE_REF + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // SHL + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // SHR + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LD_U8 + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // LD_U128 + Vector::push_back(&mut table, new_gas_cost(1, 1)); + + // CAST_U8 + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // CAST_U64 + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // CAST_U128 + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // MUT_BORORW_FIELD_GENERIC + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // IMM_BORORW_FIELD_GENERIC + Vector::push_back(&mut table, new_gas_cost(1, 1)); + // CALL_GENERIC + Vector::push_back(&mut table, new_gas_cost(582, 1)); + // PACK_GENERIC + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // UNPACK_GENERIC + Vector::push_back(&mut table, new_gas_cost(2, 1)); + // EXISTS_GENERIC + Vector::push_back(&mut table, new_gas_cost(34, 1)); + // MUT_BORROW_GLOBAL_GENERIC + Vector::push_back(&mut table, new_gas_cost(15, 1)); + // IMM_BORROW_GLOBAL_GENERIC + Vector::push_back(&mut table, new_gas_cost(14, 1)); + // MOVE_FROM_GENERIC + Vector::push_back(&mut table, new_gas_cost(13, 1)); + // MOVE_TO_GENERIC + Vector::push_back(&mut table, new_gas_cost(27, 1)); + + // VEC_PACK + Vector::push_back(&mut table, new_gas_cost(84, 1)); + // VEC_LEN + Vector::push_back(&mut table, new_gas_cost(98, 1)); + // VEC_IMM_BORROW + Vector::push_back(&mut table, new_gas_cost(1334, 1)); + // VEC_MUT_BORROW + Vector::push_back(&mut table, new_gas_cost(1902, 1)); + // VEC_PUSH_BACK + Vector::push_back(&mut table, new_gas_cost(53, 1)); + // VEC_POP_BACK + Vector::push_back(&mut table, new_gas_cost(227, 1)); + // VEC_UNPACK + Vector::push_back(&mut table, new_gas_cost(572, 1)); + // VEC_SWAP + Vector::push_back(&mut table, new_gas_cost(1436, 1)); + table + } + + public fun native_schedule(): vector { + let table = Vector::empty(); + //Hash::sha2_256 0 + Vector::push_back(&mut table, new_gas_cost(21, 1)); + //Hash::sha3_256 1 + Vector::push_back(&mut table, new_gas_cost(64, 1)); + //Signature::ed25519_verify 2 + Vector::push_back(&mut table, new_gas_cost(61, 1)); + //ED25519_THRESHOLD_VERIFY 3 this native funciton is deprecated + Vector::push_back(&mut table, new_gas_cost(3351, 1)); + //BSC::to_bytes 4 + Vector::push_back(&mut table, new_gas_cost(181, 1)); + //Vector::length 5 + Vector::push_back(&mut table, new_gas_cost(98, 1)); + //Vector::empty 6 + Vector::push_back(&mut table, new_gas_cost(84, 1)); + //Vector::borrow 7 + Vector::push_back(&mut table, new_gas_cost(1334, 1)); + //Vector::borrow_mut 8 + Vector::push_back(&mut table, new_gas_cost(1902, 1)); + //Vector::push_back 9 + Vector::push_back(&mut table, new_gas_cost(53, 1)); + //Vector::pop_back 10 + Vector::push_back(&mut table, new_gas_cost(227, 1)); + //Vector::destory_empty 11 + Vector::push_back(&mut table, new_gas_cost(572, 1)); + //Vector::swap 12 + Vector::push_back(&mut table, new_gas_cost(1436, 1)); + //Signature::ed25519_validate_pubkey 13 + Vector::push_back(&mut table, new_gas_cost(26, 1)); + //Signer::borrow_address 14 + Vector::push_back(&mut table, new_gas_cost(353, 1)); + //Account::creator_signer 15 + Vector::push_back(&mut table, new_gas_cost(24, 1)); + //Account::destroy_signer 16 + Vector::push_back(&mut table, new_gas_cost(212, 1)); + //Event::emit_event 17 + Vector::push_back(&mut table, new_gas_cost(52, 1)); + //BCS::to_address 18 + Vector::push_back(&mut table, new_gas_cost(26, 1)); + //Token::name_of 19 + Vector::push_back(&mut table, new_gas_cost(2002, 1)); + //Hash::keccak_256 20 + Vector::push_back(&mut table, new_gas_cost(64, 1)); + //Hash::ripemd160 21 + Vector::push_back(&mut table, new_gas_cost(64, 1)); + //Signature::native_ecrecover 22 + Vector::push_back(&mut table, new_gas_cost(128, 1)); + //U256::from_bytes 23 + Vector::push_back(&mut table, new_gas_cost(2, 1)); + //U256::add 24 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + //U256::sub 25 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + //U256::mul 26 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + //U256::div 27 + Vector::push_back(&mut table, new_gas_cost(10, 1)); + // U256::rem 28 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // U256::pow 29 + Vector::push_back(&mut table, new_gas_cost(8, 1)); + // TODO: settle down the gas cost + // Vector::append 30 + Vector::push_back(&mut table, new_gas_cost(40, 1)); + // Vector::remove 31 + Vector::push_back(&mut table, new_gas_cost(20, 1)); + // Vector::reverse 32 + Vector::push_back(&mut table, new_gas_cost(10, 1)); + + // Table::new_table_handle 33 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // Table::add_box 34 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // Table::borrow_box 35 + Vector::push_back(&mut table, new_gas_cost(10, 1)); + // Table::remove_box 36 + Vector::push_back(&mut table, new_gas_cost(8, 1)); + // Table::contains_box 37 + Vector::push_back(&mut table, new_gas_cost(40, 1)); + // Table::destroy_empty_box 38 + Vector::push_back(&mut table, new_gas_cost(20, 1)); + // Table::drop_unchecked_box 39 + Vector::push_back(&mut table, new_gas_cost(73, 1)); + // string.check_utf8 40 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // string.sub_str 41 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // string.is_char_boundary 42 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // Table::string.index_of 43 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // FromBCS::from_bytes 44 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // Secp256k1::ecdsa_recover_internal 45 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + // Vector::spawn_from 46 + Vector::push_back(&mut table, new_gas_cost(4, 1)); + + table + } + + public fun gas_constants(): GasConstants { + let min_price_per_gas_unit: u64 = if (ChainId::is_test()) { 0 } else { 1 }; + let maximum_number_of_gas_units: u64 = 40000000;//must less than base_block_gas_limit + + if (ChainId::is_test() || ChainId::is_dev() || ChainId::is_halley()) { + maximum_number_of_gas_units = maximum_number_of_gas_units * 10 + }; + GasConstants { + global_memory_per_byte_cost: 4, + global_memory_per_byte_write_cost: 9, + min_transaction_gas_units: 600, + large_transaction_cutoff: 600, + instrinsic_gas_per_byte: 8, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit: 10000, + max_transaction_size_in_bytes: 1024 * 128, + gas_unit_scaling_factor: 1, + default_account_size: 800, + } + } + + fun new_gas_cost(instr_gas: u64, mem_gas: u64): GasCost { + GasCost { + instruction_gas: instr_gas, + memory_gas: mem_gas, + } + } + + + /// Create a new vm config, mainly used in DAO. + public fun new_vm_config( + instruction_schedule: vector, + native_schedule: vector, + global_memory_per_byte_cost: u64, + global_memory_per_byte_write_cost: u64, + min_transaction_gas_units: u64, + large_transaction_cutoff: u64, + instrinsic_gas_per_byte: u64, + maximum_number_of_gas_units: u64, + min_price_per_gas_unit: u64, + max_price_per_gas_unit: u64, + max_transaction_size_in_bytes: u64, + gas_unit_scaling_factor: u64, + default_account_size: u64, + ): VMConfig { + let gas_constants = GasConstants { + global_memory_per_byte_cost, + global_memory_per_byte_write_cost, + min_transaction_gas_units, + large_transaction_cutoff, + instrinsic_gas_per_byte, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit, + max_transaction_size_in_bytes, + gas_unit_scaling_factor, + default_account_size, + }; + VMConfig { + gas_schedule: GasSchedule { instruction_schedule, native_schedule, gas_constants }, + } + } + + /// Initialize the table under the genesis account + public fun initialize( + account: &signer, + instruction_schedule: vector, + native_schedule: vector, + global_memory_per_byte_cost: u64, + global_memory_per_byte_write_cost: u64, + min_transaction_gas_units: u64, + large_transaction_cutoff: u64, + instrinsic_gas_per_byte: u64, + maximum_number_of_gas_units: u64, + min_price_per_gas_unit: u64, + max_price_per_gas_unit: u64, + max_transaction_size_in_bytes: u64, + gas_unit_scaling_factor: u64, + default_account_size: u64, + ) { + CoreAddresses::assert_genesis_address(account); + Config::publish_new_config( + account, + new_vm_config( + instruction_schedule, + native_schedule, + global_memory_per_byte_cost, + global_memory_per_byte_write_cost, + min_transaction_gas_units, + large_transaction_cutoff, + instrinsic_gas_per_byte, + maximum_number_of_gas_units, + min_price_per_gas_unit, + max_price_per_gas_unit, + max_transaction_size_in_bytes, + gas_unit_scaling_factor, + default_account_size, + ), + ); + } + + spec initialize { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists>(Signer::address_of(account)); + aborts_if + exists>( + Signer::address_of(account), + ); + ensures exists>(Signer::address_of(account)); + ensures + exists>( + Signer::address_of(account), + ); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/Vector.move b/release/v13/sources/Vector.move new file mode 100644 index 00000000..59db7ae5 --- /dev/null +++ b/release/v13/sources/Vector.move @@ -0,0 +1,250 @@ +address StarcoinFramework { + +/// A variable-sized container that can hold any type. Indexing is 0-based, and +/// vectors are growable. This module has many native functions. +/// Verification of modules that use this one uses model functions that are implemented +/// directly in Boogie. The specification language has built-in functions operations such +/// as `vec`. There are some helper functions defined here for specifications in other +/// modules as well. +/// +/// >Note: We did not verify most of the +/// Move functions here because many have loops, requiring loop invariants to prove, and +/// the return on investment didn't seem worth it for these simple functions. +module Vector { + + /// The index into the vector is out of bounds + const EINDEX_OUT_OF_BOUNDS: u64 = 0; + + /// Create an empty vector. + native public fun empty(): vector; + + /// Return the length of the vector. + native public fun length(v: &vector): u64; + + /// Acquire an immutable reference to the `i`th element of the vector `v`. + /// Aborts if `i` is out of bounds. + native public fun borrow(v: &vector, i: u64): ∈ + + /// Add element `e` to the end of the vector `v`. + native public fun push_back(v: &mut vector, e: Element); + + /// Return a mutable reference to the `i`th element in the vector `v`. + /// Aborts if `i` is out of bounds. + native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; + + /// Pop an element from the end of vector `v`. + /// Aborts if `v` is empty. + native public fun pop_back(v: &mut vector): Element; + + /// Destroy the vector `v`. + /// Aborts if `v` is not empty. + native public fun destroy_empty(v: vector); + + /// Spawn a sub vector from a vector + native fun spawn_from(v: &vector, offset: u64, size: u64): vector; + + spec spawn_from { + pragma opaque; + ensures [abstract] result == v[offset..offset+size]; + } + + public fun spawn_from_vec(v: &vector, offset: u64, size: u64): vector { + let len = length(v); + let end_idx = (offset + size); + assert!(end_idx <= len, EINDEX_OUT_OF_BOUNDS); + assert!(size > 0, EINDEX_OUT_OF_BOUNDS); + if (offset == 0 && end_idx == len) { + return *v + }; + spawn_from(v, offset, size) + } + + /// Swaps the elements at the `i`th and `j`th indices in the vector `v`. + /// Aborts if `i`or `j` is out of bounds. + native public fun swap(v: &mut vector, i: u64, j: u64); + + /// Return an vector of size one containing element `e`. + public fun singleton(e: Element): vector { + let v = empty(); + push_back(&mut v, e); + v + } + spec singleton { + // TODO: when using opaque here, we get verification errors. + // pragma opaque; + aborts_if false; + ensures result == vec(e); + } + spec fun spec_singleton(e: Element): vector { + vec(e) + } + + + /// Reverses the order of the elements in the vector `v` in place. + public fun reverse(v: &mut vector) { + native_reverse(v) + } + spec reverse { + pragma intrinsic = true; + } + native fun native_reverse(this: &mut vector); + + /// Pushes all of the elements of the `other` vector into the `lhs` vector. + public fun append(lhs: &mut vector, other: vector) { + native_append(lhs, other); + } + native fun native_append(lhs: &mut vector, other: vector); + + spec append { + pragma intrinsic = true; + } + spec is_empty { + pragma intrinsic = true; + } + + + /// Return `true` if the vector `v` has no elements and `false` otherwise. + public fun is_empty(v: &vector): bool { + length(v) == 0 + } + + /// Return true if `e` is in the vector `v`. + public fun contains(v: &vector, e: &Element): bool { + let i = 0; + let len = length(v); + while (i < len) { + if (borrow(v, i) == e) return true; + i = i + 1; + }; + false + } + spec contains { + pragma intrinsic = true; + } + + /// Return `(true, i)` if `e` is in the vector `v` at index `i`. + /// Otherwise, returns `(false, 0)`. + public fun index_of(v: &vector, e: &Element): (bool, u64) { + let i = 0; + let len = length(v); + while (i < len) { + if (borrow(v, i) == e) return (true, i); + i = i + 1; + }; + (false, 0) + } + spec index_of { + pragma intrinsic = true; + } + + /// Remove the `i`th element of the vector `v`, shifting all subsequent elements. + /// This is O(n) and preserves ordering of elements in the vector. + /// Aborts if `i` is out of bounds. + public fun remove(v: &mut vector, i: u64): Element { + let len = length(v); + // i out of bounds; abort + if (i >= len) abort EINDEX_OUT_OF_BOUNDS; + + native_remove(v, i) + } + spec remove { + pragma intrinsic = true; + } + native fun native_remove(this: &mut vector, i: u64): Element; + + /// Swap the `i`th element of the vector `v` with the last element and then pop the vector. + /// This is O(1), but does not preserve ordering of elements in the vector. + /// Aborts if `i` is out of bounds. + public fun swap_remove(v: &mut vector, i: u64): Element { + let last_idx = length(v) - 1; + swap(v, i, last_idx); + pop_back(v) + } + spec swap_remove { + pragma intrinsic = true; + } + + /// Split a vector into sub-vectors of size sub_len, + public fun split(v: &vector, sub_len: u64): vector> { + let result = empty>(); + let len = length(v) / sub_len; + + let rem = 0; + if (len * sub_len < length(v)) { + rem = length(v) - len * sub_len; + }; + + let i = 0; + while (i < len) { + let sub = empty(); + let j = 0; + while (j < sub_len) { + let index = sub_len * i + j; + push_back(&mut sub, *borrow(v, index)); + j = j + 1; + }; + push_back>(&mut result, sub); + i = i + 1; + }; + + if (rem > 0) { + let sub = empty(); + let index = length(v) - rem; + while (index < length(v)) { + push_back(&mut sub, *borrow(v, index)); + index = index + 1; + }; + push_back>(&mut result, sub); + }; + result + } + + spec split { + pragma verify = false; // timeout, skip + aborts_if sub_len == 0; + } + + + // ================================================================= + // Module Specification + + spec module {} // Switch to module documentation context + + /// # Helper Functions + + + /// Check whether a vector contains an element. + spec fun spec_contains(v: vector, e: Element): bool { + exists x in v: x == e + } + + /// Check if `v1` is equal to the result of adding `e` at the end of `v2` + spec fun eq_push_back(v1: vector, v2: vector, e: Element): bool { + len(v1) == len(v2) + 1 && + v1[len(v1)-1] == e && + v1[0..len(v1)-1] == v2[0..len(v2)] + } + + /// Check if `v` is equal to the result of concatenating `v1` and `v2` + spec fun eq_append(v: vector, v1: vector, v2: vector): bool { + len(v) == len(v1) + len(v2) && + v[0..len(v1)] == v1 && + v[len(v1)..len(v)] == v2 + } + + /// Check `v1` is equal to the result of removing the first element of `v2` + spec fun eq_pop_front(v1: vector, v2: vector): bool { + len(v1) + 1 == len(v2) && + v1 == v2[1..len(v2)] + } + + /// Check that `v1` is equal to the result of removing the element at index `i` from `v2`. + spec fun eq_remove_elem_at_index(i: u64, v1: vector, v2: vector): bool { + len(v1) + 1 == len(v2) && + v1[0..i] == v2[0..i] && + v1[i..len(v1)] == v2[i + 1..len(v2)] + } + +} + +} diff --git a/release/v13/sources/Version.move b/release/v13/sources/Version.move new file mode 100644 index 00000000..86330627 --- /dev/null +++ b/release/v13/sources/Version.move @@ -0,0 +1,38 @@ +address StarcoinFramework { +/// `Version` tracks version of something, like current VM version. +module Version { + use StarcoinFramework::Config; + + const EMAJOR_TO_OLD: u64 = 101; + + spec module { + pragma verify; + pragma aborts_if_is_strict; + } + + /// Version. + struct Version has copy, drop, store { + /// major number. + major: u64, + } + + /// Create a new version. + public fun new_version(major: u64): Version { + Version { major } + } + + spec new_version { + aborts_if false; + } + + /// Get version under `addr`. + public fun get(addr: address): u64 { + let version = Config::get_by_address(addr); + version.major + } + + spec get { + aborts_if !exists>(addr); + } +} +} \ No newline at end of file diff --git a/release/v13/sources/YieldFarming.move b/release/v13/sources/YieldFarming.move new file mode 100644 index 00000000..7ca65bb5 --- /dev/null +++ b/release/v13/sources/YieldFarming.move @@ -0,0 +1,204 @@ +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +address StarcoinFramework { +module YieldFarming { + use StarcoinFramework::Token; + use StarcoinFramework::Errors; + + const EDEPRECATED_FUNCTION: u64 = 19; + const ERR_FARMING_INIT_REPEATE: u64 = 101; + const ERR_FARMING_NOT_STILL_FREEZE: u64 = 102; + const ERR_FARMING_STAKE_EXISTS: u64 = 103; + const ERR_FARMING_STAKE_NOT_EXISTS: u64 = 104; + const ERR_FARMING_HAVERST_NO_GAIN: u64 = 105; + const ERR_FARMING_TOTAL_WEIGHT_IS_ZERO: u64 = 106; + const ERR_EXP_DIVIDE_BY_ZERO: u64 = 107; + const ERR_FARMING_BALANCE_EXCEEDED: u64 = 108; + const ERR_FARMING_NOT_ENOUGH_ASSET: u64 = 109; + const ERR_FARMING_TIMESTAMP_INVALID: u64 = 110; + + spec module { + pragma verify = false; + } + + /// The object of yield farming + /// RewardTokenT meaning token of yield farming + struct Farming has key, store { + treasury_token: Token::Token, + } + + struct FarmingAsset has key, store { + asset_total_weight: u128, + harvest_index: u128, + last_update_timestamp: u64, + // Release count per seconds + release_per_second: u128, + // Start time, by seconds, user can operate stake only after this timestamp + start_time: u64, + } + + /// Capability to modify parameter such as period and release amount + struct ParameterModifyCapability has key, store {} + + /// To store user's asset token + struct Stake has key, store { + asset: AssetT, + asset_weight: u128, + last_harvest_index: u128, + gain: u128, + } + + ////////////////////////////////////////////////////////////////////// + // Exponential functions + + const EXP_SCALE: u128 = 1000000000000000000;// e18 + + struct Exp has copy, store, drop { + mantissa: u128 + } + + fun exp(num: u128, denom: u128): Exp { + // if overflow move will abort + let scaledNumerator = mul_u128(num, EXP_SCALE); + let rational = div_u128(scaledNumerator, denom); + Exp { + mantissa: rational + } + } + + fun mul_u128(a: u128, b: u128): u128 { + if (a == 0 || b == 0) { + return 0 + }; + + a * b + } + + fun div_u128(a: u128, b: u128): u128 { + if ( b == 0) { + abort Errors::invalid_argument(ERR_EXP_DIVIDE_BY_ZERO) + }; + if (a == 0) { + return 0 + }; + a / b + } + + fun truncate(exp: Exp): u128 { + return exp.mantissa / EXP_SCALE + } + + /// Called by token issuer + /// this will declare a yield farming pool + public fun initialize< + PoolType: store, + RewardTokenT: store>(_account: &signer, + _treasury_token: Token::Token) { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + // Initialize asset pools + public fun initialize_asset( + _account: &signer, + _release_per_second: u128, + _delay: u64): ParameterModifyCapability { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + public fun modify_parameter( + _cap: &ParameterModifyCapability, + _broker: address, + _release_per_second: u128) { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + /// Call by stake user, staking amount of asset in order to get yield farming token + public fun stake( + _account: &signer, + _broker: address, + _asset: AssetT, + _asset_weight: u128) { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + /// Unstake asset from farming pool + public fun unstake(_account: &signer, _broker: address) + : (AssetT, Token::Token) { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + /// Harvest yield farming token from stake + public fun harvest( + _account: &signer, + _broker: address, + _amount: u128): Token::Token { + abort Errors::deprecated(EDEPRECATED_FUNCTION) + } + + /// The user can quering all yield farming amount in any time and scene + public fun query_gov_token_amount(_account: &signer, _broker: address): u128 { + 0 + } + + /// Query total stake count from yield farming resource + public fun query_total_stake(_broker: address): u128 { + 0 + } + + /// Query stake weight from user staking objects. + public fun query_stake(_account: &signer): u128 { + 0 + } + + /// Update farming asset + fun calculate_harvest_index_with_asset(_farming_asset: &FarmingAsset, _now_seconds: u64): u128 { + 0 + } + + /// There is calculating from harvest index and global parameters without asset_total_weight + public fun calculate_harvest_index_weight_zero(_harvest_index: u128, + _last_update_timestamp: u64, + _now_seconds: u64, + _release_per_second: u128): u128 { + 0 + } + + /// There is calculating from harvest index and global parameters + public fun calculate_harvest_index(_harvest_index: u128, + _asset_total_weight: u128, + _last_update_timestamp: u64, + _now_seconds: u64, + _release_per_second: u128): u128 { + 0 + } + + /// This function will return a gain index + public fun calculate_withdraw_amount(_harvest_index: u128, + _last_harvest_index: u128, + _asset_weight: u128): u128 { + 0 + } + + /// Check the Farming of TokenT is exists. + public fun exists_at(broker: address): bool { + exists>(broker) + } + + /// Check the Farming of AsssetT is exists. + public fun exists_asset_at(broker: address): bool { + exists>(broker) + } + + /// Check stake at address exists. + public fun exists_stake_at_address(account: address): bool { + exists>(account) + } +} +} \ No newline at end of file diff --git a/release/v13/sources/YieldFarmingV2.move b/release/v13/sources/YieldFarmingV2.move new file mode 100644 index 00000000..db8845bc --- /dev/null +++ b/release/v13/sources/YieldFarmingV2.move @@ -0,0 +1,492 @@ +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +address StarcoinFramework { +module YieldFarmingV2 { + use StarcoinFramework::Token; + use StarcoinFramework::Signer; + use StarcoinFramework::Timestamp; + use StarcoinFramework::Errors; + use StarcoinFramework::Math; + + const ERR_FARMING_INIT_REPEATE: u64 = 101; + const ERR_FARMING_NOT_STILL_FREEZE: u64 = 102; + const ERR_FARMING_STAKE_EXISTS: u64 = 103; + const ERR_FARMING_STAKE_NOT_EXISTS: u64 = 104; + const ERR_FARMING_HAVERST_NO_GAIN: u64 = 105; + const ERR_FARMING_TOTAL_WEIGHT_IS_ZERO: u64 = 106; + const ERR_EXP_DIVIDE_BY_ZERO: u64 = 107; + const ERR_FARMING_BALANCE_EXCEEDED: u64 = 108; + const ERR_FARMING_NOT_ENOUGH_ASSET: u64 = 109; + const ERR_FARMING_TIMESTAMP_INVALID: u64 = 110; + const ERR_FARMING_TOKEN_SCALE_OVERFLOW: u64 = 111; + const ERR_FARMING_CALC_LAST_IDX_BIGGER_THAN_NOW: u64 = 112; + const ERR_FARMING_NOT_ALIVE: u64 = 113; + const ERR_FARMING_ALIVE_STATE_INVALID: u64 = 114; + + const EXP_MAX_SCALE: u128 = 9; + + spec module { + pragma verify = false; + } + + ////////////////////////////////////////////////////////////////////// + // Exponential functions + + const EXP_SCALE: u128 = 1000000000000000000;// e18 + + struct Exp has copy, store, drop { + mantissa: u128 + } + + fun exp_direct(num: u128): Exp { + Exp { + mantissa: num + } + } + + fun exp_direct_expand(num: u128): Exp { + Exp { + mantissa: mul_u128(num, EXP_SCALE) + } + } + + + fun mantissa(a: Exp): u128 { + a.mantissa + } + + fun add_exp(a: Exp, b: Exp): Exp { + Exp { + mantissa: add_u128(a.mantissa, b.mantissa) + } + } + + fun exp(num: u128, denom: u128): Exp { + // if overflow move will abort + let scaledNumerator = mul_u128(num, EXP_SCALE); + let rational = div_u128(scaledNumerator, denom); + Exp { + mantissa: rational + } + } + + fun add_u128(a: u128, b: u128): u128 { + a + b + } + + fun sub_u128(a: u128, b: u128): u128 { + a - b + } + + fun mul_u128(a: u128, b: u128): u128 { + if (a == 0 || b == 0) { + return 0 + }; + a * b + } + + fun div_u128(a: u128, b: u128): u128 { + if (b == 0) { + abort Errors::invalid_argument(ERR_EXP_DIVIDE_BY_ZERO) + }; + if (a == 0) { + return 0 + }; + a / b + } + + fun truncate(exp: Exp): u128 { + return exp.mantissa / EXP_SCALE + } + + /// The object of yield farming + /// RewardTokenT meaning token of yield farming + struct Farming has key, store { + treasury_token: Token::Token, + } + + struct FarmingAsset has key, store { + asset_total_weight: u128, + harvest_index: u128, + last_update_timestamp: u64, + // Release count per seconds + release_per_second: u128, + // Start time, by seconds, user can operate stake only after this timestamp + start_time: u64, + // Representing the pool is alive, false: not alive, true: alive. + alive: bool, + } + + /// To store user's asset token + struct Stake has key, store { + asset: AssetT, + asset_weight: u128, + last_harvest_index: u128, + gain: u128, + } + + /// Capability to modify parameter such as period and release amount + struct ParameterModifyCapability has key, store {} + + /// Harvest ability to harvest + struct HarvestCapability has key, store { + trigger: address, + } + + /// Called by token issuer + /// this will declare a yield farming pool + public fun initialize< + PoolType: store, + RewardTokenT: store>(signer: &signer, treasury_token: Token::Token) { + let scaling_factor = Math::pow(10, (EXP_MAX_SCALE as u64)); + let token_scale = Token::scaling_factor(); + assert!(token_scale <= scaling_factor, Errors::limit_exceeded(ERR_FARMING_TOKEN_SCALE_OVERFLOW)); + assert!(!exists_at( + Signer::address_of(signer)), Errors::invalid_state(ERR_FARMING_INIT_REPEATE)); + + move_to(signer, Farming { + treasury_token, + }); + } + + /// Add asset pools + public fun add_asset( + signer: &signer, + release_per_second: u128, + delay: u64): ParameterModifyCapability { + assert!(!exists_asset_at( + Signer::address_of(signer)), + Errors::invalid_state(ERR_FARMING_INIT_REPEATE)); + + let now_seconds = Timestamp::now_seconds(); + + move_to(signer, FarmingAsset { + asset_total_weight: 0, + harvest_index: 0, + last_update_timestamp: now_seconds, + release_per_second, + start_time: now_seconds + delay, + alive: true + }); + ParameterModifyCapability {} + } + + /// Remove asset for make this pool to the state of not alive + /// Please make sure all user unstaking from this pool +// public fun remove_asset( +// broker: address, +// cap: ParameterModifyCapability) acquires FarmingAsset { +// let ParameterModifyCapability {} = cap; +// let FarmingAsset { +// asset_total_weight: _, +// harvest_index: _, +// last_update_timestamp: _, +// release_per_second: _, +// start_time: _, +// alive: _, +// } = move_from>(broker); +// } + + public fun modify_parameter( + _cap: &ParameterModifyCapability, + broker: address, + release_per_second: u128, + alive: bool) acquires FarmingAsset { + + // Not support to shuttingdown alive state. + assert!(alive, Errors::invalid_state(ERR_FARMING_ALIVE_STATE_INVALID)); + + let farming_asset = borrow_global_mut>(broker); + // assert!(farming_asset.alive != alive, Errors::invalid_state(ERR_FARMING_ALIVE_STATE_INVALID)); + + let now_seconds = Timestamp::now_seconds(); + + farming_asset.release_per_second = release_per_second; + farming_asset.last_update_timestamp = now_seconds; + + // if the pool is alive, then update index + if (farming_asset.alive) { + farming_asset.harvest_index = calculate_harvest_index_with_asset(farming_asset, now_seconds); + }; + farming_asset.alive = alive; + } + + /// Call by stake user, staking amount of asset in order to get yield farming token + public fun stake( + signer: &signer, + broker: address, + asset: AssetT, + asset_weight: u128, + _cap: &ParameterModifyCapability) acquires FarmingAsset { + let harvest_cap = stake_for_cap< + PoolType, + RewardTokenT, + AssetT>(signer, broker, asset, asset_weight, _cap); + + move_to(signer, harvest_cap); + } + + public fun stake_for_cap( + signer: &signer, + broker: address, + asset: AssetT, + asset_weight: u128, + _cap: &ParameterModifyCapability) + : HarvestCapability acquires FarmingAsset { + let account = Signer::address_of(signer); + assert!(!exists_stake_at_address(account), + Errors::invalid_state(ERR_FARMING_STAKE_EXISTS)); + + let farming_asset = borrow_global_mut>(broker); + assert!(farming_asset.alive, Errors::invalid_state(ERR_FARMING_NOT_ALIVE)); + + // Check locking time + let now_seconds = Timestamp::now_seconds(); + assert!(farming_asset.start_time <= now_seconds, Errors::invalid_state(ERR_FARMING_NOT_STILL_FREEZE)); + + let time_period = now_seconds - farming_asset.last_update_timestamp; + + if (farming_asset.asset_total_weight <= 0) { + // Stake as first user + let gain = farming_asset.release_per_second * (time_period as u128); + move_to(signer, Stake { + asset, + asset_weight, + last_harvest_index: 0, + gain, + }); + farming_asset.harvest_index = 0; + farming_asset.asset_total_weight = asset_weight; + } else { + let new_harvest_index = calculate_harvest_index_with_asset(farming_asset, now_seconds); + move_to(signer, Stake { + asset, + asset_weight, + last_harvest_index: new_harvest_index, + gain: 0, + }); + farming_asset.asset_total_weight = farming_asset.asset_total_weight + asset_weight; + farming_asset.harvest_index = new_harvest_index; + }; + farming_asset.last_update_timestamp = now_seconds; + HarvestCapability { trigger: account } + } + + /// Unstake asset from farming pool + public fun unstake( + signer: &signer, + broker: address) + : (AssetT, Token::Token) acquires HarvestCapability, Farming, FarmingAsset, Stake { + let account = Signer::address_of(signer); + let cap = move_from>(account); + unstake_with_cap(broker, cap) + } + + public fun unstake_with_cap( + broker: address, + cap: HarvestCapability) + : (AssetT, Token::Token) acquires Farming, FarmingAsset, Stake { + // Destroy capability + let HarvestCapability { trigger } = cap; + + let farming = borrow_global_mut>(broker); + let farming_asset = borrow_global_mut>(broker); + + let Stake { last_harvest_index, asset_weight, asset, gain } = + move_from>(trigger); + + let now_seconds = Timestamp::now_seconds(); + let new_harvest_index = calculate_harvest_index_with_asset(farming_asset, now_seconds); + + let period_gain = calculate_withdraw_amount(new_harvest_index, last_harvest_index, asset_weight); + let total_gain = gain + period_gain; + let withdraw_token = Token::withdraw(&mut farming.treasury_token, total_gain); + + // Dont update harvest index that because the `Stake` object has droped. + // let new_index = calculate_harvest_index_with_asset(farming_asset, now_seconds); + assert!(farming_asset.asset_total_weight >= asset_weight, Errors::invalid_state(ERR_FARMING_NOT_ENOUGH_ASSET)); + + // Update farm asset + farming_asset.asset_total_weight = farming_asset.asset_total_weight - asset_weight; + farming_asset.last_update_timestamp = now_seconds; + + if (farming_asset.alive) { + farming_asset.harvest_index = new_harvest_index; + }; + + (asset, withdraw_token) + } + + /// Harvest yield farming token from stake + public fun harvest( + signer: &signer, + broker: address, + amount: u128) : Token::Token acquires HarvestCapability, Farming, FarmingAsset, Stake { + let account = Signer::address_of(signer); + let cap = borrow_global_mut>(account); + harvest_with_cap(broker, amount, cap) + } + + public fun harvest_with_cap( + broker: address, + amount: u128, + _cap: &HarvestCapability): Token::Token acquires Farming, FarmingAsset, Stake { + let farming = borrow_global_mut>(broker); + let farming_asset = borrow_global_mut>(broker); + let stake = borrow_global_mut>(_cap.trigger); + + let now_seconds = Timestamp::now_seconds(); + let new_harvest_index = calculate_harvest_index_with_asset(farming_asset, now_seconds); + + let period_gain = calculate_withdraw_amount( + new_harvest_index, + stake.last_harvest_index, + stake.asset_weight + ); + + let total_gain = stake.gain + period_gain; + //assert!(total_gain > 0, Errors::limit_exceeded(ERR_FARMING_HAVERST_NO_GAIN)); + assert!(total_gain >= amount, Errors::limit_exceeded(ERR_FARMING_BALANCE_EXCEEDED)); + + let withdraw_amount = if (amount <= 0) { + total_gain + } else { + amount + }; + + let withdraw_token = Token::withdraw(&mut farming.treasury_token, withdraw_amount); + stake.gain = total_gain - withdraw_amount; + stake.last_harvest_index = new_harvest_index; + + if (farming_asset.alive) { + farming_asset.harvest_index = new_harvest_index; + }; + farming_asset.last_update_timestamp = now_seconds; + + withdraw_token + } + + /// The user can quering all yield farming amount in any time and scene + public fun query_gov_token_amount(account: address, broker: address): u128 acquires FarmingAsset, Stake { + let farming_asset = borrow_global_mut>(broker); + let stake = borrow_global_mut>(account); + let now_seconds = Timestamp::now_seconds(); + + let new_harvest_index = calculate_harvest_index_with_asset( + farming_asset, + now_seconds + ); + + let new_gain = calculate_withdraw_amount( + new_harvest_index, + stake.last_harvest_index, + stake.asset_weight + ); + stake.gain + new_gain + } + + /// Query total stake count from yield farming resource + public fun query_total_stake(broker: address): u128 acquires FarmingAsset { + let farming_asset = borrow_global_mut>(broker); + farming_asset.asset_total_weight + } + + /// Query stake weight from user staking objects. + public fun query_stake(account: address): u128 acquires Stake { + let stake = borrow_global_mut>(account); + stake.asset_weight + } + + /// Queyry pool info from pool type + /// return value: (alive, release_per_second, asset_total_weight, harvest_index) + public fun query_info(broker: address): (bool, u128, u128, u128) acquires FarmingAsset { + let asset = borrow_global_mut>(broker); + ( + asset.alive, + asset.release_per_second, + asset.asset_total_weight, + asset.harvest_index + ) + } + + /// Update farming asset + fun calculate_harvest_index_with_asset(farming_asset: &FarmingAsset, now_seconds: u64): u128 { + // Recalculate harvest index + if (farming_asset.asset_total_weight <= 0) { + calculate_harvest_index_weight_zero( + farming_asset.harvest_index, + farming_asset.last_update_timestamp, + now_seconds, + farming_asset.release_per_second + ) + } else { + calculate_harvest_index( + farming_asset.harvest_index, + farming_asset.asset_total_weight, + farming_asset.last_update_timestamp, + now_seconds, + farming_asset.release_per_second + ) + } + } + + /// There is calculating from harvest index and global parameters without asset_total_weight + public fun calculate_harvest_index_weight_zero(harvest_index: u128, + last_update_timestamp: u64, + now_seconds: u64, + release_per_second: u128): u128 { + assert!(last_update_timestamp <= now_seconds, Errors::invalid_argument(ERR_FARMING_TIMESTAMP_INVALID)); + let time_period = now_seconds - last_update_timestamp; + let addtion_index = release_per_second * ((time_period as u128)); + harvest_index + mantissa(exp_direct_expand(addtion_index)) + } + + /// There is calculating from harvest index and global parameters + public fun calculate_harvest_index(harvest_index: u128, + asset_total_weight: u128, + last_update_timestamp: u64, + now_seconds: u64, + release_per_second: u128): u128 { + assert!(asset_total_weight > 0, Errors::invalid_argument(ERR_FARMING_TOTAL_WEIGHT_IS_ZERO)); + assert!(last_update_timestamp <= now_seconds, Errors::invalid_argument(ERR_FARMING_TIMESTAMP_INVALID)); + + let time_period = now_seconds - last_update_timestamp; + let numr = (release_per_second * (time_period as u128)); + let denom = asset_total_weight; + harvest_index + mantissa(exp(numr, denom)) + } + + /// This function will return a gain index + public fun calculate_withdraw_amount(harvest_index: u128, + last_harvest_index: u128, + asset_weight: u128): u128 { + assert!(harvest_index >= last_harvest_index, Errors::invalid_argument(ERR_FARMING_CALC_LAST_IDX_BIGGER_THAN_NOW)); + let amount = asset_weight * (harvest_index - last_harvest_index); + truncate(exp_direct(amount)) + } + + /// Check the Farming of TokenT is exists. + public fun exists_at(broker: address): bool { + exists>(broker) + } + + /// Check the Farming of AsssetT is exists. + public fun exists_asset_at(broker: address): bool { + exists>(broker) + } + + /// Check stake at address exists. + public fun exists_stake_at_address(account: address): bool { + exists>(account) + } +} +} \ No newline at end of file diff --git a/sources/Block.move b/sources/Block.move index 87b08948..f8fb1528 100644 --- a/sources/Block.move +++ b/sources/Block.move @@ -39,6 +39,33 @@ module Block { uncles: u64, } + /// Block metadata struct. + // parents_hash is for FLexiDag block + struct BlockMetadataV2 has key { + /// number of the current block + number: u64, + /// Hash of the parent block. + parent_hash: vector, + /// Author of the current block. + author: address, + /// number of uncles. + uncles: u64, + /// An Array of the parents hash for a Dag block. + parents_hash: vector, + /// Handle of events when new blocks are emitted + new_block_events: Event::EventHandle, + } + + /// Events emitted when new block generated. + // parents_hash is for FLexiDag block + struct NewBlockEventV2 has drop, store { + number: u64, + author: address, + timestamp: u64, + uncles: u64, + parents_hash: vector, + } + // struct Checkpoint has copy,drop,store{ //number of the block @@ -75,7 +102,7 @@ module Block { account, BlockMetadata { number: 0, - parent_hash: parent_hash, + parent_hash, author: CoreAddresses::GENESIS_ADDRESS(), uncles: 0, new_block_events: Event::new_event_handle(account), @@ -88,63 +115,121 @@ module Block { aborts_if exists(Signer::address_of(account)); } + public fun initialize_blockmetadata_v2(account: &signer) acquires BlockMetadata { + CoreAddresses::assert_genesis_address(account); + + let block_meta_ref = borrow_global(CoreAddresses::GENESIS_ADDRESS()); + + // create new resource base on current block metadata + move_to( + account, + BlockMetadataV2 { + number: block_meta_ref.number, + parent_hash: block_meta_ref.parent_hash, + author: block_meta_ref.author, + uncles: block_meta_ref.uncles, + parents_hash: Vector::empty(), + new_block_events: Event::new_event_handle(account), + }); + } + + spec initialize_blockmetadata_v2 { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists(Signer::address_of(account)); + + ensures exists(Signer::address_of(account)); + } + /// Get the current block number - public fun get_current_block_number(): u64 acquires BlockMetadata { - borrow_global(CoreAddresses::GENESIS_ADDRESS()).number + public fun get_current_block_number(): u64 acquires BlockMetadataV2 { + borrow_global(CoreAddresses::GENESIS_ADDRESS()).number } spec get_current_block_number { - aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); } /// Get the hash of the parent block. - public fun get_parent_hash(): vector acquires BlockMetadata { - *&borrow_global(CoreAddresses::GENESIS_ADDRESS()).parent_hash + public fun get_parent_hash(): vector acquires BlockMetadataV2 { + *&borrow_global(CoreAddresses::GENESIS_ADDRESS()).parent_hash } spec get_parent_hash { - aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); } /// Gets the address of the author of the current block - public fun get_current_author(): address acquires BlockMetadata { - borrow_global(CoreAddresses::GENESIS_ADDRESS()).author + public fun get_current_author(): address acquires BlockMetadataV2 { + borrow_global(CoreAddresses::GENESIS_ADDRESS()).author } spec get_current_author { - aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + } + + public fun get_parents_hash(): vector acquires BlockMetadataV2 { + *&borrow_global(CoreAddresses::GENESIS_ADDRESS()).parents_hash + } + + spec get_parents_hash { + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); } /// Call at block prologue - public fun process_block_metadata(account: &signer, parent_hash: vector,author: address, timestamp: u64, uncles:u64, number:u64) acquires BlockMetadata{ + public fun process_block_metadata(account: &signer, + parent_hash: vector, + author: address, + timestamp: u64, + uncles:u64, + number:u64) acquires BlockMetadataV2{ + Self::process_block_metadata_v2(account, parent_hash, author, timestamp, uncles, number, Vector::empty()) + + } + + spec process_block_metadata { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if number != global(CoreAddresses::GENESIS_ADDRESS()).number + 1; + } + + /// Call at block prologue for flexidag + public fun process_block_metadata_v2(account: &signer, + parent_hash: vector, + author: address, + timestamp: u64, + uncles:u64, + number:u64, + parents_hash: vector) acquires BlockMetadataV2 { CoreAddresses::assert_genesis_address(account); - let block_metadata_ref = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); + let block_metadata_ref = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); assert!(number == (block_metadata_ref.number + 1), Errors::invalid_argument(EBLOCK_NUMBER_MISMATCH)); block_metadata_ref.number = number; block_metadata_ref.author= author; block_metadata_ref.parent_hash = parent_hash; block_metadata_ref.uncles = uncles; + block_metadata_ref.parents_hash = parents_hash; - Event::emit_event( + Event::emit_event( &mut block_metadata_ref.new_block_events, - NewBlockEvent { - number: number, - author: author, - timestamp: timestamp, - uncles: uncles, + NewBlockEventV2 { + number, + author, + timestamp, + uncles, + parents_hash, } ); } - spec process_block_metadata { + spec process_block_metadata_v2 { aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); - aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); - aborts_if number != global(CoreAddresses::GENESIS_ADDRESS()).number + 1; + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if number != global(CoreAddresses::GENESIS_ADDRESS()).number + 1; } spec schema AbortsIfBlockMetadataNotExist { - aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); } public fun checkpoints_init(account: &signer){ @@ -164,7 +249,7 @@ module Block { pragma verify = false; } - public entry fun checkpoint_entry(_account: signer) acquires BlockMetadata, Checkpoints { + public entry fun checkpoint_entry(_account: signer) acquires BlockMetadataV2, Checkpoints { checkpoint(); } @@ -172,7 +257,7 @@ module Block { pragma verify = false; } - public fun checkpoint() acquires BlockMetadata, Checkpoints{ + public fun checkpoint() acquires BlockMetadataV2, Checkpoints{ let parent_block_number = get_current_block_number() - 1; let parent_block_hash = get_parent_hash(); diff --git a/sources/Epoch.move b/sources/Epoch.move index de12b7ea..bc6ef8ad 100644 --- a/sources/Epoch.move +++ b/sources/Epoch.move @@ -6,7 +6,6 @@ module Epoch { use StarcoinFramework::CoreAddresses; use StarcoinFramework::Event; - use StarcoinFramework::Errors; use StarcoinFramework::Timestamp; use StarcoinFramework::Math; use StarcoinFramework::Option; @@ -119,13 +118,21 @@ module Epoch { } /// compute next block time_target. - public fun compute_next_block_time_target(config: &ConsensusConfig, last_epoch_time_target: u64, epoch_start_time: u64, now_milli_second: u64, start_block_number: u64, end_block_number: u64, total_uncles: u64): u64 { + public fun compute_next_block_time_target( + config: &ConsensusConfig, + last_epoch_time_target: u64, + epoch_start_time: u64, + now_milli_second: u64, + start_block_number: u64, + end_block_number: u64, + total_uncles: u64 + ): u64 { let total_time = now_milli_second - epoch_start_time; let blocks = end_block_number - start_block_number; let avg_block_time = total_time / blocks; let uncles_rate = total_uncles * THOUSAND / blocks; let new_epoch_block_time_target = (THOUSAND + uncles_rate) * avg_block_time / - (ConsensusConfig::uncle_rate_target(config) + THOUSAND); + (ConsensusConfig::uncle_rate_target(config) + THOUSAND); if (new_epoch_block_time_target > last_epoch_time_target * 2) { new_epoch_block_time_target = last_epoch_time_target * 2; }; @@ -148,26 +155,43 @@ module Epoch { } /// adjust_epoch try to advance to next epoch if current epoch ends. - public fun adjust_epoch(account: &signer, block_number: u64, timestamp: u64, uncles: u64, parent_gas_used:u64): u128 + public fun adjust_epoch( + account: &signer, + block_number: u64, + timestamp: u64, + uncles: u64, + parent_gas_used: u64 + ): u128 acquires Epoch, EpochData { CoreAddresses::assert_genesis_address(account); let epoch_ref = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); - assert!(epoch_ref.max_uncles_per_block >= uncles, Errors::invalid_argument(EINVALID_UNCLES_COUNT)); + // assert!(epoch_ref.max_uncles_per_block >= uncles, Errors::invalid_argument(EINVALID_UNCLES_COUNT)); let epoch_data = borrow_global_mut(CoreAddresses::GENESIS_ADDRESS()); let (new_epoch, reward_per_block) = if (block_number < epoch_ref.end_block_number) { (false, epoch_ref.reward_per_block) } else if (block_number == epoch_ref.end_block_number) { //start a new epoch - assert!(uncles == 0, Errors::invalid_argument(EINVALID_UNCLES_COUNT)); + // assert!(uncles == 0, Errors::invalid_argument(EINVALID_UNCLES_COUNT)); // block time target unit is milli_seconds. let now_milli_seconds = timestamp; let config = ConsensusConfig::get_config(); let last_epoch_time_target = epoch_ref.block_time_target; - let new_epoch_block_time_target = compute_next_block_time_target(&config, last_epoch_time_target, epoch_ref.start_time, now_milli_seconds, epoch_ref.start_block_number, epoch_ref.end_block_number, epoch_data.uncles); - let new_reward_per_block = ConsensusConfig::do_compute_reward_per_block(&config, new_epoch_block_time_target); + let new_epoch_block_time_target = compute_next_block_time_target( + &config, + last_epoch_time_target, + epoch_ref.start_time, + now_milli_seconds, + epoch_ref.start_block_number, + epoch_ref.end_block_number, + epoch_data.uncles + ); + let new_reward_per_block = ConsensusConfig::do_compute_reward_per_block( + &config, + new_epoch_block_time_target + ); //update epoch by adjust result or config, because ConsensusConfig may be updated. epoch_ref.number = epoch_ref.number + 1; @@ -183,7 +207,13 @@ module Epoch { epoch_data.uncles = 0; let last_epoch_total_gas = epoch_data.total_gas + (parent_gas_used as u128); - adjust_gas_limit(&config, epoch_ref, last_epoch_time_target, new_epoch_block_time_target, last_epoch_total_gas); + adjust_gas_limit( + &config, + epoch_ref, + last_epoch_time_target, + new_epoch_block_time_target, + last_epoch_total_gas + ); emit_epoch_event(epoch_ref, epoch_data.total_reward); (true, new_reward_per_block) } else { @@ -191,7 +221,7 @@ module Epoch { abort EUNREACHABLE }; let reward = reward_per_block + - reward_per_block * (epoch_ref.reward_per_uncle_percent as u128) * (uncles as u128) / (HUNDRED as u128); + reward_per_block * (epoch_ref.reward_per_uncle_percent as u128) * (uncles as u128) / (HUNDRED as u128); update_epoch_data(epoch_data, new_epoch, reward, uncles, parent_gas_used); reward } @@ -206,8 +236,20 @@ module Epoch { // ... } - fun adjust_gas_limit(config: &ConsensusConfig, epoch_ref: &mut Epoch, last_epoch_time_target: u64, new_epoch_time_target: u64, last_epoch_total_gas:u128) { - let new_gas_limit = compute_gas_limit(config, last_epoch_time_target, new_epoch_time_target, epoch_ref.block_gas_limit, last_epoch_total_gas); + fun adjust_gas_limit( + config: &ConsensusConfig, + epoch_ref: &mut Epoch, + last_epoch_time_target: u64, + new_epoch_time_target: u64, + last_epoch_total_gas: u128 + ) { + let new_gas_limit = compute_gas_limit( + config, + last_epoch_time_target, + new_epoch_time_target, + epoch_ref.block_gas_limit, + last_epoch_total_gas + ); if (Option::is_some(&new_gas_limit)) { epoch_ref.block_gas_limit = Option::destroy_some(new_gas_limit); } @@ -218,17 +260,31 @@ module Epoch { } /// Compute block's gas limit of next epoch. - public fun compute_gas_limit(config: &ConsensusConfig, last_epoch_time_target: u64, new_epoch_time_target: u64, last_epoch_block_gas_limit: u64, last_epoch_total_gas: u128) : Option::Option { + public fun compute_gas_limit( + config: &ConsensusConfig, + last_epoch_time_target: u64, + new_epoch_time_target: u64, + last_epoch_block_gas_limit: u64, + last_epoch_total_gas: u128 + ): Option::Option { let epoch_block_count = (ConsensusConfig::epoch_block_count(config) as u128); - let gas_limit_threshold = (last_epoch_total_gas >= Math::mul_div((last_epoch_block_gas_limit as u128) * epoch_block_count, (80 as u128), (HUNDRED as u128))); + let gas_limit_threshold = (last_epoch_total_gas >= Math::mul_div( + (last_epoch_block_gas_limit as u128) * epoch_block_count, + (80 as u128), + (HUNDRED as u128) + )); let new_gas_limit = Option::none(); let min_block_time_target = ConsensusConfig::min_block_time_target(config); let max_block_time_target = ConsensusConfig::max_block_time_target(config); - let base_block_gas_limit = ConsensusConfig::base_block_gas_limit(config); + let base_block_gas_limit = ConsensusConfig::base_block_gas_limit(config); if (last_epoch_time_target == new_epoch_time_target) { if (new_epoch_time_target == min_block_time_target && gas_limit_threshold) { - let increase_gas_limit = in_or_decrease_gas_limit(last_epoch_block_gas_limit, 110, base_block_gas_limit); + let increase_gas_limit = in_or_decrease_gas_limit( + last_epoch_block_gas_limit, + 110, + base_block_gas_limit + ); new_gas_limit = Option::some(increase_gas_limit); } else if (new_epoch_time_target == max_block_time_target && !gas_limit_threshold) { let decrease_gas_limit = in_or_decrease_gas_limit(last_epoch_block_gas_limit, 90, base_block_gas_limit); @@ -245,7 +301,7 @@ module Epoch { fun in_or_decrease_gas_limit(last_epoch_block_gas_limit: u64, percent: u64, min_block_gas_limit: u64): u64 { let tmp_gas_limit = Math::mul_div((last_epoch_block_gas_limit as u128), (percent as u128), (HUNDRED as u128)); - let new_gas_limit = if (tmp_gas_limit > (min_block_gas_limit as u128)) { + let new_gas_limit = if (tmp_gas_limit > (min_block_gas_limit as u128)) { (tmp_gas_limit as u64) } else { min_block_gas_limit @@ -255,11 +311,17 @@ module Epoch { } spec in_or_decrease_gas_limit { - include Math::MulDivAbortsIf{x: last_epoch_block_gas_limit, y: percent, z: HUNDRED}; + include Math::MulDivAbortsIf { x: last_epoch_block_gas_limit, y: percent, z: HUNDRED }; aborts_if Math::spec_mul_div() > MAX_U64; } - fun update_epoch_data(epoch_data: &mut EpochData, new_epoch: bool, reward: u128, uncles: u64, parent_gas_used:u64) { + fun update_epoch_data( + epoch_data: &mut EpochData, + new_epoch: bool, + reward: u128, + uncles: u64, + parent_gas_used: u64 + ) { if (new_epoch) { epoch_data.total_reward = reward; epoch_data.uncles = uncles; @@ -375,6 +437,5 @@ module Epoch { spec block_time_target { aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); } - } -} \ No newline at end of file +} diff --git a/sources/FlexiDagConfig.move b/sources/FlexiDagConfig.move new file mode 100644 index 00000000..75fa8e7a --- /dev/null +++ b/sources/FlexiDagConfig.move @@ -0,0 +1,54 @@ +address StarcoinFramework { + module FlexiDagConfig { + + use StarcoinFramework::Block; + use StarcoinFramework::Config; + use StarcoinFramework::CoreAddresses; + use StarcoinFramework::Signer; + + spec module { + pragma verify = false; + pragma aborts_if_is_strict; + } + + /// The struct to hold all config data needed for Flexidag. + struct FlexiDagConfig has copy, drop, store { + // todo: double check, epoch might be better? + // the height of dag genesis block + effective_height: u64, + } + + /// Create a new configuration for flexidag, mainly used in DAO. + public fun new_flexidag_config(effective_height: u64): FlexiDagConfig { + FlexiDagConfig { + effective_height, + } + } + + public fun initialize(account: &signer, effective_height: u64) { + CoreAddresses::assert_genesis_address(account); + Config::publish_new_config(account, new_flexidag_config(effective_height)); + Block::initialize_blockmetadata_v2(account); + } + + spec initialize { + aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS(); + aborts_if exists>(Signer::address_of(account)); + aborts_if exists>(Signer::address_of(account)); + ensures exists>(Signer::address_of(account)); + ensures + exists>( + Signer::address_of(account), + ); + } + + public fun effective_height(account: address): u64 { + let flexi_dag_config = Config::get_by_address(account); + flexi_dag_config.effective_height + } + + spec effective_height { + include Config::AbortsIfConfigNotExist { addr: account }; + } + } +} diff --git a/sources/Genesis.move b/sources/Genesis.move index ddf9f365..368c8699 100644 --- a/sources/Genesis.move +++ b/sources/Genesis.move @@ -447,6 +447,7 @@ module Genesis { }; StdlibUpgradeScripts::do_upgrade_from_v6_to_v7_with_language_version(&genesis_account, 6); StdlibUpgradeScripts::do_upgrade_from_v11_to_v12(&genesis_account); + StdlibUpgradeScripts::do_upgrade_from_v12_to_v13(&genesis_account); //Start time, Timestamp::is_genesis() will return false. this call should at the end of genesis init. Timestamp::set_time_has_started(&genesis_account); Account::release_genesis_signer(genesis_account); diff --git a/sources/OnChainConfigScripts.move b/sources/OnChainConfigScripts.move index 4ce105e9..7bc0bc29 100644 --- a/sources/OnChainConfigScripts.move +++ b/sources/OnChainConfigScripts.move @@ -1,5 +1,6 @@ address StarcoinFramework { module OnChainConfigScripts { + use StarcoinFramework::FlexiDagConfig; use StarcoinFramework::ConsensusConfig; use StarcoinFramework::OnChainConfigDao; use StarcoinFramework::STC; @@ -119,6 +120,15 @@ module OnChainConfigScripts { pragma verify = false; } + public entry fun propose_update_flexi_dag_effective_height(account: signer, new_height: u64, exec_delay: u64) { + let config = FlexiDagConfig::new_flexidag_config(new_height); + OnChainConfigDao::propose_update(&account, config, exec_delay); + } + + spec propose_update_flexi_dag_effective_height { + pragma verify = false; + } + public entry fun execute_on_chain_config_proposal(account: signer, proposal_id: u64) { OnChainConfigDao::execute(Signer::address_of(&account), proposal_id); } diff --git a/sources/StdlibUpgradeScripts.move b/sources/StdlibUpgradeScripts.move index 2218b334..8b7cd6bf 100644 --- a/sources/StdlibUpgradeScripts.move +++ b/sources/StdlibUpgradeScripts.move @@ -2,6 +2,8 @@ address StarcoinFramework { /// The module for StdlibUpgrade init scripts module StdlibUpgradeScripts { + use StarcoinFramework::Math::u64_max; + use StarcoinFramework::FlexiDagConfig; use StarcoinFramework::EasyGas; use StarcoinFramework::CoreAddresses; use StarcoinFramework::STC::{Self, STC}; @@ -117,5 +119,15 @@ module StdlibUpgradeScripts { Block::checkpoints_init(sender); }; } + + public entry fun upgrade_from_v12_to_v13(sender: signer) { + do_upgrade_from_v12_to_v13(&sender); + } + public fun do_upgrade_from_v12_to_v13(sender: &signer) { + { + FlexiDagConfig::initialize(sender, u64_max()); + OnChainConfigDao::plugin(sender); + }; + } } } \ No newline at end of file diff --git a/sources/TransactionManager.move b/sources/TransactionManager.move index f0426453..d98607a8 100644 --- a/sources/TransactionManager.move +++ b/sources/TransactionManager.move @@ -225,6 +225,28 @@ module TransactionManager { number: u64, chain_id: u8, parent_gas_used: u64, + ) { + Self::block_prologue_v2(account, parent_hash, timestamp, author, auth_key_vec, uncles, number, chain_id, parent_gas_used, Vector::empty()) + } + + spec block_prologue { + pragma verify = false;//fixme : timeout + } + + /// Set the metadata for the current block and distribute transaction fees and block rewards. + /// The runtime always runs this before executing the transactions in a block. + /// For Flexidag block + public fun block_prologue_v2( + account: signer, + parent_hash: vector, + timestamp: u64, + author: address, + auth_key_vec: vector, + uncles: u64, + number: u64, + chain_id: u8, + parent_gas_used: u64, + parents_hash: vector, ) { // Can only be invoked by genesis account CoreAddresses::assert_genesis_address(&account); @@ -237,20 +259,21 @@ module TransactionManager { // then deal with current block. Timestamp::update_global_time(&account, timestamp); - Block::process_block_metadata( + Block::process_block_metadata_v2( &account, parent_hash, author, timestamp, uncles, number, + parents_hash, ); let reward = Epoch::adjust_epoch(&account, number, timestamp, uncles, parent_gas_used); // pass in previous block gas fees. BlockReward::process_block_reward(&account, number, reward, author, auth_key_vec, txn_fee); } - spec block_prologue { + spec block_prologue_v2 { pragma verify = false;//fixme : timeout } diff --git a/sources/TransactionTimeout.move b/sources/TransactionTimeout.move index 0f8e55e5..c9dfa68d 100644 --- a/sources/TransactionTimeout.move +++ b/sources/TransactionTimeout.move @@ -36,7 +36,7 @@ module TransactionTimeout { } spec is_valid_transaction_timestamp { aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); - aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); include Timestamp::AbortsIfTimestampNotExists; aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64(); aborts_if Block::get_current_block_number() != 0 && !exists>(CoreAddresses::GENESIS_ADDRESS()); @@ -44,7 +44,7 @@ module TransactionTimeout { spec schema AbortsIfTimestampNotValid { aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); - aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); + aborts_if !exists(CoreAddresses::GENESIS_ADDRESS()); include Timestamp::AbortsIfTimestampNotExists; aborts_if Block::get_current_block_number() != 0 && Timestamp::now_seconds() + TransactionTimeoutConfig::duration_seconds() > max_u64(); aborts_if Block::get_current_block_number() != 0 && !exists>(CoreAddresses::GENESIS_ADDRESS());