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
+
+
+
+
+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
+}
+
+
+
+
+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)