diff --git a/build/StarcoinFramework/BuildInfo.yaml b/build/StarcoinFramework/BuildInfo.yaml index 516531b1..544cba98 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: 88545469FF3E16035E05352124B98D1C4408DD2CFDC741EEE1C13000F9EBB4E6 + source_digest: DEAD5E4489A0087D5ECEDCE704BCF29BF0E73B77A305D5324A9F453D3803B69B build_flags: dev_mode: false test_mode: false diff --git a/build/StarcoinFramework/abis/EasyGas/init_data_source.abi b/build/StarcoinFramework/abis/EasyGas/init_data_source.abi new file mode 100644 index 00000000..90a0e498 Binary files /dev/null and b/build/StarcoinFramework/abis/EasyGas/init_data_source.abi differ diff --git a/build/StarcoinFramework/abis/EasyGas/register.abi b/build/StarcoinFramework/abis/EasyGas/register.abi new file mode 100644 index 00000000..aebcb5f8 Binary files /dev/null and b/build/StarcoinFramework/abis/EasyGas/register.abi differ diff --git a/build/StarcoinFramework/abis/EasyGas/update.abi b/build/StarcoinFramework/abis/EasyGas/update.abi new file mode 100644 index 00000000..19dc8d01 Binary files /dev/null and b/build/StarcoinFramework/abis/EasyGas/update.abi differ diff --git a/build/StarcoinFramework/abis/EasyGas/withdraw_gas_fee_entry.abi b/build/StarcoinFramework/abis/EasyGas/withdraw_gas_fee_entry.abi new file mode 100644 index 00000000..b6fcd89d Binary files /dev/null and b/build/StarcoinFramework/abis/EasyGas/withdraw_gas_fee_entry.abi differ diff --git a/build/StarcoinFramework/bytecode_modules/Account.mv b/build/StarcoinFramework/bytecode_modules/Account.mv index f904edd0..a3e77c29 100644 Binary files a/build/StarcoinFramework/bytecode_modules/Account.mv and b/build/StarcoinFramework/bytecode_modules/Account.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/EasyGas.mv b/build/StarcoinFramework/bytecode_modules/EasyGas.mv new file mode 100644 index 00000000..cf3a3a36 Binary files /dev/null and b/build/StarcoinFramework/bytecode_modules/EasyGas.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/EasyGasScript.mv b/build/StarcoinFramework/bytecode_modules/EasyGasScript.mv new file mode 100644 index 00000000..260e57f8 Binary files /dev/null and b/build/StarcoinFramework/bytecode_modules/EasyGasScript.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/GenesisSignerCapability.mv b/build/StarcoinFramework/bytecode_modules/GenesisSignerCapability.mv index c7e787b2..0b2013c1 100644 Binary files a/build/StarcoinFramework/bytecode_modules/GenesisSignerCapability.mv and b/build/StarcoinFramework/bytecode_modules/GenesisSignerCapability.mv differ diff --git a/build/StarcoinFramework/bytecode_modules/StdlibUpgradeScripts.mv b/build/StarcoinFramework/bytecode_modules/StdlibUpgradeScripts.mv index a9bbbad6..1d9e31fe 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 04b09867..44f482f8 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/Account.md b/build/StarcoinFramework/docs/Account.md index 8dd831d9..40d600a1 100644 --- a/build/StarcoinFramework/docs/Account.md +++ b/build/StarcoinFramework/docs/Account.md @@ -41,6 +41,9 @@ The module for the account resource that governs every account - [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) @@ -80,9 +83,13 @@ The module for the account resource that governs every account - [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) @@ -95,6 +102,7 @@ The module for the account resource that governs every account use 0x1::Errors; use 0x1::Event; use 0x1::Hash; +use 0x1::Math; use 0x1::Option; use 0x1::STC; use 0x1::Signer; @@ -1570,6 +1578,81 @@ Helper to deposit amount to the given account balance + + + + +## 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;
+}
+
+ + +
@@ -1579,7 +1662,7 @@ Helper to deposit amount to the given account balance Helper to withdraw amount from the given account balance and return the withdrawn Token -
fun withdraw_from_balance<TokenType: store>(balance: &mut Account::Balance<TokenType>, amount: u128): Token::Token<TokenType>
+
public fun withdraw_from_balance<TokenType: store>(balance: &mut Account::Balance<TokenType>, amount: u128): Token::Token<TokenType>
 
@@ -1588,7 +1671,7 @@ Helper to withdraw amount from the given account balance and return Implementation -
fun withdraw_from_balance<TokenType: store>(balance: &mut Balance<TokenType>, amount: u128): Token<TokenType>{
+
public fun withdraw_from_balance<TokenType: store>(balance: &mut Balance<TokenType>, amount: u128): Token<TokenType>{
     Token::withdraw(&mut balance.token, amount)
 }
 
@@ -3120,6 +3203,31 @@ Checks if an account exists at check_addr + + + + +## 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
+}
+
+ + +
@@ -3149,6 +3257,64 @@ It verifies: 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);
 
@@ -3173,54 +3339,29 @@ It verifies:
             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!(
-        (txn_gas_price as u128) * (txn_max_gas_units as u128) <= MAX_U64,
+        max_transaction_fee_stc <= MAX_U64,
         Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT),
     );
-    let max_transaction_fee = txn_gas_price * txn_max_gas_units;
-    if (max_transaction_fee > 0) {
-        assert!(
-            STC::is_stc<TokenType>(),
-            Errors::invalid_argument(EBAD_TRANSACTION_FEE_TOKEN)
-        );
-
-        let balance_amount = balance<TokenType>(txn_sender);
-        assert!(balance_amount >= (max_transaction_fee as u128), 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));
-}
-
- - - -
- -
-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_gas_price * txn_max_gas_units > max_u64();
-aborts_if txn_gas_price * txn_max_gas_units > 0 && !exists<Balance<TokenType>>(txn_sender);
-aborts_if txn_gas_price * txn_max_gas_units > 0 && Token::spec_token_code<TokenType>() != Token::spec_token_code<STC>();
-aborts_if txn_gas_price * txn_max_gas_units > 0 && global<Balance<TokenType>>(txn_sender).token.value < txn_gas_price * txn_max_gas_units;
-aborts_if txn_gas_price * txn_max_gas_units > 0 && txn_sequence_number >= max_u64();
-aborts_if txn_sequence_number < global<Account>(txn_sender).sequence_number;
-aborts_if txn_sequence_number != global<Account>(txn_sender).sequence_number;
+}
 
@@ -3252,7 +3393,7 @@ It collects gas and bumps the sequence number txn_max_gas_units: u64, gas_units_remaining: u64, ) acquires Account, Balance { - txn_epilogue_v2<TokenType>(account, txn_sender, txn_sequence_number, Vector::empty(), txn_gas_price, txn_max_gas_units, gas_units_remaining) + txn_epilogue_v3<TokenType>(account, txn_sender, txn_sequence_number, Vector::empty(), txn_gas_price, txn_max_gas_units, gas_units_remaining,1,1) }
@@ -3270,6 +3411,39 @@ It collects gas and bumps the sequence number + + + + +## 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)
+}
+
+ + +
@@ -3298,32 +3472,14 @@ It collects gas and bumps the sequence number txn_max_gas_units: u64, gas_units_remaining: u64, ) acquires Account, Balance { - CoreAddresses::assert_genesis_address(account); - - // Load the transaction sender's account and balance resources - let sender_account = borrow_global_mut<Account>(txn_sender); - let sender_balance = borrow_global_mut<Balance<TokenType>>(txn_sender); - - // Charge for gas - let transaction_fee_amount =(txn_gas_price * (txn_max_gas_units - gas_units_remaining) as u128); - assert!( - balance_for(sender_balance) >= transaction_fee_amount, - Errors::limit_exceeded(EINSUFFICIENT_BALANCE) - ); - - // 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 > 0) { - let transaction_fee = withdraw_from_balance( - sender_balance, - transaction_fee_amount - ); - TransactionFee::pay_fee(transaction_fee); - }; + 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) } @@ -3346,11 +3502,93 @@ It collects gas and bumps the sequence number 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); + 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()); + !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(); + 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);
+    };
+}
 
diff --git a/build/StarcoinFramework/docs/EasyGas.md b/build/StarcoinFramework/docs/EasyGas.md new file mode 100644 index 00000000..c0faf26a --- /dev/null +++ b/build/StarcoinFramework/docs/EasyGas.md @@ -0,0 +1,113 @@ + + + +# 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) + + +
use 0x1::EasyGas;
+
+ + + + + +## 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);
+}
+
+ + + +
diff --git a/build/StarcoinFramework/docs/README.md b/build/StarcoinFramework/docs/README.md index 8810b9b2..3f151f78 100644 --- a/build/StarcoinFramework/docs/README.md +++ b/build/StarcoinFramework/docs/README.md @@ -35,6 +35,8 @@ This is the root document for the Move StarcoinFramework module documentation. T - [`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) diff --git a/build/StarcoinFramework/docs/StdlibUpgradeScripts.md b/build/StarcoinFramework/docs/StdlibUpgradeScripts.md index 58dfb3ff..a757b9ba 100644 --- a/build/StarcoinFramework/docs/StdlibUpgradeScripts.md +++ b/build/StarcoinFramework/docs/StdlibUpgradeScripts.md @@ -25,6 +25,7 @@ The module for StdlibUpgrade init scripts use 0x1::Collection; use 0x1::Config; use 0x1::CoreAddresses; +use 0x1::EasyGas; use 0x1::GenesisNFT; use 0x1::GenesisSignerCapability; use 0x1::LanguageVersion; @@ -339,6 +340,10 @@ deprecated, use do_upgrade_from_v6_to_v7_with_language_version.
public fun do_upgrade_from_v11_to_v12(sender: &signer) {
     {
+        EasyGas::initialize(sender,
+            @0x8c109349c6bd91411d6bc962e080c4a3,
+            b"STAR",b"STAR",
+            @0x8c109349c6bd91411d6bc962e080c4a3);
         Block::checkpoints_init(sender);
     };
 }
diff --git a/build/StarcoinFramework/docs/TransactionManager.md b/build/StarcoinFramework/docs/TransactionManager.md
index f463b735..8cf62406 100644
--- a/build/StarcoinFramework/docs/TransactionManager.md
+++ b/build/StarcoinFramework/docs/TransactionManager.md
@@ -13,16 +13,21 @@
 -  [Function `epilogue`](#0x1_TransactionManager_epilogue)
 -  [Function `epilogue_v2`](#0x1_TransactionManager_epilogue_v2)
 -  [Function `block_prologue`](#0x1_TransactionManager_block_prologue)
+-  [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;
@@ -31,6 +36,7 @@
 use 0x1::TransactionFee;
 use 0x1::TransactionPublishOption;
 use 0x1::TransactionTimeout;
+use 0x1::Vector;
 
@@ -40,6 +46,24 @@ ## Constants + + + + +
const MAX_U64: u128 = 18446744073709551615;
+
+ + + + + + + +
const EDEPRECATED_FUNCTION: u64 = 19;
+
+ + + @@ -49,6 +73,150 @@ + + + + +
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;
+
+ + + @@ -153,13 +321,21 @@ It verifies: // 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)); - Account::txn_prologue<TokenType>( + 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), @@ -209,8 +385,6 @@ It verifies: 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 && StarcoinFramework::Token::spec_token_code<TokenType>() != StarcoinFramework::Token::spec_token_code<STC>(); -aborts_if txn_gas_price * txn_max_gas_units > 0 && global<Account::Balance<TokenType>>(txn_sender).token.value < txn_gas_price * txn_max_gas_units; 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; @@ -322,7 +496,13 @@ It collects gas and bumps the sequence number success: bool, ) { CoreAddresses::assert_genesis_address(&account); - Account::txn_epilogue_v2<TokenType>( + 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, @@ -330,6 +510,8 @@ It collects gas and bumps the sequence number 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( @@ -413,6 +595,177 @@ The runtime always runs this before executing the transactions in a block. +
+ + + +## 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;
+
+ + +
diff --git a/build/StarcoinFramework/source_maps/Account.mvsm b/build/StarcoinFramework/source_maps/Account.mvsm index a732ac0a..afe77bad 100644 Binary files a/build/StarcoinFramework/source_maps/Account.mvsm and b/build/StarcoinFramework/source_maps/Account.mvsm differ diff --git a/build/StarcoinFramework/source_maps/EasyGas.mvsm b/build/StarcoinFramework/source_maps/EasyGas.mvsm new file mode 100644 index 00000000..e040ec35 Binary files /dev/null and b/build/StarcoinFramework/source_maps/EasyGas.mvsm differ diff --git a/build/StarcoinFramework/source_maps/EasyGasScript.mvsm b/build/StarcoinFramework/source_maps/EasyGasScript.mvsm new file mode 100644 index 00000000..02e6e053 Binary files /dev/null and b/build/StarcoinFramework/source_maps/EasyGasScript.mvsm differ diff --git a/build/StarcoinFramework/source_maps/GenesisSignerCapability.mvsm b/build/StarcoinFramework/source_maps/GenesisSignerCapability.mvsm index 70bc337e..41be8a29 100644 Binary files a/build/StarcoinFramework/source_maps/GenesisSignerCapability.mvsm and b/build/StarcoinFramework/source_maps/GenesisSignerCapability.mvsm differ diff --git a/build/StarcoinFramework/source_maps/StdlibUpgradeScripts.mvsm b/build/StarcoinFramework/source_maps/StdlibUpgradeScripts.mvsm index 104c652b..7f06a692 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 54e1151c..6f75505a 100644 Binary files a/build/StarcoinFramework/source_maps/TransactionManager.mvsm and b/build/StarcoinFramework/source_maps/TransactionManager.mvsm differ diff --git a/integration-tests/account/txn_prologue_and_epilogue.exp b/integration-tests/account/txn_prologue_and_epilogue.exp index c8f0ae6c..e771d6b8 100644 --- a/integration-tests/account/txn_prologue_and_epilogue.exp +++ b/integration-tests/account/txn_prologue_and_epilogue.exp @@ -1,14 +1,14 @@ -processed 12 tasks +processed 16 tasks -task 3 'run'. lines 8-23: +task 3 'run'. lines 7-28: { - "gas_used": 401102, + "gas_used": 527547, "status": "Executed" } -task 4 'run'. lines 27-59: +task 4 'run'. lines 30-64: { - "gas_used": 91881, + "gas_used": 93051, "status": { "MoveAbort": { "location": { @@ -22,9 +22,9 @@ task 4 'run'. lines 27-59: } } -task 5 'run'. lines 63-90: +task 5 'run'. lines 68-94: { - "gas_used": 168011, + "gas_used": 169688, "status": { "MoveAbort": { "location": { @@ -38,9 +38,9 @@ task 5 'run'. lines 63-90: } } -task 6 'run'. lines 94-122: +task 6 'run'. lines 98-126: { - "gas_used": 140032, + "gas_used": 146018, "status": { "MoveAbort": { "location": { @@ -54,9 +54,9 @@ task 6 'run'. lines 94-122: } } -task 7 'run'. lines 126-156: +task 7 'run'. lines 130-160: { - "gas_used": 174473, + "gas_used": 193967, "status": { "MoveAbort": { "location": { @@ -70,15 +70,15 @@ task 7 'run'. lines 126-156: } } -task 8 'run'. lines 160-205: +task 8 'run'. lines 164-210: { - "gas_used": 239024, + "gas_used": 319957, "status": "Executed" } -task 9 'run'. lines 209-239: +task 9 'run'. lines 214-244: { - "gas_used": 174341, + "gas_used": 193835, "status": { "MoveAbort": { "location": { @@ -92,9 +92,9 @@ task 9 'run'. lines 209-239: } } -task 10 'run'. lines 243-274: +task 10 'run'. lines 248-279: { - "gas_used": 95560, + "gas_used": 112720, "status": { "MoveAbort": { "location": { @@ -108,9 +108,9 @@ task 10 'run'. lines 243-274: } } -task 11 'run'. lines 278-309: +task 11 'run'. lines 283-314: { - "gas_used": 89451, + "gas_used": 90621, "status": { "MoveAbort": { "location": { @@ -123,3 +123,37 @@ task 11 'run'. lines 278-309: } } } + +task 12 'run'. lines 316-352: +{ + "gas_used": 197881, + "status": { + "MoveAbort": { + "location": { + "Module": { + "address": "0x00000000000000000000000000000001", + "name": "Account" + } + }, + "abort_code": "1031" + } + } +} + +task 13 'run'. lines 354-365: +{ + "gas_used": 603097, + "status": "Executed" +} + +task 14 'run'. lines 367-417: +{ + "gas_used": 470181, + "status": "Executed" +} + +task 15 'run'. lines 420-443: +{ + "gas_used": 37331, + "status": "Executed" +} diff --git a/integration-tests/account/txn_prologue_and_epilogue.move b/integration-tests/account/txn_prologue_and_epilogue.move index ddb27004..9c602e3a 100644 --- a/integration-tests/account/txn_prologue_and_epilogue.move +++ b/integration-tests/account/txn_prologue_and_epilogue.move @@ -1,9 +1,8 @@ //# init -n dev -//# faucet --addr alice --amount 10000000 - -//# faucet --addr Genesis +//# faucet --addr Genesis --amount 1000000000000 +//# faucet --addr alice --amount 10000000 //# run --signers alice // create txn sender account @@ -11,6 +10,8 @@ script { use StarcoinFramework::STC::STC; use StarcoinFramework::Account; use StarcoinFramework::Authenticator; + use StarcoinFramework::DummyToken; + use StarcoinFramework::DummyToken::DummyToken; fun main(account: signer) { let txn_public_key = x"c48b687a1dd8265101b33df6ae0b6825234e3f28df9ecb38fb286cf76dae919d"; @@ -18,15 +19,18 @@ script { let address = Authenticator::derived_address(auth_key_vec); Account::create_account_with_address(address); Account::pay_from(&account, address, 5000); + let coin = DummyToken::mint(&account, 500); + Account::deposit(address, coin); + assert!(Account::balance(address) == 500, 1000); + } } // check: EXECUTED - - //# run --signers alice // prologue sender is not genesis script { + use StarcoinFramework::TransactionManager; use StarcoinFramework::Account; use StarcoinFramework::STC::STC; use StarcoinFramework::Authenticator; @@ -46,13 +50,14 @@ script { let txn_gas_price = 1; let txn_max_gas_units = 1000; - Account::txn_prologue( + TransactionManager::txn_prologue_v2( &account, txn_sender, txn_sequence_number, txn_public_key, txn_gas_price, - txn_max_gas_units + txn_max_gas_units, + 1,1 ); } } @@ -76,7 +81,6 @@ script { let txn_sequence_number = 0; let txn_gas_price = 1; let txn_max_gas_units = 10000; //EPROLOGUE_CANT_PAY_GAS_DEPOSIT - Account::txn_prologue( &account, txn_sender, @@ -160,6 +164,7 @@ script { //# run --signers Genesis // successfully executed script { + use StarcoinFramework::TransactionManager; use StarcoinFramework::Account; use StarcoinFramework::STC::STC; use StarcoinFramework::Authenticator; @@ -177,13 +182,13 @@ script { let txn_gas_price = 1; let txn_max_gas_units = 1000; - Account::txn_prologue( + TransactionManager::txn_prologue_v2( &account, txn_sender, txn_sequence_number, txn_public_key, txn_gas_price, - txn_max_gas_units + txn_max_gas_units,1,1 ); // execute the txn... @@ -306,4 +311,133 @@ script { ); } } -// check: "Keep(ABORTED { code: 2818" \ No newline at end of file +// check: "Keep(ABORTED { code: 2818" + +//# run --signers Genesis +// epilouge cant pay gas deposit + +script { + use StarcoinFramework::Account; + use StarcoinFramework::Authenticator; + use StarcoinFramework::Vector; + use StarcoinFramework::DummyToken::DummyToken; + + fun main(account: signer) { + let txn_public_key = x"c48b687a1dd8265101b33df6ae0b6825234e3f28df9ecb38fb286cf76dae919d"; + let auth_key_vec = Authenticator::ed25519_authentication_key(copy txn_public_key); + let txn_sender = Authenticator::derived_address(copy auth_key_vec); + Vector::push_back(&mut txn_public_key, 0u8); //create preimage + + let seq = Account::sequence_number(txn_sender); + assert!(seq == 1, 1005); + + let txn_sequence_number = 1; + let txn_gas_price = 1; + let txn_max_gas_units = 2510; + Account::do_accept_token(&account); + + Account::txn_prologue_v2( + &account, + txn_sender, + txn_sequence_number, + txn_public_key, + txn_gas_price, + txn_max_gas_units, + 2, + 10, + ); + } + +} +// check: "Keep(ABORTED { code: 1031" + +//# run --signers Genesis +script { + use StarcoinFramework::STC::STC; + use StarcoinFramework::Account::{deposit, withdraw}; + use StarcoinFramework::EasyGas; + fun main(account: signer){ + EasyGas::initialize(&account,@0x1,b"DummyToken",b"DummyToken",@alice); + let token = withdraw(&account,100000000000); + deposit(EasyGas::get_gas_fee_address(), token); + } +} +//check: EXECUTED + +//# run --signers Genesis +// successfully executed +script { + use StarcoinFramework::TransactionManager; + use StarcoinFramework::Account; + use StarcoinFramework::Authenticator; + use StarcoinFramework::Vector; + use StarcoinFramework::DummyToken::DummyToken; + + fun main(account: signer) { + let txn_public_key = x"c48b687a1dd8265101b33df6ae0b6825234e3f28df9ecb38fb286cf76dae919d"; + let auth_key_vec = Authenticator::ed25519_authentication_key(copy txn_public_key); + let txn_sender = Authenticator::derived_address(copy auth_key_vec); + Vector::push_back(&mut txn_public_key, 0u8); //create preimage + let seq = Account::sequence_number(txn_sender); + assert!(seq == 1, 1005); + + let txn_sequence_number = 1; + let txn_gas_price = 1; + let txn_max_gas_units = 2500; + Account::do_accept_token(&account); + + TransactionManager::txn_prologue_v2( + &account, + txn_sender, + txn_sequence_number, + txn_public_key, + txn_gas_price, + txn_max_gas_units, + 2, + 10, + ); + // execute the txn... + let gas_units_remaining = 10; + TransactionManager::txn_epilogue_v3( + &account, + txn_sender, + txn_sequence_number, + Vector::empty(), + txn_gas_price, + txn_max_gas_units, + gas_units_remaining, + 2, + 10, + ); + let seq = Account::sequence_number(txn_sender); + assert!(seq == 2, 1006); + } + +} +// check: EXECUTED + + +//# run --signers Genesis +script { + use StarcoinFramework::Account; + + fun main(_account: signer){ + let (txn_gas_price, txn_max_gas_units, gas_units_remaining, + stc_price, stc_price_scaling + ) = (1, 100000, 0, 43793, 1000000000000000); + let (stc_amount, token_amount) = Account::transaction_fee_simulate(txn_gas_price, txn_max_gas_units, gas_units_remaining, + stc_price, stc_price_scaling); + assert!(stc_amount==100000, 100); + assert!(token_amount==1, 101); + + let (txn_gas_price, txn_max_gas_units, gas_units_remaining, + stc_price, stc_price_scaling + ) = (1000000, 100000, 0, 43793, 1000000000000000); + let (stc_amount, token_amount) = Account::transaction_fee_simulate(txn_gas_price, txn_max_gas_units, gas_units_remaining, + stc_price, stc_price_scaling); + assert!(stc_amount==100000000000, 102); + assert!(token_amount==4, 103); + + } +} +// check: EXECUTED diff --git a/integration-tests/oracle/EasyGas.exp b/integration-tests/oracle/EasyGas.exp new file mode 100644 index 00000000..21ea1304 --- /dev/null +++ b/integration-tests/oracle/EasyGas.exp @@ -0,0 +1,34 @@ +processed 7 tasks + +task 5 'run'. lines 14-20: +{ + "gas_used": 32320, + "status": { + "MoveAbort": { + "location": { + "Module": { + "address": "0x00000000000000000000000000000001", + "name": "CoreAddresses" + } + }, + "abort_code": "2818" + } + } +} + +task 6 'run'. lines 22-34: +{ + "gas_used": 175859, + "status": { + "ExecutionFailure": { + "location": { + "Module": { + "address": "0x00000000000000000000000000000001", + "name": "EasyGas" + } + }, + "function": 2, + "code_offset": 3 + } + } +} diff --git a/integration-tests/oracle/EasyGas.move b/integration-tests/oracle/EasyGas.move new file mode 100644 index 00000000..5183e683 --- /dev/null +++ b/integration-tests/oracle/EasyGas.move @@ -0,0 +1,34 @@ +//# init -n dev + +//# faucet --addr alice + +//# faucet --addr bob + +//# faucet --addr lili + +//# publish +module alice::STAR { + struct STAR has store, copy, drop {} +} + +//# run --signers alice +script { + use StarcoinFramework::EasyGas; + fun main(signer: signer) { + EasyGas::initialize(&signer, @alice, b"STAR", b"STAR", @bob); + } +} + +//# run --signers bob +script { + use alice::STAR::STAR; + use StarcoinFramework::EasyGas; + + fun main(signer: signer) { + EasyGas::register_oracle(&signer, 9); + EasyGas::init_oracle_source(&signer, 0); + EasyGas::update_oracle(&signer, 100); + let star_price= EasyGas::gas_oracle_read(); + assert!(star_price==100,1000); + } +} \ No newline at end of file diff --git a/integration-tests/transaction_scripts/create_account.move b/integration-tests/transaction_scripts/create_account.move index 5dc8523b..27ecbe6c 100644 --- a/integration-tests/transaction_scripts/create_account.move +++ b/integration-tests/transaction_scripts/create_account.move @@ -13,4 +13,4 @@ script { fun main(account: signer, fresh_address: address, initial_amount: u128) { Account::create_account_with_initial_amount_v2(account, fresh_address, initial_amount); } -} +} \ No newline at end of file diff --git a/scripts/dev_setup.sh b/scripts/dev_setup.sh index 706bd653..21020108 100755 --- a/scripts/dev_setup.sh +++ b/scripts/dev_setup.sh @@ -19,7 +19,7 @@ Z3_VERSION=4.11.2 CVC5_VERSION=0.0.3 DOTNET_VERSION=6.0 BOOGIE_VERSION=2.15.8 -MPM_VERSION=v1.13.7-alpha +MPM_VERSION=v1.13.7 SCRIPT_PATH="$( cd "$( dirname "$0" )" >/dev/null 2>&1 && pwd )" diff --git a/sources/Account.move b/sources/Account.move index 0e15332c..34dc4036 100644 --- a/sources/Account.move +++ b/sources/Account.move @@ -13,8 +13,10 @@ module Account { use StarcoinFramework::TransactionFee; use StarcoinFramework::CoreAddresses; use StarcoinFramework::Errors; - use StarcoinFramework::STC::{Self, STC}; + use StarcoinFramework::STC::{Self, STC, is_stc}; use StarcoinFramework::BCS; + use StarcoinFramework::Math; + friend StarcoinFramework::TransactionManager; spec module { pragma verify = false; @@ -159,7 +161,7 @@ module Account { 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; @@ -196,7 +198,6 @@ module Account { let signer_cap = SignerCapability {addr: Token::token_address() }; signer_cap } - #[test_only] public fun get_genesis_capability_for_test(): SignerCapability { get_genesis_capability() @@ -322,7 +323,7 @@ module Account { 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); @@ -363,10 +364,11 @@ module Account { (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) @@ -406,11 +408,11 @@ module Account { 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); @@ -452,10 +454,23 @@ module Account { 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 - fun withdraw_from_balance(balance: &mut Balance, amount: u128): Token{ + public fun withdraw_from_balance(balance: &mut Balance, amount: u128): Token{ Token::withdraw(&mut balance.token, amount) } @@ -463,6 +478,7 @@ module Account { aborts_if balance.token.value < amount; } + /// Withdraw `amount` Token from the account balance public fun withdraw(account: &signer, amount: u128): Token acquires Account, Balance { @@ -967,6 +983,11 @@ module Account { *&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 @@ -979,6 +1000,35 @@ module Account { 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); @@ -1003,48 +1053,31 @@ module Account { 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!( - (txn_gas_price as u128) * (txn_max_gas_units as u128) <= MAX_U64, + max_transaction_fee_stc <= MAX_U64, Errors::invalid_argument(EPROLOGUE_CANT_PAY_GAS_DEPOSIT), ); - let max_transaction_fee = txn_gas_price * txn_max_gas_units; - if (max_transaction_fee > 0) { - assert!( - STC::is_stc(), - Errors::invalid_argument(EBAD_TRANSACTION_FEE_TOKEN) - ); - - let balance_amount = balance(txn_sender); - assert!(balance_amount >= (max_transaction_fee as u128), 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)); - } - 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_gas_price * txn_max_gas_units > max_u64(); - aborts_if txn_gas_price * txn_max_gas_units > 0 && !exists>(txn_sender); - aborts_if txn_gas_price * txn_max_gas_units > 0 && Token::spec_token_code() != Token::spec_token_code(); - //abort condition for assert!(balance_amount >= max_transaction_fee) - aborts_if txn_gas_price * txn_max_gas_units > 0 && global>(txn_sender).token.value < txn_gas_price * txn_max_gas_units; - 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; } + /// The epilogue is invoked at the end of transactions. /// It collects gas and bumps the sequence number public fun txn_epilogue( @@ -1055,13 +1088,25 @@ module Account { txn_max_gas_units: u64, gas_units_remaining: u64, ) acquires Account, Balance { - txn_epilogue_v2(account, txn_sender, txn_sequence_number, Vector::empty(), txn_gas_price, txn_max_gas_units, gas_units_remaining) + 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( @@ -1073,31 +1118,81 @@ module Account { txn_max_gas_units: u64, gas_units_remaining: u64, ) acquires Account, Balance { - CoreAddresses::assert_genesis_address(account); + 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) + } - // Load the transaction sender's account and balance resources - let sender_account = borrow_global_mut(txn_sender); - let sender_balance = borrow_global_mut>(txn_sender); + 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 =(txn_gas_price * (txn_max_gas_units - gas_units_remaining) as u128); + 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_for(sender_balance) >= transaction_fee_amount, + 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 > 0) { - let transaction_fee = withdraw_from_balance( - sender_balance, - transaction_fee_amount + if (transaction_fee_amount_stc > 0) { + let transaction_fee_token = withdraw_from_balance( + borrow_global_mut>(txn_sender), + transaction_fee_amount_token ); - TransactionFee::pay_fee(transaction_fee); + 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); }; } @@ -1106,18 +1201,10 @@ module Account { 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(); - } + 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); diff --git a/sources/EasyGas.move b/sources/EasyGas.move new file mode 100644 index 00000000..9c2a3bdf --- /dev/null +++ b/sources/EasyGas.move @@ -0,0 +1,142 @@ +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; + + 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); + //TODO:error code define + 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, 100); + 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::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); + } + +} +} diff --git a/sources/GenesisSignerCapability.move b/sources/GenesisSignerCapability.move index 3b4e51a4..618eb536 100644 --- a/sources/GenesisSignerCapability.move +++ b/sources/GenesisSignerCapability.move @@ -7,7 +7,7 @@ module StarcoinFramework::GenesisSignerCapability { friend StarcoinFramework::Oracle; friend StarcoinFramework::Genesis; friend StarcoinFramework::StdlibUpgradeScripts; - + friend StarcoinFramework::EasyGas; const ENOT_GENESIS_ACCOUNT: u64 = 11; @@ -28,12 +28,10 @@ module StarcoinFramework::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() diff --git a/sources/StdlibUpgradeScripts.move b/sources/StdlibUpgradeScripts.move index 85a248f0..8d296ea7 100644 --- a/sources/StdlibUpgradeScripts.move +++ b/sources/StdlibUpgradeScripts.move @@ -2,6 +2,7 @@ address StarcoinFramework { /// The module for StdlibUpgrade init scripts module StdlibUpgradeScripts { + use StarcoinFramework::EasyGas; use StarcoinFramework::CoreAddresses; use StarcoinFramework::STC::{Self, STC}; use StarcoinFramework::Token::{Self, LinearTimeMintKey}; @@ -106,6 +107,10 @@ module StdlibUpgradeScripts { } public fun do_upgrade_from_v11_to_v12(sender: &signer) { { + EasyGas::initialize(sender, + @0x8c109349c6bd91411d6bc962e080c4a3, + b"STAR",b"STAR", + @0x8c109349c6bd91411d6bc962e080c4a3); Block::checkpoints_init(sender); }; } diff --git a/sources/TransactionManager.move b/sources/TransactionManager.move index 70aa0b2b..01a8f1e7 100644 --- a/sources/TransactionManager.move +++ b/sources/TransactionManager.move @@ -3,6 +3,10 @@ address StarcoinFramework { /// 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; @@ -10,7 +14,7 @@ module TransactionManager { use StarcoinFramework::PackageTxnManager; use StarcoinFramework::BlockReward; use StarcoinFramework::Block; - use StarcoinFramework::STC::STC; + use StarcoinFramework::STC::{STC, is_stc}; use StarcoinFramework::TransactionFee; use StarcoinFramework::Timestamp; use StarcoinFramework::ChainId; @@ -19,7 +23,8 @@ module TransactionManager { 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; @@ -62,13 +67,21 @@ module TransactionManager { // 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)); - Account::txn_prologue( + 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), @@ -109,8 +122,6 @@ module TransactionManager { 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 && StarcoinFramework::Token::spec_token_code() != StarcoinFramework::Token::spec_token_code(); - aborts_if txn_gas_price * txn_max_gas_units > 0 && global>(txn_sender).token.value < txn_gas_price * txn_max_gas_units; 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; @@ -159,7 +170,13 @@ module TransactionManager { success: bool, ) { CoreAddresses::assert_genesis_address(&account); - Account::txn_epilogue_v2( + 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, @@ -167,6 +184,8 @@ module TransactionManager { 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( @@ -234,5 +253,148 @@ module TransactionManager { spec block_prologue { 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 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; + + 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; + } +} } -} \ No newline at end of file