From 6d7d7d12bcc8984a350b765ac24ab643e6351655 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 26 Nov 2021 16:41:53 -0500 Subject: [PATCH] Community wallet transfer transaction script (#859) * reproduced error * more specific error codes * transfer community wallet should fail on normal tx api * implement txs subcommand to execute community transfer tx script. * community wallet script test pass. * create integration test * add changelog * storage helper not initializing genesis waypoint * patch tx script for trying to schedule payment to non-slow wallet * mark payments as processed * patch processing the finalized transfer state * patch tests for error code change * fix bug where query was resetting approved list. * txs errors should return the context of the error * more descriptive error messages for txs cli --- Makefile | 2 +- .../management/genesis/src/ol_node_files.rs | 6 +- .../diem-framework/modules/0L/Wallet.move | 104 ++++++---- .../0L_transaction_scripts/ol_transfer.move | 22 ++ .../diem-framework/modules/DiemAccount.move | 16 +- .../diem-framework/modules/doc/DiemAccount.md | 38 +++- language/diem-framework/modules/doc/Wallet.md | 193 ++++++++++++------ .../diem-framework/modules/doc/ol_transfer.md | 43 ++++ .../current/docs/modules/DiemAccount.md | 38 +++- .../artifacts/current/docs/modules/Wallet.md | 193 ++++++++++++------ .../current/docs/modules/ol_transfer.md | 43 ++++ .../error_description.errmap | Bin 14333 -> 14512 bytes .../artifacts/current/modules/031_Wallet.mv | Bin 2890 -> 3098 bytes .../current/modules/043_DiemAccount.mv | Bin 13388 -> 13453 bytes .../current/modules/077_TransferScripts.mv | Bin 470 -> 698 bytes .../ol_transfer/community_transfer.abi | Bin 0 -> 91 bytes .../current/transaction_script_builder.rs | 49 +++++ language/diem-framework/staged/stdlib.mv | Bin 108730 -> 109231 bytes .../tests/0L/accountlimits/0LLimits.ignore | 96 --------- .../0L/accountlimits/UpdateLimits.ignore | 68 ------ ...ment_to_balance_with_comm_should_fail.move | 52 +++++ .../transfer_community_wallet_script.move | 41 ++++ ...unity_wallet_script_fails_to_unhosted.move | 41 ++++ ...sted_account_with_coin_fails_tx_limit.move | 30 +++ .../upgrade_to_validator_from_coin.move | 80 +------- .../wallet/community_wallet_process_unit.move | 15 ++ .../tests/0L/wallet/slow_wallet_set_self.move | 18 ++ ol/changelog/5_0_4.md | 38 ++++ ol/integration-tests/test-tx-tools.mk | 116 +++++++++++ ol/tower/src/backlog.rs | 2 +- ol/txs/src/commands.rs | 7 + ol/txs/src/commands/autopay_batch_cmd.rs | 2 +- ol/txs/src/commands/autopay_cmd.rs | 2 +- ol/txs/src/commands/community_pay_cmd.rs | 71 +++++++ ol/txs/src/commands/create_account_cmd.rs | 2 +- ol/txs/src/commands/create_validator_cmd.rs | 2 +- ol/txs/src/commands/demo_cmd.rs | 2 +- ol/txs/src/commands/oracle_upgrade_cmd.rs | 2 +- ol/txs/src/commands/relay_cmd.rs | 2 +- ol/txs/src/commands/transfer_cmd.rs | 8 +- ol/txs/src/commands/valset_cmd.rs | 2 +- ol/txs/src/commands/wallet_cmd.rs | 2 +- ol/txs/src/submit_tx.rs | 57 ++++-- 43 files changed, 1069 insertions(+), 436 deletions(-) create mode 100644 language/diem-framework/releases/artifacts/current/script_abis/ol_transfer/community_transfer.abi delete mode 100644 language/move-lang/functional-tests/tests/0L/accountlimits/0LLimits.ignore delete mode 100644 language/move-lang/functional-tests/tests/0L/accountlimits/UpdateLimits.ignore create mode 100644 language/move-lang/functional-tests/tests/0L/diem_account/make_payment_to_balance_with_comm_should_fail.move create mode 100644 language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script.move create mode 100644 language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script_fails_to_unhosted.move create mode 100644 language/move-lang/functional-tests/tests/0L/diem_account/unhosted_account_with_coin_fails_tx_limit.move create mode 100644 language/move-lang/functional-tests/tests/0L/wallet/slow_wallet_set_self.move create mode 100644 ol/changelog/5_0_4.md create mode 100644 ol/integration-tests/test-tx-tools.mk create mode 100644 ol/txs/src/commands/community_pay_cmd.rs diff --git a/Makefile b/Makefile index de0cb26fb8..2cd58efafb 100644 --- a/Makefile +++ b/Makefile @@ -455,7 +455,7 @@ debug: ##### DEVNET TESTS ##### -devnet: clear fix fix-genesis dev-wizard start +devnet: clear fix dev-wizard dev-genesis start # runs a smoke test from fixtures. # Uses genesis blob from fixtures, assumes 3 validators, and test settings. # This will work for validator nodes alice, bob, carol, and any fullnodes; 'eve' diff --git a/config/management/genesis/src/ol_node_files.rs b/config/management/genesis/src/ol_node_files.rs index ca540e4c93..3a3d03badf 100644 --- a/config/management/genesis/src/ol_node_files.rs +++ b/config/management/genesis/src/ol_node_files.rs @@ -111,9 +111,9 @@ pub fn write_node_config_files( } }; - // storage_helper - // .insert_waypoint(&namespace, genesis_waypoint) - // .unwrap(); + storage_helper + .insert_waypoint(&namespace, genesis_waypoint) + .unwrap(); // Write the genesis waypoint without a namespaced storage. let mut disk_storage = OnDiskStorageConfig::default(); diff --git a/language/diem-framework/modules/0L/Wallet.move b/language/diem-framework/modules/0L/Wallet.move index 6d5d75f2eb..1c3effcd32 100644 --- a/language/diem-framework/modules/0L/Wallet.move +++ b/language/diem-framework/modules/0L/Wallet.move @@ -15,6 +15,8 @@ module Wallet { const APPROVED: u8 = 1; const REJECTED: u8 = 2; + const EIS_NOT_SLOW_WALLET: u64 = 0231010; + //////// COMMUNITY WALLETS //////// struct CommunityWalletList has key { @@ -122,6 +124,12 @@ module Wallet { public fun new_timed_transfer( sender: &signer, payee: address, value: u64, description: vector ): u64 acquires CommunityTransfers, CommunityWalletList { + // firstly check if payee is a slow wallet + // TODO: This function should check if the account is a slow wallet before sending + // but there's a circular dependency with DiemAccount which has the slow wallet struct. + // curretly we move that check to the transaction script to initialize the payment. + // assert(DiemAccount::is_slow(payee), EIS_NOT_SLOW_WALLET); + let sender_addr = Signer::address_of(sender); let list = get_comm_list(); assert( @@ -152,34 +160,6 @@ module Wallet { Vector::push_back(&mut transfers.proposed, t); return transfers.max_uid } - - // Todo: Can be private, used only in tests - // Utlity to query a CommunityWallet transfer wallet. - // Note: does not need to be a public function, except for use in tests. - public fun find( - uid: u64, - type_of: u8 - ): (Option, u64) acquires CommunityTransfers { - let c = borrow_global(@0x0); - let list = if (type_of == 0) { - &c.proposed - } else if (type_of == 1) { - &c.approved - } else { - &c.rejected - }; - - let len = Vector::length(list); - let i = 0; - while (i < len) { - let t = *Vector::borrow(list, i); - if (t.uid == uid) { - return (Option::some(t), i) - }; - i = i + 1; - }; - (Option::none(), 0) - } // A validator casts a vote to veto a proposed/pending transaction // by a community wallet. @@ -232,6 +212,32 @@ module Wallet { } + // private function. Once vetoed, the CommunityWallet transaction is + // removed from proposed list. + public fun mark_processed(vm: &signer, t: TimedTransfer) acquires CommunityTransfers { + CoreAddresses::assert_vm(vm); + + let c = borrow_global_mut(@0x0); + let list = *&c.proposed; + let len = Vector::length(&list); + let i = 0; + while (i < len) { + let search = *Vector::borrow(&list, i); + if (search.uid == t.uid) { + Vector::remove(&mut c.proposed, i); + Vector::push_back(&mut c.approved, search); + }; + + i = i + 1; + }; + + } + + public fun reset_rejection_counter(vm: &signer, wallet: address) acquires CommunityFreeze { + CoreAddresses::assert_diem_root(vm); + borrow_global_mut(wallet).consecutive_rejections = 0; + } + // private function to tally vetos. // checks if a voter is in the validator set. // tallies everytime called. Only counts votes in the validator set. @@ -283,9 +289,8 @@ module Wallet { // Utility to list CommunityWallet transfers due, by epoch. Anyone can call this. // This is used by VM in DiemAccount at epoch boundaries to process the wallet transfers. public fun list_tx_by_epoch(epoch: u64): vector acquires CommunityTransfers { - let c = borrow_global_mut(@0x0); - // reset approved list - c.approved = Vector::empty(); + let c = borrow_global(@0x0); + // loop proposed list let pending = Vector::empty(); let len = Vector::length(&c.proposed); @@ -295,19 +300,46 @@ module Wallet { if (t.expire_epoch == epoch) { Vector::push_back(&mut pending, *t); - // TODO: clear the freeze count on community wallet - // add to approved list }; i = i + 1; }; return pending } - public fun reset_rejection_counter(vm: &signer, wallet: address) acquires CommunityFreeze { - CoreAddresses::assert_diem_root(vm); - borrow_global_mut(wallet).consecutive_rejections = 0; + + public fun list_transfers(type_of: u8): vector acquires CommunityTransfers { + let c = borrow_global(@0x0); + if (type_of == 0) { + *&c.proposed + } else if (type_of == 1) { + *&c.approved + } else { + *&c.rejected + } + } + + // Todo: Can be private, used only in tests + // Utlity to query a CommunityWallet transfer wallet. + // Note: does not need to be a public function, except for use in tests. + public fun find( + uid: u64, + type_of: u8 + ): (Option, u64) acquires CommunityTransfers { + let list = &list_transfers(type_of); + + let len = Vector::length(list); + let i = 0; + while (i < len) { + let t = *Vector::borrow(list, i); + if (t.uid == uid) { + return (Option::some(t), i) + }; + i = i + 1; + }; + (Option::none(), 0) } + // Private function to freeze a community wallet // community wallets get frozen if 3 consecutive attempts to transfer are rejected. fun maybe_freeze(wallet: address) acquires CommunityFreeze { diff --git a/language/diem-framework/modules/0L_transaction_scripts/ol_transfer.move b/language/diem-framework/modules/0L_transaction_scripts/ol_transfer.move index 26ca4767c0..98cc8f89fb 100644 --- a/language/diem-framework/modules/0L_transaction_scripts/ol_transfer.move +++ b/language/diem-framework/modules/0L_transaction_scripts/ol_transfer.move @@ -5,6 +5,7 @@ module TransferScripts { use 0x1::GAS::GAS; use 0x1::Globals; use 0x1::Signer; + use 0x1::Wallet; public(script) fun balance_transfer( sender: signer, @@ -25,5 +26,26 @@ module TransferScripts { assert(DiemAccount::balance(sender_addr) < sender_balance_pre, 02); } + + public(script) fun community_transfer( + sender: signer, + destination: address, + unscaled_value: u64, + memo: vector, + ) { + // IMPORTANT: the human representation of a value is unscaled. The user which expects to send 10 coins, will input that as an unscaled_value. This script converts it to the Move internal scale by multiplying by COIN_SCALING_FACTOR. + let value = unscaled_value * Globals::get_coin_scaling_factor(); + let sender_addr = Signer::address_of(&sender); + assert(Wallet::is_comm(sender_addr), 0); + + // confirm the destination account has a slow wallet + // TODO: this check only happens in this script since there's + // a circular dependecy issue with DiemAccount and Wallet which impedes + // checking in Wallet module + assert(DiemAccount::is_slow(destination), 1); + + let uid = Wallet::new_timed_transfer(&sender, destination, value, memo); + assert(Wallet::transfer_is_proposed(uid), 2); + } } } \ No newline at end of file diff --git a/language/diem-framework/modules/DiemAccount.move b/language/diem-framework/modules/DiemAccount.move index 40536b7617..7645a12baf 100644 --- a/language/diem-framework/modules/DiemAccount.move +++ b/language/diem-framework/modules/DiemAccount.move @@ -197,6 +197,11 @@ module DiemAccount { //////// 0L //////// const EBELOW_MINIMUM_VALUE_BOOTSTRAP_COIN: u64 = 120125; + const EWITHDRAWAL_NOT_FOR_COMMUNITY_WALLET: u64 = 120126; + const EWITHDRAWAL_TRANSFERS_DISABLED_SYSTEMWIDE: u64 = 120127; + const EWITHDRAWAL_SLOW_WAL_EXCEEDS_UNLOCKED_LIMIT: u64 = 120128; + + /////// 0L end ///////// @@ -485,10 +490,12 @@ module DiemAccount { add_currencies_for_account(&new_signer, false); make_account(new_signer, new_account_authkey_prefix); + // if the initial coin sent is the minimum amount, don't check transfer limits. if (value <= BOOTSTRAP_COIN_VALUE) { onboarding_gas_transfer(sender, new_account, value); new_account } + // otherwise, if the onboarder wants to send more, then it must respect the transfer limits. else { let with_cap = extract_withdraw_capability(sender); pay_from( @@ -1232,14 +1239,14 @@ module DiemAccount { let community_wallets = Wallet::get_comm_list(); assert( !Vector::contains(&community_wallets, &sender_addr), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_NOT_FOR_COMMUNITY_WALLET) ); /////// 0L ///////// if (!DiemConfig::check_transfer_enabled()) { // only VM can make TXs if transfers are not enabled. assert( sender_addr == CoreAddresses::DIEM_ROOT_ADDRESS(), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_TRANSFERS_DISABLED_SYSTEMWIDE) ); }; // Abort if we already extracted the unique withdraw capability for this account. @@ -1372,12 +1379,13 @@ module DiemAccount { let t: Wallet::TimedTransfer = *Vector::borrow(&v, i); // TODO: Is this the best way to access a struct property from // outside a module? - let (payer, payee, value, description) = Wallet::get_tx_args(t); + let (payer, payee, value, description) = Wallet::get_tx_args(*&t); if (Wallet::is_frozen(payer)) { i = i + 1; continue }; vm_make_payment_no_limit(payer, payee, value, description, b"", vm); + Wallet::mark_processed(vm, t); Wallet::reset_rejection_counter(vm, payer); i = i + 1; }; @@ -1492,7 +1500,7 @@ module DiemAccount { if (is_slow(*&cap.account_address)) { assert( amount < unlocked_amount(*&cap.account_address), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_SLOW_WAL_EXCEEDS_UNLOCKED_LIMIT) ); }; diff --git a/language/diem-framework/modules/doc/DiemAccount.md b/language/diem-framework/modules/doc/DiemAccount.md index 1cf64c4069..1c1aa210af 100644 --- a/language/diem-framework/modules/doc/DiemAccount.md +++ b/language/diem-framework/modules/doc/DiemAccount.md @@ -945,6 +945,33 @@ The withdrawal of funds would have exceeded the the account's limits + + + + +
const EWITHDRAWAL_NOT_FOR_COMMUNITY_WALLET: u64 = 120126;
+
+ + + + + + + +
const EWITHDRAWAL_SLOW_WAL_EXCEEDS_UNLOCKED_LIMIT: u64 = 120128;
+
+ + + + + + + +
const EWITHDRAWAL_TRANSFERS_DISABLED_SYSTEMWIDE: u64 = 120127;
+
+ + + The WithdrawCapability for this account has already been extracted @@ -1444,10 +1471,12 @@ Initialize this module. This is only callable from genesis. add_currencies_for_account<GAS>(&new_signer, false); make_account(new_signer, new_account_authkey_prefix); + // if the initial coin sent is the minimum amount, don't check transfer limits. if (value <= BOOTSTRAP_COIN_VALUE) { onboarding_gas_transfer<GAS>(sender, new_account, value); new_account } + // otherwise, if the onboarder wants to send more, then it must respect the transfer limits. else { let with_cap = extract_withdraw_capability(sender); pay_from<GAS>( @@ -2638,14 +2667,14 @@ the sender's account balance. let community_wallets = Wallet::get_comm_list(); assert( !Vector::contains(&community_wallets, &sender_addr), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_NOT_FOR_COMMUNITY_WALLET) ); /////// 0L ///////// if (!DiemConfig::check_transfer_enabled()) { // only VM can make TXs if transfers are not enabled. assert( sender_addr == CoreAddresses::DIEM_ROOT_ADDRESS(), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_TRANSFERS_DISABLED_SYSTEMWIDE) ); }; // Abort if we already extracted the unique withdraw capability for this account. @@ -2782,12 +2811,13 @@ Return the withdraw capability to the account it originally came from let t: Wallet::TimedTransfer = *Vector::borrow(&v, i); // TODO: Is this the best way to access a struct property from // outside a module? - let (payer, payee, value, description) = Wallet::get_tx_args(t); + let (payer, payee, value, description) = Wallet::get_tx_args(*&t); if (Wallet::is_frozen(payer)) { i = i + 1; continue }; vm_make_payment_no_limit<GAS>(payer, payee, value, description, b"", vm); + Wallet::mark_processed(vm, t); Wallet::reset_rejection_counter(vm, payer); i = i + 1; }; @@ -2958,7 +2988,7 @@ subject to the dual attestation protocol if (is_slow(*&cap.account_address)) { assert( amount < unlocked_amount(*&cap.account_address), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_SLOW_WAL_EXCEEDS_UNLOCKED_LIMIT) ); }; diff --git a/language/diem-framework/modules/doc/Wallet.md b/language/diem-framework/modules/doc/Wallet.md index 4e5f3a4e3b..d32906f87d 100644 --- a/language/diem-framework/modules/doc/Wallet.md +++ b/language/diem-framework/modules/doc/Wallet.md @@ -16,13 +16,15 @@ - [Function `set_comm`](#0x1_Wallet_set_comm) - [Function `vm_remove_comm`](#0x1_Wallet_vm_remove_comm) - [Function `new_timed_transfer`](#0x1_Wallet_new_timed_transfer) -- [Function `find`](#0x1_Wallet_find) - [Function `veto`](#0x1_Wallet_veto) - [Function `reject`](#0x1_Wallet_reject) +- [Function `mark_processed`](#0x1_Wallet_mark_processed) +- [Function `reset_rejection_counter`](#0x1_Wallet_reset_rejection_counter) - [Function `tally_veto`](#0x1_Wallet_tally_veto) - [Function `calculate_proportional_voting_threshold`](#0x1_Wallet_calculate_proportional_voting_threshold) - [Function `list_tx_by_epoch`](#0x1_Wallet_list_tx_by_epoch) -- [Function `reset_rejection_counter`](#0x1_Wallet_reset_rejection_counter) +- [Function `list_transfers`](#0x1_Wallet_list_transfers) +- [Function `find`](#0x1_Wallet_find) - [Function `maybe_freeze`](#0x1_Wallet_maybe_freeze) - [Function `get_tx_args`](#0x1_Wallet_get_tx_args) - [Function `get_tx_epoch`](#0x1_Wallet_get_tx_epoch) @@ -272,6 +274,15 @@ + + + + +
const EIS_NOT_SLOW_WALLET: u64 = 231010;
+
+ + + @@ -455,6 +466,12 @@
public fun new_timed_transfer(
   sender: &signer, payee: address, value: u64, description: vector<u8>
 ): u64 acquires CommunityTransfers, CommunityWalletList {
+  // firstly check if payee is a slow wallet
+  // TODO: This function should check if the account is a slow wallet before sending
+  // but there's a circular dependency with DiemAccount which has the slow wallet struct.
+  // curretly we move that check to the transaction script to initialize the payment.
+  // assert(DiemAccount::is_slow(payee), EIS_NOT_SLOW_WALLET);
+
   let sender_addr = Signer::address_of(sender);
   let list = get_comm_list();
   assert(
@@ -489,51 +506,6 @@
 
 
 
-
-
-
-
-## Function `find`
-
-
-
-
public fun find(uid: u64, type_of: u8): (Option::Option<Wallet::TimedTransfer>, u64)
-
- - - -
-Implementation - - -
public fun find(
-  uid: u64,
-  type_of: u8
-): (Option<TimedTransfer>, u64) acquires CommunityTransfers {
-  let c = borrow_global<CommunityTransfers>(@0x0);
-  let list = if (type_of == 0) {
-    &c.proposed
-  } else if (type_of == 1) {
-    &c.approved
-  } else {
-    &c.rejected
-  };
-
-  let len = Vector::length(list);
-  let i = 0;
-  while (i < len) {
-    let t = *Vector::borrow<TimedTransfer>(list, i);
-    if (t.uid == uid) {
-      return (Option::some<TimedTransfer>(t), i)
-    };
-    i = i + 1;
-  };
-  (Option::none<TimedTransfer>(), 0)
-}
-
- - -
@@ -618,6 +590,70 @@ + + + + +## Function `mark_processed` + + + +
public fun mark_processed(vm: &signer, t: Wallet::TimedTransfer)
+
+ + + +
+Implementation + + +
public fun mark_processed(vm: &signer, t: TimedTransfer) acquires CommunityTransfers {
+  CoreAddresses::assert_vm(vm);
+
+  let c = borrow_global_mut<CommunityTransfers>(@0x0);
+  let list = *&c.proposed;
+  let len = Vector::length(&list);
+  let i = 0;
+  while (i < len) {
+    let search = *Vector::borrow<TimedTransfer>(&list, i);
+    if (search.uid == t.uid) {
+      Vector::remove<TimedTransfer>(&mut c.proposed, i);
+      Vector::push_back(&mut c.approved, search);
+    };
+
+    i = i + 1;
+  };
+
+}
+
+ + + +
+ + + +## Function `reset_rejection_counter` + + + +
public fun reset_rejection_counter(vm: &signer, wallet: address)
+
+ + + +
+Implementation + + +
public fun reset_rejection_counter(vm: &signer, wallet: address) acquires CommunityFreeze {
+  CoreAddresses::assert_diem_root(vm);
+  borrow_global_mut<CommunityFreeze>(wallet).consecutive_rejections = 0;
+}
+
+ + +
@@ -717,9 +753,8 @@
public fun list_tx_by_epoch(epoch: u64): vector<TimedTransfer> acquires CommunityTransfers {
-    let c = borrow_global_mut<CommunityTransfers>(@0x0);
-    // reset approved list
-    c.approved = Vector::empty<TimedTransfer>();
+    let c = borrow_global<CommunityTransfers>(@0x0);
+
     // loop proposed list
     let pending = Vector::empty<TimedTransfer>();
     let len = Vector::length(&c.proposed);
@@ -729,8 +764,6 @@
       if (t.expire_epoch == epoch) {
 
         Vector::push_back<TimedTransfer>(&mut pending, *t);
-        // TODO: clear the freeze count on community wallet
-        // add to approved list
       };
       i = i + 1;
     };
@@ -742,13 +775,13 @@
 
 
 
-
+
 
-## Function `reset_rejection_counter`
+## Function `list_transfers`
 
 
 
-
public fun reset_rejection_counter(vm: &signer, wallet: address)
+
public fun list_transfers(type_of: u8): vector<Wallet::TimedTransfer>
 
@@ -757,9 +790,53 @@ Implementation -
public fun reset_rejection_counter(vm: &signer, wallet: address) acquires CommunityFreeze {
-  CoreAddresses::assert_diem_root(vm);
-  borrow_global_mut<CommunityFreeze>(wallet).consecutive_rejections = 0;
+
public fun list_transfers(type_of: u8): vector<TimedTransfer> acquires CommunityTransfers {
+  let c = borrow_global<CommunityTransfers>(@0x0);
+  if (type_of == 0) {
+    *&c.proposed
+  } else if (type_of == 1) {
+    *&c.approved
+  } else {
+    *&c.rejected
+  }
+}
+
+ + + + + + + +## Function `find` + + + +
public fun find(uid: u64, type_of: u8): (Option::Option<Wallet::TimedTransfer>, u64)
+
+ + + +
+Implementation + + +
public fun find(
+  uid: u64,
+  type_of: u8
+): (Option<TimedTransfer>, u64) acquires CommunityTransfers {
+  let list = &list_transfers(type_of);
+
+  let len = Vector::length(list);
+  let i = 0;
+  while (i < len) {
+    let t = *Vector::borrow<TimedTransfer>(list, i);
+    if (t.uid == uid) {
+      return (Option::some<TimedTransfer>(t), i)
+    };
+    i = i + 1;
+  };
+  (Option::none<TimedTransfer>(), 0)
 }
 
diff --git a/language/diem-framework/modules/doc/ol_transfer.md b/language/diem-framework/modules/doc/ol_transfer.md index f0b9400e5e..ffcff9e43e 100644 --- a/language/diem-framework/modules/doc/ol_transfer.md +++ b/language/diem-framework/modules/doc/ol_transfer.md @@ -6,12 +6,14 @@ - [Function `balance_transfer`](#0x1_TransferScripts_balance_transfer) +- [Function `community_transfer`](#0x1_TransferScripts_community_transfer)
use 0x1::DiemAccount;
 use 0x1::GAS;
 use 0x1::Globals;
 use 0x1::Signer;
+use 0x1::Wallet;
 
@@ -53,6 +55,47 @@ +
+ + + +## Function `community_transfer` + + + +
public(script) fun community_transfer(sender: signer, destination: address, unscaled_value: u64, memo: vector<u8>)
+
+ + + +
+Implementation + + +
public(script) fun community_transfer(
+    sender: signer,
+    destination: address,
+    unscaled_value: u64,
+    memo: vector<u8>,
+) {
+    // IMPORTANT: the human representation of a value is unscaled. The user which expects to send 10 coins, will input that as an unscaled_value. This script converts it to the Move internal scale by multiplying by COIN_SCALING_FACTOR.
+    let value = unscaled_value * Globals::get_coin_scaling_factor();
+    let sender_addr = Signer::address_of(&sender);
+    assert(Wallet::is_comm(sender_addr), 0);
+
+    // confirm the destination account has a slow wallet
+    // TODO: this check only happens in this script since there's
+    // a circular dependecy issue with DiemAccount and Wallet which impedes
+    // checking in Wallet module
+    assert(DiemAccount::is_slow(destination), 1);
+
+    let uid = Wallet::new_timed_transfer(&sender, destination, value, memo);
+    assert(Wallet::transfer_is_proposed(uid), 2);
+}
+
+ + +
diff --git a/language/diem-framework/releases/artifacts/current/docs/modules/DiemAccount.md b/language/diem-framework/releases/artifacts/current/docs/modules/DiemAccount.md index 1cf64c4069..1c1aa210af 100644 --- a/language/diem-framework/releases/artifacts/current/docs/modules/DiemAccount.md +++ b/language/diem-framework/releases/artifacts/current/docs/modules/DiemAccount.md @@ -945,6 +945,33 @@ The withdrawal of funds would have exceeded the the account's limits + + + + +
const EWITHDRAWAL_NOT_FOR_COMMUNITY_WALLET: u64 = 120126;
+
+ + + + + + + +
const EWITHDRAWAL_SLOW_WAL_EXCEEDS_UNLOCKED_LIMIT: u64 = 120128;
+
+ + + + + + + +
const EWITHDRAWAL_TRANSFERS_DISABLED_SYSTEMWIDE: u64 = 120127;
+
+ + + The WithdrawCapability for this account has already been extracted @@ -1444,10 +1471,12 @@ Initialize this module. This is only callable from genesis. add_currencies_for_account<GAS>(&new_signer, false); make_account(new_signer, new_account_authkey_prefix); + // if the initial coin sent is the minimum amount, don't check transfer limits. if (value <= BOOTSTRAP_COIN_VALUE) { onboarding_gas_transfer<GAS>(sender, new_account, value); new_account } + // otherwise, if the onboarder wants to send more, then it must respect the transfer limits. else { let with_cap = extract_withdraw_capability(sender); pay_from<GAS>( @@ -2638,14 +2667,14 @@ the sender's account balance. let community_wallets = Wallet::get_comm_list(); assert( !Vector::contains(&community_wallets, &sender_addr), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_NOT_FOR_COMMUNITY_WALLET) ); /////// 0L ///////// if (!DiemConfig::check_transfer_enabled()) { // only VM can make TXs if transfers are not enabled. assert( sender_addr == CoreAddresses::DIEM_ROOT_ADDRESS(), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_TRANSFERS_DISABLED_SYSTEMWIDE) ); }; // Abort if we already extracted the unique withdraw capability for this account. @@ -2782,12 +2811,13 @@ Return the withdraw capability to the account it originally came from let t: Wallet::TimedTransfer = *Vector::borrow(&v, i); // TODO: Is this the best way to access a struct property from // outside a module? - let (payer, payee, value, description) = Wallet::get_tx_args(t); + let (payer, payee, value, description) = Wallet::get_tx_args(*&t); if (Wallet::is_frozen(payer)) { i = i + 1; continue }; vm_make_payment_no_limit<GAS>(payer, payee, value, description, b"", vm); + Wallet::mark_processed(vm, t); Wallet::reset_rejection_counter(vm, payer); i = i + 1; }; @@ -2958,7 +2988,7 @@ subject to the dual attestation protocol if (is_slow(*&cap.account_address)) { assert( amount < unlocked_amount(*&cap.account_address), - Errors::limit_exceeded(EWITHDRAWAL_EXCEEDS_LIMITS) + Errors::limit_exceeded(EWITHDRAWAL_SLOW_WAL_EXCEEDS_UNLOCKED_LIMIT) ); }; diff --git a/language/diem-framework/releases/artifacts/current/docs/modules/Wallet.md b/language/diem-framework/releases/artifacts/current/docs/modules/Wallet.md index 4e5f3a4e3b..d32906f87d 100644 --- a/language/diem-framework/releases/artifacts/current/docs/modules/Wallet.md +++ b/language/diem-framework/releases/artifacts/current/docs/modules/Wallet.md @@ -16,13 +16,15 @@ - [Function `set_comm`](#0x1_Wallet_set_comm) - [Function `vm_remove_comm`](#0x1_Wallet_vm_remove_comm) - [Function `new_timed_transfer`](#0x1_Wallet_new_timed_transfer) -- [Function `find`](#0x1_Wallet_find) - [Function `veto`](#0x1_Wallet_veto) - [Function `reject`](#0x1_Wallet_reject) +- [Function `mark_processed`](#0x1_Wallet_mark_processed) +- [Function `reset_rejection_counter`](#0x1_Wallet_reset_rejection_counter) - [Function `tally_veto`](#0x1_Wallet_tally_veto) - [Function `calculate_proportional_voting_threshold`](#0x1_Wallet_calculate_proportional_voting_threshold) - [Function `list_tx_by_epoch`](#0x1_Wallet_list_tx_by_epoch) -- [Function `reset_rejection_counter`](#0x1_Wallet_reset_rejection_counter) +- [Function `list_transfers`](#0x1_Wallet_list_transfers) +- [Function `find`](#0x1_Wallet_find) - [Function `maybe_freeze`](#0x1_Wallet_maybe_freeze) - [Function `get_tx_args`](#0x1_Wallet_get_tx_args) - [Function `get_tx_epoch`](#0x1_Wallet_get_tx_epoch) @@ -272,6 +274,15 @@ + + + + +
const EIS_NOT_SLOW_WALLET: u64 = 231010;
+
+ + + @@ -455,6 +466,12 @@
public fun new_timed_transfer(
   sender: &signer, payee: address, value: u64, description: vector<u8>
 ): u64 acquires CommunityTransfers, CommunityWalletList {
+  // firstly check if payee is a slow wallet
+  // TODO: This function should check if the account is a slow wallet before sending
+  // but there's a circular dependency with DiemAccount which has the slow wallet struct.
+  // curretly we move that check to the transaction script to initialize the payment.
+  // assert(DiemAccount::is_slow(payee), EIS_NOT_SLOW_WALLET);
+
   let sender_addr = Signer::address_of(sender);
   let list = get_comm_list();
   assert(
@@ -489,51 +506,6 @@
 
 
 
-
-
-
-
-## Function `find`
-
-
-
-
public fun find(uid: u64, type_of: u8): (Option::Option<Wallet::TimedTransfer>, u64)
-
- - - -
-Implementation - - -
public fun find(
-  uid: u64,
-  type_of: u8
-): (Option<TimedTransfer>, u64) acquires CommunityTransfers {
-  let c = borrow_global<CommunityTransfers>(@0x0);
-  let list = if (type_of == 0) {
-    &c.proposed
-  } else if (type_of == 1) {
-    &c.approved
-  } else {
-    &c.rejected
-  };
-
-  let len = Vector::length(list);
-  let i = 0;
-  while (i < len) {
-    let t = *Vector::borrow<TimedTransfer>(list, i);
-    if (t.uid == uid) {
-      return (Option::some<TimedTransfer>(t), i)
-    };
-    i = i + 1;
-  };
-  (Option::none<TimedTransfer>(), 0)
-}
-
- - -
@@ -618,6 +590,70 @@ + + + + +## Function `mark_processed` + + + +
public fun mark_processed(vm: &signer, t: Wallet::TimedTransfer)
+
+ + + +
+Implementation + + +
public fun mark_processed(vm: &signer, t: TimedTransfer) acquires CommunityTransfers {
+  CoreAddresses::assert_vm(vm);
+
+  let c = borrow_global_mut<CommunityTransfers>(@0x0);
+  let list = *&c.proposed;
+  let len = Vector::length(&list);
+  let i = 0;
+  while (i < len) {
+    let search = *Vector::borrow<TimedTransfer>(&list, i);
+    if (search.uid == t.uid) {
+      Vector::remove<TimedTransfer>(&mut c.proposed, i);
+      Vector::push_back(&mut c.approved, search);
+    };
+
+    i = i + 1;
+  };
+
+}
+
+ + + +
+ + + +## Function `reset_rejection_counter` + + + +
public fun reset_rejection_counter(vm: &signer, wallet: address)
+
+ + + +
+Implementation + + +
public fun reset_rejection_counter(vm: &signer, wallet: address) acquires CommunityFreeze {
+  CoreAddresses::assert_diem_root(vm);
+  borrow_global_mut<CommunityFreeze>(wallet).consecutive_rejections = 0;
+}
+
+ + +
@@ -717,9 +753,8 @@
public fun list_tx_by_epoch(epoch: u64): vector<TimedTransfer> acquires CommunityTransfers {
-    let c = borrow_global_mut<CommunityTransfers>(@0x0);
-    // reset approved list
-    c.approved = Vector::empty<TimedTransfer>();
+    let c = borrow_global<CommunityTransfers>(@0x0);
+
     // loop proposed list
     let pending = Vector::empty<TimedTransfer>();
     let len = Vector::length(&c.proposed);
@@ -729,8 +764,6 @@
       if (t.expire_epoch == epoch) {
 
         Vector::push_back<TimedTransfer>(&mut pending, *t);
-        // TODO: clear the freeze count on community wallet
-        // add to approved list
       };
       i = i + 1;
     };
@@ -742,13 +775,13 @@
 
 
 
-
+
 
-## Function `reset_rejection_counter`
+## Function `list_transfers`
 
 
 
-
public fun reset_rejection_counter(vm: &signer, wallet: address)
+
public fun list_transfers(type_of: u8): vector<Wallet::TimedTransfer>
 
@@ -757,9 +790,53 @@ Implementation -
public fun reset_rejection_counter(vm: &signer, wallet: address) acquires CommunityFreeze {
-  CoreAddresses::assert_diem_root(vm);
-  borrow_global_mut<CommunityFreeze>(wallet).consecutive_rejections = 0;
+
public fun list_transfers(type_of: u8): vector<TimedTransfer> acquires CommunityTransfers {
+  let c = borrow_global<CommunityTransfers>(@0x0);
+  if (type_of == 0) {
+    *&c.proposed
+  } else if (type_of == 1) {
+    *&c.approved
+  } else {
+    *&c.rejected
+  }
+}
+
+ + + + + + + +## Function `find` + + + +
public fun find(uid: u64, type_of: u8): (Option::Option<Wallet::TimedTransfer>, u64)
+
+ + + +
+Implementation + + +
public fun find(
+  uid: u64,
+  type_of: u8
+): (Option<TimedTransfer>, u64) acquires CommunityTransfers {
+  let list = &list_transfers(type_of);
+
+  let len = Vector::length(list);
+  let i = 0;
+  while (i < len) {
+    let t = *Vector::borrow<TimedTransfer>(list, i);
+    if (t.uid == uid) {
+      return (Option::some<TimedTransfer>(t), i)
+    };
+    i = i + 1;
+  };
+  (Option::none<TimedTransfer>(), 0)
 }
 
diff --git a/language/diem-framework/releases/artifacts/current/docs/modules/ol_transfer.md b/language/diem-framework/releases/artifacts/current/docs/modules/ol_transfer.md index f0b9400e5e..ffcff9e43e 100644 --- a/language/diem-framework/releases/artifacts/current/docs/modules/ol_transfer.md +++ b/language/diem-framework/releases/artifacts/current/docs/modules/ol_transfer.md @@ -6,12 +6,14 @@ - [Function `balance_transfer`](#0x1_TransferScripts_balance_transfer) +- [Function `community_transfer`](#0x1_TransferScripts_community_transfer)
use 0x1::DiemAccount;
 use 0x1::GAS;
 use 0x1::Globals;
 use 0x1::Signer;
+use 0x1::Wallet;
 
@@ -53,6 +55,47 @@ +
+ + + +## Function `community_transfer` + + + +
public(script) fun community_transfer(sender: signer, destination: address, unscaled_value: u64, memo: vector<u8>)
+
+ + + +
+Implementation + + +
public(script) fun community_transfer(
+    sender: signer,
+    destination: address,
+    unscaled_value: u64,
+    memo: vector<u8>,
+) {
+    // IMPORTANT: the human representation of a value is unscaled. The user which expects to send 10 coins, will input that as an unscaled_value. This script converts it to the Move internal scale by multiplying by COIN_SCALING_FACTOR.
+    let value = unscaled_value * Globals::get_coin_scaling_factor();
+    let sender_addr = Signer::address_of(&sender);
+    assert(Wallet::is_comm(sender_addr), 0);
+
+    // confirm the destination account has a slow wallet
+    // TODO: this check only happens in this script since there's
+    // a circular dependecy issue with DiemAccount and Wallet which impedes
+    // checking in Wallet module
+    assert(DiemAccount::is_slow(destination), 1);
+
+    let uid = Wallet::new_timed_transfer(&sender, destination, value, memo);
+    assert(Wallet::transfer_is_proposed(uid), 2);
+}
+
+ + +
diff --git a/language/diem-framework/releases/artifacts/current/error_description/error_description.errmap b/language/diem-framework/releases/artifacts/current/error_description/error_description.errmap index 19898b72d00e82e7319eeec8407561967c8ce6a6..7838e4a3acba977d39f8960575ae764b07ce4b1b 100644 GIT binary patch delta 225 zcmeyHzoBr$S0O_taRvzBat#WK4+wH~^Ne6fYGVcq3cGp+$NTw*#0UHMhsTFI`uMnp zY~~csU}F^DTqIeBDeoLyaAg5yK| VeEglgfm(e$eLX`qKbDn}0s#FWJa7O2 delta 45 wcmdl`_&0yUS0NEbaRvzBat#WK4+wH~^NiT6CY-^>D6+X{RYghaL# zL1k#D5K^=qT?%@nv=ksjL(5N~px`GUA->r}&O^D{IUb)gXXZQK`DXs;{4jg|;ru_I zl~SHmo7;1G^cN-H^OF3;-*NhL^1HF$_|iP^TkaQsDg5Vu^Q~WJ9-RO1$a$q^31%)J zy=!55EmCS8L-li*6DiCBWlAkZGl*uXfNvR5y{$;;b7YmAx1{WaHo@*p)uc=_-GO~o z!P?hwh%y(u8SG9MQF<#n%wAZw=OQ%G)+m z_MP5{)SPssC-c&`CuBkTGO$asB1?8d=4{sv$2VwYgUMOVCRm-AAlfnJf}uq;J{W@q zR*xUj}G+2b6iK>ZTC$Km~ny32!pdy}WrLxI75ueJ4gYv4R znm=fOGUm2&RdNlZqe-HXniFKwT-$;5iYQP{jp$5D{563+m!{@pyex5}9!gtGW&Cd1 zKf-~FdP-)!YLYgeV(EGy0an1V6By*Fq+B(YQ+U;U>eNVu=uK;et|4^5CWcsv`U}Vb z3a8G10V^#mvo(a(hld8bEi>?BH7Mv|;*K!$9JJT$V*ZJfkMo-pTqE_RY?rbTW0CJ5 zPEcW_dtL^u#7o^|vh0OkAl!Q*pD^A4qQ$dbRm-UiDP(9&1St9VYtuQJlC4~vj(tSC z(Z`%%26`Yv$Av}2VdEn6rpcUsrlVSN&xD?Vp`|i1RQlS)S0{ceV4`qp2->WtBAsSD zBUrw?10buxR2*fKo1Mm{&*-FD3t6z<>bB3;Cvcr}WvHK?>s%40tM&2NHUsO^Ci5}Q zczjsV%Ed_zMXru3tfYCq!bn|TwjDhw4`p)=q>&hu5c?ayj_1NpW0iYJ?E&O+5!InA zDq1|&o1>vZ8kdOi>L8W(qulT~M3F&-tAS8E=M1&lsKYKGPvlSlcH_Ouj`xB-)hWdl zD5kZ?r9nD{9sikK^P74ZqDXO3XU79KI%>y0Z`kK<1y3D2ljEzxcyGbg=DA5HP;|BV zLJMq2eX0v@*B!+>7LwR3T(Vi&o)8T2g1e*%!8lX2irn$(#ybODK0}`=%(P3YN}q{j xS2^P?Cm!}9U$~3^y8hW%dg2q5&P_?I0ZcRA;{-1+Ux&aukB6j1pE^x6Y?L6BTvA=5>X@~KtT{8IPD{# zXe1<%I7Em8Ck_#aCqM|Uh?mGIH*Qe97IwnycFjz6clB3aeKp?HHJQSDLHL&&bq)oQPSTHnCDK3^Z?YL5mw zi*IRIe`8S{{^p7XdE4D`9_(%1Pj~d)-yP1Wy*C`|0r!0l{&;bMu)qUeUyz0!nAtyQH(5}!L&z_%O zr+2U_KYaA*lanW(pS<_Q$-|EyKY0K7Pv(1UKj2-Lee&?5$DciHzv8RyU%Yj1!;FQI z?4}0X(y^^$Vu+d@p`o{yj8431_Kea8-C}@HQzqU z4yVO^XLm7@;699u=NPDj=y^#UI5HLY)q&CRK<^Y?aM1@JS-62{vlKa9(TEW{Hrsql zSWJU;%(_`=YSE|%+yusuH6%v=St@qA=A)VNNT(We7D%%Jq<+WQi)1VZMMn_=l(wcu zGfI0qT`0L$lO^Ml@ikf9hzmUdM-^hkE;ej(F7=*8QuKLE=g06&aWaN#O^?E%aoiPX zT>71@>fvQ{wtecBrhPv{f&ok0g~pT9<6y}N>_SXlsYxZN8cpFyg@(Ai=j@ImX`Enz z{l-NrSb`FFp06^ml#ETym(rY2gNa-Po0uE&R>uaen`zDyB}(WO@`#Io7%XWkA*_}O zN*U@b^<%m=wVZ{(j^gtH3+a=#8X>J+3s8+>R#D_GceBPjJP%uK#5|SQ^g=$F;TMr| zF4pHuM2gt2#_fZ=S%u)5JV+U9EfWyn`i;}1Q#S1{`Nip}23Gk{NdT3GpbCR)g{7${ zyk~L1Jq{brC8t)#x~)CQ4mh z(DM8>hixgOs`rR#T^@#MEm_t5JA{ Sj4{^mtidpS$}Cv41pfj`=WZPU diff --git a/language/diem-framework/releases/artifacts/current/modules/043_DiemAccount.mv b/language/diem-framework/releases/artifacts/current/modules/043_DiemAccount.mv index 4d9290c13e95469753f55fb9230340c4d02fa629..0f6e4fff02c81966338da156cd3cb1a398d4cbca 100644 GIT binary patch delta 1342 zcmX|AU2s!H7~OOC-n+@&?M=3%Y0{+RrfGYd(xhoaO9Uwut027Sj3e++9LJ&vIzUlD zL|T4IQ9+Q^2c2<7{6hu#3nHQdqT_?(I65Of!IO@T;}7BwD2TXe)%&n_=bSye-=00+ zCyTe;)4MDAJvRs;RUosC*{kVEYzUgCocCdE6ej}gROEO%Fx(xscXWSAoo}xB2z>7} zM;w{n)^1pgD%CXq{6qZfk&-oAWr*`144u3R)w-W{K1)b^Z)_LvYF=>2~- z<21_OCiWQAXTExR;u+Mc!PIB8h56+hyz3}pYBbZGicQY2j9ek42=g$V$phfhcLHHM z;vmf$)tStVs8^ZB;f`v($`U3wspOU~x5A?}mgi8pFwG*GW5VV(^*Hl27V%h(`l4}X zFq;ig)8Z^!UeFGW<;Ly^Ph+wWhZx@6ig1R8vvE>V(Ijd9c%Ek-6LrNuwa3)`bLp1ji z9IB%+{zV0~Vw}L}sO)Xf;BxgNPh(RP$=8&Q3}rsjg0{SyBOOMU+SB-4#Kt-m}yzn8Tbv^OzzoVmwAy zRmiw655loBtXYc^qK?C3}XT~($p>Ajx>bd-Uys0kabCI{; zfSXL`w#1Ac%~N-Sn$qrJqnh8|hD~aueSP>S=uGQY4sN*AHRanXTF51iK}a{mrF;i& zNF?P#DMW%N-GqEsEi4S+QT0LL)08gRqs$VVl+x9qT*z`Ok?8w3ZjY%09navnn$y{V zt!i!Oz1XI{>%6P<1d1*ZI_|AZ1b846(VkbMbdyf|+)59mw1eaQ5#toPMKo~&ETu+^ lQ}ZWbb!ya@L-cLz9Z0#_um4oGu((A9w|$L{NzN7f{{ig~`IGF(GIg{@)^XL2j+5X5^ z%ZK)6eijxXqy=P=wP+3f99Kg2F@6fpRdF=L4#y_^&`-6fd$D$e@-ILjI&>7*CNFm4eFBEKX}%N(3BsFer#uoEmZ@p;9#0G1S61LG=L1D)?S-F=%5}Pd>B(z13 zJ3DLp5gfqq+ZcWa!9fhai(mqQ9ZVuViNtFlx!_|2#}I7B=pyr3nB0N>9gT&qLmPL6 zN285X?YCQvVrIWZ^DM8wo_Pi_eK7kiEwFrJUG5f&xIWoAHyfYR$#QCyR5B`}bb$y# zs1grF-57_@d>zZ*hLnCVKMtkOP}>o#*f>zQ z1a#l%riH zMUt*=lI|y2(64FEXZotS-ezdR|W@ z_6azI&CDd%nKj?g)LXCD^yIKX@9rsMqdwiUK6)5*fpf2busrJ7>WE&_TSL|R3%%~A1Wm;sswE9f%=^e#ZZTEeXH5KP5bEF`3zco^cI9_vH7G59Am-P9*-8ind z_4i?$KHmQrUeVpb!~S*z9+6aybTb(ep-9X`-irDi=2a|KBRY5S`iCkGtLV%!UXltYVQuLDHoXTU!gQQx8x3-%bEPjNpZjsBg>Uv({G(2^2#YnG&lB(zQquG;|%TTrp5;S1USz-3QX1o P=VKRBPd9(jpfBJDpzb?; delta 188 zcmWNHI|{-;6h+^C^X8K`LoikrR)Sb41}g)u#vfRS>kzC$R*)sw+GuNO<0jmM(_9Yc z+=u;|Q$PMiCZYnQticSYvKG6U`P~zt=YhNPD82cRFCZpE0w=^QL^d1akyVh)g*6bU zctQ-wn0)TeI+LuS_BLr>br|fn=j~;G>?&Nl4;Cw)D_0mLEve!pDh(y8$ZN8Z6w9bk O_2khY#>#a|u0el~vK2D` diff --git a/language/diem-framework/releases/artifacts/current/script_abis/ol_transfer/community_transfer.abi b/language/diem-framework/releases/artifacts/current/script_abis/ol_transfer/community_transfer.abi new file mode 100644 index 0000000000000000000000000000000000000000..2151bb3b5c535f6de9c7d68c69a61bc72869f085 GIT binary patch literal 91 zcmZP+O3u&CEzQd;sf;fvO3W)xOD$qR1&sV5P>JB=qRfJlVg?3g?v&KxlFYoslFa-( c7QWKF;^f4f)Rg$L#GKMpCYIdP+ encode_cancel_burn_with_amount_script_function(token, preburn_address, amount), + CommunityTransfer { + destination, + unscaled_value, + memo, + } => encode_community_transfer_script_function(destination, unscaled_value, memo), CreateAccUser { challenge, solution, @@ -4327,6 +4338,26 @@ pub fn encode_cancel_burn_with_amount_script_function( )) } +pub fn encode_community_transfer_script_function( + destination: AccountAddress, + unscaled_value: u64, + memo: Vec, +) -> TransactionPayload { + TransactionPayload::ScriptFunction(ScriptFunction::new( + ModuleId::new( + AccountAddress::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), + ident_str!("TransferScripts").to_owned(), + ), + ident_str!("community_transfer").to_owned(), + vec![], + vec![ + bcs::to_bytes(&destination).unwrap(), + bcs::to_bytes(&unscaled_value).unwrap(), + bcs::to_bytes(&memo).unwrap(), + ], + )) +} + pub fn encode_create_acc_user_script_function( challenge: Vec, solution: Vec, @@ -8181,6 +8212,20 @@ fn decode_cancel_burn_with_amount_script_function( } } +fn decode_community_transfer_script_function( + payload: &TransactionPayload, +) -> Option { + if let TransactionPayload::ScriptFunction(script) = payload { + Some(ScriptFunctionCall::CommunityTransfer { + destination: bcs::from_bytes(script.args().get(0)?).ok()?, + unscaled_value: bcs::from_bytes(script.args().get(1)?).ok()?, + memo: bcs::from_bytes(script.args().get(2)?).ok()?, + }) + } else { + None + } +} + fn decode_create_acc_user_script_function( payload: &TransactionPayload, ) -> Option { @@ -9271,6 +9316,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::LazyeS{5uToT^XBc$=rQMa})!|EC9}kSh5eH155=c=%ioz;ki`c=k zup}^7BAYugXP(7^ZNMhsNVtqea6*VVl2DaY&MHV16!{e@`9XjLLJZj0`JQA8%9Y)H z{bswTXL`DOzV7G$>^}XvyJux>>Gs_2=64Kdj2U8So^BL9emGagp(L0S|@D%$W z@%mb^3Y{EtPAO%HsZ*85oYSxr{}LqLic$r;eA!Dd@AP~Ru}0Y>^RihMycXFi3$o;u zWxFhUowCtu^17o3?rrbnq&>l1>j}rTQfQ8`#3Q5>_<*>W;ECv+drzJxTrZxEpTtPS z0~gv#Cs=|>b(D3KBckQ^wM-7&cQ(!U*RLBL>mM5#*f4tO;K*od`tMc!mt5UHc=_<^ zwf@k+$YuSPj|{IK93358Gr52J!xSV2FdiA}zjDYO85|nEaxhxGvnQI^xx~BVW&yu4 z7X4;t<BQ3|IHztcVOo!vBj(C^a=uugdD(LuE+FU?3#XPy6@&Vy z_x|?Uo=YhlhjS_D(b(F$zH1%rgv<(eYA+DWHP@m|B;{=FXdY6=N?)c}MzfO0%aU=( z$Pkn8bK|^-KlPSN4N3UUqUiwj2@C40DKhK;8v!$TMt>(==!#WNrxCQeLb zhC8u|jcLu42#75C6iShhDZE|F4NtilK4UMJ@j`jcf+Dt}qEOP;EL8D#W>T8wQfx?3 z%o>+D!$K#`=W^L(U?L6yJH09U$WV%W7J6KP%bJyDJ5; zH0ysY^K&a{505Co|H-!P9#9m0>a6)w9vT`)^VDTK4<-iT_ z+LmJb4(kRy-qv?RPkkY4g4s4!YANmQn6)LN%(=JU0+PTFixiT?UvSn!Mr6DF2( zfDLEVzHCZ|Vh2SFcXicICuu=eH667V=it%CUCpGUJ9g#9lW|m;2b+pB$Jf-&k|xVj zknxVvbec$sNyL#=9hP7`_c^l9L;lDnY|1U7n8e1RF)T5&6w->#Wjuf*X!f*m2sun) zp|#RwQOE9Orc1OTrV1A@7hSfyXFMH;sS|U)knMcd4AYsp0vt$*Q+F+zaw(!2bRFkR z&nlxt(s&h3@oLtY3bqtES)FoKY_*3AZ>_7{WL?tNd^|MTMOuCCbK@r6tagGx^v0eH zd=3;Q-kp!#HtilIJr1u3U%)F#i{Ju=(nnq67zeK5VT!nb!GZ%}9bhaxAD@bF2B50z zFozt@CbvCQ>RoaBx?@!fd9#wQyh^-8*C(7;^vfh~urGS*mCXKbd6H;WNmK3a@RWBa?ey`>hxs7OebF>VzTh65B{l z@E)Z{SnxiLx-9iC8pE+u|E4Rds52kXm7B;(eMoCzD*q3iL*zIgkvNgje_=SAb%3N~ z`Hx9JU_K#WUFK7|29xbUx(;Y@hybpQpFz&tjaz6wIr5~#1PE^W2Gk3i`AsNdOak}^k zs_vnxQ{7A1y;Q%ASh4yXRewR%n`q5SkQKQ*Y2lrdAH5+(T;RNh$xq)}lS&?6dse-B z25;zlJw`NOns>^~=;YUF@`3js=c1x2(b*roMGaBchkp{Csxo=#!&3$2qP_!5T62rq zR9mpo1~z3<;xyxt2~d_|bfS`KkD>#oQz3fuz-CHE7k!-1W-wP-*SMl+R7Hy*Q&ib# z>&M?ubaf@CszcZ0>`y)gTt%gWi>Vx4aBu-NMzvk~VJ3Lc$I9hP*$RZpWe3eK|S;7QVMN#%p{t+Co@ z;Tu!Y<)BeDf>me16**HEv!0@&YKp#kq(MJTIuXOEYKexA3ZC2KP# zBQ7!tGFfOXW03vsNONNWvPPRTIk730HCBRWNXs@gdx9wIY!^U-lPki zNm8ZS5~6dRZ+EJ$+E$`w>Z(0P)JNZ`{R8Mm&(?fS^BSL}qIKm0^b*F6(T6X?-SLx~ zsdgTx3+X4dCppc{>;qI9MKbK zfkr3J)fY*%*7_8kIPWEr3&G}JLHQBxUDWC$AC9S^qcjo@b>Yw{oqdlex+fZcULJ!s zbPY&KPh|R>uRHSmQ`9|lH}@VDDR-a9**^mEa2aEa7f5VLp2IkI7<0j63hk6g5I|F- zkdSc!p6CcXMs*3m5&7s&WYR^^N`c70VuiG0nByx~3G}=SwE&H8?U5Z+4iXX|g|CRA zEbfSFukN7UvU|b6`t^fjsY?dd4{TUHIMd6fR}T*jU9n-^*wwWoJLuLnwQjUOX2tK( z^~0Oe8wNM^kF6UTTr;B*1v|2}TXxc%UeSX}#dB7}E#hdqDN#8Q25_V`zCmB16;l+F zF@dVRy_4pji!#A<+q9}ZrW$xtPWERiE;1c`Y+UuSGX)I($AZZ007RR>*n~-l@;>i= H+H3v?sXEk{ delta 2828 zcmZ8jYmgjO6~5nn^zFI#?#%S=%w%SEcDHADXD8XsV>gpcnyd)~VvJH0SVbvHk?=?$ zDjMDd1eXv%6a>pQHjyN-JVekC;t0VAL8~lE(XzA(tdfVN{_uw=1bHQxho^T9v7FsL z-F?n|ob#P?zT3NB^7j4Sdt!C6&mnVdTb!&up;sTG>bs&uh#N1~=l^dQ4nZz}py5q6NSPF69- zz{EoMOtpM4e^DMJ$AeBVZ*fVK}E7T|y~AKkfMt!XvXkExq{AJtuc5ooY$x`GRYcVHp-*yscMFKexAR zI|D)f($8Ia)!M5zt-bKNwd*h2c+rOTvF|>N_{r@f+Ph-?7dKwBKK}Fe#c{Z!v7%oL z7zHi|X~70vLv%F-=a_92Ek8#LixD+f%@gwt4}B@QAj1MNDhN=P4TD0m;}Q&JijEm+ zSWrq{xC9H95njlhs^thAHyC4e;VWl(o;hDtNrAcuRFM>LT9xU}ownNf)_2Opf4Q$T zIi!{uWkiI6^RixE6bvo0P(@b3dJ#8*{=GEk>90hH)yWbGZ$`lwMZmy|9a_%eLMltX zaK|jm%L0!migap6mgQA}gqI4P3ctW*>by6r(=g5ByLOf;nF2qtteUPfinLeD_}QI3 zknt-!t7jDb=?!3!msuA1T+=G7stp@qF4dX>r+T9TB!|GMih%A>Er;O{X@eY!k#^D` z&mxvgHPE%9pysBphDRV!R6a8eL8uxX(yBs~QB8{2@c#jUtDFU_{`#sjSL+k*{%LZqV;CDg2_-W}4G(8DsLUaAJfpIadq@;$8PgJUmoGtP!*?7I4rZ1#+ph2LpC;(6ZJWKY-4@H-%(#G5gD{=&0WG%~lxKt!N**wN` z1P1zN9ORZ>4U3#p9*f_dny^D+K+QIkfuIymJTNkuPct*1s}<$VW<)Rcjiq)N5)R^2Re>t|ON3 z-Fmr9(a0bt#!@5$$BAaZhq_bPX5i7v=Y)#L3i3ARD8babo&SYKT1n9GD37!w04~UF z!HHg^P}6OIlw>56kes#GJy;)G`I`;LX_->Any=m@eul%oe8&77>WJOv>%E2Db^mYU zdGEdPr-ga^q~~GZS!&$TrLWBG_~RwR*)1Eq^{Jk!D|1;SHLQV$wA;PZS#BarI zbl=RsO2*R7e3@W+3;(=;EZ)MyuY;9tO^Lw2E!HpJJ^m0{kKnxT)E*)%dRmZyF1N%bjwRMiE%m z4DLiIiYhcBSX0-jO*_weBDA_mx({JHkXAcA=Nkx%Cbqeam;9|e<4DDMlW#p39)@RY1i&AX(KR&y{# zgicJM(uxfpfS9Ys;tP&e>>xE&WEd$QjPE?!gN^ZHN3W0%BluoBJf?8cwYx#}db;Zm zehA~$bvgKyv(8u}veq_De^AjSQgt}ein8rURl5-CSzFg&mU$c|lV^px?%q0yvMe@q zb?dzto>ieKMOUx|ajwn47HQT5BipN}RjOpGxUwyaYg1dL89V`_2J}EeEnJjEf;t%n zsw0oNP{5|-YG8i-`hGefas_e~!I@4tA`{`PXwGO0 zx-0nw(7^S{-+^=FkDz;#RUDs`H_X;U$-Nvk3?)x`y4}PC%ppm%WZ_tQ=^$Q zjVZ=_f+|0on@rXVoP!@H`vv9|o&q@~HI+}K`EnW{vYV4*0wHcrLW#N9lAIy2IlmX; zRCTt6)C|*o-;u1;xDEoKK2EiFdRmb+LUX2C z%T|C2mJIu$Ysr~Dmd<|;Y9XolC+O4=-reZSQc)bAM&CYG5uzR;Iz4CaULW6yC;uRh zvk@JehSLX1XG1QLYNDwxd=CB;&wG1#+5L*Iqw_T-WkFmi)B(qq(9*^x_J%?K9V=Lqa(@fzyd#i;RTmo@TH5^C+F|L z?ePtbd@{NdwJ|(account, CoreAddresses::DIEM_ROOT_ADDRESS(), 0, 10, 0, 0); -// } -// } -// // check: "Keep(EXECUTED)" - -// //! new-transaction -// //! sender: diemroot -// //! execute-as: alice -// script { -// use 0x1::AccountLimits; -// use 0x1::GAS::GAS; -// fun main(lr: signer, alice_account: signer) { -// AccountLimits::publish_unrestricted_limits(alice_account); -// AccountLimits::update_limits_definition(lr, @{{alice}}, 0, 10, 0, 0); -// AccountLimits::publish_window(lr, alice_account, @{{alice}}); -// } -// } -// // check: "Keep(EXECUTED)" - - -// //! new-transaction -// //! sender: diemroot -// //! execute-as: bob -// script { -// use 0x1::AccountLimits; -// use 0x1::GAS::GAS; -// fun main(lr: signer, bob_account: signer) { -// AccountLimits::publish_unrestricted_limits(bob_account); -// AccountLimits::update_limits_definition(lr, @{{bob}}, 0, 10, 0, 0); -// AccountLimits::publish_window(lr, bob_account, @{{bob}}); -// assert(AccountLimits::has_limits_published(@{{bob}}), 1); -// assert(AccountLimits::has_limits_published(@{{alice}}), 2); -// } -// } -// // check: "Keep(EXECUTED)" - -// //! new-transaction -// // Transfers between accounts is disabled -// //! sender: alice -// //! gas-currency: GAS -// script { -// use 0x1::DiemAccount; -// use 0x1::GAS::GAS; -// fun main(account: signer) { -// let with_cap = DiemAccount::extract_withdraw_capability(account); -// DiemAccount::pay_from(&with_cap, @{{bob}}, 11, x"", x""); -// assert(DiemAccount::balance(@{{alice}}) == 89, 0); -// assert(DiemAccount::balance(@{{bob}}) == 111, 1); -// DiemAccount::restore_withdraw_capability(with_cap); -// } -// } -// // check: "Keep(ABORTED { code: 776," - -// //! new-transaction -// // Transfers between accounts is disabled -// //! sender: alice -// //! gas-currency: GAS -// script { -// use 0x1::DiemAccount; -// use 0x1::GAS::GAS; -// fun main(account: signer) { -// let with_cap = DiemAccount::extract_withdraw_capability(account); -// DiemAccount::pay_from(&with_cap, @{{bob}}, 10, x"", x""); -// assert(DiemAccount::balance(@{{alice}}) == 90, 0); -// assert(DiemAccount::balance(@{{bob}}) == 110, 1); -// DiemAccount::restore_withdraw_capability(with_cap); -// } -// } -// // check: "Keep(EXECUTED)" - -// //! new-transaction -// // Transfers between accounts is disabled -// //! sender: alice -// //! gas-currency: GAS -// script { -// use 0x1::DiemAccount; -// use 0x1::GAS::GAS; -// fun main(account: signer) { -// let with_cap = DiemAccount::extract_withdraw_capability(account); -// DiemAccount::pay_from(&with_cap, @{{bob}}, 10, x"", x""); -// assert(DiemAccount::balance(@{{alice}}) == 80, 0); -// assert(DiemAccount::balance(@{{bob}}) == 120, 1); -// DiemAccount::restore_withdraw_capability(with_cap); -// } -// } -// // check: "Keep(ABORTED { code: 776," diff --git a/language/move-lang/functional-tests/tests/0L/accountlimits/UpdateLimits.ignore b/language/move-lang/functional-tests/tests/0L/accountlimits/UpdateLimits.ignore deleted file mode 100644 index cf05fdb5a5..0000000000 --- a/language/move-lang/functional-tests/tests/0L/accountlimits/UpdateLimits.ignore +++ /dev/null @@ -1,68 +0,0 @@ -// //! account: alice, 1000000GAS, 0, validator -// //! account: bob, 100, 0 - -// //! block-prologue -// //! proposer: alice -// //! block-time: 31000000 -// //! round: 1 - -// //! new-transaction -// //! sender: diemroot -// script { -// use 0x1::DiemTimestamp; -// use 0x1::DiemConfig; -// fun main() { -// let time = DiemTimestamp::now_seconds(); -// assert(time == 31, 7357001); -// assert(DiemConfig::get_current_epoch() == 1, 0); -// } -// } -// // check: EXECUTED - -// //! block-prologue -// //! proposer: alice -// //! block-time: 61000000 -// //! round: 15 - -// // check: NewEpochEvent - -// // Will only work if transfer limit epoch is set to 1 -// //! new-transaction -// //! sender: alice -// script { -// use 0x1::DiemTimestamp; -// use 0x1::DiemAccount; -// use 0x1::DiemConfig; -// use 0x1::AccountLimits; -// use 0x1::GAS::GAS; -// fun main(account: signer) { -// let time = DiemTimestamp::now_seconds(); -// assert(time == 61, 7357001); -// assert(DiemConfig::get_current_epoch() == 2, 0); -// let with_cap = DiemAccount::extract_withdraw_capability(account); -// assert(AccountLimits::has_limits_published(@{{alice}}), 1); -// DiemAccount::pay_from(&with_cap, @{{bob}}, 10, x"", x""); -// assert(DiemAccount::balance(@{{alice}}) == 999990, 2); -// assert(DiemAccount::balance(@{{bob}}) == 110, 3); -// DiemAccount::restore_withdraw_capability(with_cap); -// } -// } -// // check: EXECUTED - -// // Check if the transfer will work in the same epoch for more amount -// //! new-transaction -// //! sender: alice -// script { -// use 0x1::DiemAccount; -// use 0x1::AccountLimits; -// use 0x1::GAS::GAS; -// fun main(account: signer) { -// let with_cap = DiemAccount::extract_withdraw_capability(account); -// assert(AccountLimits::has_limits_published(@{{alice}}), 1); -// DiemAccount::pay_from(&with_cap, @{{bob}}, 10, x"", x""); -// assert(DiemAccount::balance(@{{alice}}) == 999980, 2); -// assert(DiemAccount::balance(@{{bob}}) == 120, 3); -// DiemAccount::restore_withdraw_capability(with_cap); -// } -// } -// // check: ABORTED { code: 1544 \ No newline at end of file diff --git a/language/move-lang/functional-tests/tests/0L/diem_account/make_payment_to_balance_with_comm_should_fail.move b/language/move-lang/functional-tests/tests/0L/diem_account/make_payment_to_balance_with_comm_should_fail.move new file mode 100644 index 0000000000..4f365f9859 --- /dev/null +++ b/language/move-lang/functional-tests/tests/0L/diem_account/make_payment_to_balance_with_comm_should_fail.move @@ -0,0 +1,52 @@ +//! account: alice, 1000GAS, 0, validator +//! account: bob, 1000000GAS // the slow wallet +//! account: carol, 0GAS // the community wallet + +// Community wallets cannot use the slow wallet transfer scripts + +//! new-transaction +//! sender: bob +script { + use 0x1::DiemAccount; + use 0x1::Vector; + + fun main(bob: signer) { + // BOB Sets wallet to slow wallet + DiemAccount::set_slow(&bob); + let list = DiemAccount::get_slow_list(); + // alice, the validator, is already a slow wallet, adding bob + assert(Vector::length
(&list) == 2, 735701); + } +} +// check: EXECUTED + +//! new-transaction +//! sender: carol +script { + use 0x1::Wallet; + use 0x1::Vector; + + fun main(carol: signer) { + Wallet::set_comm(&carol); + let list = Wallet::get_comm_list(); + assert(Vector::length(&list) == 1, 7357001); + } +} +// check: EXECUTED + +//! new-transaction +//! sender: carol +script { + use 0x1::DiemAccount; + use 0x1::GAS::GAS; + + fun main(carol: signer) { + // CAROL the community wallet sends funds to BOB the slow wallet + let value = 1000; + let with_cap = DiemAccount::extract_withdraw_capability(&carol); + DiemAccount::pay_from(&with_cap, @{{bob}}, value, b"balance_transfer", b""); + DiemAccount::restore_withdraw_capability(with_cap); + + } +} +// check: ABORTED \ No newline at end of file diff --git a/language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script.move b/language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script.move new file mode 100644 index 0000000000..d946322e16 --- /dev/null +++ b/language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script.move @@ -0,0 +1,41 @@ +//! account: alice, 1000GAS, 0, validator +//! account: bob, 0GAS // the slow wallet +//! account: carol, 1000000000GAS // the community wallet + +// Community wallets cannot use the slow wallet transfer scripts + +//! new-transaction +//! sender: bob +script { + use 0x1::DiemAccount; + use 0x1::Vector; + + fun main(bob: signer) { + // BOB Sets wallet to slow wallet + DiemAccount::set_slow(&bob); + let list = DiemAccount::get_slow_list(); + // alice, the validator, is already a slow wallet, adding bob + assert(Vector::length
(&list) == 2, 735701); + } +} +// check: EXECUTED + +//! new-transaction +//! sender: carol +script { + use 0x1::Wallet; + use 0x1::Vector; + + fun main(carol: signer) { + Wallet::set_comm(&carol); + let list = Wallet::get_comm_list(); + assert(Vector::length(&list) == 1, 7357001); + } +} +// check: EXECUTED + +//! new-transaction +//! sender: carol +//! args: {{bob}}, 1, b"thanks for your service" +stdlib_script::TransferScripts::community_transfer +// check: "Keep(EXECUTED)" diff --git a/language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script_fails_to_unhosted.move b/language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script_fails_to_unhosted.move new file mode 100644 index 0000000000..d89453b5c9 --- /dev/null +++ b/language/move-lang/functional-tests/tests/0L/diem_account/transfer_community_wallet_script_fails_to_unhosted.move @@ -0,0 +1,41 @@ +//! account: alice, 1000GAS, 0, validator +//! account: bob, 0GAS // the slow wallet +//! account: carol, 1000000000GAS // the community wallet + +// Community wallets cannot use the slow wallet transfer scripts + +//! new-transaction +//! sender: bob +script { + use 0x1::DiemAccount; + use 0x1::Vector; + + fun main(_bob: signer) { + // BOB Sets wallet to slow wallet + // DiemAccount::set_slow(&bob); + let list = DiemAccount::get_slow_list(); + // alice, the validator, is already a slow wallet, adding bob + assert(Vector::length
(&list) == 1, 735701); + } +} +// check: EXECUTED + +//! new-transaction +//! sender: carol +script { + use 0x1::Wallet; + use 0x1::Vector; + + fun main(carol: signer) { + Wallet::set_comm(&carol); + let list = Wallet::get_comm_list(); + assert(Vector::length(&list) == 1, 7357001); + } +} +// check: EXECUTED + +//! new-transaction +//! sender: carol +//! args: {{bob}}, 1, b"thanks for your service" +stdlib_script::TransferScripts::community_transfer +// check: ABORTED diff --git a/language/move-lang/functional-tests/tests/0L/diem_account/unhosted_account_with_coin_fails_tx_limit.move b/language/move-lang/functional-tests/tests/0L/diem_account/unhosted_account_with_coin_fails_tx_limit.move new file mode 100644 index 0000000000..e1e5e57df6 --- /dev/null +++ b/language/move-lang/functional-tests/tests/0L/diem_account/unhosted_account_with_coin_fails_tx_limit.move @@ -0,0 +1,30 @@ +//! account: bob, 10000000, 0, validator + +//! new-transaction +//! sender: bob +script { + use 0x1::DiemAccount; + use 0x1::GAS::GAS; + + fun main(sender: signer) { + // Some fake account. + + let new_account: address = @0x3DC18D1CF61FAAC6AC70E3A63F062E4A; + let new_account_authkey_prefix = x"2bffcbd0e9016013cb8ca78459f69d2a"; + let value = 10000000; // exceeds Bob's transaction limit + + // This should fail with account limit exceeded error + let eve_addr = DiemAccount::create_user_account_with_coin( + &sender, + new_account, + new_account_authkey_prefix, + value, + ); + + assert(DiemAccount::balance(eve_addr) == value, 735701); + + // is NOT a slow wallet + assert(!DiemAccount::is_slow(eve_addr), 735702); + } +} +// check: "ABORTED { code: 120128," diff --git a/language/move-lang/functional-tests/tests/0L/diem_account/upgrade_to_validator_from_coin.move b/language/move-lang/functional-tests/tests/0L/diem_account/upgrade_to_validator_from_coin.move index 90da42eea9..66cba40565 100644 --- a/language/move-lang/functional-tests/tests/0L/diem_account/upgrade_to_validator_from_coin.move +++ b/language/move-lang/functional-tests/tests/0L/diem_account/upgrade_to_validator_from_coin.move @@ -1,5 +1,7 @@ //! account: bob, 10000000, 0, validator +// 1. create an end-user account for eve. + //! new-transaction //! sender: bob script { @@ -29,14 +31,14 @@ script { // check: EXECUTED +// 2. Now that the account has been created, try to upgrade it to validator. + //! new-transaction //! sender: bob script { -// use 0x1::VDF; use 0x1::DiemAccount; -// use 0x1::TowerState; use 0x1::TestFixtures; -// use 0x1::Vector; +use 0x1::ValidatorUniverse; // Test Prefix: 1301 @@ -48,9 +50,6 @@ fun main(sender: signer) { // // Parse key and check // let (eve_addr, _auth_key) = VDF::extract_address_from_challenge(&challenge); // assert(eve_addr == @0x3DC18D1CF61FAAC6AC70E3A63F062E4B, 7357401001); - - // let epochs_since_creation = 10; - // TowerState::test_helper_set_rate_limit(&sender, epochs_since_creation); DiemAccount::create_validator_account_with_proof( &sender, @@ -69,74 +68,7 @@ fun main(sender: signer) { ); // the prospective validator is in the current miner list. - // assert(Vector::contains
(&TowerState::get_miner_list(), &eve_addr), 7357401002); + assert(ValidatorUniverse::is_in_universe(@0x3DC18D1CF61FAAC6AC70E3A63F062E4B), 735703); } } // check: EXECUTED - - -//! new-transaction -//! sender: bob -script { - use 0x1::DiemAccount; - use 0x1::GAS::GAS; - - fun main(sender: signer) { - // Some fake account. - - let new_account: address = @0x3DC18D1CF61FAAC6AC70E3A63F062E4A; - let new_account_authkey_prefix = x"2bffcbd0e9016013cb8ca78459f69d2a"; - let value = 10000000; // exceeds Bob's transaction limit - - // This should fail with account limit exceeded error - let eve_addr = DiemAccount::create_user_account_with_coin( - &sender, - new_account, - new_account_authkey_prefix, - value, - ); - - assert(DiemAccount::balance(eve_addr) == value, 735701); - - // is NOT a slow wallet - assert(!DiemAccount::is_slow(eve_addr), 735702); - } -} -// check: "ABORTED { code: 12016," - - - - - - -// //! new-transaction -// //! sender: diemroot -// script { -// use 0x1::EpochBoundary; -// use 0x1::DiemAccount; -// use 0x1::GAS::GAS; -// use 0x1::ValidatorUniverse; -// use 0x1::ValidatorConfig; - -// fun main(vm: signer) { -// let eve_addr = @0x3DC18D1CF61FAAC6AC70E3A63F062E4B; -// /// set the fullnode proof price to 0, to check if onboarding subsidy is given. -// // FullnodeSubsidy::test_set_fullnode_fixtures(&vm, 0, 0, 0, 0, 0); -// EpochBoundary::reconfigure(&vm, 10); -// // need to remove testnet for this test, since testnet does not ratelimit -// // account creation. -// let oper_eve = ValidatorConfig::get_operator(eve_addr); -// let bal = DiemAccount::balance(oper_eve); -// // we expect 1 gas (1,000,000 microgas) from bob's transfer -// assert(bal == 1000000, 7357401003); - -// // validator should have jailedbit -// assert(ValidatorUniverse::exists_jailedbit(eve_addr), 7357401004); -// // validator should be in universe if just joined. -// assert(ValidatorUniverse::is_in_universe(eve_addr), 7357401005); -// // should not be jailed -// assert(!ValidatorUniverse::is_jailed(eve_addr), 7357401006); -// // is a slow wallet -// assert(DiemAccount::is_slow(eve_addr), 7357401007); -// } -// } \ No newline at end of file diff --git a/language/move-lang/functional-tests/tests/0L/wallet/community_wallet_process_unit.move b/language/move-lang/functional-tests/tests/0L/wallet/community_wallet_process_unit.move index c8d112b128..9f6f18a1b9 100644 --- a/language/move-lang/functional-tests/tests/0L/wallet/community_wallet_process_unit.move +++ b/language/move-lang/functional-tests/tests/0L/wallet/community_wallet_process_unit.move @@ -1,5 +1,8 @@ // Todo: These GAS values have no effect, all accounts start with 1M GAS + +//! account: dummy, 1000000GAS, 0, validator + //! account: alice, 1000000GAS, 0 //! account: bob, 1000000GAS, 0 @@ -30,6 +33,8 @@ script { script { use 0x1::DiemAccount; use 0x1::GAS::GAS; + use 0x1::Wallet; + use 0x1::Vector; fun main(vm: signer) { let bob_balance = DiemAccount::balance(@{{bob}}); @@ -39,6 +44,16 @@ script { let bob_balance = DiemAccount::balance(@{{bob}}); assert(bob_balance == 1000100, 7357005); + + // assert the community wallet queue has moved the pending transfer to the completed + let list: vector = Wallet::list_transfers(0); + assert(Vector::length(&list) == 0, 7357006); + + let list: vector = Wallet::list_transfers(1); + assert(Vector::length(&list) == 1, 7357007); + + let list: vector = Wallet::list_transfers(2); + assert(Vector::length(&list) == 0, 7357008); } } diff --git a/language/move-lang/functional-tests/tests/0L/wallet/slow_wallet_set_self.move b/language/move-lang/functional-tests/tests/0L/wallet/slow_wallet_set_self.move new file mode 100644 index 0000000000..3c80464eae --- /dev/null +++ b/language/move-lang/functional-tests/tests/0L/wallet/slow_wallet_set_self.move @@ -0,0 +1,18 @@ +//! account: alice, 1000GAS, 0, validator +//! account: bob, 1000000GAS + +//! new-transaction +//! sender: bob +script { + use 0x1::DiemAccount; + use 0x1::Vector; + + fun main(bob: signer) { + // BOB Sets wallet to slow wallet + DiemAccount::set_slow(&bob); + let list = DiemAccount::get_slow_list(); + // alice, the validator, is already a slow wallet, adding bob + assert(Vector::length
(&list) == 2, 735701); + } +} +// check: EXECUTED diff --git a/ol/changelog/5_0_4.md b/ol/changelog/5_0_4.md new file mode 100644 index 0000000000..7fa05f5be2 --- /dev/null +++ b/ol/changelog/5_0_4.md @@ -0,0 +1,38 @@ +## 5.0.4 +This upgrade has changes to the network bytecode and user cli tools. + +A hot network upgrade is required before the tools can be used. [Read about network upgrades.](../documentation/network-upgrades/stdlib_hot_upgrade.md) + +The stdlib payload hash for voting is: 4fc1070c0a7442cb14949b5bc6e534e7c0c2daeee57da03c479b21a49975f418 + +### Summary +v5.0.4 includes new tools (transaction scripts and cli tools) to interface with the Wallet module. The scripts enable "community wallets" to send payments to "slow wallets". + +### Changes +##### Move Changes +##### - Community wallet transfer transaction script + +Background on Community Wallet transfers: + +Community wallets do not use the normal balance transfer tools avaiable to end-user wallets, or slow wallets; They have a purpose-built transfer mechanism. Transactions are not immediate, they are initially "proposed" and after 3 epochs (days) the transaction will post. This allows time for reviewers of the wallet (the accounts in the validator set normally) to evaluate the transaction. Each veto by a wallet reviewer/validator adds one extra epoch/day of review time. With sufficient vetos (2/3rds of validators) the transfer proposal gets rejected. + +This logic was already implemented in the state machine (Wallet.move module), however initiating a transfer as a community wallet, was not possible because the cli tools and API to execute the transaction were not yet implemented. This change creates the Move (tranaction script) and Rust (TXS app subcommand). + +https://github.com/OLSF/libra/pull/859 + + +##### Tests + +- A functional test was created for the Move tx scriopt. + +- An integration test for command line tools was created ./ol/integraion-tests/test-tx-tools.mk + +- QA of the transfer functionality was conducted on Devnet. +##### Compatibility +The Move framework changes are backwards compatible with `diem-node` from v5.0.1 +### Rust changes +Changes are isolated to ./ol/txs transaction sending tool, not to any consensus modules. + +NOTE: If you have not yet upgraded to previous versions (e.g. v5.0.2) there is a critical upgrade to `diem-node`. It's safe to skip 5.0.2 directly to 5.0.4 if that upgrade was not yet done. + + diff --git a/ol/integration-tests/test-tx-tools.mk b/ol/integration-tests/test-tx-tools.mk new file mode 100644 index 0000000000..d94106bacf --- /dev/null +++ b/ol/integration-tests/test-tx-tools.mk @@ -0,0 +1,116 @@ +SHELL=/usr/bin/env bash +DATA_PATH = ${HOME}/.0L +SWARM_TEMP = ${DATA_PATH}/swarm_temp +LOG=${DATA_PATH}/test-autopay.log +UNAME := $(shell uname) + +NODE_ENV=test +TEST=y + +ifndef SOURCE_PATH +SOURCE_PATH = ${HOME}/libra +endif +MAKE_FILE = ${SOURCE_PATH}/ol/integration-tests/test-tx-tools.mk + +# alice +ifndef PERSONA +PERSONA=alice +endif + +MNEM="talent sunset lizard pill fame nuclear spy noodle basket okay critic grow sleep legend hurry pitch blanket clerk impose rough degree sock insane purse" + +NUM_NODES = 2 + +START_TEXT = "To run the Diem CLI client" + +ifndef SUCCESS_TEXT +SUCCESS_TEXT = "transaction executed" +endif + +ifndef AUTOPAY_FILE +AUTOPAY_FILE = alice.autopay_batch.json +endif + +export + +# Start a swarm with alice, bob, carol. They are all slow wallets. +# make bob a community wallet. +# have bob send a tx to carol. + +#test: swarm check-swarm set-community send-tx check-tx check-transfer stop +test: swarm check-swarm set-community send-tx check-tx check-transfer + + +swarm: + @echo Building Swarm + rm -rf ${SWARM_TEMP} + mkdir ${SWARM_TEMP} + cd ${SOURCE_PATH} && cargo build -p diem-node -p cli + cd ${SOURCE_PATH} && cargo run -p diem-swarm -- --diem-node ${SOURCE_PATH}/target/debug/diem-node -c ${SWARM_TEMP} -n ${NUM_NODES} &> ${LOG} & + +stop: + killall diem-swarm diem-node tower ol txs cli | true + +init: + cd ${SOURCE_PATH} && cargo r -p ol -- --swarm-path ${SWARM_TEMP} --swarm-persona ${PERSONA} init --source-path ${SOURCE_PATH} + +tx: balance + cd ${SOURCE_PATH} && NODE_ENV=test TEST=y cargo r -p txs -- --swarm-path ${SWARM_TEMP} --swarm-persona ${PERSONA} community-pay -a e660402d586ad220ed9beff47d662d54 -c 1 -m hello + + +set-community: + cd ${SOURCE_PATH} && NODE_ENV=test TEST=y cargo r -p txs -- --swarm-path ${SWARM_TEMP} --swarm-persona bob wallet -c + +resources: + cd ${SOURCE_PATH} && cargo run -p ol -- --account 00000000000000000000000000000000 --swarm-path ${SWARM_TEMP} --swarm-persona ${PERSONA} query --resources + +balance: + cd ${SOURCE_PATH} && cargo run -p ol -- --swarm-path ${SWARM_TEMP} --swarm-persona ${PERSONA} query --balance + +balance-carol: + cd ${SOURCE_PATH} && cargo run -p ol -- --account e660402d586ad220ed9beff47d662d54 --swarm-path ${SWARM_TEMP} --swarm-persona ${PERSONA} query --balance + + +check-swarm: + @while [[ ${NOW} -le ${END} ]] ; do \ + if grep -q ${START_TEXT} ${LOG} ; then \ + break; \ + else \ + echo . ; \ + fi ; \ + echo "Sleeping for 5 secs" ; \ + sleep 5 ; \ + done + +send-tx: + PERSONA=bob make -f ${MAKE_FILE} init + PERSONA=bob make -f ${MAKE_FILE} tx &>> ${LOG} & + +check-tx: + @while [[ ${NOW} -le ${END} ]] ; do \ + if grep -q ${SUCCESS_TEXT} ${LOG} ; then \ + echo TX SUCCESS ; \ + break ; \ + else \ + echo . ; \ + fi ; \ + echo "Sleeping for 5 secs" ; \ + sleep 5 ; \ + done + +check-transfer: +# swarm accounts start with a balance of 10, but go below that with gas tx costs. +# all tests above push the balance back up to 10, 11 or 15 + +# check for the memo field of the community transfer + @while [[ ${NOW} -le ${END} ]] ; do \ + if PERSONA=carol make -f ${MAKE_FILE} resources | grep -e "expire_epoch"; then \ + echo TX SUCCESS ; \ + break ; \ + else \ + echo . ; \ + fi ; \ + echo "Sleeping for 5 secs" ; \ + sleep 5 ; \ + done + \ No newline at end of file diff --git a/ol/tower/src/backlog.rs b/ol/tower/src/backlog.rs index 8d4715c596..910322f5b0 100644 --- a/ol/tower/src/backlog.rs +++ b/ol/tower/src/backlog.rs @@ -43,7 +43,7 @@ pub fn process_backlog( let view = commit_proof_tx( &tx_params, block, is_operator )?; - match eval_tx_status(&view) { + match eval_tx_status(view) { Ok(_) => {}, Err(e) => { println!("WARN: could not fetch TX status, continuing to next block in backlog after 30 seconds. Message: {:?} ", e); diff --git a/ol/txs/src/commands.rs b/ol/txs/src/commands.rs index 3b5e5ef708..6cfa1bc0dc 100644 --- a/ol/txs/src/commands.rs +++ b/ol/txs/src/commands.rs @@ -15,6 +15,8 @@ pub mod demo_cmd; pub mod create_account_cmd; pub mod transfer_cmd; pub mod wallet_cmd; +pub mod community_pay_cmd; + mod relay_cmd; mod valset_cmd; @@ -41,6 +43,7 @@ use self::{ wallet_cmd::WalletCmd, authkey_cmd::AuthkeyCmd, transfer_cmd::TransferCmd, + community_pay_cmd::CommunityPayCmd, }; use std::path::PathBuf; @@ -59,6 +62,10 @@ pub enum TxsCmd { #[options(help = "transfer funds between accounts")] Transfer(TransferCmd), + /// Community payment proposal tx + #[options(help = "create a community wallet payment proposal")] + CommunityPay(CommunityPayCmd), + /// The `oracle-upgrade` subcommand #[options(help = "submit an oracle transaction to upgrade stdlib")] OracleUpgrade(OracleUpgradeCmd), diff --git a/ol/txs/src/commands/autopay_batch_cmd.rs b/ol/txs/src/commands/autopay_batch_cmd.rs index 31c9632ccd..08ddcd33db 100644 --- a/ol/txs/src/commands/autopay_batch_cmd.rs +++ b/ol/txs/src/commands/autopay_batch_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `autopay-batch` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/autopay_cmd.rs b/ol/txs/src/commands/autopay_cmd.rs index b14c457abf..3f5ddfbccc 100644 --- a/ol/txs/src/commands/autopay_cmd.rs +++ b/ol/txs/src/commands/autopay_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `autopay` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/community_pay_cmd.rs b/ol/txs/src/commands/community_pay_cmd.rs new file mode 100644 index 0000000000..7ae8878f91 --- /dev/null +++ b/ol/txs/src/commands/community_pay_cmd.rs @@ -0,0 +1,71 @@ +//! `community-pay` subcommand + +#![allow(clippy::never_loop)] + +use crate::{entrypoint, submit_tx::{TxError, maybe_submit, tx_params_wrapper}}; +use abscissa_core::{Command, Options, Runnable}; + +use diem_json_rpc_types::views::TransactionView; +use diem_transaction_builder::stdlib as transaction_builder; +use diem_types::{account_address::AccountAddress}; +use ol_types::config::TxType; +use std::{path::PathBuf, process::exit}; + +#[derive(Command, Debug, Default, Options)] + +/// create a community wallet payment/transfer proposal +pub struct CommunityPayCmd { + #[options(short = "a", help = "the new user's address")] + destination_account: String, + #[options(short = "c", help = "the amount of coins to send to new user")] + coins: u64, + #[options(short = "m", help = "string, text of memo to accompany payment")] + memo: String, +} + +impl Runnable for CommunityPayCmd { + fn run(&self) { + let entry_args = entrypoint::get_args(); + let destination = match self.destination_account.parse::(){ + Ok(a) => a, + Err(e) => { + println!("ERROR: could not parse this account address: {}, message: {}", self.destination_account, &e.to_string()); + exit(1); + }, + }; + + match community_payment_proposal(destination, self.coins.clone(), self.memo.clone(), entry_args.save_path) { + Ok(_) => println!("Success: community payment proposed for 3 epochs(days) from now: {}", self.destination_account), + Err(e) => { + println!("ERROR: could not create community transfer proposal:"); + if let Some(loc) = &e.location { + if loc.contains("TransferScripts") { + match e.abort_code { + Some(0) => println!("this account is not a community wallet\n"), + Some(1) => println!("destination account is not a slow wallet\n"), + _ => println!("misc error, could not propose the tx: {:?}\n", &e), + } + } else { + println!("misc error, could not propose the tx: {:?}\n", &e) + } + }; + + exit(1); + }, + } + } +} + +/// create an account by sending coin to it +pub fn community_payment_proposal(destination: AccountAddress, coins: u64, memo: String, save_path: Option) -> Result{ + let tx_params = tx_params_wrapper(TxType::Mgmt).unwrap(); + + // NOTE: coins here do not have the scaling factor. Rescaling is the responsibility of the Move script. See the script in ol_accounts.move for detail. + let script = transaction_builder::encode_community_transfer_script_function( + destination, + coins, + memo.as_bytes().to_vec(), + ); + + maybe_submit(script, &tx_params, save_path) +} \ No newline at end of file diff --git a/ol/txs/src/commands/create_account_cmd.rs b/ol/txs/src/commands/create_account_cmd.rs index 27cc483c79..0e2c79f36a 100644 --- a/ol/txs/src/commands/create_account_cmd.rs +++ b/ol/txs/src/commands/create_account_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `create-account` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/create_validator_cmd.rs b/ol/txs/src/commands/create_validator_cmd.rs index ca372e67a3..00535f0bba 100644 --- a/ol/txs/src/commands/create_validator_cmd.rs +++ b/ol/txs/src/commands/create_validator_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `create-validator` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/demo_cmd.rs b/ol/txs/src/commands/demo_cmd.rs index 12cddafeb7..ae1e814b35 100644 --- a/ol/txs/src/commands/demo_cmd.rs +++ b/ol/txs/src/commands/demo_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `demo` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/oracle_upgrade_cmd.rs b/ol/txs/src/commands/oracle_upgrade_cmd.rs index 6b7e60a9b2..6c52e88bab 100644 --- a/ol/txs/src/commands/oracle_upgrade_cmd.rs +++ b/ol/txs/src/commands/oracle_upgrade_cmd.rs @@ -1,4 +1,4 @@ -//! `OracleUpgrade` subcommand +//! `oracle-upgrade` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/relay_cmd.rs b/ol/txs/src/commands/relay_cmd.rs index e41a780187..24a5c01e3d 100644 --- a/ol/txs/src/commands/relay_cmd.rs +++ b/ol/txs/src/commands/relay_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `relay` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/transfer_cmd.rs b/ol/txs/src/commands/transfer_cmd.rs index ac4c6f024a..3abb2b41f0 100644 --- a/ol/txs/src/commands/transfer_cmd.rs +++ b/ol/txs/src/commands/transfer_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `transfer` subcommand #![allow(clippy::never_loop)] @@ -13,7 +13,7 @@ use std::{path::PathBuf, process::exit}; /// `CreateAccount` subcommand #[derive(Command, Debug, Default, Options)] pub struct TransferCmd { - #[options(short = "a", help = "the new user's long address (authentication key)")] + #[options(short = "a", help = "the new user's address")] destination_account: String, #[options(short = "c", help = "the amount of coins to send to new user")] coins: u64, @@ -31,9 +31,9 @@ impl Runnable for TransferCmd { }; match balance_transfer(destination, self.coins, entry_args.save_path) { - Ok(_) => println!("Success. Balance transfer success: {}", self.destination_account), + Ok(_) => println!("Success: Balance transfer posted: {}", self.destination_account), Err(e) => { - println!("ERROR: could not create account, message: {:?}", &e); + println!("ERROR: execute balance transfer message: {:?}", &e); exit(1); }, } diff --git a/ol/txs/src/commands/valset_cmd.rs b/ol/txs/src/commands/valset_cmd.rs index d78cf99002..9cc9a6ba76 100644 --- a/ol/txs/src/commands/valset_cmd.rs +++ b/ol/txs/src/commands/valset_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `val-set` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/commands/wallet_cmd.rs b/ol/txs/src/commands/wallet_cmd.rs index 3ae0bed684..39d4c6374b 100644 --- a/ol/txs/src/commands/wallet_cmd.rs +++ b/ol/txs/src/commands/wallet_cmd.rs @@ -1,4 +1,4 @@ -//! `CreateAccount` subcommand +//! `wallet` subcommand #![allow(clippy::never_loop)] diff --git a/ol/txs/src/submit_tx.rs b/ol/txs/src/submit_tx.rs index 657082e2b6..173d6c588e 100644 --- a/ol/txs/src/submit_tx.rs +++ b/ol/txs/src/submit_tx.rs @@ -71,11 +71,15 @@ pub struct TxError { pub err: Option, /// transaction view if the transaction got that far pub tx_view: Option, + /// Move module or script where error occurred + pub location: Option, + /// Move abort code used in error + pub abort_code: Option, } impl From for TxError { fn from(e: Error) -> Self { - TxError{ err: Some(e), tx_view: None } + TxError{ err: Some(e), tx_view: None, location: None, abort_code: None } } } @@ -89,6 +93,8 @@ pub fn maybe_submit( DiemClient::new(tx_params.url.clone(), tx_params.waypoint).map_err(|e| TxError { err: Some(e), tx_view: None, + location: None, + abort_code: None, })?; let (mut account_data, txn) = stage(script, tx_params, &mut client)?; @@ -98,16 +104,12 @@ pub fn maybe_submit( } match submit_tx(client, txn.clone(), &mut account_data) { - Ok(res) => match eval_tx_status(&res) { - Ok(_) => Ok(res), - Err(e) => Err(TxError { - err: Some(e), - tx_view: Some(res), - }), - }, + Ok(res) => eval_tx_status(res), Err(e) => Err(TxError { err: Some(e), tx_view: None, + location: None, + abort_code: None, }), } } @@ -119,7 +121,13 @@ pub fn save_dont_send_tx( save_path: Option, ) -> Result { let mut client = DiemClient::new(tx_params.url.clone(), tx_params.waypoint) - .map_err(|e| { TxError { err: Some(e), tx_view: None }})?; + .map_err(|e| { TxError { + err: Some(e), + tx_view: None, + location: None, + abort_code: None, + } + })?; let (_account_data, txn) = stage(script, tx_params, &mut client)?; if let Some(path) = save_path { @@ -486,16 +494,33 @@ pub fn wait_for_tx( } /// Evaluate the response of a submitted txs transaction. -pub fn eval_tx_status(result: &TransactionView) -> Result<(), Error> { - match result.vm_status == VMStatusView::Executed { - true => { +pub fn eval_tx_status(result: TransactionView) -> Result { + match &result.vm_status { + VMStatusView::Executed => { println!("\nSuccess: transaction executed"); - Ok(()) - } - false => { + Ok(result) + }, + VMStatusView::MoveAbort {location, abort_code, explanation: _ } => { println!("Transaction failed"); + Err(TxError{ + err: Some(Error::msg(format!("Rejected with code: {:?}", result.vm_status))), + tx_view: Some(result.clone()), + location: Some(location.to_string()), + abort_code: Some(*abort_code), + }) + // let msg = format!("Rejected with code: {:?}", result.vm_status); + // let e = Error::msg(msg); + // Err(e.context(result.vm_status.clone())) + }, + _ => { let msg = format!("Rejected with code: {:?}", result.vm_status); - Err(Error::msg(msg)) + let e = Error::msg(msg); + Err(TxError{ + err: Some(e), + tx_view: Some(result), + location: None, + abort_code: None, + }) } } }