diff --git a/crates/rooch-framework-tests/src/binding_test.rs b/crates/rooch-framework-tests/src/binding_test.rs index ccadf979a4..79b2cc8394 100644 --- a/crates/rooch-framework-tests/src/binding_test.rs +++ b/crates/rooch-framework-tests/src/binding_test.rs @@ -5,7 +5,7 @@ use anyhow::{bail, Result}; use move_core_types::vm_status::KeptVMStatus; use moveos_store::MoveOSStore; use moveos_types::module_binding::{ModuleBinding, MoveFunctionCaller}; -use rooch_executor::actor::executor::ExecutorActor; +use rooch_executor::actor::{executor::ExecutorActor, messages::ExecuteTransactionResult}; use rooch_store::RoochStore; use rooch_types::transaction::AbstractTransaction; @@ -27,8 +27,7 @@ impl RustBindingTest { //TODO let the module bundle to execute the function pub fn execute(&mut self, tx: T) -> Result<()> { - let verified_tx = self.executor.validate(tx)?; - let execute_result = self.executor.execute(verified_tx)?; + let execute_result = self.execute_as_result(tx)?; if execute_result.transaction_info.status != KeptVMStatus::Executed { bail!( "tx should success, error: {:?}", @@ -37,4 +36,12 @@ impl RustBindingTest { } Ok(()) } + + pub fn execute_as_result( + &mut self, + tx: T, + ) -> Result { + let verified_tx = self.executor.validate(tx)?; + self.executor.execute(verified_tx) + } } 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 1c14096993..8b23dd3a4c 100644 --- a/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs @@ -1,6 +1,11 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 +use move_core_types::account_address::AccountAddress; +use move_core_types::ident_str; +use move_core_types::language_storage::ModuleId; +use move_core_types::value::MoveValue; +use moveos_types::move_types::FunctionId; use moveos_types::{module_binding::ModuleBinding, transaction::MoveAction}; use rooch_key::keystore::{AccountKeystore, InMemKeystore}; use rooch_types::{addresses::ROOCH_FRAMEWORK_ADDRESS, framework::empty::Empty}; @@ -156,5 +161,40 @@ fn test_session_key_ed25519() { binding_test.execute(tx).unwrap(); - // TODO test the session key call function is out the scope. + // test the session key call function is out the scope. + + let action = MoveAction::new_function_call( + FunctionId::new( + ModuleId::new(ROOCH_FRAMEWORK_ADDRESS, ident_str!("account").to_owned()), + ident_str!("create_account_entry").to_owned(), + ), + vec![], + vec![MoveValue::Address(AccountAddress::random()) + .simple_serialize() + .unwrap()], + ); + let tx_data = RoochTransactionData::new(sender, sequence_number + 2, action); + let tx = keystore + .sign_transaction_via_session_key(&sender, tx_data, &session_auth_key) + .unwrap(); + + // the session key is not in the scope of account module, so the transaction should be rejected when validate. + // TODO Get the validate VMStatus and check the error code. + let execute_result = binding_test.execute_as_result(tx); + assert!(execute_result.is_err(), "expect move abort"); + //let result = binding_test.execute_as_result(tx).unwrap(); + // match result.transaction_info.status { + // KeptVMStatus::MoveAbort(l, code) => { + // match l{ + // AbortLocation::Module(module_id) => { + // assert_eq!(module_id, ModuleId::new(ROOCH_FRAMEWORK_ADDRESS, ident_str!("session_key").to_owned()), "expect session key module"); + // } + // _ => panic!("expect move abort in module"), + // } + // let (_category, reason) = error::explain(code); + // // EFunctionCallBeyoundSessionScope = 5 + // assert_eq!(reason, 5, "expect EFunctionCallBeyoundSessionScope"); + // } + // _ => panic!("expect move abort"), + // } } diff --git a/crates/rooch-framework/doc/session_key.md b/crates/rooch-framework/doc/session_key.md index 1eca342442..5de528416f 100644 --- a/crates/rooch-framework/doc/session_key.md +++ b/crates/rooch-framework/doc/session_key.md @@ -9,7 +9,7 @@ - [Struct `SessionKey`](#0x3_session_key_SessionKey) - [Resource `SessionKeys`](#0x3_session_key_SessionKeys) - [Constants](#@Constants_0) -- [Function `is_expired`](#0x3_session_key_is_expired) +- [Function `new_session_scope`](#0x3_session_key_new_session_scope) - [Function `exists_session_key`](#0x3_session_key_exists_session_key) - [Function `get_session_key`](#0x3_session_key_get_session_key) - [Function `create_session_key`](#0x3_session_key_create_session_key) @@ -27,6 +27,7 @@ use 0x2::storage_context; use 0x2::table; use 0x2::tx_context; +use 0x2::tx_meta; use 0x3::auth_validator; use 0x3::ed25519; use 0x3::native_validator; @@ -157,8 +158,19 @@ The session's scope ## Constants + + +The function call is beyond the session's scope + + +
const EFunctionCallBeyoundSessionScope: u64 = 5;
+
+ + + +The session is expired
const ESessionIsExpired: u64 = 4;
@@ -168,6 +180,7 @@ The session's scope
 
 
 
+The session key already exists
 
 
 
const ESessionKeyAlreadyExists: u64 = 2;
@@ -177,6 +190,7 @@ The session's scope
 
 
 
+Create session key in this context is not allowed
 
 
 
const ESessionKeyCreatePermissionDenied: u64 = 1;
@@ -186,6 +200,7 @@ The session's scope
 
 
 
+The session key is invalid
 
 
 
const ESessionKeyIsInvalid: u64 = 3;
@@ -193,13 +208,13 @@ The session's scope
 
 
 
-
+
 
-## Function `is_expired`
+## Function `new_session_scope`
 
 
 
-
public fun is_expired(_ctx: &storage_context::StorageContext, _session_key: &session_key::SessionKey): bool
+
public fun new_session_scope(module_address: address, module_name: ascii::String, function_name: ascii::String): session_key::SessionScope
 
@@ -208,9 +223,12 @@ The session's scope Implementation -
public fun is_expired(_ctx: &StorageContext, _session_key: &SessionKey) : bool {
-    //TODO check the session key is expired or not after the timestamp is supported
-    return false
+
public fun new_session_scope(module_address: address, module_name: std::ascii::String, function_name: std::ascii::String) : SessionScope {
+    SessionScope {
+        module_address: module_address,
+        module_name: module_name,
+        function_name: function_name,
+    }
 }
 
@@ -383,14 +401,9 @@ If the session key is expired or invalid, abort the tx, otherwise return option: let session_key = option::extract(&mut session_key_option); assert!(!is_expired(ctx, &session_key), error::permission_denied(ESessionIsExpired)); - //TODO validate session scopes + assert!(in_session_scope(ctx, &session_key), error::permission_denied(EFunctionCallBeyoundSessionScope)); - if(scheme == ed25519::scheme()){ - validator::validate_signature(&authenticator_payload, &storage_context::tx_hash(ctx)); - }else{ - //TODO support other built-in validators - abort 1 - }; + validator::validate_signature(&authenticator_payload, &storage_context::tx_hash(ctx)); option::some(auth_key) }
diff --git a/crates/rooch-framework/error_description.errmap b/crates/rooch-framework/error_description.errmap index d19fe8eec3..d32f47945b 100644 Binary files a/crates/rooch-framework/error_description.errmap and b/crates/rooch-framework/error_description.errmap differ diff --git a/crates/rooch-framework/sources/empty.move b/crates/rooch-framework/sources/empty.move index 380b08faf2..5c01e95c46 100644 --- a/crates/rooch-framework/sources/empty.move +++ b/crates/rooch-framework/sources/empty.move @@ -6,4 +6,5 @@ module rooch_framework::empty{ public entry fun empty(){ //Just do nothing } + } \ No newline at end of file diff --git a/crates/rooch-framework/sources/session_key.move b/crates/rooch-framework/sources/session_key.move index aeb4391356..c0e60e695e 100644 --- a/crates/rooch-framework/sources/session_key.move +++ b/crates/rooch-framework/sources/session_key.move @@ -6,16 +6,23 @@ module rooch_framework::session_key { use moveos_std::storage_context::{Self, StorageContext}; use moveos_std::account_storage; use moveos_std::table::{Self, Table}; + use moveos_std::tx_meta::{Self, FunctionCallMeta}; use rooch_framework::auth_validator; use rooch_framework::native_validator::{Self as validator}; use rooch_framework::ed25519; friend rooch_framework::transaction_validator; + /// Create session key in this context is not allowed const ESessionKeyCreatePermissionDenied: u64 = 1; + /// The session key already exists const ESessionKeyAlreadyExists: u64 = 2; + /// The session key is invalid const ESessionKeyIsInvalid: u64 = 3; + /// The session is expired const ESessionIsExpired: u64 = 4; + /// The function call is beyond the session's scope + const EFunctionCallBeyoundSessionScope: u64 = 5; /// The session's scope struct SessionScope has store,copy,drop { @@ -42,7 +49,15 @@ module rooch_framework::session_key { keys: Table, SessionKey>, } - public fun is_expired(_ctx: &StorageContext, _session_key: &SessionKey) : bool { + public fun new_session_scope(module_address: address, module_name: std::ascii::String, function_name: std::ascii::String) : SessionScope { + SessionScope { + module_address: module_address, + module_name: module_name, + function_name: function_name, + } + } + + fun is_expired(_ctx: &StorageContext, _session_key: &SessionKey) : bool { //TODO check the session key is expired or not after the timestamp is supported return false } @@ -117,17 +132,52 @@ module rooch_framework::session_key { let session_key = option::extract(&mut session_key_option); assert!(!is_expired(ctx, &session_key), error::permission_denied(ESessionIsExpired)); - //TODO validate session scopes + assert!(in_session_scope(ctx, &session_key), error::permission_denied(EFunctionCallBeyoundSessionScope)); - if(scheme == ed25519::scheme()){ - validator::validate_signature(&authenticator_payload, &storage_context::tx_hash(ctx)); - }else{ - //TODO support other built-in validators - abort 1 - }; + validator::validate_signature(&authenticator_payload, &storage_context::tx_hash(ctx)); option::some(auth_key) } + /// Check the current tx is in the session scope or not + fun in_session_scope(ctx: &StorageContext, session_key: &SessionKey): bool{ + let idx = 0; + let tx_meta = storage_context::tx_meta(ctx); + + let function_call_meta_option = tx_meta::function_meta(&tx_meta); + // session key can not be used to execute script or publish module + // only support function call now + if (option::is_none(&function_call_meta_option)){ + return false + }; + let function_call_meta = option::extract(&mut function_call_meta_option); + while(idx < vector::length(&session_key.scopes)){ + let scope = vector::borrow(&session_key.scopes, idx); + if(check_scope_match(scope, &function_call_meta)){ + return true + }; + idx = idx + 1; + }; + false + } + + fun is_asterisk(str: &std::ascii::String) : bool { + let asterisk = std::ascii::string(b"*"); + str == &asterisk + } + + fun check_scope_match(scope: &SessionScope, function_call_meta:&FunctionCallMeta) : bool { + if (&scope.module_address != tx_meta::function_meta_module_address(function_call_meta)){ + return false + }; + if (!is_asterisk(&scope.module_name) && &scope.module_name != tx_meta::function_meta_module_name(function_call_meta)){ + return false + }; + if (!is_asterisk(&scope.function_name) && &scope.function_name != tx_meta::function_meta_function_name(function_call_meta)){ + return false + }; + true + } + public(friend) fun active_session_key(ctx: &mut StorageContext, authentication_key: vector) { let sender_addr = storage_context::sender(ctx); assert!(account_storage::global_exists(ctx, sender_addr), error::not_found(ESessionKeyIsInvalid)); @@ -137,4 +187,44 @@ module rooch_framework::session_key { //TODO set the last active time to now when the timestamp is supported session_key.last_active_time = session_key.last_active_time + 1; } + + #[test] + fun test_check_scope_match() { + let scope = new_session_scope(@0x1, std::ascii::string(b"test"), std::ascii::string(b"test")); + let function_call_meta = tx_meta::new_function_call_meta(@0x1, std::ascii::string(b"test"), std::ascii::string(b"test")); + assert!(check_scope_match(&scope, &function_call_meta), 1000); + + let function_call_meta = tx_meta::new_function_call_meta(@0x2, std::ascii::string(b"test"), std::ascii::string(b"test")); + assert!(!check_scope_match(&scope, &function_call_meta), 1001); + + let function_call_meta = tx_meta::new_function_call_meta(@0x1, std::ascii::string(b"test1"), std::ascii::string(b"test")); + assert!(!check_scope_match(&scope, &function_call_meta), 1002); + + let function_call_meta = tx_meta::new_function_call_meta(@0x1, std::ascii::string(b"test"), std::ascii::string(b"test1")); + assert!(!check_scope_match(&scope, &function_call_meta), 1003); + } + + #[test] + fun test_check_scope_match_asterisk() { + let scope = new_session_scope(@0x1, std::ascii::string(b"*"), std::ascii::string(b"*")); + + let function_call_meta = tx_meta::new_function_call_meta(@0x1, std::ascii::string(b"test"), std::ascii::string(b"test")); + assert!(check_scope_match(&scope, &function_call_meta), 1000); + + let function_call_meta = tx_meta::new_function_call_meta(@0x1, std::ascii::string(b"test2"), std::ascii::string(b"test2")); + assert!(check_scope_match(&scope, &function_call_meta), 1001); + + let function_call_meta = tx_meta::new_function_call_meta(@0x2, std::ascii::string(b"test"), std::ascii::string(b"test")); + assert!(!check_scope_match(&scope, &function_call_meta), 1002); + + let scope = new_session_scope(@0x1, std::ascii::string(b"test"), std::ascii::string(b"*")); + + let function_call_meta = tx_meta::new_function_call_meta(@0x1, std::ascii::string(b"test"), std::ascii::string(b"test1")); + assert!(check_scope_match(&scope, &function_call_meta), 1003); + + let function_call_meta = tx_meta::new_function_call_meta(@0x1, std::ascii::string(b"test1"), std::ascii::string(b"test")); + assert!(!check_scope_match(&scope, &function_call_meta), 1004); + + + } } \ No newline at end of file diff --git a/crates/rooch-genesis/genesis/genesis b/crates/rooch-genesis/genesis/genesis index 65e05583ed..597125e377 100644 Binary files a/crates/rooch-genesis/genesis/genesis and b/crates/rooch-genesis/genesis/genesis differ diff --git a/moveos/moveos-stdlib/moveos-stdlib/doc/README.md b/moveos/moveos-stdlib/moveos-stdlib/doc/README.md index f7be112e5a..e41be0f086 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/doc/README.md +++ b/moveos/moveos-stdlib/moveos-stdlib/doc/README.md @@ -30,6 +30,7 @@ This is the reference documentation of the MoveOS standard library. - [`0x2::storage_context`](storage_context.md#0x2_storage_context) - [`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::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 00d495ae99..b81e16f13a 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/doc/storage_context.md +++ b/moveos/moveos-stdlib/moveos-stdlib/doc/storage_context.md @@ -19,12 +19,14 @@ and let developers can customize the storage - [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)
use 0x1::option;
 use 0x2::object_id;
 use 0x2::object_storage;
 use 0x2::tx_context;
+use 0x2::tx_meta;
 
@@ -316,4 +318,28 @@ Get a value from the context map + + + + +## Function `tx_meta` + + + +
public fun tx_meta(self: &storage_context::StorageContext): tx_meta::TxMeta
+
+ + + +
+Implementation + + +
public fun tx_meta(self: &StorageContext): TxMeta {
+    tx_context::tx_meta(&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 4629c38552..817fb0a998 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/doc/tx_context.md +++ b/moveos/moveos-stdlib/moveos-stdlib/doc/tx_context.md @@ -14,9 +14,11 @@ - [Function `tx_hash`](#0x2_tx_context_tx_hash) - [Function `add`](#0x2_tx_context_add) - [Function `get`](#0x2_tx_context_get) +- [Function `tx_meta`](#0x2_tx_context_tx_meta) -
use 0x1::hash;
+
use 0x1::error;
+use 0x1::hash;
 use 0x1::option;
 use 0x1::string;
 use 0x1::vector;
@@ -24,6 +26,7 @@
 use 0x2::copyable_any;
 use 0x2::object_id;
 use 0x2::simple_map;
+use 0x2::tx_meta;
 use 0x2::type_info;
 
@@ -83,22 +86,11 @@ the VM and passed in to the entrypoint of the transaction as &mut < ## Constants - + -Expected an tx hash of length 32, but found a different length -
const EBadTxHashLength: u64 = 0;
-
- - - - - -Number of bytes in an tx hash (which will be the transaction digest) - - -
const TX_HASH_LENGTH: u64 = 32;
+
const EInvalidContext: u64 = 1;
 
@@ -290,4 +282,33 @@ Get a value from the context map + + + + +## Function `tx_meta` + +Get the transaction meta data +The TxMeta is writed by the VM before the transaction execution. +The meta data is only available when executing or validating a transaction, otherwise abort(eg. readonly function call). + + +
public fun tx_meta(self: &tx_context::TxContext): tx_meta::TxMeta
+
+ + + +
+Implementation + + +
public fun tx_meta(self: &TxContext): TxMeta {
+    let meta = get<TxMeta>(self);
+    assert!(option::is_some(&meta), error::invalid_state(EInvalidContext));
+    option::extract(&mut meta)
+}
+
+ + +
diff --git a/moveos/moveos-stdlib/moveos-stdlib/doc/tx_meta.md b/moveos/moveos-stdlib/moveos-stdlib/doc/tx_meta.md new file mode 100644 index 0000000000..db9c0d2025 --- /dev/null +++ b/moveos/moveos-stdlib/moveos-stdlib/doc/tx_meta.md @@ -0,0 +1,393 @@ + + + +# Module `0x2::tx_meta` + + + +- [Struct `TxMeta`](#0x2_tx_meta_TxMeta) +- [Struct `FunctionCallMeta`](#0x2_tx_meta_FunctionCallMeta) +- [Constants](#@Constants_0) +- [Function `move_action_script_type`](#0x2_tx_meta_move_action_script_type) +- [Function `move_action_function_type`](#0x2_tx_meta_move_action_function_type) +- [Function `move_action_module_bundle_type`](#0x2_tx_meta_move_action_module_bundle_type) +- [Function `action_type`](#0x2_tx_meta_action_type) +- [Function `is_script_call`](#0x2_tx_meta_is_script_call) +- [Function `is_function_call`](#0x2_tx_meta_is_function_call) +- [Function `is_module_publish`](#0x2_tx_meta_is_module_publish) +- [Function `function_meta`](#0x2_tx_meta_function_meta) +- [Function `function_meta_module_address`](#0x2_tx_meta_function_meta_module_address) +- [Function `function_meta_module_name`](#0x2_tx_meta_function_meta_module_name) +- [Function `function_meta_function_name`](#0x2_tx_meta_function_meta_function_name) + + +
use 0x1::ascii;
+use 0x1::option;
+
+ + + + + +## Struct `TxMeta` + +The transaction Meta data +We can not define MoveAction in Move, so we define a simple meta data struct to represent it + + +
struct TxMeta has copy, drop, store
+
+ + + +
+Fields + + +
+
+action_type: u8 +
+
+ The MoveAction type of the current transaction +
+
+function_meta: option::Option<tx_meta::FunctionCallMeta> +
+
+ If the action type is MoveActionFunctionType, this field is the function call meta data +
+
+ + +
+ + + +## Struct `FunctionCallMeta` + +The FunctionCall Meta data + + +
struct FunctionCallMeta has copy, drop, store
+
+ + + +
+Fields + + +
+
+module_address: address +
+
+ +
+
+module_name: ascii::String +
+
+ +
+
+function_name: ascii::String +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const MoveActionFunctionType: u8 = 1;
+
+ + + + + + + +
const MoveActionModuleBundleType: u8 = 2;
+
+ + + + + + + +
const MoveActionScriptType: u8 = 0;
+
+ + + + + +## Function `move_action_script_type` + + + +
public fun move_action_script_type(): u8
+
+ + + +
+Implementation + + +
public fun move_action_script_type(): u8 { MoveActionScriptType }
+
+ + + +
+ + + +## Function `move_action_function_type` + + + +
public fun move_action_function_type(): u8
+
+ + + +
+Implementation + + +
public fun move_action_function_type(): u8 { MoveActionFunctionType }
+
+ + + +
+ + + +## Function `move_action_module_bundle_type` + + + +
public fun move_action_module_bundle_type(): u8
+
+ + + +
+Implementation + + +
public fun move_action_module_bundle_type(): u8 { MoveActionModuleBundleType }
+
+ + + +
+ + + +## Function `action_type` + + + +
public fun action_type(self: &tx_meta::TxMeta): u8
+
+ + + +
+Implementation + + +
public fun action_type(self: &TxMeta): u8 {
+    self.action_type
+}
+
+ + + +
+ + + +## Function `is_script_call` + + + +
public fun is_script_call(self: &tx_meta::TxMeta): bool
+
+ + + +
+Implementation + + +
public fun is_script_call(self: &TxMeta): bool {
+    self.action_type == MoveActionScriptType
+}
+
+ + + +
+ + + +## Function `is_function_call` + + + +
public fun is_function_call(self: &tx_meta::TxMeta): bool
+
+ + + +
+Implementation + + +
public fun is_function_call(self: &TxMeta): bool {
+    self.action_type == MoveActionFunctionType
+}
+
+ + + +
+ + + +## Function `is_module_publish` + + + +
public fun is_module_publish(self: &tx_meta::TxMeta): bool
+
+ + + +
+Implementation + + +
public fun is_module_publish(self: &TxMeta): bool {
+    self.action_type == MoveActionModuleBundleType
+}
+
+ + + +
+ + + +## Function `function_meta` + + + +
public fun function_meta(self: &tx_meta::TxMeta): option::Option<tx_meta::FunctionCallMeta>
+
+ + + +
+Implementation + + +
public fun function_meta(self: &TxMeta): Option<FunctionCallMeta> {
+    *&self.function_meta
+}
+
+ + + +
+ + + +## Function `function_meta_module_address` + + + +
public fun function_meta_module_address(function_meta: &tx_meta::FunctionCallMeta): &address
+
+ + + +
+Implementation + + +
public fun function_meta_module_address(function_meta: &FunctionCallMeta): &address {
+    &function_meta.module_address
+}
+
+ + + +
+ + + +## Function `function_meta_module_name` + + + +
public fun function_meta_module_name(function_meta: &tx_meta::FunctionCallMeta): &ascii::String
+
+ + + +
+Implementation + + +
public fun function_meta_module_name(function_meta: &FunctionCallMeta): &std::ascii::String {
+    &function_meta.module_name
+}
+
+ + + +
+ + + +## Function `function_meta_function_name` + + + +
public fun function_meta_function_name(function_meta: &tx_meta::FunctionCallMeta): &ascii::String
+
+ + + +
+Implementation + + +
public fun function_meta_function_name(function_meta: &FunctionCallMeta): &std::ascii::String {
+    &function_meta.function_name
+}
+
+ + + +
diff --git a/moveos/moveos-stdlib/moveos-stdlib/error_description.errmap b/moveos/moveos-stdlib/moveos-stdlib/error_description.errmap index 2c15b6a014..64eb4a0a9d 100644 Binary files a/moveos/moveos-stdlib/moveos-stdlib/error_description.errmap and b/moveos/moveos-stdlib/moveos-stdlib/error_description.errmap differ diff --git a/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move b/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move index 43f12d2362..5106396ea4 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/storage_context.move @@ -8,6 +8,7 @@ module moveos_std::storage_context { use moveos_std::object_storage::{ObjectStorage}; use moveos_std::tx_context::{Self, TxContext}; use moveos_std::object_id::{ObjectID}; + use moveos_std::tx_meta::{TxMeta}; #[test_only] use moveos_std::object_storage::{Self}; @@ -77,6 +78,10 @@ module moveos_std::storage_context { tx_context::get(&self.tx_context) } + public fun tx_meta(self: &StorageContext): TxMeta { + tx_context::tx_meta(&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 d27f8ba16a..f035e2029c 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/sources/tx_context.move +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/tx_context.move @@ -6,23 +6,20 @@ module moveos_std::tx_context { use std::hash; use std::string::String; use std::option::{Self, Option}; + use std::error; use moveos_std::bcs; use moveos_std::object_id::{Self, ObjectID}; use moveos_std::simple_map::{Self, SimpleMap}; use moveos_std::copyable_any::{Self, Any}; use moveos_std::type_info; + use moveos_std::tx_meta::{TxMeta}; friend moveos_std::object; friend moveos_std::raw_table; friend moveos_std::account_storage; friend moveos_std::event; - /// Number of bytes in an tx hash (which will be the transaction digest) - const TX_HASH_LENGTH: u64 = 32; - - /// Expected an tx hash of length 32, but found a different length - const EBadTxHashLength: u64 = 0; - + const EInvalidContext: u64 = 1; /// Information about the transaction currently being executed. /// This cannot be constructed by a transaction--it is a privileged object created by @@ -93,7 +90,16 @@ module moveos_std::tx_context { option::none() } } - + + /// Get the transaction meta data + /// The TxMeta is writed by the VM before the transaction execution. + /// The meta data is only available when executing or validating a transaction, otherwise abort(eg. readonly function call). + public fun tx_meta(self: &TxContext): TxMeta { + let meta = get(self); + assert!(option::is_some(&meta), error::invalid_state(EInvalidContext)); + option::extract(&mut meta) + } + #[test_only] /// Create a TxContext for unit test public fun new_test_context(sender: address): TxContext { diff --git a/moveos/moveos-stdlib/moveos-stdlib/sources/tx_meta.move b/moveos/moveos-stdlib/moveos-stdlib/sources/tx_meta.move new file mode 100644 index 0000000000..6d93a19cf1 --- /dev/null +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/tx_meta.move @@ -0,0 +1,77 @@ +module moveos_std::tx_meta { + + use std::option::Option; + + + const MoveActionScriptType: u8 = 0; + public fun move_action_script_type(): u8 { MoveActionScriptType } + const MoveActionFunctionType: u8 = 1; + public fun move_action_function_type(): u8 { MoveActionFunctionType } + const MoveActionModuleBundleType: u8 = 2; + public fun move_action_module_bundle_type(): u8 { MoveActionModuleBundleType } + + /// The transaction Meta data + /// We can not define MoveAction in Move, so we define a simple meta data struct to represent it + struct TxMeta has store, copy, drop { + /// The MoveAction type of the current transaction + action_type: u8, + /// If the action type is MoveActionFunctionType, this field is the function call meta data + function_meta: Option, + } + + /// The FunctionCall Meta data + struct FunctionCallMeta has store, copy, drop { + module_address: address, + module_name: std::ascii::String, + function_name: std::ascii::String, + //TODO should we support ty_args and args? + //ty_args: vector, + //args: vector>, + } + + #[test_only] + public fun new_function_call_meta( + module_address: address, + module_name: std::ascii::String, + function_name: std::ascii::String, + ): FunctionCallMeta { + FunctionCallMeta { + module_address, + module_name, + function_name, + } + } + + public fun action_type(self: &TxMeta): u8 { + self.action_type + } + + public fun is_script_call(self: &TxMeta): bool { + self.action_type == MoveActionScriptType + } + + public fun is_function_call(self: &TxMeta): bool { + self.action_type == MoveActionFunctionType + } + + public fun is_module_publish(self: &TxMeta): bool { + self.action_type == MoveActionModuleBundleType + } + + public fun function_meta(self: &TxMeta): Option { + *&self.function_meta + } + + public fun function_meta_module_address(function_meta: &FunctionCallMeta): &address { + &function_meta.module_address + } + + public fun function_meta_module_name(function_meta: &FunctionCallMeta): &std::ascii::String { + &function_meta.module_name + } + + public fun function_meta_function_name(function_meta: &FunctionCallMeta): &std::ascii::String { + &function_meta.function_name + } + +} \ No newline at end of file diff --git a/moveos/moveos-stdlib/moveos-stdlib/sources/type_info.move b/moveos/moveos-stdlib/moveos-stdlib/sources/type_info.move index ceac71480a..336eaed66a 100644 --- a/moveos/moveos-stdlib/moveos-stdlib/sources/type_info.move +++ b/moveos/moveos-stdlib/moveos-stdlib/sources/type_info.move @@ -17,6 +17,7 @@ module moveos_std::type_info { struct TypeInfo has copy, drop, store { account_address: address, + //TODO should use ascii::String to represent module_name and struct_name module_name: vector, struct_name: vector, } @@ -39,6 +40,7 @@ module moveos_std::type_info { public native fun type_of(): TypeInfo; + //TODO should use ascii::String to represent type_name //TODO check the gas cost, and decide whether to implement by native function. public fun type_name(): string::String{ let ascii = std::type_name::into_string(std::type_name::get()); diff --git a/moveos/moveos-types/src/event.rs b/moveos/moveos-types/src/event.rs index 747166dc28..e7bb91005d 100644 --- a/moveos/moveos-types/src/event.rs +++ b/moveos/moveos-types/src/event.rs @@ -4,6 +4,7 @@ // Copyright (c) The Diem Core Contributors // SPDX-License-Identifier: Apache-2.0 +use crate::moveos_std::type_info::TypeInfo; use crate::object::ObjectID; use crate::state::MoveStructType; use anyhow::{ensure, Error, Result}; @@ -13,7 +14,6 @@ use schemars::JsonSchema; // #[cfg(any(test, feature = "fuzzing"))] // use rand::{rngs::OsRng, RngCore}; use crate::h256; -use crate::move_types::TypeInfo; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; diff --git a/moveos/moveos-types/src/lib.rs b/moveos/moveos-types/src/lib.rs index c8717a8372..1295069bc0 100644 --- a/moveos/moveos-types/src/lib.rs +++ b/moveos/moveos-types/src/lib.rs @@ -15,6 +15,7 @@ pub mod move_simple_map; pub mod move_std; pub mod move_string; pub mod move_types; +pub mod moveos_std; pub mod object; pub mod serde; pub mod startup_info; diff --git a/moveos/moveos-types/src/move_string.rs b/moveos/moveos-types/src/move_string.rs index c01275ad72..72bbe4a244 100644 --- a/moveos/moveos-types/src/move_string.rs +++ b/moveos/moveos-types/src/move_string.rs @@ -115,6 +115,13 @@ pub struct MoveAsciiString { bytes: Vec, } +impl MoveAsciiString { + pub fn new(bytes: Vec) -> anyhow::Result { + ensure!(bytes.is_ascii(), "string is not ascii"); + Ok(MoveAsciiString { bytes }) + } +} + impl std::fmt::Display for MoveAsciiString { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { //DO not check ascii when display diff --git a/moveos/moveos-types/src/move_types.rs b/moveos/moveos-types/src/move_types.rs index e7931b0b61..8720f37cc4 100644 --- a/moveos/moveos-types/src/move_types.rs +++ b/moveos/moveos-types/src/move_types.rs @@ -203,28 +203,6 @@ pub fn struct_tag_match(filter: &StructTag, target: &StructTag) -> bool { true } -/// The structure of TypeInfo is consistent of contract type_info -#[derive(Clone, Debug, Eq, Ord, PartialOrd, PartialEq, Serialize, Deserialize, Hash)] -pub struct TypeInfo { - pub account_address: AccountAddress, - pub module_name: Identifier, - pub struct_name: Identifier, -} - -impl TypeInfo { - pub fn new( - account_address: AccountAddress, - module_name: Identifier, - struct_name: Identifier, - ) -> Self { - Self { - account_address, - module_name, - struct_name, - } - } -} - pub fn as_struct_tag(type_tag: TypeTag) -> Result { if let TypeTag::Struct(struct_tag) = type_tag { Ok(*struct_tag) diff --git a/moveos/moveos-types/src/moveos_std/mod.rs b/moveos/moveos-types/src/moveos_std/mod.rs new file mode 100644 index 0000000000..b83e4c3e12 --- /dev/null +++ b/moveos/moveos-types/src/moveos_std/mod.rs @@ -0,0 +1,6 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +/// This mod contains all the Rust to Move type mapping that are used in MoveosStd +pub mod tx_meta; +pub mod type_info; diff --git a/moveos/moveos-types/src/moveos_std/tx_meta.rs b/moveos/moveos-types/src/moveos_std/tx_meta.rs new file mode 100644 index 0000000000..282201d88f --- /dev/null +++ b/moveos/moveos-types/src/moveos_std/tx_meta.rs @@ -0,0 +1,88 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + addresses::MOVEOS_STD_ADDRESS, + move_option::MoveOption, + move_string::MoveAsciiString, + state::{MoveStructState, MoveStructType}, + transaction::MoveAction, +}; +use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr}; +use serde::{Deserialize, Serialize}; + +pub const MODULE_NAME: &IdentStr = ident_str!("tx_meta"); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TxMeta { + pub action_type: u8, + pub function_meta: MoveOption, +} + +impl MoveStructType for TxMeta { + const ADDRESS: AccountAddress = MOVEOS_STD_ADDRESS; + const MODULE_NAME: &'static IdentStr = MODULE_NAME; + const STRUCT_NAME: &'static IdentStr = ident_str!("TxMeta"); +} + +impl MoveStructState for TxMeta { + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + move_core_types::value::MoveTypeLayout::U8, + MoveOption::::type_layout(), + ]) + } +} + +impl TxMeta { + pub fn new(action_type: u8, function_meta: MoveOption) -> Self { + Self { + action_type, + function_meta, + } + } + + pub fn new_from_move_action(move_action: &MoveAction) -> Self { + let function_meta = match move_action { + MoveAction::Function(call) => Some(FunctionCallMeta { + module_address: *call.function_id.module_id.address(), + module_name: MoveAsciiString::new( + call.function_id.module_id.name().as_bytes().to_vec(), + ) + .expect("module name must be valid ascii"), + function_name: MoveAsciiString::new( + call.function_id.function_name.as_bytes().to_vec(), + ) + .expect("module name must be valid ascii"), + }), + _ => None, + }; + Self { + action_type: move_action.action_type(), + function_meta: function_meta.into(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FunctionCallMeta { + pub module_address: AccountAddress, + pub module_name: MoveAsciiString, + pub function_name: MoveAsciiString, +} + +impl MoveStructType for FunctionCallMeta { + const ADDRESS: AccountAddress = MOVEOS_STD_ADDRESS; + const MODULE_NAME: &'static IdentStr = MODULE_NAME; + const STRUCT_NAME: &'static IdentStr = ident_str!("FunctionCallMeta"); +} + +impl MoveStructState for FunctionCallMeta { + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + move_core_types::value::MoveTypeLayout::Address, + MoveAsciiString::type_layout(), + MoveAsciiString::type_layout(), + ]) + } +} diff --git a/moveos/moveos-types/src/moveos_std/type_info.rs b/moveos/moveos-types/src/moveos_std/type_info.rs new file mode 100644 index 0000000000..4ef7064a83 --- /dev/null +++ b/moveos/moveos-types/src/moveos_std/type_info.rs @@ -0,0 +1,57 @@ +// 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, Identifier}, +}; +use serde::{Deserialize, Serialize}; + +const MODULE_NAME: &IdentStr = ident_str!("type_info"); + +/// The structure of TypeInfo is consistent of contract type_info +#[derive(Clone, Debug, Eq, Ord, PartialOrd, PartialEq, Serialize, Deserialize, Hash)] +pub struct TypeInfo { + pub account_address: AccountAddress, + pub module_name: Vec, + pub struct_name: Vec, +} + +impl TypeInfo { + pub fn new( + account_address: AccountAddress, + module_name: Identifier, + struct_name: Identifier, + ) -> Self { + Self { + account_address, + module_name: module_name.as_bytes().to_vec(), + struct_name: struct_name.as_bytes().to_vec(), + } + } +} + +impl MoveStructType for TypeInfo { + const ADDRESS: AccountAddress = MOVEOS_STD_ADDRESS; + const MODULE_NAME: &'static IdentStr = MODULE_NAME; + const STRUCT_NAME: &'static IdentStr = ident_str!("TypeInfo"); +} + +impl MoveStructState for TypeInfo { + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + move_core_types::value::MoveTypeLayout::Address, + move_core_types::value::MoveTypeLayout::Vector(Box::new( + move_core_types::value::MoveTypeLayout::U8, + )), + move_core_types::value::MoveTypeLayout::Vector(Box::new( + move_core_types::value::MoveTypeLayout::U8, + )), + ]) + } +} diff --git a/moveos/moveos-types/src/transaction.rs b/moveos/moveos-types/src/transaction.rs index 1fc59fc986..faa834d27e 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, state::StateChangeSet, - tx_context::TxContext, + event::Event, h256, h256::H256, move_types::FunctionId, moveos_std::tx_meta::TxMeta, + state::StateChangeSet, tx_context::TxContext, }; use move_core_types::{ account_address::AccountAddress, @@ -113,6 +113,14 @@ pub enum MoveAction { } impl MoveAction { + pub fn action_type(&self) -> u8 { + match self { + MoveAction::Script(_) => 0, + MoveAction::Function(_) => 1, + MoveAction::ModuleBundle(_) => 2, + } + } + pub fn new_module_bundle(modules: Vec>) -> Self { Self::ModuleBundle(modules) } @@ -192,15 +200,12 @@ impl MoveOSTransaction { 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); - Self { - ctx, - action: sender_and_action.1, - pre_execute_functions: vec![], - post_execute_functions: vec![], - } + Self::new(ctx, sender_and_action.1) } - pub fn new(ctx: TxContext, action: MoveAction) -> Self { + pub fn new(mut ctx: TxContext, action: MoveAction) -> Self { + ctx.add(TxMeta::new_from_move_action(&action)) + .expect("add TxMeta to TxContext should success"); Self { ctx, action,