diff --git a/client/src/main.rs b/client/src/main.rs index 17e252dc55..283c1275a0 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -64,7 +64,7 @@ use substrate_api_client::{ use substrate_client_keystore::LocalKeystore; use substratee_stf::{ShardIdentifier, TrustedCallSigned, TrustedOperation}; use substratee_worker_api::direct_client::DirectApi as DirectWorkerApi; -use substratee_worker_primitives::{DirectCallStatus, RpcRequest, RpcResponse, RpcReturnValue}; +use substratee_worker_primitives::{DirectRequestStatus, RpcRequest, RpcResponse, RpcReturnValue}; type AccountPublic = ::Signer; const KEYSTORE_PATH: &str = "my_keystore"; @@ -459,10 +459,7 @@ fn get_state(matches: &ArgMatches<'_>, getter: TrustedOperation) -> Option shard, - Err(e) => panic!(e), - }; + let shard = read_shard(matches).unwrap(); // compose jsonrpc call let data = Request { @@ -484,7 +481,7 @@ fn get_state(matches: &ArgMatches<'_>, getter: TrustedOperation) -> Option { let response: RpcResponse = serde_json::from_str(&response).unwrap(); if let Ok(return_value) = RpcReturnValue::decode(&mut response.result.as_slice()) { - if return_value.status == DirectCallStatus::Error { + if return_value.status == DirectRequestStatus::Error { println!( "[Error] {}", String::decode(&mut return_value.value.as_slice()).unwrap() @@ -532,10 +529,7 @@ fn send_request(matches: &ArgMatches<'_>, call: TrustedCallSigned) -> Option shard, - Err(e) => panic!(e), - }; + let shard = read_shard(matches).unwrap(); let arg_signer = matches.value_of("xt-signer").unwrap(); let signer = get_pair_from_str(arg_signer); @@ -619,10 +613,7 @@ fn send_direct_request( return None; } }; - let shard = match read_shard(matches) { - Ok(shard) => shard, - Err(e) => panic!(e), - }; + let shard = read_shard(matches).unwrap(); // compose jsonrpc call let data = Request { @@ -650,13 +641,13 @@ fn send_direct_request( let response: RpcResponse = serde_json::from_str(&response).unwrap(); if let Ok(return_value) = RpcReturnValue::decode(&mut response.result.as_slice()) { match return_value.status { - DirectCallStatus::Error => { + DirectRequestStatus::Error => { if let Ok(value) = String::decode(&mut return_value.value.as_slice()) { println!("[Error] {}", value); } return None; } - DirectCallStatus::TrustedOperationStatus(status) => { + DirectRequestStatus::TrustedOperationStatus(status) => { if let Ok(value) = Hash::decode(&mut return_value.value.as_slice()) { println!("Trusted call {:?} is {:?}", value, status); } diff --git a/enclave/src/constants.rs b/enclave/src/constants.rs index c50c0ba730..889944aac1 100644 --- a/enclave/src/constants.rs +++ b/enclave/src/constants.rs @@ -14,6 +14,7 @@ limitations under the License. */ +use substratee_stf::Index; pub const RSA3072_SEALED_KEY_FILE: &str = "rsa3072_key_sealed.bin"; pub const SEALED_SIGNER_SEED_FILE: &str = "ed25519_key_sealed.bin"; @@ -49,5 +50,8 @@ pub static RUNTIME_SPEC_VERSION: u32 = 1; pub static RUNTIME_TRANSACTION_VERSION: u32 = 1; // timeouts for getter and call execution -pub static CALLTIMEOUT: i64 = 300; // timeout in ms +pub static CALLTIMEOUT: i64 = 300; // timeout in ms pub static GETTERTIMEOUT: i64 = 300; // timeout in ms + +// maximum allowed tops in the future pool per account +pub static MAX_ALLOWED_FUTURE_TOP: Index = 64; diff --git a/enclave/src/lib.rs b/enclave/src/lib.rs index 6218cbfe79..90c95405c7 100644 --- a/enclave/src/lib.rs +++ b/enclave/src/lib.rs @@ -80,7 +80,7 @@ use substratee_stf::{ use rpc::author::{hash::TrustedOperationOrHash, Author, AuthorApi}; use rpc::worker_api_direct; -use rpc::{api::FillerChainApi, basic_pool::BasicPool}; +use rpc::{api::SideChainApi, basic_pool::BasicPool}; mod aes; mod attestation; @@ -108,7 +108,7 @@ pub enum Timeout { } pub type Hash = sp_core::H256; -type BPool = BasicPool, Block>; +type BPool = BasicPool, Block>; #[no_mangle] pub unsafe extern "C" fn init() -> sgx_status_t { @@ -588,6 +588,7 @@ fn execute_top_pool_calls( Ok((calls, _)) => calls, Err(_) => return Err(sgx_status_t::SGX_ERROR_UNEXPECTED), }; + debug!("Got following trusted calls from pool: {:?}", trusted_calls); // call execution for trusted_call_signed in trusted_calls.into_iter() { match handle_trusted_worker_call( diff --git a/enclave/src/rpc/api.rs b/enclave/src/rpc/api.rs index f5fd018591..4ffa48f75a 100644 --- a/enclave/src/rpc/api.rs +++ b/enclave/src/rpc/api.rs @@ -21,7 +21,7 @@ extern crate alloc; use alloc::{boxed::Box, vec::Vec}; use log::*; -use codec::Encode; +use codec::{Encode, Decode}; use jsonrpc_core::futures::future::{ready, Future, Ready}; use std::{marker::PhantomData, pin::Pin}; @@ -29,33 +29,41 @@ use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Hash as HashT, Header as HeaderT}, transaction_validity::{ - TransactionValidity, TransactionValidityError, UnknownTransaction, ValidTransaction, + TransactionValidity, TransactionValidityError, UnknownTransaction, + InvalidTransaction, ValidTransaction, }, }; use crate::top_pool::pool::{ChainApi, ExtrinsicHash, NumberFor}; use crate::top_pool::primitives::TrustedOperationSource; +use crate::state; -use substratee_stf::{Getter, TrustedOperation as StfTrustedOperation}; +use substratee_stf::{Getter, TrustedOperation as StfTrustedOperation, AccountId, + Index, ShardIdentifier, Stf}; use substratee_worker_primitives::BlockHash as SidechainBlockHash; use crate::rpc::error; +use crate::constants::MAX_ALLOWED_FUTURE_TOP; + +/// Future that resolves to account nonce. +pub type Result = core::result::Result; + /// The operation pool logic for full client. -pub struct FillerChainApi { +pub struct SideChainApi { _marker: PhantomData, } -impl FillerChainApi { +impl SideChainApi { /// Create new operation pool logic. pub fn new() -> Self { - FillerChainApi { + SideChainApi { _marker: Default::default(), } } } -impl ChainApi for FillerChainApi +impl ChainApi for SideChainApi where Block: BlockT, { @@ -71,17 +79,23 @@ where fn validate_transaction( &self, - _at: &BlockId, _source: TrustedOperationSource, uxt: StfTrustedOperation, + _shard: ShardIdentifier, ) -> Self::ValidationFuture { let operation = match uxt { - StfTrustedOperation::direct_call(call) => ValidTransaction { - priority: 1 << 20, - requires: vec![], - provides: vec![vec![call.nonce as u8], call.signature.encode()], - longevity: 3, - propagate: true, + StfTrustedOperation::direct_call(signed_call) => { + let from = signed_call.call.account(); + let requires = vec![]; + let provides = vec![from.encode()]; + + ValidTransaction { + priority: 1 << 20, + requires: requires, + provides: provides, + longevity: 64, + propagate: true, + } }, StfTrustedOperation::get(getter) => match getter { Getter::public(_) => { @@ -93,7 +107,7 @@ where priority: 1 << 20, requires: vec![], provides: vec![trusted_getter.signature.encode()], - longevity: 3, + longevity: 64, propagate: true, }, }, diff --git a/enclave/src/rpc/author/mod.rs b/enclave/src/rpc/author/mod.rs index 21dbea7e91..a725635955 100644 --- a/enclave/src/rpc/author/mod.rs +++ b/enclave/src/rpc/author/mod.rs @@ -134,13 +134,10 @@ where shard: ShardIdentifier, ) -> FutureResult, RpcError> { // check if shard already exists - let shards = match state::list_shards() { - Ok(shards) => shards, - Err(_) => return Box::pin(ready(Err(ClientError::InvalidShard.into()))), - }; - if !shards.contains(&shard) { - return Box::pin(ready(Err(ClientError::InvalidShard.into()))); - } + if !state::exists(&shard) { + //FIXME: Should this be an error? -> Issue error handling + return Box::pin(ready(Err(ClientError::InvalidShard.into()))) + } // decrypt call let rsa_keypair = rsa3072::unseal_pair().unwrap(); let request_vec: Vec = match rsa3072::decrypt(&ext.as_slice(), &rsa_keypair) { @@ -237,13 +234,10 @@ where fn watch_top(&self, ext: Vec, shard: ShardIdentifier) -> FutureResult, RpcError> { // check if shard already exists - let shards = match state::list_shards() { - Ok(shards) => shards, - Err(_) => return Box::pin(ready(Err(ClientError::InvalidShard.into()))), - }; - if !shards.contains(&shard) { - return Box::pin(ready(Err(ClientError::InvalidShard.into()))); - } + if !state::exists(&shard) { + //FIXME: Should this be an error? -> Issue error handling + return Box::pin(ready(Err(ClientError::InvalidShard.into()))) + } // decrypt call let rsa_keypair = rsa3072::unseal_pair().unwrap(); let request_vec: Vec = match rsa3072::decrypt(&ext.as_slice(), &rsa_keypair) { diff --git a/enclave/src/rpc/worker_api_direct.rs b/enclave/src/rpc/worker_api_direct.rs index 0e0708847f..86a7b02a6a 100644 --- a/enclave/src/rpc/worker_api_direct.rs +++ b/enclave/src/rpc/worker_api_direct.rs @@ -38,7 +38,7 @@ use codec::{Decode, Encode}; use log::*; use crate::rpc::{ - api::FillerChainApi, + api::SideChainApi, author::{Author, AuthorApi}, basic_pool::BasicPool, }; @@ -57,7 +57,7 @@ use chain_relay::Block; use substratee_node_primitives::Request; use substratee_worker_primitives::RpcReturnValue; -use substratee_worker_primitives::{TrustedOperationStatus, DirectCallStatus}; +use substratee_worker_primitives::{TrustedOperationStatus, DirectRequestStatus}; use crate::utils::write_slice_and_whitespace_pad; use crate::rsa3072; @@ -84,9 +84,9 @@ extern "C" { #[no_mangle] // initialise tx pool and store within static atomic pointer pub unsafe extern "C" fn initialize_pool() -> sgx_status_t { - let api = Arc::new(FillerChainApi::new()); + let api = Arc::new(SideChainApi::new()); let tx_pool = BasicPool::create(PoolOptions::default(), api); - let pool_ptr = Arc::new(SgxMutex::, Block>>::new( + let pool_ptr = Arc::new(SgxMutex::, Block>>::new( tx_pool, )); let ptr = Arc::into_raw(pool_ptr); @@ -95,9 +95,9 @@ pub unsafe extern "C" fn initialize_pool() -> sgx_status_t { sgx_status_t::SGX_SUCCESS } -pub fn load_top_pool() -> Option<&'static SgxMutex, Block>>> { +pub fn load_top_pool() -> Option<&'static SgxMutex, Block>>> { let ptr = GLOBAL_TX_POOL.load(Ordering::SeqCst) - as *mut SgxMutex, Block>>; + as *mut SgxMutex, Block>>; if ptr.is_null() { None } else { @@ -134,7 +134,7 @@ fn compute_encoded_return_error(error_msg: String) -> Vec { let return_value = RpcReturnValue { value: error_msg.encode(), do_watch: false, - status: DirectCallStatus::Error, + status: DirectRequestStatus::Error, }; return_value.encode() } @@ -173,7 +173,7 @@ fn init_io_handler() -> IoHandler { RpcReturnValue { do_watch: true, value: hash_value.encode(), - status: DirectCallStatus::TrustedOperationStatus(TrustedOperationStatus::Submitted), + status: DirectRequestStatus::TrustedOperationStatus(TrustedOperationStatus::Submitted), }.encode() }, Err(rpc_error) => compute_encoded_return_error(rpc_error.message) @@ -220,7 +220,7 @@ fn init_io_handler() -> IoHandler { RpcReturnValue { do_watch: false, value: hash_value.encode(), - status: DirectCallStatus::TrustedOperationStatus(TrustedOperationStatus::Submitted), + status: DirectRequestStatus::TrustedOperationStatus(TrustedOperationStatus::Submitted), }.encode() }, Err(rpc_error) => compute_encoded_return_error(rpc_error.message) @@ -259,26 +259,26 @@ fn init_io_handler() -> IoHandler { }; if let Ok(vec_of_operations) = author.pending_tops(shard) { retrieved_operations.push(vec_of_operations); - } + } } let json_value = RpcReturnValue { do_watch: false, value: retrieved_operations.encode(), - status: DirectCallStatus::Ok, + status: DirectRequestStatus::Ok, }; Ok(json!(json_value.encode())) } Err(e) => { let error_msg: String = format!("Could not retrieve pending calls due to: {}", e); Ok(json!(compute_encoded_return_error(error_msg))) - } + } } }); // author_getShieldingKey let rsa_pubkey_name: &str = "author_getShieldingKey"; rpc_methods_vec.push(rsa_pubkey_name); - io.add_sync_method(rsa_pubkey_name, move |_: Params| { + io.add_sync_method(rsa_pubkey_name, move |_: Params| { let rsa_pubkey = match rsa3072::unseal_pubkey() { Ok(key) => key, Err(status) => { @@ -286,7 +286,7 @@ fn init_io_handler() -> IoHandler { return Ok(json!(compute_encoded_return_error(error_msg))) }, }; - + let rsa_pubkey_json = match serde_json::to_string(&rsa_pubkey) { Ok(k) => k, Err(x) => { @@ -296,11 +296,10 @@ fn init_io_handler() -> IoHandler { return Ok(json!(compute_encoded_return_error(error_msg))) }, }; - let json_value = RpcReturnValue::new(rsa_pubkey_json.encode(), false, DirectCallStatus::Ok); - Ok(json!(json_value.encode())) + let json_value = RpcReturnValue::new(rsa_pubkey_json.encode(), false, DirectRequestStatus::Ok); + Ok(json!(json_value.encode())) }); - // chain_subscribeAllHeads let chain_subscribe_all_heads_name: &str = "chain_subscribeAllHeads"; rpc_methods_vec.push(chain_subscribe_all_heads_name); diff --git a/enclave/src/tests.rs b/enclave/src/tests.rs index 15af3ee12e..4419ff0b87 100644 --- a/enclave/src/tests.rs +++ b/enclave/src/tests.rs @@ -3,7 +3,10 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -50,13 +53,16 @@ use sp_runtime::traits::Header as HeaderT; use sgx_externalities::SgxExternalitiesTypeTrait; use substratee_stf::StateTypeDiff as StfStateTypeDiff; use substratee_stf::{ShardIdentifier, Stf, TrustedCall}; +use substratee_stf::{TrustedGetter, TrustedOperation, Index}; +use substratee_stf::sgx::{AccountData, AccountInfo}; use jsonrpc_core::futures::executor; use sp_core::ed25519 as spEd25519; -use substratee_stf::{TrustedGetter, TrustedOperation}; + use rpc::author::{Author, AuthorApi}; -use rpc::{api::FillerChainApi, basic_pool::BasicPool}; +use rpc::{api::SideChainApi, basic_pool::BasicPool}; + #[no_mangle] pub extern "C" fn test_main_entrance() -> size_t { @@ -81,31 +87,41 @@ pub extern "C" fn test_main_entrance() -> size_t { top_pool::pool::test_should_notify_about_pool_events, top_pool::pool::test_should_clear_stale_transactions, top_pool::pool::test_should_ban_mined_transactions, + //FIXME: This test sometimes fails, sometimes succeeds.. top_pool::pool::test_should_limit_futures, top_pool::pool::test_should_error_if_reject_immediately, top_pool::pool::test_should_reject_transactions_with_no_provides, + top_pool::ready::tests::test_should_replace_transaction_that_provides_the_same_tag, + top_pool::ready::tests::test_should_replace_multiple_transactions_correctly, + top_pool::ready::tests::test_should_return_best_transactions_in_correct_order, + top_pool::ready::tests::test_should_order_refs, top_pool::rotator::tests::test_should_not_ban_if_not_stale, top_pool::rotator::tests::test_should_ban_stale_extrinsic, top_pool::rotator::tests::test_should_clear_banned, top_pool::rotator::tests::test_should_garbage_collect, + top_pool::tracked_map::tests::test_basic, state::test_write_and_load_state_works, state::test_sgx_state_decode_encode_works, state::test_encrypt_decrypt_state_type_works, test_time_is_overdue, test_time_is_not_overdue, test_compose_block_and_confirmation, - // these unit tests need an ipfs node running.. - // hence not really unit tests..? - //ipfs::test_creates_ipfs_content_struct_works, - //ipfs::test_verification_ok_for_correct_content, - //ipfs::test_verification_fails_for_incorrect_content, - //test_ocall_read_write_ipfs, - test_ocall_worker_request, test_submit_trusted_call_to_top_pool, test_submit_trusted_getter_to_top_pool, test_differentiate_getter_and_call_works, test_create_block_and_confirmation_works, + + // needs node to be running.. unit tests? + test_ocall_worker_request, test_create_state_diff, + test_executing_call_updates_account_nonce, + test_invalid_nonce_call_is_not_executed + + // these unit tests (?) need an ipfs node running.. + //ipfs::test_creates_ipfs_content_struct_works, + //ipfs::test_verification_ok_for_correct_content, + //ipfs::test_verification_fails_for_incorrect_content, + //test_ocall_read_write_ipfs, ) } @@ -219,6 +235,7 @@ fn test_time_is_not_overdue() { #[allow(unused)] fn test_compose_block_and_confirmation() { // given + ensure_no_empty_shard_directory_exists(); let latest_onchain_header = Header::new( 1, Default::default(), @@ -257,19 +274,23 @@ fn test_compose_block_and_confirmation() { assert!(stripped_opaque_call.starts_with(&shard.encode())); let stripped_opaque_call = stripped_opaque_call.split_off(shard.encode().len()); assert!(stripped_opaque_call.starts_with(&block_hash_encoded)); + + // clean up + state::remove_shard_dir(&shard); } #[allow(unused)] fn test_submit_trusted_call_to_top_pool() { // given + ensure_no_empty_shard_directory_exists(); // create top pool - let api: Arc> = Arc::new(FillerChainApi::new()); + let api: Arc> = Arc::new(SideChainApi::new()); let tx_pool = BasicPool::create(Default::default(), api); let author = Author::new(Arc::new(&tx_pool)); // create trusted call signed - let nonce = 1; + let nonce = 0; let mrenclave = [0u8; 32]; let shard = ShardIdentifier::default(); // ensure state starts empty @@ -304,14 +325,18 @@ fn test_submit_trusted_call_to_top_pool() { let call_one = format! {"{:?}", calls[0]}; let call_two = format! {"{:?}", signed_call}; assert_eq!(call_one, call_two); + + // clean up + state::remove_shard_dir(&shard); } #[allow(unused)] fn test_submit_trusted_getter_to_top_pool() { // given + ensure_no_empty_shard_directory_exists(); // create top pool - let api: Arc> = Arc::new(FillerChainApi::new()); + let api: Arc> = Arc::new(SideChainApi::new()); let tx_pool = BasicPool::create(Default::default(), api); let author = Author::new(Arc::new(&tx_pool)); @@ -344,14 +369,18 @@ fn test_submit_trusted_getter_to_top_pool() { let getter_one = format! {"{:?}", getters[0]}; let getter_two = format! {"{:?}", signed_getter}; assert_eq!(getter_one, getter_two); + + // clean up + state::remove_shard_dir(&shard); } #[allow(unused)] fn test_differentiate_getter_and_call_works() { // given + ensure_no_empty_shard_directory_exists(); // create top pool - let api: Arc> = Arc::new(FillerChainApi::new()); + let api: Arc> = Arc::new(SideChainApi::new()); let tx_pool = BasicPool::create(Default::default(), api); let author = Author::new(Arc::new(&tx_pool)); // create trusted getter signed @@ -372,7 +401,7 @@ fn test_differentiate_getter_and_call_works() { .unwrap(); // create trusted call signed - let nonce = 1; + let nonce = 0; let mrenclave = [0u8; 32]; let call = TrustedCall::balance_set_balance( signer_pair.public().into(), @@ -408,13 +437,15 @@ fn test_differentiate_getter_and_call_works() { let call_two = format! {"{:?}", signed_call}; assert_eq!(call_one, call_two); assert_eq!(getter_one, getter_two); + + // clean up + state::remove_shard_dir(&shard); } #[allow(unused)] #[allow(unused_assignments)] fn test_create_block_and_confirmation_works() { // given - ensure_no_empty_shard_directory_exists(); // create top pool @@ -442,8 +473,7 @@ fn test_create_block_and_confirmation_works() { let author = Arc::new(Author::new(pool)); // create trusted call signed - let nonce = 1; - //let mrenclave = [0u8; 32]; + let nonce = 0; let mrenclave = attestation::get_mrenclave_of_self().unwrap().m; let signer_pair = ed25519::unseal_pair().unwrap(); let call = TrustedCall::balance_transfer( @@ -517,9 +547,9 @@ fn test_create_state_diff() { let account_with_money = pair_with_money.public(); let account_without_money = signer_without_money.public(); let account_with_money_key_hash = - substratee_stf::sgx::nonce_key_hash(&account_with_money.into()); + substratee_stf::sgx::account_key_hash(&account_with_money.into()); let account_without_money_key_hash = - substratee_stf::sgx::nonce_key_hash(&account_without_money.into()); + substratee_stf::sgx::account_key_hash(&account_without_money.into()); let _prev_state_hash = state::write(state.clone(), &shard).unwrap(); // load top pool @@ -530,7 +560,7 @@ fn test_create_state_diff() { let author = Arc::new(Author::new(pool)); // create trusted call signed - let nonce = 1; + let nonce = 0; let mrenclave = attestation::get_mrenclave_of_self().unwrap().m; let call = TrustedCall::balance_transfer( account_with_money.into(), @@ -565,7 +595,7 @@ fn test_create_state_diff() { .as_ref() .unwrap(); let new_balance_acc_with_money = - substratee_stf::sgx::AccountInfo::decode(&mut acc_info_vec.as_slice()) + AccountInfo::decode(&mut acc_info_vec.as_slice()) .unwrap() .data .free; @@ -575,7 +605,7 @@ fn test_create_state_diff() { .as_ref() .unwrap(); let new_balance_acc_wo_money = - substratee_stf::sgx::AccountInfo::decode(&mut acc_info_vec.as_slice()) + AccountInfo::decode(&mut acc_info_vec.as_slice()) .unwrap() .data .free; @@ -590,6 +620,161 @@ fn test_create_state_diff() { assert_eq!(new_balance_acc_with_money, 1000); assert_eq!(new_block_number, 1); + // clean up + state::remove_shard_dir(&shard); +} + +#[allow(unused)] +fn test_executing_call_updates_account_nonce() { + // given + + ensure_no_empty_shard_directory_exists(); + + // create top pool + unsafe { rpc::worker_api_direct::initialize_pool() }; + let shard = ShardIdentifier::default(); + // Header::new(Number, extrinsicroot, stateroot, parenthash, digest) + let latest_onchain_header = Header::new( + 1, + Default::default(), + Default::default(), + [69; 32].into(), + Default::default(), + ); + let _rsa_pair = rsa3072::unseal_pair().unwrap(); + + // ensure that state starts empty + state::init_shard(&shard).unwrap(); + let mut state = Stf::init_state(); + + // create accounts + let signer_without_money = ed25519::unseal_pair().unwrap(); + let pair_with_money = spEd25519::Pair::from_seed(b"12345678901234567890123456789012"); + let account_with_money = pair_with_money.public(); + let account_without_money = signer_without_money.public(); + let account_with_money_key_hash = + substratee_stf::sgx::account_key_hash(&account_with_money.into()); + let account_without_money_key_hash = + substratee_stf::sgx::account_key_hash(&account_without_money.into()); + + let _prev_state_hash = state::write(state.clone(), &shard).unwrap(); + // load top pool + { + let &ref pool_mutex = rpc::worker_api_direct::load_top_pool().unwrap(); + let pool_guard = pool_mutex.lock().unwrap(); + let pool = Arc::new(pool_guard.deref()); + let author = Arc::new(Author::new(pool.clone())); + + // create trusted call signed + let nonce = 0; + let mrenclave = attestation::get_mrenclave_of_self().unwrap().m; + let call = TrustedCall::balance_transfer( + account_with_money.into(), + account_without_money.into(), + 1000, + ); + let signed_call = call.sign(&pair_with_money.into(), nonce, &mrenclave, &shard); + let trusted_operation: TrustedOperation = signed_call.clone().into_trusted_operation(true); + // encrypt call + let mut encrypted_top: Vec = Vec::new(); + let rsa_pubkey = rsa3072::unseal_pubkey().unwrap(); + rsa_pubkey + .encrypt_buffer(&trusted_operation.encode(), &mut encrypted_top) + .unwrap(); + + // submit trusted call to top pool + let result = async { author.submit_top(encrypted_top.clone(), shard).await }; + executor::block_on(result).unwrap(); + } + + // when + let (_, signed_blocks) = crate::execute_top_pool_calls(latest_onchain_header).unwrap(); + + // then + let mut state = state::load(&shard).unwrap(); + let nonce = Stf::account_nonce(&mut state, &account_with_money.into()); + assert_eq!(nonce, 1); + + // clean up + state::remove_shard_dir(&shard); +} + + +#[allow(unused)] +fn test_invalid_nonce_call_is_not_executed() { + // given + + ensure_no_empty_shard_directory_exists(); + + // create top pool + unsafe { rpc::worker_api_direct::initialize_pool() }; + let shard = ShardIdentifier::default(); + // Header::new(Number, extrinsicroot, stateroot, parenthash, digest) + let latest_onchain_header = Header::new( + 1, + Default::default(), + Default::default(), + [69; 32].into(), + Default::default(), + ); + let _rsa_pair = rsa3072::unseal_pair().unwrap(); + + // ensure that state starts empty + state::init_shard(&shard).unwrap(); + let mut state = Stf::init_state(); + + // create accounts + let signer_without_money = ed25519::unseal_pair().unwrap(); + let pair_with_money = spEd25519::Pair::from_seed(b"12345678901234567890123456789012"); + let account_with_money = pair_with_money.public(); + let account_without_money = signer_without_money.public(); + let account_with_money_key_hash = + substratee_stf::sgx::account_key_hash(&account_with_money.into()); + let account_without_money_key_hash = + substratee_stf::sgx::account_key_hash(&account_without_money.into()); + + let _prev_state_hash = state::write(state.clone(), &shard).unwrap(); + // load top pool + { + let &ref pool_mutex = rpc::worker_api_direct::load_top_pool().unwrap(); + let pool_guard = pool_mutex.lock().unwrap(); + let pool = Arc::new(pool_guard.deref()); + let author = Arc::new(Author::new(pool.clone())); + + // create trusted call signed + let nonce = 10; + let mrenclave = attestation::get_mrenclave_of_self().unwrap().m; + let call = TrustedCall::balance_transfer( + account_with_money.into(), + account_without_money.into(), + 1000, + ); + let signed_call = call.sign(&pair_with_money.into(), nonce, &mrenclave, &shard); + let trusted_operation: TrustedOperation = signed_call.clone().into_trusted_operation(true); + // encrypt call + let mut encrypted_top: Vec = Vec::new(); + let rsa_pubkey = rsa3072::unseal_pubkey().unwrap(); + rsa_pubkey + .encrypt_buffer(&trusted_operation.encode(), &mut encrypted_top) + .unwrap(); + + // submit trusted call to top pool + let result = async { author.submit_top(encrypted_top.clone(), shard).await }; + executor::block_on(result).unwrap(); + } + + // when + let (_, signed_blocks) = crate::execute_top_pool_calls(latest_onchain_header).unwrap(); + + // then + let mut state = state::load(&shard).unwrap(); + let nonce = Stf::account_nonce(&mut state, &account_with_money.into()); + assert_eq!(nonce, 0); + + let acc_data_with_money = Stf::account_data(&mut state, &account_with_money.into()).unwrap(); + assert_eq!(acc_data_with_money.free, 2000); + + // clean up state::remove_shard_dir(&shard); } \ No newline at end of file diff --git a/enclave/src/top_pool.rs b/enclave/src/top_pool.rs index a1bd2da7da..1603313191 100644 --- a/enclave/src/top_pool.rs +++ b/enclave/src/top_pool.rs @@ -1,7 +1,7 @@ mod future; mod listener; -mod ready; -mod tracked_map; + + mod validated_pool; mod watcher; @@ -10,3 +10,5 @@ pub mod error; pub mod pool; pub mod primitives; pub mod rotator; // pub necessary for unit tests +pub mod ready; // pub necessary for unit tests +pub mod tracked_map; // pub necessary for unit tests diff --git a/enclave/src/top_pool/base_pool.rs b/enclave/src/top_pool/base_pool.rs index 489a20e4b9..695442f526 100644 --- a/enclave/src/top_pool/base_pool.rs +++ b/enclave/src/top_pool/base_pool.rs @@ -89,8 +89,7 @@ pub struct PruneStatus { } /// Immutable operation -#[cfg_attr(test, derive(Clone))] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone)] pub struct TrustedOperation { /// Raw extrinsic representing that operation. pub data: Extrinsic, @@ -784,7 +783,7 @@ pub fn test_should_promote_a_subgraph() { ); } -pub fn test_should_handle_a_cycle() { +pub fn test_should_handle_a_cycle() { // given let shard = ShardIdentifier::default(); let mut pool = test_pool(); @@ -1269,7 +1268,7 @@ pub fn test_should_clear_future_queue() { assert_eq!(pool.future.len(shard), 0); } -pub fn test_should_accept_future_transactions_when_explicitly_asked_to() { +pub fn test_should_accept_future_transactions_when_explicitly_asked_to() { // given let mut pool = test_pool(); pool.reject_future_operations = true; diff --git a/enclave/src/top_pool/pool.rs b/enclave/src/top_pool/pool.rs index f156637671..0ca10c027f 100644 --- a/enclave/src/top_pool/pool.rs +++ b/enclave/src/top_pool/pool.rs @@ -30,7 +30,7 @@ use crate::top_pool::{ validated_pool::{ValidatedOperation, ValidatedPool}, }; -use substratee_stf::{ShardIdentifier, TrustedOperation as StfTrustedOperation}; +use substratee_stf::{ShardIdentifier, TrustedOperation as StfTrustedOperation, Index}; use substratee_worker_primitives::BlockHash as SidechainBlockHash; /// Modification notification event stream type; @@ -67,9 +67,9 @@ pub trait ChainApi: Send + Sync { /// Verify extrinsic at given block. fn validate_transaction( &self, - at: &BlockId, source: TrustedOperationSource, uxt: StfTrustedOperation, + shard: ShardIdentifier, ) -> Self::ValidationFuture; /// Returns a block number given the block id. @@ -304,9 +304,9 @@ where .validated_pool .api() .validate_transaction( - parent, TrustedOperationSource::InBlock, extrinsic.clone(), + shard, ) .await; @@ -416,6 +416,7 @@ where check: CheckBannedBeforeVerify, shard: ShardIdentifier, ) -> Result, ValidatedOperationFor>, B::Error> { + //FIXME: Nicer verify // we need a block number to compute tx validity //let block_number = self.resolve_block_number(at)?; // dummy blocknumber @@ -454,11 +455,12 @@ where return (hash.clone(), ValidatedOperation::Invalid(hash, err.into())); } - // no runtime validation check for now. Issue is open. + //FIXME: + // no runtime validation check for now. let validation_result = self .validated_pool .api() - .validate_transaction(block_id, source, xt.clone()) + .validate_transaction(source, xt.clone(), shard) .await; let status = match validation_result { @@ -579,7 +581,7 @@ pub mod test { pub type Block = sp_runtime::generic::Block; } -const INVALID_NONCE: u32 = 254; +const INVALID_NONCE: Index = 254; const SOURCE: TrustedOperationSource = TrustedOperationSource::External; #[derive(Clone, Debug, Default)] @@ -599,13 +601,12 @@ impl ChainApi for TestApi { /// Verify extrinsic at given block. fn validate_transaction( &self, - at: &BlockId, _source: TrustedOperationSource, uxt: StfTrustedOperation, + _shard: ShardIdentifier, ) -> Self::ValidationFuture { let hash = self.hash_and_length(&uxt).0; - let block_number = self.block_id_to_number(at).unwrap().unwrap() as u32; - let nonce: u32 = match uxt { + let nonce: Index = match uxt { StfTrustedOperation::direct_call(signed_call) => signed_call.nonce, _ => 0, }; @@ -623,12 +624,13 @@ impl ChainApi for TestApi { if self.invalidate.lock().unwrap().contains(&hash) { return futures::future::ready(Ok(InvalidTrustedOperation::Custom(0).into())); } - futures::future::ready(if nonce < block_number { + + futures::future::ready(if nonce > 254 { Ok(InvalidTrustedOperation::Stale.into()) } else { let mut operation = ValidTransaction { priority: 4, - requires: if nonce > block_number { + requires: if nonce > 0 { vec![vec![nonce as u8 - 1]] } else { vec![] @@ -689,9 +691,8 @@ impl ChainApi for TestApi { } } -fn to_top(call: TrustedCall, nonce: u32) -> TrustedOperation { - TrustedCallSigned::new(call, nonce, Default::default()) - .into_trusted_operation(true) +fn to_top(call: TrustedCall, nonce: Index) -> TrustedOperation { + TrustedCallSigned::new(call, nonce, Default::default()).into_trusted_operation(true) } fn test_pool() -> Pool { diff --git a/enclave/src/top_pool/ready.rs b/enclave/src/top_pool/ready.rs index 560fb7d6ce..cd57483eeb 100644 --- a/enclave/src/top_pool/ready.rs +++ b/enclave/src/top_pool/ready.rs @@ -658,10 +658,15 @@ fn remove_item(vec: &mut Vec, item: &T) { vec.swap_remove(idx); } } -/* -#[cfg(test)] -mod tests { - use super::*; + +pub mod tests { + use super::*; + + use crate::top_pool::{ + primitives::{TrustedOperationSource as Source}, + }; + + fn tx(id: u8) -> TrustedOperation> { TrustedOperation { @@ -686,8 +691,7 @@ mod tests { ready.import(x, shard) } - #[test] - fn should_replace_transaction_that_provides_the_same_tag() { + pub fn test_should_replace_transaction_that_provides_the_same_tag() { // given let shard = ShardIdentifier::default(); let mut ready = ReadyOperations::default(); @@ -715,8 +719,7 @@ mod tests { assert_eq!(ready.get(shard).count(), 1); } - #[test] - fn should_replace_multiple_transactions_correctly() { + pub fn test_should_replace_multiple_transactions_correctly() { // given let shard = ShardIdentifier::default(); let mut ready = ReadyOperations::default(); @@ -753,8 +756,7 @@ mod tests { assert_eq!(ready.get(shard).count(), 3); } - #[test] - fn should_return_best_transactions_in_correct_order() { + pub fn test_should_return_best_transactions_in_correct_order() { // given let shard = ShardIdentifier::default(); let mut ready = ReadyOperations::default(); @@ -799,26 +801,7 @@ mod tests { assert_eq!(it.next(), None); } - #[test] - /*fn can_report_heap_size() { - let mut ready = ReadyOperations::default(); - let tx = TrustedOperation { - data: vec![5], - bytes: 1, - hash: 5, - priority: 1, - valid_till: u64::max_value(), // use the max_value() here for testing. - requires: vec![], - provides: vec![], - propagate: true, - source: Source::External, - }; - import(&mut ready, tx).unwrap(); - - assert!(parity_util_mem::malloc_size(&ready) > 200); - }*/ - #[test] - fn should_order_refs() { + pub fn test_should_order_refs() { let mut id = 1; let mut with_priority = |priority, longevity| { id += 1; @@ -858,5 +841,4 @@ mod tests { } ); } -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/enclave/src/top_pool/tracked_map.rs b/enclave/src/top_pool/tracked_map.rs index 8338cb7fa2..209fca85d8 100644 --- a/enclave/src/top_pool/tracked_map.rs +++ b/enclave/src/top_pool/tracked_map.rs @@ -166,19 +166,16 @@ where self.inner_guard.get_mut(key) } } -/* -#[cfg(test)] -mod tests { + +pub mod tests { use super::*; impl Size for i32 { fn size(&self) -> usize { *self as usize / 10 } } - - #[test] - fn basic() { - let map = TrackedMap::default(); + pub fn test_basic() { + let mut map = TrackedMap::default(); map.write().insert(5, 10); map.write().insert(6, 20); @@ -194,4 +191,4 @@ mod tests { assert_eq!(map.bytes(), 1); assert_eq!(map.len(), 1); } -}*/ +} \ No newline at end of file diff --git a/stf/src/cli.rs b/stf/src/cli.rs index 48dc545fd0..089920f1a5 100644 --- a/stf/src/cli.rs +++ b/stf/src/cli.rs @@ -15,7 +15,7 @@ */ -use crate::{AccountId, KeyPair, ShardIdentifier, TrustedCall, TrustedGetter, TrustedOperation}; +use crate::{AccountId, KeyPair, ShardIdentifier, TrustedCall, TrustedGetter, TrustedOperation, Index}; use base58::{FromBase58, ToBase58}; use clap::{AppSettings, Arg, ArgMatches}; use clap_nested::{Command, Commander, MultiCommand}; @@ -158,9 +158,25 @@ pub fn cmd<'a>( amount ); let (mrenclave, shard) = get_identifiers(matches); - let nonce = 0; // FIXME: hard coded for now - // generate trusted call signed + // get nonce let key_pair = sr25519_core::Pair::from(from.clone()); + let top: TrustedOperation = TrustedGetter::nonce( + sr25519_core::Public::from(from.public()).into(), + ) + .sign(&KeyPair::Sr25519(key_pair.clone())) + .into(); + let res = perform_operation(matches, &top); + let nonce: Index = if let Some(n) = res { + if let Ok(nonce) = Index::decode(&mut n.as_slice()) { + nonce + } else { + info!("could not decode value. maybe hasn't been set? {:x?}", n); + 0 + } + } else { + 0 + }; + debug!("got nonce: {:?}", nonce); let top: TrustedOperation = TrustedCall::balance_transfer( sr25519_core::Public::from(from.public()).into(), to, @@ -207,10 +223,25 @@ pub fn cmd<'a>( ); let (mrenclave, shard) = get_identifiers(matches); - let key_pair = sr25519_core::Pair::from(signer.clone()); - - let nonce = 0; // FIXME: hard coded for now - + // get nonce + let key_pair = sr25519_core::Pair::from(who.clone()); + let top: TrustedOperation = TrustedGetter::nonce( + sr25519_core::Public::from(who.public()).into(), + ) + .sign(&KeyPair::Sr25519(key_pair.clone())) + .into(); + let res = perform_operation(matches, &top); + let nonce: Index = if let Some(n) = res { + if let Ok(nonce) = Index::decode(&mut n.as_slice()) { + nonce + } else { + info!("could not decode value. maybe hasn't been set? {:x?}", n); + 0 + } + } else { + 0 + }; + debug!("got nonce: {:?}", nonce); let top: TrustedOperation = TrustedCall::balance_set_balance( sr25519_core::Public::from(signer.public()).into(), sr25519_core::Public::from(who.public()).into(), @@ -312,9 +343,25 @@ pub fn cmd<'a>( ); let (mrenclave, shard) = get_identifiers(matches); - let nonce = 0; // FIXME: hard coded for now + // get nonce let key_pair = sr25519_core::Pair::from(from.clone()); - + let top: TrustedOperation = TrustedGetter::nonce( + sr25519_core::Public::from(from.public()).into(), + ) + .sign(&KeyPair::Sr25519(key_pair.clone())) + .into(); + let res = perform_operation(matches, &top); + let nonce: Index = if let Some(n) = res { + if let Ok(nonce) = Index::decode(&mut n.as_slice()) { + nonce + } else { + info!("could not decode value. maybe hasn't been set? {:x?}", n); + 0 + } + } else { + 0 + }; + debug!("got nonce: {:?}", nonce); let top: TrustedOperation = TrustedCall::balance_unshield( sr25519_core::Public::from(from.public()).into(), to, diff --git a/stf/src/lib.rs b/stf/src/lib.rs index bd77bf340e..ce9918facc 100644 --- a/stf/src/lib.rs +++ b/stf/src/lib.rs @@ -33,6 +33,11 @@ use codec::{Compact, Decode, Encode}; use my_node_runtime::Balance; #[cfg(feature = "sgx")] use sgx_runtime::Balance; +#[cfg(feature = "std")] +pub use my_node_runtime::Index; +#[cfg(feature = "sgx")] +pub use sgx_runtime::Index; + use sp_core::crypto::AccountId32; //use sp_core::{Encode, Decode}; use sp_core::{ed25519, sr25519, Pair, H256}; @@ -53,6 +58,7 @@ pub static UNSHIELD: u8 = 5u8; pub static CALL_CONFIRMED: u8 = 3u8; pub type ShardIdentifier = H256; +//pub type Index = u32; #[derive(Clone)] pub enum KeyPair { @@ -162,7 +168,7 @@ pub enum TrustedCall { } impl TrustedCall { - fn account(&self) -> &AccountId { + pub fn account(&self) -> &AccountId { match self { TrustedCall::balance_set_balance(account, _, _, _) => account, TrustedCall::balance_transfer(account, _, _) => account, @@ -174,7 +180,7 @@ impl TrustedCall { pub fn sign( &self, pair: &KeyPair, - nonce: u32, + nonce: Index, mrenclave: &[u8; 32], shard: &ShardIdentifier, ) -> TrustedCallSigned { @@ -196,6 +202,7 @@ impl TrustedCall { pub enum TrustedGetter { free_balance(AccountId), reserved_balance(AccountId), + nonce(AccountId), } impl TrustedGetter { @@ -203,6 +210,7 @@ impl TrustedGetter { match self { TrustedGetter::free_balance(account) => account, TrustedGetter::reserved_balance(account) => account, + TrustedGetter::nonce(account) => account, } } @@ -235,12 +243,12 @@ impl TrustedGetterSigned { #[derive(Encode, Decode, Clone, Debug)] pub struct TrustedCallSigned { pub call: TrustedCall, - pub nonce: u32, + pub nonce: Index, pub signature: Signature, } impl TrustedCallSigned { - pub fn new(call: TrustedCall, nonce: u32, signature: Signature) -> Self { + pub fn new(call: TrustedCall, nonce: Index, signature: Signature) -> Self { TrustedCallSigned { call, nonce, diff --git a/stf/src/sgx.rs b/stf/src/sgx.rs index f7f77e74f9..bc542b2774 100644 --- a/stf/src/sgx.rs +++ b/stf/src/sgx.rs @@ -19,7 +19,7 @@ use support::traits::UnfilteredDispatchable; use crate::{ AccountId, Getter, PublicGetter, ShardIdentifier, State, Stf, TrustedCall, TrustedCallSigned, - TrustedGetter, SUBSRATEE_REGISTRY_MODULE, UNSHIELD, + TrustedGetter, SUBSRATEE_REGISTRY_MODULE, UNSHIELD, Index, }; /// Simple blob that holds a call in encoded format @@ -32,8 +32,7 @@ impl Encode for OpaqueCall { } } -type Index = u32; -type AccountData = balances::AccountData; +pub type AccountData = balances::AccountData; pub type AccountInfo = system::AccountInfo; const ALICE_ENCODED: [u8; 32] = [ @@ -200,75 +199,105 @@ impl Stf { calls: &mut Vec, ) -> Result<(), StfError> { let call_hash = blake2_256(&call.encode()); - ext.execute_with(|| match call.call { - TrustedCall::balance_set_balance(root, who, free_balance, reserved_balance) => { - Self::ensure_root(root)?; - debug!( - "balance_set_balance({:x?}, {}, {})", - who.encode(), - free_balance, - reserved_balance - ); - sgx_runtime::BalancesCall::::set_balance( - MultiAddress::Id(AccountId32::from(who)), - free_balance, - reserved_balance, - ) - .dispatch_bypass_filter(sgx_runtime::Origin::root()) - .map_err(|_| StfError::Dispatch("balance_set_balance".to_string()))?; - Ok(()) - } - TrustedCall::balance_transfer(from, to, value) => { - let origin = sgx_runtime::Origin::signed(AccountId32::from(from.clone())); - debug!( - "balance_transfer({:x?}, {:x?}, {})", - from.encode(), - to.encode(), - value - ); - if let Some(info) = get_account_info(&from) { - debug!("sender balance is {}", info.data.free); - } else { - debug!("sender balance is zero"); + ext.execute_with(|| { + let sender = call.call.account().clone(); + validate_nonce(&sender, call.nonce)?; + let result = match call.call { + TrustedCall::balance_set_balance(root, who, free_balance, reserved_balance) => { + Self::ensure_root(root)?; + debug!( + "balance_set_balance({:x?}, {}, {})", + who.encode(), + free_balance, + reserved_balance + ); + sgx_runtime::BalancesCall::::set_balance( + MultiAddress::Id(AccountId32::from(who)), + free_balance, + reserved_balance, + ) + .dispatch_bypass_filter(sgx_runtime::Origin::root()) + .map_err(|_| StfError::Dispatch("balance_set_balance".to_string()))?; + Ok(()) } - sgx_runtime::BalancesCall::::transfer( - MultiAddress::Id(AccountId32::from(to)), - value, - ) - .dispatch_bypass_filter(origin) - .map_err(|_| StfError::Dispatch("balance_transfer".to_string()))?; - Ok(()) - } - TrustedCall::balance_unshield(account_incognito, beneficiary, value, shard) => { - debug!( - "balance_unshield({:x?}, {:x?}, {}, {})", - account_incognito.encode(), - beneficiary.encode(), - value, - shard - ); - - Self::unshield_funds(account_incognito, value)?; - calls.push(OpaqueCall( - ( - [SUBSRATEE_REGISTRY_MODULE, UNSHIELD], - beneficiary, + TrustedCall::balance_transfer(from, to, value) => { + let origin = sgx_runtime::Origin::signed(AccountId32::from(from.clone())); + debug!( + "balance_transfer({:x?}, {:x?}, {})", + from.encode(), + to.encode(), + value + ); + if let Some(info) = get_account_info(&from.clone()) { + debug!("sender balance is {}", info.data.free); + } else { + debug!("sender balance is zero"); + } + sgx_runtime::BalancesCall::::transfer( + MultiAddress::Id(AccountId32::from(to)), value, - shard, - call_hash, ) - .encode(), - )); - Ok(()) + .dispatch_bypass_filter(origin) + .map_err(|_| StfError::Dispatch("balance_transfer".to_string()))?; + Ok(()) + } + TrustedCall::balance_unshield(account_incognito, beneficiary, value, shard) => { + debug!( + "balance_unshield({:x?}, {:x?}, {}, {})", + account_incognito.encode(), + beneficiary.encode(), + value, + shard + ); + + Self::unshield_funds(account_incognito.clone(), value)?; + calls.push(OpaqueCall( + ( + [SUBSRATEE_REGISTRY_MODULE, UNSHIELD], + beneficiary, + value, + shard, + call_hash, + ) + .encode(), + )); + Ok(()) + } + TrustedCall::balance_shield(who, value) => { + debug!("balance_shield({:x?}, {})", who.encode(), value); + Self::shield_funds(who, value)?; + Ok(()) + } + }?; + increment_nonce(&sender); + Ok(()) + }) + } + + pub fn account_nonce(ext: &mut State, account: &AccountId) -> Index { + ext.execute_with(|| { + if let Some(info) = get_account_info(account) { + debug!("Account {:?} nonce is {}",account.encode(), info.nonce); + info.nonce + } else { + 0 as Index } - TrustedCall::balance_shield(who, value) => { - debug!("balance_shield({:x?}, {})", who.encode(), value); - Self::shield_funds(who, value)?; - Ok(()) + }) + } + + //FIXME: Add Test feature as this function is only used for unit testing currently + pub fn account_data(ext: &mut State, account: &AccountId) -> Option { + ext.execute_with(|| { + if let Some(info) = get_account_info(account) { + debug!("Account {:?} data is {:?}", account.encode(), info.data); + Some(info.data) + } else { + None } }) } + pub fn get_state(ext: &mut State, getter: Getter) -> Option> { ext.execute_with(|| match getter { Getter::trusted(g) => match g.getter { @@ -290,6 +319,15 @@ impl Stf { None } } + TrustedGetter::nonce(who) => { + if let Some(info) = get_account_info(&who) { + debug!("AccountInfo for {:x?} is {:?}", who.encode(), info); + debug!("Account nonce is {}" ,info.nonce); + Some(info.nonce.encode()) + } else { + None + } + } }, Getter::public(g) => match g { PublicGetter::some_value => Some(42u32.encode()), @@ -383,8 +421,8 @@ pub fn shards_key_hash() -> Vec { vec![] } -// get the AccountInfo key where the nonce is stored -pub fn nonce_key_hash(account: &AccountId) -> Vec { +// get the AccountInfo key where the account is stored +pub fn account_key_hash(account: &AccountId) -> Vec { storage_map_key( "System", "Account", @@ -410,6 +448,33 @@ fn get_account_info(who: &AccountId) -> Option { } } +fn validate_nonce(who: &AccountId, nonce: Index) -> Result<(), StfError> { + // validate + let expected_nonce = get_account_info(who) + .map_or_else(|| 0, |acc| acc.nonce); + if expected_nonce == nonce { + return Ok(()) + } + Err(StfError::InvalidNonce(nonce)) +} + +/// increment nonce after a successful call execution +fn increment_nonce(account: &AccountId) { + //FIXME: Proper error handling - should be taken into + // consideration after implementing pay fee check + if let Some(mut acc_info) = get_account_info(account) { + debug!("incrementing account nonce"); + acc_info.nonce += 1; + sp_io::storage::set( + &account_key_hash(account), + &acc_info.encode(), + ); + debug!("updated account {:?} nonce: {:?}", account.encode(), get_account_info(account).unwrap().nonce); + } else { + error!("tried to increment nonce of a non-existent account") + } +} + pub fn storage_value_key(module_prefix: &str, storage_prefix: &str) -> Vec { let mut bytes = sp_core::twox_128(module_prefix.as_bytes()).to_vec(); bytes.extend(&sp_core::twox_128(storage_prefix.as_bytes())[..]); @@ -475,4 +540,6 @@ pub enum StfError { MissingFunds, #[display(fmt = "Account does not exist {:?}", _0)] InexistentAccount(AccountId), + #[display(fmt = "Invalid Nonce {:?}", _0)] + InvalidNonce(Index), } diff --git a/substratee-worker-primitives/src/lib.rs b/substratee-worker-primitives/src/lib.rs index fda80d3990..09d057eefa 100644 --- a/substratee-worker-primitives/src/lib.rs +++ b/substratee-worker-primitives/src/lib.rs @@ -18,12 +18,12 @@ pub type ShardIdentifier = H256; //use sp_core::ed25519::Signature; #[derive(Debug, Clone, PartialEq, Encode, Decode)] -pub enum DirectCallStatus { - /// DirectCall was successfully executed +pub enum DirectRequestStatus { + /// Direct request was successfully executed Ok, /// Trusted Call Status TrustedOperationStatus(TrustedOperationStatus), - /// DirectCall could not be executed + /// Direct request could not be executed Error, } @@ -59,11 +59,11 @@ pub enum TrustedOperationStatus { pub struct RpcReturnValue { pub value: Vec, pub do_watch: bool, - pub status: DirectCallStatus, + pub status: DirectRequestStatus, //pub signature: Signature, } impl RpcReturnValue { - pub fn new(val: Vec, watch: bool, status: DirectCallStatus) -> Self { + pub fn new(val: Vec, watch: bool, status: DirectRequestStatus) -> Self { Self { value: val, do_watch: watch, diff --git a/worker/src/enclave/worker_api_direct_server.rs b/worker/src/enclave/worker_api_direct_server.rs index 84bf2ca086..bc416aa6ae 100644 --- a/worker/src/enclave/worker_api_direct_server.rs +++ b/worker/src/enclave/worker_api_direct_server.rs @@ -29,7 +29,7 @@ use std::thread; use ws::{listen, CloseCode, Handler, Message, Result, Sender}; use substratee_worker_primitives::{ - DirectCallStatus, RpcResponse, RpcReturnValue, TrustedOperationStatus, + DirectRequestStatus, RpcResponse, RpcReturnValue, TrustedOperationStatus, }; static WATCHED_LIST: AtomicPtr<()> = AtomicPtr::new(0 as *mut ()); @@ -177,7 +177,7 @@ pub fn handle_direct_invocation_request(req: DirectWsServerRequest) -> Result<() RpcReturnValue::decode(&mut full_rpc_response.result.as_slice()) { match result_of_rpc_response.status { - DirectCallStatus::TrustedOperationStatus(_) => { + DirectRequestStatus::TrustedOperationStatus(_) => { if result_of_rpc_response.do_watch { // start watching the call with the specific hash if let Ok(hash) = Hash::decode(&mut result_of_rpc_response.value.as_slice()) @@ -247,7 +247,7 @@ pub unsafe extern "C" fn ocall_update_status_event( _ => {} }; // update response - result.status = DirectCallStatus::TrustedOperationStatus(status_update); + result.status = DirectRequestStatus::TrustedOperationStatus(status_update); event.result = result.encode(); client_event .client @@ -287,7 +287,7 @@ pub unsafe extern "C" fn ocall_send_status( // create return value // TODO: Signature? let submitted = - DirectCallStatus::TrustedOperationStatus(TrustedOperationStatus::Submitted); + DirectRequestStatus::TrustedOperationStatus(TrustedOperationStatus::Submitted); let result = RpcReturnValue::new(status_slice.to_vec(), false, submitted); // update response diff --git a/worker/src/main.rs b/worker/src/main.rs index 27baaec3b5..9ff4ad7d0f 100644 --- a/worker/src/main.rs +++ b/worker/src/main.rs @@ -68,7 +68,7 @@ mod tests; const BLOCK_SYNC_BATCH_SIZE: u32 = 1000; const VERSION: &str = env!("CARGO_PKG_VERSION"); /// start block production every ... ms -const BLOCK_PRODUCTION_INTERVAL: u64 = 6000; +const BLOCK_PRODUCTION_INTERVAL: u64 = 1000; fn main() { // Setup logging diff --git a/worker/src/tests/commons.rs b/worker/src/tests/commons.rs index 08e68b0a8a..c154bb292c 100644 --- a/worker/src/tests/commons.rs +++ b/worker/src/tests/commons.rs @@ -30,7 +30,7 @@ use std::{fs, str}; use crate::enclave::api::*; use crate::{enclave_account, ensure_account_has_funds}; use substrate_api_client::Api; -use substratee_stf::{KeyPair, ShardIdentifier, TrustedCall, TrustedGetter, TrustedGetterSigned}; +use substratee_stf::{KeyPair, ShardIdentifier, TrustedCall, TrustedGetter, TrustedGetterSigned, Index}; #[derive(Debug, Serialize, Deserialize)] pub struct Message { @@ -40,7 +40,7 @@ pub struct Message { } /// Who must be root account -pub fn encrypted_set_balance(eid: sgx_enclave_id_t, who: AccountKeyring, nonce: u32) -> Vec { +pub fn encrypted_set_balance(eid: sgx_enclave_id_t, who: AccountKeyring, nonce: Index) -> Vec { info!("*** Get the public key from the TEE\n"); let rsa_pubkey: Rsa3072PubKey = enclave_shielding_key(eid).unwrap(); info!("deserialized rsa key"); @@ -58,7 +58,7 @@ pub fn encrypted_set_balance(eid: sgx_enclave_id_t, who: AccountKeyring, nonce: ) } -pub fn encrypted_unshield(eid: sgx_enclave_id_t, who: AccountKeyring, nonce: u32) -> Vec { +pub fn encrypted_unshield(eid: sgx_enclave_id_t, who: AccountKeyring, nonce: Index) -> Vec { info!("*** Get the public key from the TEE\n"); let rsa_pubkey: Rsa3072PubKey = enclave_shielding_key(eid).unwrap(); info!("deserialized rsa key"); diff --git a/worker/worker-api/src/direct_client.rs b/worker/worker-api/src/direct_client.rs index 29b7860995..7c60e8d9e9 100644 --- a/worker/worker-api/src/direct_client.rs +++ b/worker/worker-api/src/direct_client.rs @@ -7,7 +7,7 @@ use codec::Decode; use ws::{connect, CloseCode, Handler, Handshake, Message, Result as ClientResult, Sender}; -use substratee_worker_primitives::{DirectCallStatus, RpcRequest, RpcResponse, RpcReturnValue}; +use substratee_worker_primitives::{DirectRequestStatus, RpcRequest, RpcResponse, RpcReturnValue}; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; @@ -123,7 +123,7 @@ impl DirectApi { } }; let shielding_pubkey_string: String = match return_value.status { - DirectCallStatus::Ok => match String::decode(&mut return_value.value.as_slice()) { + DirectRequestStatus::Ok => match String::decode(&mut return_value.value.as_slice()) { Ok(key) => key, Err(err) => return Err(format! {"Could not retrieve shielding pubkey: {:?}", err}), },