Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions crates/rooch-framework-tests/src/binding_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -27,8 +27,7 @@ impl RustBindingTest {

//TODO let the module bundle to execute the function
pub fn execute<T: AbstractTransaction>(&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: {:?}",
Expand All @@ -37,4 +36,12 @@ impl RustBindingTest {
}
Ok(())
}

pub fn execute_as_result<T: AbstractTransaction>(
&mut self,
tx: T,
) -> Result<ExecuteTransactionResult> {
let verified_tx = self.executor.validate(tx)?;
self.executor.execute(verified_tx)
}
}
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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"),
// }
}
41 changes: 27 additions & 14 deletions crates/rooch-framework/doc/session_key.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -27,6 +27,7 @@
<b>use</b> <a href="">0x2::storage_context</a>;
<b>use</b> <a href="">0x2::table</a>;
<b>use</b> <a href="">0x2::tx_context</a>;
<b>use</b> <a href="">0x2::tx_meta</a>;
<b>use</b> <a href="auth_validator.md#0x3_auth_validator">0x3::auth_validator</a>;
<b>use</b> <a href="ed25519.md#0x3_ed25519">0x3::ed25519</a>;
<b>use</b> <a href="native_validator.md#0x3_native_validator">0x3::native_validator</a>;
Expand Down Expand Up @@ -157,8 +158,19 @@ The session's scope
## Constants


<a name="0x3_session_key_EFunctionCallBeyoundSessionScope"></a>

The function call is beyond the session's scope


<pre><code><b>const</b> <a href="session_key.md#0x3_session_key_EFunctionCallBeyoundSessionScope">EFunctionCallBeyoundSessionScope</a>: u64 = 5;
</code></pre>



<a name="0x3_session_key_ESessionIsExpired"></a>

The session is expired


<pre><code><b>const</b> <a href="session_key.md#0x3_session_key_ESessionIsExpired">ESessionIsExpired</a>: u64 = 4;
Expand All @@ -168,6 +180,7 @@ The session's scope

<a name="0x3_session_key_ESessionKeyAlreadyExists"></a>

The session key already exists


<pre><code><b>const</b> <a href="session_key.md#0x3_session_key_ESessionKeyAlreadyExists">ESessionKeyAlreadyExists</a>: u64 = 2;
Expand All @@ -177,6 +190,7 @@ The session's scope

<a name="0x3_session_key_ESessionKeyCreatePermissionDenied"></a>

Create session key in this context is not allowed


<pre><code><b>const</b> <a href="session_key.md#0x3_session_key_ESessionKeyCreatePermissionDenied">ESessionKeyCreatePermissionDenied</a>: u64 = 1;
Expand All @@ -186,20 +200,21 @@ The session's scope

<a name="0x3_session_key_ESessionKeyIsInvalid"></a>

The session key is invalid


<pre><code><b>const</b> <a href="session_key.md#0x3_session_key_ESessionKeyIsInvalid">ESessionKeyIsInvalid</a>: u64 = 3;
</code></pre>



<a name="0x3_session_key_is_expired"></a>
<a name="0x3_session_key_new_session_scope"></a>

## Function `is_expired`
## Function `new_session_scope`



<pre><code><b>public</b> <b>fun</b> <a href="session_key.md#0x3_session_key_is_expired">is_expired</a>(_ctx: &<a href="_StorageContext">storage_context::StorageContext</a>, _session_key: &<a href="session_key.md#0x3_session_key_SessionKey">session_key::SessionKey</a>): bool
<pre><code><b>public</b> <b>fun</b> <a href="session_key.md#0x3_session_key_new_session_scope">new_session_scope</a>(module_address: <b>address</b>, module_name: <a href="_String">ascii::String</a>, function_name: <a href="_String">ascii::String</a>): <a href="session_key.md#0x3_session_key_SessionScope">session_key::SessionScope</a>
</code></pre>


Expand All @@ -208,9 +223,12 @@ The session's scope
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="session_key.md#0x3_session_key_is_expired">is_expired</a>(_ctx: &StorageContext, _session_key: &<a href="session_key.md#0x3_session_key_SessionKey">SessionKey</a>) : bool {
//TODO check the session key is expired or not after the timestamp is supported
<b>return</b> <b>false</b>
<pre><code><b>public</b> <b>fun</b> <a href="session_key.md#0x3_session_key_new_session_scope">new_session_scope</a>(module_address: <b>address</b>, module_name: std::ascii::String, function_name: std::ascii::String) : <a href="session_key.md#0x3_session_key_SessionScope">SessionScope</a> {
<a href="session_key.md#0x3_session_key_SessionScope">SessionScope</a> {
module_address: module_address,
module_name: module_name,
function_name: function_name,
}
}
</code></pre>

Expand Down Expand Up @@ -383,14 +401,9 @@ If the session key is expired or invalid, abort the tx, otherwise return option:
<b>let</b> <a href="session_key.md#0x3_session_key">session_key</a> = <a href="_extract">option::extract</a>(&<b>mut</b> session_key_option);
<b>assert</b>!(!<a href="session_key.md#0x3_session_key_is_expired">is_expired</a>(ctx, &<a href="session_key.md#0x3_session_key">session_key</a>), <a href="_permission_denied">error::permission_denied</a>(<a href="session_key.md#0x3_session_key_ESessionIsExpired">ESessionIsExpired</a>));

//TODO validate session scopes
<b>assert</b>!(<a href="session_key.md#0x3_session_key_in_session_scope">in_session_scope</a>(ctx, &<a href="session_key.md#0x3_session_key">session_key</a>), <a href="_permission_denied">error::permission_denied</a>(<a href="session_key.md#0x3_session_key_EFunctionCallBeyoundSessionScope">EFunctionCallBeyoundSessionScope</a>));

<b>if</b>(scheme == <a href="ed25519.md#0x3_ed25519_scheme">ed25519::scheme</a>()){
validator::validate_signature(&authenticator_payload, &<a href="_tx_hash">storage_context::tx_hash</a>(ctx));
}<b>else</b>{
//TODO support other built-in validators
<b>abort</b> 1
};
validator::validate_signature(&authenticator_payload, &<a href="_tx_hash">storage_context::tx_hash</a>(ctx));
<a href="_some">option::some</a>(auth_key)
}
</code></pre>
Expand Down
Binary file modified crates/rooch-framework/error_description.errmap
Binary file not shown.
1 change: 1 addition & 0 deletions crates/rooch-framework/sources/empty.move
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ module rooch_framework::empty{
public entry fun empty(){
//Just do nothing
}

}
106 changes: 98 additions & 8 deletions crates/rooch-framework/sources/session_key.move
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -42,7 +49,15 @@ module rooch_framework::session_key {
keys: Table<vector<u8>, 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
}
Expand Down Expand Up @@ -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<u8>) {
let sender_addr = storage_context::sender(ctx);
assert!(account_storage::global_exists<SessionKeys>(ctx, sender_addr), error::not_found(ESessionKeyIsInvalid));
Expand All @@ -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);


}
}
Binary file modified crates/rooch-genesis/genesis/genesis
Binary file not shown.
1 change: 1 addition & 0 deletions moveos/moveos-stdlib/moveos-stdlib/doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading