diff --git a/crates/rooch-executor/src/actor/executor.rs b/crates/rooch-executor/src/actor/executor.rs index fd9c850f15..9b9956a50d 100644 --- a/crates/rooch-executor/src/actor/executor.rs +++ b/crates/rooch-executor/src/actor/executor.rs @@ -81,7 +81,7 @@ impl ExecutorActor { let multi_chain_address_sender = tx.sender(); let resolved_sender = self.resolve_or_generate(multi_chain_address_sender.clone())?; - let authenticator = tx.authenticator_info(); + let authenticator = tx.authenticator_info()?; let mut moveos_tx = tx.construct_moveos_transaction(resolved_sender)?; diff --git a/crates/rooch-framework-tests/src/tests/bitcoin_validator_tests.rs b/crates/rooch-framework-tests/src/tests/bitcoin_validator_tests.rs index 1435686e08..bfb515891d 100644 --- a/crates/rooch-framework-tests/src/tests/bitcoin_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/bitcoin_validator_tests.rs @@ -22,11 +22,11 @@ fn test_validate() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::Ecdsa) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); bitcoin_validator diff --git a/crates/rooch-framework-tests/src/tests/ethereum_validator_tests.rs b/crates/rooch-framework-tests/src/tests/ethereum_validator_tests.rs index 1fb35ed50c..f072040c84 100644 --- a/crates/rooch-framework-tests/src/tests/ethereum_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/ethereum_validator_tests.rs @@ -22,11 +22,11 @@ fn test_validate() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::EcdsaRecoverable) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); ethereum_validator diff --git a/crates/rooch-framework-tests/src/tests/native_validator_tests.rs b/crates/rooch-framework-tests/src/tests/native_validator_tests.rs index 07cd2a7f14..c02ba5eb07 100644 --- a/crates/rooch-framework-tests/src/tests/native_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/native_validator_tests.rs @@ -22,11 +22,11 @@ fn test_validate() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::Ed25519) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); native_validator diff --git a/crates/rooch-framework-tests/src/tests/nostr_validator_tests.rs b/crates/rooch-framework-tests/src/tests/nostr_validator_tests.rs index eae24382b9..f7dfc9abaf 100644 --- a/crates/rooch-framework-tests/src/tests/nostr_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/nostr_validator_tests.rs @@ -21,11 +21,11 @@ fn test_validate() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::Schnorr) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); nostr_validator diff --git a/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs b/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs index eb16fc296e..5fad5e1ca0 100644 --- a/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs @@ -30,11 +30,11 @@ fn test_validate_ed25519() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::Ed25519) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); transaction_validator @@ -55,11 +55,11 @@ fn test_validate_ecdsa() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::Ecdsa) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); transaction_validator @@ -80,11 +80,11 @@ fn test_validate_ecdsa_recoverable() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::EcdsaRecoverable) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); transaction_validator @@ -105,11 +105,11 @@ fn test_validate_schnorr() { let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::Schnorr) .unwrap(); - let auth_info = tx.authenticator_info(); + let auth_info = tx.authenticator_info().unwrap(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); transaction_validator @@ -143,7 +143,7 @@ fn test_session_key_ed25519() { expiration_time, max_inactive_interval, ); - let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number, action); let tx = keystore .sign_transaction(&sender, tx_data, BuiltinScheme::Ed25519) .unwrap(); @@ -164,7 +164,7 @@ fn test_session_key_ed25519() { // send transaction via session key let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); - let tx_data = RoochTransactionData::new(sender, sequence_number + 1, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number + 1, action); let tx = keystore .sign_transaction_via_session_key(&sender, tx_data, &session_auth_key) .unwrap(); @@ -183,7 +183,7 @@ fn test_session_key_ed25519() { .simple_serialize() .unwrap()], ); - let tx_data = RoochTransactionData::new(sender, sequence_number + 2, action); + let tx_data = RoochTransactionData::new_for_test(sender, sequence_number + 2, action); let tx = keystore .sign_transaction_via_session_key(&sender, tx_data, &session_auth_key) .unwrap(); diff --git a/crates/rooch-framework/doc/README.md b/crates/rooch-framework/doc/README.md index b2ab35c79f..1092054e8d 100644 --- a/crates/rooch-framework/doc/README.md +++ b/crates/rooch-framework/doc/README.md @@ -32,6 +32,7 @@ This is the reference documentation of the Rooch Framework. - [`0x3::ethereum_address`](ethereum_address.md#0x3_ethereum_address) - [`0x3::ethereum_validator`](ethereum_validator.md#0x3_ethereum_validator) - [`0x3::gas_coin`](gas_coin.md#0x3_gas_coin) +- [`0x3::gas_price`](gas_price.md#0x3_gas_price) - [`0x3::genesis`](genesis.md#0x3_genesis) - [`0x3::hash`](hash.md#0x3_hash) - [`0x3::multi_ed25519_validator`](multi_ed25519_validator.md#0x3_multi_ed25519_validator) diff --git a/crates/rooch-framework/doc/gas_price.md b/crates/rooch-framework/doc/gas_price.md new file mode 100644 index 0000000000..a72d03d2b1 --- /dev/null +++ b/crates/rooch-framework/doc/gas_price.md @@ -0,0 +1,39 @@ + + + +# Module `0x3::gas_price` + + + +- [Function `get_gas_price_per_unit`](#0x3_gas_price_get_gas_price_per_unit) + + +
+ + + + + +## Function `get_gas_price_per_unit` + +Returns the gas price per unit of gas. + + +
public fun get_gas_price_per_unit(): u64
+
+ + + +
+Implementation + + +
public fun get_gas_price_per_unit(): u64 {
+    //TODO we should provide a algorithm to cordanate the gas price based on the network throughput
+    return 1
+}
+
+ + + +
diff --git a/crates/rooch-framework/doc/transaction_validator.md b/crates/rooch-framework/doc/transaction_validator.md index 3e6ed7d1c5..d29d5aba91 100644 --- a/crates/rooch-framework/doc/transaction_validator.md +++ b/crates/rooch-framework/doc/transaction_validator.md @@ -12,12 +12,14 @@
use 0x1::error;
 use 0x1::option;
 use 0x2::storage_context;
+use 0x2::tx_result;
 use 0x3::account;
 use 0x3::account_authentication;
 use 0x3::address_mapping;
 use 0x3::auth_validator;
 use 0x3::auth_validator_registry;
 use 0x3::builtin_validators;
+use 0x3::gas_price;
 use 0x3::session_key;
 
@@ -131,7 +133,7 @@ This function is for Rooch to validate the transaction sender's authenticator. If the authenticator is invaid, abort this function. -
public fun validate(ctx: &storage_context::StorageContext, tx_sequence_number: u64, scheme: u64, authenticator_payload: vector<u8>): auth_validator::TxValidateResult
+
public fun validate(ctx: &storage_context::StorageContext, _chain_id: u64, scheme: u64, authenticator_payload: vector<u8>): auth_validator::TxValidateResult
 
@@ -142,12 +144,16 @@ If the authenticator is invaid, abort this function.
public fun validate(
     ctx: &StorageContext,
-    tx_sequence_number: u64,
+    _chain_id: u64,
     scheme: u64,
     authenticator_payload: vector<u8>
 ): TxValidateResult {
-    // === validate the sequence number ===
 
+    // === validate the chain id ===
+    //TODO validate the chain id
+
+    // === validate the sequence number ===
+    let tx_sequence_number = storage_context::sequence_number(ctx);
     assert!(
         (tx_sequence_number as u128) < MAX_U64,
         error::out_of_range(EValidateSequenceNumberTooBig)
@@ -166,6 +172,10 @@ If the authenticator is invaid, abort this function.
         error::invalid_argument(EValidateSequenceNumberTooNew)
     );
 
+    // === validate gas ===
+    let _max_gas_amount = storage_context::max_gas_amount(ctx);
+    //TODO check the account can pay the gas fee
+
     // === validate the authenticator ===
 
     // if the authenticator authenticator_payload is session key, validate the session key
diff --git a/crates/rooch-framework/sources/gas_coin.move b/crates/rooch-framework/sources/gas_coin.move
index 908c4c7e8a..742743f461 100644
--- a/crates/rooch-framework/sources/gas_coin.move
+++ b/crates/rooch-framework/sources/gas_coin.move
@@ -60,4 +60,5 @@ module rooch_framework::gas_coin {
         let MintCapStore { mint_cap } = account_storage::global_move_from(ctx,@rooch_framework);
         coin::destroy_mint_cap(mint_cap);
     }
+
 }
diff --git a/crates/rooch-framework/sources/gas_price.move b/crates/rooch-framework/sources/gas_price.move
new file mode 100644
index 0000000000..7cbc0de357
--- /dev/null
+++ b/crates/rooch-framework/sources/gas_price.move
@@ -0,0 +1,8 @@
+module rooch_framework::gas_price {
+
+    /// Returns the gas price per unit of gas.
+    public fun get_gas_price_per_unit(): u64 {
+        //TODO we should provide a algorithm to cordanate the gas price based on the network throughput
+        return 1
+    }
+}
\ No newline at end of file
diff --git a/crates/rooch-framework/sources/transaction_validator.move b/crates/rooch-framework/sources/transaction_validator.move
index 52de795ac0..8300817360 100644
--- a/crates/rooch-framework/sources/transaction_validator.move
+++ b/crates/rooch-framework/sources/transaction_validator.move
@@ -2,12 +2,14 @@ module rooch_framework::transaction_validator {
     use std::error;
     use std::option;
     use moveos_std::storage_context::{Self, StorageContext};
+    use moveos_std::tx_result;
     use rooch_framework::account;
     use rooch_framework::address_mapping::{Self, MultiChainAddress};
     use rooch_framework::account_authentication;
     use rooch_framework::auth_validator::{Self, TxValidateResult};
     use rooch_framework::auth_validator_registry;
     use rooch_framework::session_key;
+    use rooch_framework::gas_price;
 
     const MAX_U64: u128 = 18446744073709551615;
 
@@ -36,12 +38,16 @@ module rooch_framework::transaction_validator {
     /// If the authenticator is invaid, abort this function.
     public fun validate(
         ctx: &StorageContext,
-        tx_sequence_number: u64,
+        _chain_id: u64,
         scheme: u64,
         authenticator_payload: vector
     ): TxValidateResult {
-        // === validate the sequence number ===
 
+        // === validate the chain id ===
+        //TODO validate the chain id
+
+        // === validate the sequence number ===
+        let tx_sequence_number = storage_context::sequence_number(ctx);
         assert!(
             (tx_sequence_number as u128) < MAX_U64,
             error::out_of_range(EValidateSequenceNumberTooBig)
@@ -60,6 +66,10 @@ module rooch_framework::transaction_validator {
             error::invalid_argument(EValidateSequenceNumberTooNew)
         );
 
+        // === validate gas ===
+        let _max_gas_amount = storage_context::max_gas_amount(ctx);
+        //TODO check the account can pay the gas fee
+
         // === validate the authenticator ===
 
         // if the authenticator authenticator_payload is session key, validate the session key
@@ -101,7 +111,11 @@ module rooch_framework::transaction_validator {
             if (!address_mapping::exists_mapping(ctx, multichain_address)) {
                 address_mapping::bind_no_check(ctx, sender, multichain_address);
             };
-        }
+        };
+        let max_gas_amount = storage_context::max_gas_amount(ctx);
+        let gas_price = gas_price::get_gas_price_per_unit();
+        let _gas = max_gas_amount * gas_price;
+        //TODO prepare the gas coin
     }
 
     /// Transaction post_execute function.
@@ -110,8 +124,7 @@ module rooch_framework::transaction_validator {
     fun post_execute(
         ctx: &mut StorageContext,
     ) {
-        //TODO handle transaction gas fee
-
+    
         // Active the session key
 
         let session_key_opt = auth_validator::get_session_key_from_tx_ctx_option(ctx);
@@ -122,5 +135,9 @@ module rooch_framework::transaction_validator {
 
         // Increment sequence number
         account::increment_sequence_number(ctx);
+
+        let tx_result = storage_context::tx_result(ctx);
+        let _gas_used = tx_result::gas_used(&tx_result);
+        //TODO Charge gas fee and return remaining gas
     }
 }
diff --git a/crates/rooch-genesis/genesis/genesis b/crates/rooch-genesis/genesis/genesis
index 326d51af76..a0b3b471aa 100644
Binary files a/crates/rooch-genesis/genesis/genesis and b/crates/rooch-genesis/genesis/genesis differ
diff --git a/crates/rooch-rpc-client/src/client_config.rs b/crates/rooch-rpc-client/src/client_config.rs
index 5bd5ad92bb..6a512be257 100644
--- a/crates/rooch-rpc-client/src/client_config.rs
+++ b/crates/rooch-rpc-client/src/client_config.rs
@@ -7,6 +7,7 @@ use rooch_config::rpc::server_config::ServerConfig;
 use rooch_config::Config;
 use rooch_key::keystore::{AccountKeystore, Keystore};
 use rooch_types::address::RoochAddress;
+use rooch_types::chain_id::ChainID;
 use serde::Deserialize;
 use serde::Serialize;
 use serde_with::serde_as;
@@ -63,6 +64,7 @@ impl ClientConfig {
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct Env {
+    pub chain_id: u64,
     pub alias: String,
     pub rpc: String,
     pub ws: Option,
@@ -91,6 +93,7 @@ impl Env {
 impl Default for Env {
     fn default() -> Self {
         Env {
+            chain_id: ChainID::Dev as u64,
             alias: "default".to_string(),
             rpc: ServerConfig::default().url(false),
             ws: None,
@@ -101,7 +104,11 @@ impl Default for Env {
 impl Display for Env {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         let mut writer = String::new();
-        writeln!(writer, "Active environment : {}", self.alias)?;
+        writeln!(
+            writer,
+            "Active environment : {}, ChainID: {}",
+            self.alias, self.chain_id
+        )?;
         write!(writer, "RPC URL: {}", self.rpc)?;
         if let Some(ws) = &self.ws {
             writeln!(writer)?;
diff --git a/crates/rooch-rpc-client/src/wallet_context.rs b/crates/rooch-rpc-client/src/wallet_context.rs
index cffcbb70cc..142f7dc5ce 100644
--- a/crates/rooch-rpc-client/src/wallet_context.rs
+++ b/crates/rooch-rpc-client/src/wallet_context.rs
@@ -5,6 +5,7 @@ use crate::client_config::{ClientConfig, DEFAULT_EXPIRATION_SECS};
 use crate::Client;
 use anyhow::anyhow;
 use move_core_types::account_address::AccountAddress;
+use moveos_types::gas_config::GasConfig;
 use moveos_types::transaction::MoveAction;
 use rooch_config::{rooch_config_dir, Config, PersistedConfig, ROOCH_CLIENT_CONFIG};
 use rooch_key::keystore::AccountKeystore;
@@ -85,13 +86,20 @@ impl WalletContext {
         action: MoveAction,
     ) -> RoochResult {
         let client = self.get_client().await?;
-
+        let chain_id = self.config.get_active_env()?.chain_id;
         let sequence_number = client
             .get_sequence_number(sender)
             .await
             .map_err(RoochError::from)?;
         log::debug!("use sequence_number: {}", sequence_number);
-        let tx_data = RoochTransactionData::new(sender, sequence_number, action);
+        //TODO max gas amount from cli option or dry run estimate
+        let tx_data = RoochTransactionData::new(
+            sender,
+            sequence_number,
+            chain_id,
+            GasConfig::DEFAULT_MAX_GAS_AMOUNT,
+            action,
+        );
         Ok(tx_data)
     }
 
diff --git a/crates/rooch-rpc-server/src/lib.rs b/crates/rooch-rpc-server/src/lib.rs
index 1e8d1872d6..d379b36208 100644
--- a/crates/rooch-rpc-server/src/lib.rs
+++ b/crates/rooch-rpc-server/src/lib.rs
@@ -30,6 +30,7 @@ use rooch_rpc_api::api::RoochRpcModule;
 use rooch_sequencer::actor::sequencer::SequencerActor;
 use rooch_sequencer::proxy::SequencerProxy;
 use rooch_store::RoochStore;
+use rooch_types::chain_id::ChainID;
 use serde_json::json;
 use std::env;
 use std::fmt::Debug;
@@ -119,6 +120,8 @@ pub async fn start_server(is_mock_storage: bool) -> Result {
     let _ = tracing_subscriber::fmt::try_init();
 
     let config = ServerConfig::default();
+    //TODO load chain id from config or CLI option.
+    let chain_id = ChainID::Dev as u64;
 
     let addr: SocketAddr = format!("{}:{}", config.host, config.port).parse()?;
     let actor_system = ActorSystem::global_system();
@@ -186,16 +189,10 @@ pub async fn start_server(is_mock_storage: bool) -> Result {
         .await?;
 
     let mut rpc_module_builder = RpcModuleBuilder::new();
-    rpc_module_builder
-        .register_module(RoochServer::new(rpc_service.clone()))
-        .unwrap();
-    rpc_module_builder
-        .register_module(WalletServer::new(rpc_service.clone()))
-        .unwrap();
-
-    rpc_module_builder
-        .register_module(EthServer::new(rpc_service.clone()))
-        .unwrap();
+    rpc_module_builder.register_module(RoochServer::new(rpc_service.clone()))?;
+    rpc_module_builder.register_module(WalletServer::new(rpc_service.clone()))?;
+
+    rpc_module_builder.register_module(EthServer::new(chain_id, rpc_service.clone()))?;
 
     // let rpc_api = build_rpc_api(rpc_api);
     let methods_names = rpc_module_builder.module.method_names().collect::>();
diff --git a/crates/rooch-rpc-server/src/server/eth_server.rs b/crates/rooch-rpc-server/src/server/eth_server.rs
index 4b74cbcd2c..ff0feab2cc 100644
--- a/crates/rooch-rpc-server/src/server/eth_server.rs
+++ b/crates/rooch-rpc-server/src/server/eth_server.rs
@@ -32,12 +32,16 @@ use std::time::SystemTime;
 use tracing::info;
 
 pub struct EthServer {
+    chain_id: u64,
     rpc_service: RpcService,
 }
 
 impl EthServer {
-    pub fn new(rpc_service: RpcService) -> Self {
-        Self { rpc_service }
+    pub fn new(chain_id: u64, rpc_service: RpcService) -> Self {
+        Self {
+            chain_id,
+            rpc_service,
+        }
     }
 }
 
@@ -48,7 +52,7 @@ impl EthAPIServer for EthServer {
     }
 
     async fn get_chain_id(&self) -> RpcResult {
-        Ok(format!("0x{:X}", 10001))
+        Ok(format!("0x{:X}", self.chain_id))
     }
 
     async fn get_block_number(&self) -> RpcResult {
diff --git a/crates/rooch-types/src/chain_id.rs b/crates/rooch-types/src/chain_id.rs
new file mode 100644
index 0000000000..5c3aabda9b
--- /dev/null
+++ b/crates/rooch-types/src/chain_id.rs
@@ -0,0 +1,46 @@
+// Copyright (c) RoochNetwork
+// SPDX-License-Identifier: Apache-2.0
+
+use std::fmt::Display;
+
+#[cfg(any(test, feature = "fuzzing"))]
+use proptest_derive::Arbitrary;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
+#[repr(u64)]
+pub enum ChainID {
+    Dev = 20230103,
+    Test = 20230102,
+    Main = 20230101,
+}
+
+impl Display for ChainID {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ChainID::Dev => write!(f, "Dev"),
+            ChainID::Test => write!(f, "Test"),
+            ChainID::Main => write!(f, "Main"),
+        }
+    }
+}
+
+impl From for u64 {
+    fn from(chain_id: ChainID) -> Self {
+        chain_id as u64
+    }
+}
+
+impl TryFrom for ChainID {
+    type Error = anyhow::Error;
+
+    fn try_from(value: u64) -> Result {
+        match value {
+            20230103 => Ok(ChainID::Dev),
+            20230102 => Ok(ChainID::Test),
+            20230101 => Ok(ChainID::Main),
+            _ => Err(anyhow::anyhow!("chain id {} is invalid", value)),
+        }
+    }
+}
diff --git a/crates/rooch-types/src/error.rs b/crates/rooch-types/src/error.rs
index b6469c6213..1f5ff3a8ce 100644
--- a/crates/rooch-types/src/error.rs
+++ b/crates/rooch-types/src/error.rs
@@ -87,6 +87,8 @@ pub enum RoochError {
     InvalidSignature { error: String },
     #[error("Value was not signed by the correct sender: {}", error)]
     IncorrectSigner { error: String },
+    #[error("Invalid chain ID")]
+    InvalidChainID,
 
     #[error("Clean server error: {0}")]
     CleanServerError(String),
diff --git a/crates/rooch-types/src/framework/transaction_validator.rs b/crates/rooch-types/src/framework/transaction_validator.rs
index a89edc0fce..671dae06c2 100644
--- a/crates/rooch-types/src/framework/transaction_validator.rs
+++ b/crates/rooch-types/src/framework/transaction_validator.rs
@@ -35,9 +35,7 @@ impl<'a> TransactionValidator<'a> {
             Self::function_id(Self::VALIDATE_FUNCTION_NAME),
             vec![],
             vec![
-                MoveValue::U64(auth.seqence_number)
-                    .simple_serialize()
-                    .unwrap(),
+                MoveValue::U64(auth.chain_id).simple_serialize().unwrap(),
                 MoveValue::U64(auth.authenticator.scheme)
                     .simple_serialize()
                     .unwrap(),
diff --git a/crates/rooch-types/src/lib.rs b/crates/rooch-types/src/lib.rs
index fa48c225c2..230dd69f18 100644
--- a/crates/rooch-types/src/lib.rs
+++ b/crates/rooch-types/src/lib.rs
@@ -6,6 +6,7 @@ pub mod address;
 pub mod addresses;
 pub mod authentication_key;
 pub mod block;
+pub mod chain_id;
 pub mod coin_id;
 pub mod crypto;
 pub mod error;
diff --git a/crates/rooch-types/src/transaction/ethereum.rs b/crates/rooch-types/src/transaction/ethereum.rs
index 267dce42d7..9c1b9e0c72 100644
--- a/crates/rooch-types/src/transaction/ethereum.rs
+++ b/crates/rooch-types/src/transaction/ethereum.rs
@@ -120,15 +120,12 @@ impl AbstractTransaction for EthereumTransaction {
         self.0.hash()
     }
 
-    fn authenticator_info(&self) -> AuthenticatorInfo {
-        AuthenticatorInfo {
-            //TODO should change the seqence_number to u256?
-            seqence_number: self.0.nonce.as_u64(),
-            authenticator: Authenticator::ecdsa_recoverable(
-                self.convert_eth_transaction_signature_to_rooch_signature()
-                    .unwrap(),
-            ),
-        }
+    fn authenticator_info(&self) -> Result {
+        let chain_id = self.0.chain_id.ok_or(RoochError::InvalidChainID)?.as_u64();
+        let authenticator = Authenticator::ecdsa_recoverable(
+            self.convert_eth_transaction_signature_to_rooch_signature()?,
+        );
+        Ok(AuthenticatorInfo::new(chain_id, authenticator))
     }
 
     fn construct_moveos_transaction(
@@ -136,7 +133,9 @@ impl AbstractTransaction for EthereumTransaction {
         resolved_sender: AccountAddress,
     ) -> Result {
         let action = self.decode_calldata_to_action()?;
-        let tx_ctx = TxContext::new(resolved_sender, self.tx_hash());
+        let sequence_number = self.0.nonce.as_u64();
+        let gas = self.0.gas.as_u64();
+        let tx_ctx = TxContext::new(resolved_sender, sequence_number, gas, self.tx_hash());
         Ok(MoveOSTransaction::new(tx_ctx, action))
     }
 
diff --git a/crates/rooch-types/src/transaction/mod.rs b/crates/rooch-types/src/transaction/mod.rs
index 8ad05e0112..927013c004 100644
--- a/crates/rooch-types/src/transaction/mod.rs
+++ b/crates/rooch-types/src/transaction/mod.rs
@@ -26,14 +26,14 @@ pub struct RawTransaction {
 
 #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
 pub struct AuthenticatorInfo {
-    pub seqence_number: u64,
+    pub chain_id: u64,
     pub authenticator: Authenticator,
 }
 
 impl AuthenticatorInfo {
-    pub fn new(seqence_number: u64, authenticator: Authenticator) -> Self {
+    pub fn new(chain_id: u64, authenticator: Authenticator) -> Self {
         Self {
-            seqence_number,
+            chain_id,
             authenticator,
         }
     }
@@ -61,7 +61,7 @@ pub trait AbstractTransaction {
 
     fn tx_hash(&self) -> H256;
 
-    fn authenticator_info(&self) -> AuthenticatorInfo;
+    fn authenticator_info(&self) -> Result;
 
     fn construct_moveos_transaction(
         self,
@@ -122,7 +122,7 @@ impl AbstractTransaction for TypedTransaction {
         }
     }
 
-    fn authenticator_info(&self) -> AuthenticatorInfo {
+    fn authenticator_info(&self) -> Result {
         match self {
             TypedTransaction::Rooch(tx) => tx.authenticator_info(),
             TypedTransaction::Ethereum(tx) => tx.authenticator_info(),
diff --git a/crates/rooch-types/src/transaction/rooch.rs b/crates/rooch-types/src/transaction/rooch.rs
index d8b5d9ca97..405ceb93c7 100644
--- a/crates/rooch-types/src/transaction/rooch.rs
+++ b/crates/rooch-types/src/transaction/rooch.rs
@@ -4,10 +4,11 @@
 use super::{
     authenticator::Authenticator, AbstractTransaction, AuthenticatorInfo, TransactionType,
 };
-use crate::address::RoochAddress;
 use crate::H256;
+use crate::{address::RoochAddress, chain_id::ChainID};
 use anyhow::Result;
 use move_core_types::account_address::AccountAddress;
+use moveos_types::gas_config::GasConfig;
 use moveos_types::{
     transaction::{MoveAction, MoveOSTransaction},
     tx_context::TxContext,
@@ -21,16 +22,37 @@ pub struct RoochTransactionData {
     pub sender: RoochAddress,
     // Sequence number of this transaction corresponding to sender's account.
     pub sequence_number: u64,
+    // The ChainID of the transaction.
+    pub chain_id: u64,
+    // The max gas to be used.
+    pub max_gas_amount: u64,
     // The MoveAction to execute.
     pub action: MoveAction,
-    //TODO how to define Gas paramter and AppID(Or ChainID)
 }
 
 impl RoochTransactionData {
-    pub fn new(sender: RoochAddress, sequence_number: u64, action: MoveAction) -> Self {
+    pub fn new(
+        sender: RoochAddress,
+        sequence_number: u64,
+        chain_id: u64,
+        max_gas_amount: u64,
+        action: MoveAction,
+    ) -> Self {
         Self {
             sender,
             sequence_number,
+            chain_id,
+            max_gas_amount,
+            action,
+        }
+    }
+
+    pub fn new_for_test(sender: RoochAddress, sequence_number: u64, action: MoveAction) -> Self {
+        Self {
+            sender,
+            sequence_number,
+            chain_id: ChainID::Dev as u64,
+            max_gas_amount: GasConfig::DEFAULT_MAX_GAS_AMOUNT,
             action,
         }
     }
@@ -66,6 +88,14 @@ impl RoochTransaction {
         self.data.sequence_number
     }
 
+    pub fn chain_id(&self) -> u64 {
+        self.data.chain_id
+    }
+
+    pub fn max_gas_amount(&self) -> u64 {
+        self.data.max_gas_amount
+    }
+
     pub fn action(&self) -> &MoveAction {
         &self.data.action
     }
@@ -91,7 +121,7 @@ impl RoochTransaction {
             vec![],
         );
 
-        let transaction_data = RoochTransactionData::new(sender, sequence_number, payload);
+        let transaction_data = RoochTransactionData::new_for_test(sender, sequence_number, payload);
         let mut rng = rand::thread_rng();
         let ed25519_keypair: Ed25519KeyPair = Ed25519KeyPair::generate(&mut rng);
         let auth =
@@ -103,7 +133,12 @@ impl RoochTransaction {
 impl From for MoveOSTransaction {
     fn from(tx: RoochTransaction) -> Self {
         let tx_hash = tx.tx_hash();
-        let tx_ctx = TxContext::new(tx.data.sender.into(), tx_hash);
+        let tx_ctx = TxContext::new(
+            tx.data.sender.into(),
+            tx.data.sequence_number,
+            tx.data.max_gas_amount,
+            tx_hash,
+        );
         MoveOSTransaction::new(tx_ctx, tx.data.action)
     }
 }
@@ -130,11 +165,11 @@ impl AbstractTransaction for RoochTransaction {
         self.data.hash()
     }
 
-    fn authenticator_info(&self) -> AuthenticatorInfo {
-        AuthenticatorInfo {
-            seqence_number: self.sequence_number(),
-            authenticator: self.authenticator.clone(),
-        }
+    fn authenticator_info(&self) -> Result {
+        Ok(AuthenticatorInfo::new(
+            self.chain_id(),
+            self.authenticator.clone(),
+        ))
     }
 
     fn construct_moveos_transaction(
diff --git a/crates/rooch/src/commands/env/commands/add.rs b/crates/rooch/src/commands/env/commands/add.rs
index ebc4eef877..2916fc3d31 100644
--- a/crates/rooch/src/commands/env/commands/add.rs
+++ b/crates/rooch/src/commands/env/commands/add.rs
@@ -13,6 +13,8 @@ pub struct AddCommand {
     #[clap(flatten)]
     pub context_options: WalletContextOptions,
     #[clap(long)]
+    pub chain_id: u64,
+    #[clap(long)]
     pub alias: String,
     #[clap(long, value_hint = ValueHint::Url)]
     pub rpc: String,
@@ -23,8 +25,15 @@ pub struct AddCommand {
 impl AddCommand {
     pub async fn execute(self) -> RoochResult<()> {
         let mut context = self.context_options.build().await?;
-        let AddCommand { alias, rpc, ws, .. } = self;
+        let AddCommand {
+            chain_id,
+            alias,
+            rpc,
+            ws,
+            ..
+        } = self;
         let env = Env {
+            chain_id,
             ws,
             rpc,
             alias: alias.clone(),
diff --git a/crates/rooch/src/commands/init.rs b/crates/rooch/src/commands/init.rs
index a816ac037b..f62206f4cd 100644
--- a/crates/rooch/src/commands/init.rs
+++ b/crates/rooch/src/commands/init.rs
@@ -10,6 +10,7 @@ use rooch_config::store_config::StoreConfig;
 use rooch_config::{rooch_config_dir, Config, ROOCH_CLIENT_CONFIG, ROOCH_KEYSTORE_FILENAME};
 use rooch_key::keystore::{AccountKeystore, FileBasedKeystore, Keystore};
 use rooch_rpc_client::client_config::{ClientConfig, Env};
+use rooch_types::chain_id::ChainID;
 use rooch_types::error::RoochError;
 use rooch_types::{crypto::BuiltinScheme, error::RoochResult};
 use std::fs;
@@ -40,6 +41,8 @@ impl CommandAction for Init {
         if !client_config_path.exists() {
             let env = match std::env::var_os("ROOCH_CONFIG_WITH_RPC_URL") {
                 Some(v) => Some(Env {
+                    //TODO get chain id from env
+                    chain_id: ChainID::Dev as u64,
                     alias: "custom".to_string(),
                     rpc: v.into_string().unwrap(),
                     ws: None,
@@ -74,7 +77,14 @@ impl CommandAction for Init {
                         } else {
                             alias
                         };
+                        print!("Environment ChainID for [{url}] : ");
+                        let chain_id = read_line()?;
+                        let chain_id = chain_id
+                            .trim()
+                            .parse::()
+                            .unwrap_or(ChainID::Dev as u64);
                         Env {
+                            chain_id,
                             alias,
                             rpc: url,
                             ws: None,
diff --git a/moveos/moveos-stdlib/moveos-stdlib/doc/README.md b/moveos/moveos-stdlib/moveos-stdlib/doc/README.md
index e41be0f086..b9d754eda9 100644
--- a/moveos/moveos-stdlib/moveos-stdlib/doc/README.md
+++ b/moveos/moveos-stdlib/moveos-stdlib/doc/README.md
@@ -31,6 +31,7 @@ This is the reference documentation of the MoveOS standard library.
 -  [`0x2::table`](table.md#0x2_table)
 -  [`0x2::tx_context`](tx_context.md#0x2_tx_context)
 -  [`0x2::tx_meta`](tx_meta.md#0x2_tx_meta)
+-  [`0x2::tx_result`](tx_result.md#0x2_tx_result)
 -  [`0x2::type_info`](type_info.md#0x2_type_info)
 -  [`0x2::type_table`](type_table.md#0x2_type_table)
 
diff --git a/moveos/moveos-stdlib/moveos-stdlib/doc/storage_context.md b/moveos/moveos-stdlib/moveos-stdlib/doc/storage_context.md
index b81e16f13a..ea2be2d4bf 100644
--- a/moveos/moveos-stdlib/moveos-stdlib/doc/storage_context.md
+++ b/moveos/moveos-stdlib/moveos-stdlib/doc/storage_context.md
@@ -14,12 +14,15 @@ and let developers can customize the storage
 -  [Function `object_storage`](#0x2_storage_context_object_storage)
 -  [Function `object_storage_mut`](#0x2_storage_context_object_storage_mut)
 -  [Function `sender`](#0x2_storage_context_sender)
+-  [Function `sequence_number`](#0x2_storage_context_sequence_number)
+-  [Function `max_gas_amount`](#0x2_storage_context_max_gas_amount)
 -  [Function `fresh_address`](#0x2_storage_context_fresh_address)
 -  [Function `fresh_object_id`](#0x2_storage_context_fresh_object_id)
 -  [Function `tx_hash`](#0x2_storage_context_tx_hash)
 -  [Function `add`](#0x2_storage_context_add)
 -  [Function `get`](#0x2_storage_context_get)
 -  [Function `tx_meta`](#0x2_storage_context_tx_meta)
+-  [Function `tx_result`](#0x2_storage_context_tx_result)
 
 
 
use 0x1::option;
@@ -27,6 +30,7 @@ and let developers can customize the storage
 use 0x2::object_storage;
 use 0x2::tx_context;
 use 0x2::tx_meta;
+use 0x2::tx_result;
 
@@ -193,6 +197,56 @@ Return the address of the user that signed the current transaction + + + + +## Function `sequence_number` + +Return the sequence number of the current transaction + + +
public fun sequence_number(self: &storage_context::StorageContext): u64
+
+ + + +
+Implementation + + +
public fun sequence_number(self: &StorageContext): u64 {
+    tx_context::sequence_number(&self.tx_context)
+}
+
+ + + +
+ + + +## Function `max_gas_amount` + +Return the maximum gas amount that can be used by the current transaction + + +
public fun max_gas_amount(self: &storage_context::StorageContext): u64
+
+ + + +
+Implementation + + +
public fun max_gas_amount(self: &StorageContext): u64 {
+    tx_context::max_gas_amount(&self.tx_context)
+}
+
+ + +
@@ -342,4 +396,28 @@ Get a value from the context map + + + + +## Function `tx_result` + + + +
public fun tx_result(self: &storage_context::StorageContext): tx_result::TxResult
+
+ + + +
+Implementation + + +
public fun tx_result(self: &StorageContext): TxResult {
+    tx_context::tx_result(&self.tx_context)
+}
+
+ + +
diff --git a/moveos/moveos-stdlib/moveos-stdlib/doc/tx_context.md b/moveos/moveos-stdlib/moveos-stdlib/doc/tx_context.md index 61f530dcdf..fdfa14972b 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/doc/tx_context.md +++ b/moveos/moveos-stdlib/moveos-stdlib/doc/tx_context.md @@ -8,6 +8,8 @@ - [Struct `TxContext`](#0x2_tx_context_TxContext) - [Constants](#@Constants_0) - [Function `sender`](#0x2_tx_context_sender) +- [Function `sequence_number`](#0x2_tx_context_sequence_number) +- [Function `max_gas_amount`](#0x2_tx_context_max_gas_amount) - [Function `fresh_address`](#0x2_tx_context_fresh_address) - [Function `fresh_object_id`](#0x2_tx_context_fresh_object_id) - [Function `derive_id`](#0x2_tx_context_derive_id) @@ -16,6 +18,7 @@ - [Function `get`](#0x2_tx_context_get) - [Function `contains`](#0x2_tx_context_contains) - [Function `tx_meta`](#0x2_tx_context_tx_meta) +- [Function `tx_result`](#0x2_tx_context_tx_result)
use 0x1::error;
@@ -28,6 +31,7 @@
 use 0x2::object_id;
 use 0x2::simple_map;
 use 0x2::tx_meta;
+use 0x2::tx_result;
 use 0x2::type_info;
 
@@ -57,6 +61,18 @@ the VM and passed in to the entrypoint of the transaction as &mut <
The address of the user that signed the current transaction +
+
+sequence_number: u64 +
+
+ Sequence number of this transaction corresponding to sender's account. +
+
+max_gas_amount: u64 +
+
+
tx_hash: vector<u8> @@ -120,6 +136,56 @@ transaction + + + + +## Function `sequence_number` + +Return the sequence number of the current transaction + + +
public fun sequence_number(self: &tx_context::TxContext): u64
+
+ + + +
+Implementation + + +
public fun sequence_number(self: &TxContext): u64 {
+    self.sequence_number
+}
+
+ + + +
+ + + +## Function `max_gas_amount` + +Return the max gas to be used + + +
public fun max_gas_amount(self: &tx_context::TxContext): u64
+
+ + + +
+Implementation + + +
public fun max_gas_amount(self: &TxContext): u64 {
+    self.max_gas_amount
+}
+
+ + +
@@ -338,4 +404,31 @@ The meta data is only available when executing or validating a transaction, othe + + + + +## Function `tx_result` + +The result is only available in the post_execute function. + + +
public fun tx_result(self: &tx_context::TxContext): tx_result::TxResult
+
+ + + +
+Implementation + + +
public fun tx_result(self: &TxContext): TxResult {
+    let result = get<TxResult>(self);
+    assert!(option::is_some(&result), error::invalid_state(EInvalidContext));
+    option::extract(&mut result)
+}
+
+ + +
diff --git a/moveos/moveos-stdlib/moveos-stdlib/doc/tx_result.md b/moveos/moveos-stdlib/moveos-stdlib/doc/tx_result.md new file mode 100644 index 0000000000..be355d89ed --- /dev/null +++ b/moveos/moveos-stdlib/moveos-stdlib/doc/tx_result.md @@ -0,0 +1,99 @@ + + + +# Module `0x2::tx_result` + + + +- [Struct `TxResult`](#0x2_tx_result_TxResult) +- [Function `is_executed`](#0x2_tx_result_is_executed) +- [Function `gas_used`](#0x2_tx_result_gas_used) + + +
+ + + + + +## Struct `TxResult` + +The result of a transaction. +The VM will put this struct in the TxContext after the transaction execution. +We can get the result in the post_execute function. + + +
struct TxResult has copy, drop, store
+
+ + + +
+Fields + + +
+
+executed: bool +
+
+ The transaction is executed successfully or not. +
+
+gas_used: u64 +
+
+ The gas used by the transaction. +
+
+ + +
+ + + +## Function `is_executed` + + + +
public fun is_executed(self: &tx_result::TxResult): bool
+
+ + + +
+Implementation + + +
public fun is_executed(self: &TxResult) : bool {
+    self.executed
+}
+
+ + + +
+ + + +## Function `gas_used` + + + +
public fun gas_used(self: &tx_result::TxResult): u64
+
+ + + +
+Implementation + + +
public fun gas_used(self: &TxResult) : u64 {
+    self.gas_used
+}
+
+ + + +
diff --git a/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move b/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move index 5106396ea4..0a9bdb385d 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move @@ -9,6 +9,7 @@ module moveos_std::storage_context { use moveos_std::tx_context::{Self, TxContext}; use moveos_std::object_id::{ObjectID}; use moveos_std::tx_meta::{TxMeta}; + use moveos_std::tx_result::{TxResult}; #[test_only] use moveos_std::object_storage::{Self}; @@ -53,6 +54,16 @@ module moveos_std::storage_context { tx_context::sender(&self.tx_context) } + /// Return the sequence number of the current transaction + public fun sequence_number(self: &StorageContext): u64 { + tx_context::sequence_number(&self.tx_context) + } + + /// Return the maximum gas amount that can be used by the current transaction + public fun max_gas_amount(self: &StorageContext): u64 { + tx_context::max_gas_amount(&self.tx_context) + } + /// Generate a new unique address public fun fresh_address(self: &mut StorageContext): address { tx_context::fresh_address(&mut self.tx_context) @@ -82,6 +93,10 @@ module moveos_std::storage_context { tx_context::tx_meta(&self.tx_context) } + public fun tx_result(self: &StorageContext): TxResult { + tx_context::tx_result(&self.tx_context) + } + #[test_only] /// Create a StorageContext for unit test public fun new_test_context(sender: address): StorageContext { diff --git a/moveos/moveos-stdlib/moveos-stdlib/sources/tx_context.move b/moveos/moveos-stdlib/moveos-stdlib/sources/tx_context.move index 4638695602..629703ffdb 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/sources/tx_context.move +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/tx_context.move @@ -13,6 +13,7 @@ module moveos_std::tx_context { use moveos_std::copyable_any::{Self, Any}; use moveos_std::type_info; use moveos_std::tx_meta::{TxMeta}; + use moveos_std::tx_result::{TxResult}; friend moveos_std::object; friend moveos_std::raw_table; @@ -27,6 +28,10 @@ module moveos_std::tx_context { struct TxContext has drop { /// The address of the user that signed the current transaction sender: address, + /// Sequence number of this transaction corresponding to sender's account. + sequence_number: u64, + // The max gas to be used. + max_gas_amount: u64, /// Hash of the current transaction tx_hash: vector, /// Counter recording the number of fresh id's created while executing @@ -40,6 +45,16 @@ module moveos_std::tx_context { /// transaction public fun sender(self: &TxContext): address { self.sender + } + + /// Return the sequence number of the current transaction + public fun sequence_number(self: &TxContext): u64 { + self.sequence_number + } + + /// Return the max gas to be used + public fun max_gas_amount(self: &TxContext): u64 { + self.max_gas_amount } /// Generate a new unique address, @@ -106,12 +121,21 @@ module moveos_std::tx_context { option::extract(&mut meta) } + /// The result is only available in the `post_execute` function. + public fun tx_result(self: &TxContext): TxResult { + let result = get(self); + assert!(option::is_some(&result), error::invalid_state(EInvalidContext)); + option::extract(&mut result) + } + #[test_only] /// Create a TxContext for unit test public fun new_test_context(sender: address): TxContext { let tx_hash = hash::sha3_256(b"test_tx"); TxContext { sender, + sequence_number: 0, + max_gas_amount: 100000000, tx_hash, ids_created: 0, map: simple_map::create(), diff --git a/moveos/moveos-stdlib/moveos-stdlib/sources/tx_result.move b/moveos/moveos-stdlib/moveos-stdlib/sources/tx_result.move new file mode 100644 index 0000000000..c014a8c50f --- /dev/null +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/tx_result.move @@ -0,0 +1,21 @@ +module moveos_std::tx_result { + + /// The result of a transaction. + /// The VM will put this struct in the TxContext after the transaction execution. + /// We can get the result in the `post_execute` function. + struct TxResult has copy, store, drop { + /// The transaction is executed successfully or not. + executed: bool, + /// The gas used by the transaction. + gas_used: u64, + } + + public fun is_executed(self: &TxResult) : bool { + self.executed + } + + public fun gas_used(self: &TxResult) : u64 { + self.gas_used + } + +} \ No newline at end of file diff --git a/moveos/moveos-types/src/gas_config.rs b/moveos/moveos-types/src/gas_config.rs new file mode 100644 index 0000000000..bacca2c74f --- /dev/null +++ b/moveos/moveos-types/src/gas_config.rs @@ -0,0 +1,11 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +//TODO this config should be an on-chain config +pub struct GasConfig { + pub max_gas_amount: u64, +} + +impl GasConfig { + pub const DEFAULT_MAX_GAS_AMOUNT: u64 = 100000000u64; +} diff --git a/moveos/moveos-types/src/lib.rs b/moveos/moveos-types/src/lib.rs index 1295069bc0..f1a7ac1fb3 100644 --- a/moveos/moveos-types/src/lib.rs +++ b/moveos/moveos-types/src/lib.rs @@ -6,6 +6,7 @@ pub mod addresses; pub mod event; pub mod event_filter; pub mod function_return_value; +pub mod gas_config; pub mod h256; pub mod module_binding; pub mod move_any; diff --git a/moveos/moveos-types/src/moveos_std/mod.rs b/moveos/moveos-types/src/moveos_std/mod.rs index dbce8c84db..edda7347aa 100644 --- a/moveos/moveos-types/src/moveos_std/mod.rs +++ b/moveos/moveos-types/src/moveos_std/mod.rs @@ -4,4 +4,5 @@ /// This mod contains all the Rust to Move type mapping that are used in MoveosStd pub mod module_upgrade_flag; pub mod tx_meta; +pub mod tx_result; pub mod type_info; diff --git a/moveos/moveos-types/src/moveos_std/tx_result.rs b/moveos/moveos-types/src/moveos_std/tx_result.rs new file mode 100644 index 0000000000..e3ca5b4f41 --- /dev/null +++ b/moveos/moveos-types/src/moveos_std/tx_result.rs @@ -0,0 +1,43 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + addresses::MOVEOS_STD_ADDRESS, + state::{MoveStructState, MoveStructType}, +}; +use move_core_types::{ + account_address::AccountAddress, ident_str, identifier::IdentStr, vm_status::KeptVMStatus, +}; +use serde::{Deserialize, Serialize}; + +pub const MODULE_NAME: &IdentStr = ident_str!("tx_result"); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TxResult { + pub executed: bool, + pub gas_used: u64, +} + +impl MoveStructType for TxResult { + const ADDRESS: AccountAddress = MOVEOS_STD_ADDRESS; + const MODULE_NAME: &'static IdentStr = MODULE_NAME; + const STRUCT_NAME: &'static IdentStr = ident_str!("TxResult"); +} + +impl MoveStructState for TxResult { + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + move_core_types::value::MoveTypeLayout::Bool, + move_core_types::value::MoveTypeLayout::U64, + ]) + } +} + +impl TxResult { + pub fn new(status: &KeptVMStatus, gas_used: u64) -> Self { + Self { + executed: matches!(status, KeptVMStatus::Executed), + gas_used, + } + } +} diff --git a/moveos/moveos-types/src/transaction.rs b/moveos/moveos-types/src/transaction.rs index faa834d27e..760f9d5ae9 100644 --- a/moveos/moveos-types/src/transaction.rs +++ b/moveos/moveos-types/src/transaction.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - event::Event, h256, h256::H256, move_types::FunctionId, moveos_std::tx_meta::TxMeta, - state::StateChangeSet, tx_context::TxContext, + event::Event, gas_config::GasConfig, h256, h256::H256, move_types::FunctionId, + moveos_std::tx_meta::TxMeta, state::StateChangeSet, tx_context::TxContext, }; use move_core_types::{ account_address::AccountAddress, @@ -199,7 +199,13 @@ impl MoveOSTransaction { pub fn new_for_test(sender: AccountAddress, action: MoveAction) -> Self { let sender_and_action = (sender, action); let tx_hash = h256::sha3_256_of(bcs::to_bytes(&sender_and_action).unwrap().as_slice()); - let ctx = TxContext::new(sender_and_action.0, tx_hash); + //TODO pass the sequence_number + let ctx = TxContext::new( + sender_and_action.0, + 0, + GasConfig::DEFAULT_MAX_GAS_AMOUNT, + tx_hash, + ); Self::new(ctx, sender_and_action.1) } diff --git a/moveos/moveos-types/src/tx_context.rs b/moveos/moveos-types/src/tx_context.rs index 7512dd7bd6..4009d76c15 100644 --- a/moveos/moveos-types/src/tx_context.rs +++ b/moveos/moveos-types/src/tx_context.rs @@ -4,6 +4,7 @@ //Source origin from https://github.com/MystenLabs/sui/blob/598f106ef5fbdfbe1b644236f0caf46c94f4d1b7/crates/sui-types/src/base_types.rs use crate::addresses::MOVEOS_STD_ADDRESS; +use crate::gas_config::GasConfig; use crate::h256::{self, H256}; use crate::move_any::{AnyTrait, CopyableAny}; use crate::move_simple_map::SimpleMap; @@ -23,6 +24,10 @@ pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext"); pub struct TxContext { /// Signer/sender of the transaction pub sender: AccountAddress, + /// Sequence number of this transaction corresponding to sender's account. + pub sequence_number: u64, + // The max gas to be used. + pub max_gas_amount: u64, /// Hash of the current transaction /// Use the type `Vec` is to keep consistency with the `TxContext` type in Move pub tx_hash: Vec, @@ -36,7 +41,9 @@ impl std::fmt::Debug for TxContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TxContext") .field("sender", &self.sender) - .field("tx_hash", &hex::encode(&self.tx_hash)) + .field("sequence_number", &self.sequence_number) + .field("max_gas_amount", &self.max_gas_amount) + .field("tx_hash", &self.tx_hash) .field("ids_created", &self.ids_created) .field("map", &self.map) .finish() @@ -44,9 +51,16 @@ impl std::fmt::Debug for TxContext { } impl TxContext { - pub fn new(sender: AccountAddress, tx_hash: H256) -> Self { + pub fn new( + sender: AccountAddress, + sequence_number: u64, + max_gas_amount: u64, + tx_hash: H256, + ) -> Self { Self { sender, + sequence_number, + max_gas_amount, tx_hash: tx_hash.0.to_vec(), ids_created: 0, map: SimpleMap::create(), @@ -55,13 +69,16 @@ impl TxContext { /// Create a new TxContext with a zero tx_hash for read-only function call cases pub fn new_readonly_ctx(sender: AccountAddress) -> Self { - Self::new(sender, H256::zero()) + //TODO define read-only function gas limit + Self::new(sender, 0, GasConfig::DEFAULT_MAX_GAS_AMOUNT, H256::zero()) } /// Spawn a new TxContext with a new `ids_created` counter and empty map pub fn spawn(self) -> Self { Self { sender: self.sender, + sequence_number: self.sequence_number, + max_gas_amount: self.max_gas_amount, tx_hash: self.tx_hash, ids_created: 0, map: SimpleMap::create(), @@ -73,6 +90,8 @@ impl TxContext { pub fn zero() -> Self { Self { sender: AccountAddress::ZERO, + sequence_number: 0, + max_gas_amount: GasConfig::DEFAULT_MAX_GAS_AMOUNT, tx_hash: vec![0u8; h256::LENGTH], ids_created: 0, map: SimpleMap::create(), @@ -102,7 +121,12 @@ impl TxContext { // for testing pub fn random_for_testing_only() -> Self { - Self::new(AccountAddress::random(), H256::random()) + Self::new( + AccountAddress::random(), + 0, + GasConfig::DEFAULT_MAX_GAS_AMOUNT, + H256::random(), + ) } pub fn add(&mut self, value: T) -> Result<()> { @@ -137,6 +161,8 @@ impl MoveStructState for TxContext { fn struct_layout() -> MoveStructLayout { MoveStructLayout::new(vec![ MoveTypeLayout::Address, + MoveTypeLayout::U64, + MoveTypeLayout::U64, MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), MoveTypeLayout::U64, MoveTypeLayout::Struct(SimpleMap::::struct_layout()), diff --git a/moveos/moveos/src/gas/mod.rs b/moveos/moveos/src/gas/mod.rs index d6e4955763..ed315cd79f 100644 --- a/moveos/moveos/src/gas/mod.rs +++ b/moveos/moveos/src/gas/mod.rs @@ -1,4 +1,22 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 +use move_vm_types::gas::{GasMeter, UnmeteredGasMeter}; + pub mod table; + +pub trait SwitchableGasMeter: GasMeter { + fn stop_metering(&mut self); + fn start_metering(&mut self); + fn is_metering(&self) -> bool; +} + +impl SwitchableGasMeter for UnmeteredGasMeter { + fn stop_metering(&mut self) {} + + fn start_metering(&mut self) {} + + fn is_metering(&self) -> bool { + false + } +} diff --git a/moveos/moveos/src/gas/table.rs b/moveos/moveos/src/gas/table.rs index 64f027ac29..34d39462b8 100644 --- a/moveos/moveos/src/gas/table.rs +++ b/moveos/moveos/src/gas/table.rs @@ -11,6 +11,8 @@ use move_vm_types::views::{TypeView, ValueView}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +use super::SwitchableGasMeter; + /// The size in bytes for a reference on the stack pub const REFERENCE_SIZE: AbstractMemorySize = AbstractMemorySize::new(8); @@ -33,6 +35,7 @@ pub struct MoveOSGasMeter { pub gas_model_version: u64, cost_table: CostTable, gas_left: InternalGas, + //TODO we do not need to use gas_price in gas meter. gas_price: u64, initial_budget: InternalGas, charge: bool, @@ -40,12 +43,12 @@ pub struct MoveOSGasMeter { impl Default for MoveOSGasMeter { fn default() -> Self { - Self::new() + Self::new_for_test() } } impl MoveOSGasMeter { - pub fn new() -> Self { + pub fn new_for_test() -> Self { Self { gas_model_version: 0, cost_table: CostTable { @@ -53,10 +56,25 @@ impl MoveOSGasMeter { stack_height_tiers: Default::default(), stack_size_tiers: Default::default(), }, - gas_left: InternalGas::zero(), + gas_left: InternalGas::new(u64::max_value()), gas_price: 0, - initial_budget: InternalGas::new(10000000), - charge: false, + initial_budget: InternalGas::new(u64::max_value()), + charge: true, + } + } + + pub fn new(max_gas_amount: u64) -> Self { + Self { + gas_model_version: 0, + cost_table: CostTable { + instruction_tiers: Default::default(), + stack_height_tiers: Default::default(), + stack_size_tiers: Default::default(), + }, + gas_left: InternalGas::new(max_gas_amount), + gas_price: 0, + initial_budget: InternalGas::new(max_gas_amount), + charge: true, } } @@ -69,6 +87,7 @@ impl MoveOSGasMeter { _decr_size: u64, ) -> PartialVMResult<()> { // #TODO: Various resources are used to charge for the execution of an instruction. + self.deduct_gas(InternalGas::new(1))?; Ok(()) } @@ -139,7 +158,7 @@ fn get_simple_instruction_stack_change( impl GasMeter for MoveOSGasMeter { fn balance_internal(&self) -> InternalGas { - InternalGas::new(1000000) + self.gas_left } fn charge_simple_instr(&mut self, instr: SimpleInstruction) -> PartialVMResult<()> { @@ -460,3 +479,17 @@ impl GasMeter for MoveOSGasMeter { Ok(()) } } + +impl SwitchableGasMeter for MoveOSGasMeter { + fn stop_metering(&mut self) { + self.charge = false; + } + + fn start_metering(&mut self) { + self.charge = true; + } + + fn is_metering(&self) -> bool { + self.charge + } +} diff --git a/moveos/moveos/src/moveos.rs b/moveos/moveos/src/moveos.rs index 36fe7648f8..4c69024d86 100644 --- a/moveos/moveos/src/moveos.rs +++ b/moveos/moveos/src/moveos.rs @@ -132,7 +132,7 @@ impl MoveOS { post_execute_functions, } = tx; - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new_for_test(); let session = self .vm .new_readonly_session(&self.db, ctx.clone(), gas_meter); @@ -163,7 +163,7 @@ impl MoveOS { action ); } - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new(ctx.max_gas_amount); let mut session = self.vm.new_session( &self.db, ctx, @@ -243,7 +243,7 @@ impl MoveOS { function_call: FunctionCall, ) -> FunctionResult { //TODO limit the view function max gas usage - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new_for_test(); let mut session = self .vm .new_readonly_session(&self.db, tx_context.clone(), gas_meter); diff --git a/moveos/moveos/src/vm/moveos_vm.rs b/moveos/moveos/src/vm/moveos_vm.rs index 9cff95ee20..4c07b37d3e 100644 --- a/moveos/moveos/src/vm/moveos_vm.rs +++ b/moveos/moveos/src/vm/moveos_vm.rs @@ -12,7 +12,7 @@ use move_binary_format::{ CompiledModule, }; -use crate::gas::table::MoveOSGasMeter; +use crate::gas::{table::MoveOSGasMeter, SwitchableGasMeter}; use move_core_types::{ account_address::AccountAddress, identifier::Identifier, @@ -29,7 +29,6 @@ use move_vm_runtime::{ }; use move_vm_types::{ data_store::DataStore, - gas::GasMeter, loaded_data::runtime_types::{CachedStructIndex, StructType, Type}, }; use moveos_stdlib::natives::moveos_stdlib::{ @@ -39,8 +38,9 @@ use moveos_stdlib::natives::moveos_stdlib::{ use moveos_types::{ event::{Event, EventID}, function_return_value::FunctionReturnValue, + gas_config::GasConfig, move_types::FunctionId, - moveos_std::module_upgrade_flag::ModuleUpgradeFlag, + moveos_std::{module_upgrade_flag::ModuleUpgradeFlag, tx_result::TxResult}, object::ObjectID, state_resolver::MoveOSResolver, storage_context::StorageContext, @@ -66,7 +66,7 @@ impl MoveOSVM { }) } - pub fn new_session<'r, S: MoveOSResolver, G: GasMeter>( + pub fn new_session<'r, S: MoveOSResolver, G: SwitchableGasMeter>( &self, remote: &'r S, ctx: TxContext, @@ -90,13 +90,12 @@ impl MoveOSVM { remote: &'r S, ctx: TxContext, ) -> MoveOSSession<'r, '_, S, MoveOSGasMeter> { - //Do not charge gas for genesis session - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new(GasConfig::DEFAULT_MAX_GAS_AMOUNT); // Genesis session do not need to execute pre_execute and post_execute function MoveOSSession::new(&self.inner, remote, ctx, vec![], vec![], gas_meter, false) } - pub fn new_readonly_session<'r, S: MoveOSResolver, G: GasMeter>( + pub fn new_readonly_session<'r, S: MoveOSResolver, G: SwitchableGasMeter>( &self, remote: &'r S, ctx: TxContext, @@ -124,7 +123,7 @@ pub struct MoveOSSession<'r, 'l, S, G> { impl<'r, 'l, S, G> MoveOSSession<'r, 'l, S, G> where S: MoveOSResolver, - G: GasMeter, + G: SwitchableGasMeter, { pub fn new( vm: &'l MoveVM, @@ -450,7 +449,7 @@ where self, vm_status: VMStatus, ) -> VMResult<(TxContext, TransactionOutput)> { - let (finalized_session, status) = match vm_status.keep_or_discard() { + let (finalized_session, status, gas_used) = match vm_status.keep_or_discard() { Ok(status) => self.post_execute(status), Err(discard_status) => { //This should not happen, if it happens, it means that the VM or verifer has a bug @@ -511,8 +510,7 @@ where Event::new(EventID::new(event_handle_id, e.1), e.2, e.3, i as u64) }) .collect(); - //TODO calculate the gas_used with gas_meter - let gas_used = 0; + Ok(( ctx.tx_context, TransactionOutput { @@ -529,6 +527,9 @@ where // the read_only function should not execute pre_execute function // this ensure via the check in new_session let mut pre_execute_session = self; + // we do not charge gas for pre_execute function + // TODO should we charge gas for custom authencation validator? + pre_execute_session.gas_meter.stop_metering(); for function_call in pre_execute_session.pre_execute_functions.clone() { let pre_execute_function_id = function_call.function_id.clone(); let result = pre_execute_session.execute_function_bypass_visibility(function_call); @@ -543,12 +544,14 @@ where panic!("pre_execute function should success") } } + pre_execute_session.gas_meter.start_metering(); pre_execute_session } - fn post_execute(self, execute_status: KeptVMStatus) -> (Self, KeptVMStatus) { + fn post_execute(self, execute_status: KeptVMStatus) -> (Self, KeptVMStatus, u64) { if self.read_only { - (self, execute_status) + //TODO calculate readonly function gas usage + (self, execute_status, 0) } else { let mut post_execute_session = match &execute_status { KeptVMStatus::Executed => self, @@ -558,13 +561,28 @@ where self.respawn() } }; + let max_gas_amount = post_execute_session.ctx.tx_context.max_gas_amount; + let gas_left: u64 = post_execute_session.gas_meter.balance_internal().into(); + let gas_used = max_gas_amount.checked_sub(gas_left).unwrap_or_else(|| panic!("gas_left({gas_left}) should always be less than or equal to max gas amount({max_gas_amount})")); + + // we do not charge gas for post_execute function + post_execute_session.gas_meter.stop_metering(); + + //TODO is it a good approach to add tx_result to TxContext? + let tx_result = TxResult::new(&execute_status, gas_used); + post_execute_session + .ctx + .tx_context + .add(tx_result) + .expect("Add tx_result to TxContext should always success"); + for function_call in post_execute_session.post_execute_functions.clone() { //TODO handle post_execute function error post_execute_session .execute_function_bypass_visibility(function_call) .expect("post_execute function should always success"); } - (post_execute_session, execute_status) + (post_execute_session, execute_status, gas_used) } } diff --git a/moveos/moveos/src/vm/unit_tests/vm_arguments_tests.rs b/moveos/moveos/src/vm/unit_tests/vm_arguments_tests.rs index f25403bf84..908cf52036 100644 --- a/moveos/moveos/src/vm/unit_tests/vm_arguments_tests.rs +++ b/moveos/moveos/src/vm/unit_tests/vm_arguments_tests.rs @@ -317,7 +317,7 @@ fn call_script_with_args_ty_args_signers( ) -> VMResult<()> { let moveos_vm = MoveOSVM::new(vec![], VMConfig::default()).unwrap(); let remote_view = RemoteStore::new(); - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new_for_test(); let ctx = TxContext::random_for_testing_only(); let mut session = moveos_vm.new_session(&remote_view, ctx, vec![], vec![], gas_meter); @@ -345,7 +345,7 @@ fn call_script_function_with_args_ty_args_signers( let mut remote_view = RemoteStore::new(); let id = module.self_id(); remote_view.add_module(module); - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new_for_test(); let ctx = TxContext::random_for_testing_only(); let mut session: crate::vm::moveos_vm::MoveOSSession<'_, '_, RemoteStore, MoveOSGasMeter> = moveos_vm.new_session(&remote_view, ctx, vec![], vec![], gas_meter); @@ -828,7 +828,7 @@ fn call_missing_item() { // mising module let moveos_vm = MoveOSVM::new(vec![], VMConfig::default()).unwrap(); let mut remote_view = RemoteStore::new(); - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new_for_test(); let ctx = TxContext::random_for_testing_only(); let mut session = moveos_vm.new_session(&remote_view, ctx.clone(), vec![], vec![], gas_meter); let func_call = FunctionCall::new( @@ -846,7 +846,7 @@ fn call_missing_item() { // missing function remote_view.add_module(module); - let gas_meter = MoveOSGasMeter::new(); + let gas_meter = MoveOSGasMeter::new_for_test(); let mut session = moveos_vm.new_session(&remote_view, ctx, vec![], vec![], gas_meter); let error = session .execute_function_bypass_visibility(func_call)