From 973789043e21f774aae47e4af3d4a6a01fc7fb0e Mon Sep 17 00:00:00 2001 From: Warren He Date: Wed, 9 Mar 2022 16:54:17 -0800 Subject: [PATCH 01/14] dispatcher: decode incoming messages --- runtime-sdk/src/context.rs | 20 ++++++ runtime-sdk/src/dispatcher.rs | 103 ++++++++++++++++++++++++++-- runtime-sdk/src/modules/core/mod.rs | 10 ++- runtime-sdk/src/types/in_msg.rs | 39 +++++++++++ runtime-sdk/src/types/mod.rs | 1 + 5 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 runtime-sdk/src/types/in_msg.rs diff --git a/runtime-sdk/src/context.rs b/runtime-sdk/src/context.rs index 48cde0a932..b68630e90f 100644 --- a/runtime-sdk/src/context.rs +++ b/runtime-sdk/src/context.rs @@ -269,6 +269,8 @@ pub struct RuntimeBatchContext<'a, R: runtime::Runtime, S: NestedStore> { max_messages: u32, /// Emitted messages. messages: Vec<(roothash::Message, MessageEventHookInvocation)>, + /// Number of processed incoming messages. + in_msgs_processed: usize, /// Per-context values. values: BTreeMap<&'static str, Box>, @@ -306,6 +308,7 @@ impl<'a, R: runtime::Runtime, S: NestedStore> RuntimeBatchContext<'a, R, S> { block_etags: EventTags::new(), max_messages, messages: Vec::new(), + in_msgs_processed: 0, values: BTreeMap::new(), _runtime: PhantomData, } @@ -337,10 +340,25 @@ impl<'a, R: runtime::Runtime, S: NestedStore> RuntimeBatchContext<'a, R, S> { block_etags: EventTags::new(), max_messages: ctx.max_messages, messages: Vec::new(), + in_msgs_processed: 0, values: BTreeMap::new(), _runtime: PhantomData, } } + + // Load how many roothash messages that this runtime handled in this round. This becomes valid + // after the dispatcher finishes executing incoming blocks, and it doesn't get updated during + // the execution of those incoming messages. The dispatcher calls this in order to report back + // to the node how many messages it got through. + pub fn get_in_msgs_processed(&self) -> usize { + self.in_msgs_processed + } + + // Save how many roothash incoming messages that this runtime handled in this round. This is + // for the dispatcher to call, and modules shouldn't need to use this. + pub fn set_in_msgs_processed(&mut self, count: usize) { + self.in_msgs_processed = count; + } } impl<'a, R: runtime::Runtime, S: NestedStore> Context for RuntimeBatchContext<'a, R, S> { @@ -465,6 +483,7 @@ impl<'a, R: runtime::Runtime, S: NestedStore> Context for RuntimeBatchContext<'a _ => remaining_messages, }, messages: Vec::new(), + in_msgs_processed: self.in_msgs_processed, values: BTreeMap::new(), _runtime: PhantomData, }; @@ -694,6 +713,7 @@ impl<'round, 'store, R: runtime::Runtime, S: Store> Context _ => remaining_messages, }, messages: Vec::new(), + in_msgs_processed: 0, values: BTreeMap::new(), _runtime: PhantomData, }; diff --git a/runtime-sdk/src/dispatcher.rs b/runtime-sdk/src/dispatcher.rs index 7c6ce30647..8971efc6a0 100644 --- a/runtime-sdk/src/dispatcher.rs +++ b/runtime-sdk/src/dispatcher.rs @@ -7,7 +7,7 @@ use std::{ }; use anyhow::anyhow; -use slog::error; +use slog::{error, warn}; use thiserror::Error; use oasis_core_runtime::{ @@ -40,7 +40,10 @@ use crate::{ storage, storage::Prefix, types, - types::transaction::{AuthProof, Transaction, TransactionWeight}, + types::{ + in_msg::IncomingMessageData, + transaction::{AuthProof, Transaction, TransactionWeight}, + }, }; /// Unique module name. @@ -162,6 +165,16 @@ impl Dispatcher { } } + /// Decode a roothash incoming message's data field. + pub fn decode_in_msg( + in_msg: &roothash::IncomingMessage, + ) -> Result { + let data: types::in_msg::IncomingMessageData = cbor::from_slice(&in_msg.data) + .map_err(|e| modules::core::Error::MalformedIncomingMessageData(in_msg.id, e.into()))?; + data.validate_basic()?; + Ok(data) + } + /// Run the dispatch steps inside a transaction context. This includes the before call hooks, /// the call itself and after call hooks. The after call hooks are called regardless if the call /// succeeds or not. @@ -321,6 +334,49 @@ impl Dispatcher { } } + /// Execute the given roothash incoming message. This includes executing the embedded + /// transaction if there is one. + pub fn execute_in_msg( + ctx: &mut C, + in_msg: &roothash::IncomingMessage, + data: &IncomingMessageData, + tx: &Option, + ) -> Result<(), RuntimeError> { + if let Some(tx) = tx { + let tx_size = match data + .ut + .as_ref() + .unwrap_or_else(|| panic!("incoming message {} has tx but no ut", in_msg.id)) + .len() + .try_into() + { + Ok(tx_size) => tx_size, + Err(err) => { + warn!(ctx.get_logger("dispatcher"), "incoming message transaction too large"; "id" => in_msg.id, "err" => ?err); + return Ok(()); + } + }; + // Use the ID as index. + let index = in_msg.id.try_into().unwrap(); + Self::execute_tx(ctx, tx_size, tx.clone(), index)?; + } + Ok(()) + } + + /// Prefetch prefixes for the given roothash incoming message. This includes prefetching the + /// prefixes for the embedded transaction if there is one. + pub fn prefetch_in_msg( + prefixes: &mut BTreeSet, + _in_msg: &roothash::IncomingMessage, + _data: &IncomingMessageData, + tx: &Option, + ) -> Result<(), RuntimeError> { + if let Some(tx) = tx { + Self::prefetch_tx(prefixes, tx.clone())?; + } + Ok(()) + } + fn handle_last_round_messages(ctx: &mut C) -> Result<(), modules::core::Error> { let message_events = ctx.runtime_round_results().messages.clone(); @@ -455,6 +511,8 @@ impl Dispatcher { // Query block weight limits for next round. let block_weight_limits = R::Modules::get_block_weight_limits(&mut ctx); + let in_msgs_count = ctx.get_in_msgs_processed(); + // Commit the context and retrieve the emitted messages. let (block_tags, messages) = ctx.commit(); let (messages, handlers) = messages.into_iter().unzip(); @@ -468,7 +526,7 @@ impl Dispatcher { block_tags: block_tags.into_tags(), batch_weight_limits: Some(block_weight_limits), tx_reject_hashes: vec![], - in_msgs_count: 0, // TODO: Support processing incoming messages. + in_msgs_count, }) } } @@ -478,7 +536,7 @@ impl transaction::dispatcher::Dispatcher for Dispatche &self, rt_ctx: transaction::Context<'_>, batch: &TxnBatch, - _in_msgs: &[roothash::IncomingMessage], + in_msgs: &[roothash::IncomingMessage], ) -> Result { self.execute_batch_common( rt_ctx, @@ -486,8 +544,22 @@ impl transaction::dispatcher::Dispatcher for Dispatche // If prefetch limit is set enable prefetch. let prefetch_enabled = R::PREFETCH_LIMIT > 0; - let mut txs = Vec::with_capacity(batch.len()); let mut prefixes: BTreeSet = BTreeSet::new(); + let mut in_msgs_parsed = Vec::with_capacity(in_msgs.len()); + for in_msg in in_msgs { + let data = Self::decode_in_msg(in_msg).unwrap_or_else(|err| { + warn!(ctx.get_logger("dispatcher"), "incoming message data malformed"; "id" => in_msg.id, "err" => ?err); + IncomingMessageData::noop() + }); + let tx = data.ut.as_ref().and_then(|ut| Self::decode_tx(ctx, ut).map_err(|err| { + warn!(ctx.get_logger("dispatcher"), "incoming message transaction malformed"; "id" => in_msg.id, "err" => ?err); + }).ok()); + if prefetch_enabled { + Self::prefetch_in_msg(&mut prefixes, in_msg, &data, &tx)?; + } + in_msgs_parsed.push((in_msg, data, tx)); + } + let mut txs = Vec::with_capacity(batch.len()); for tx in batch.iter() { let tx_size = tx.len().try_into().map_err(|_| { Error::MalformedTransactionInBatch(anyhow!("transaction too large")) @@ -510,6 +582,12 @@ impl transaction::dispatcher::Dispatcher for Dispatche .prefetch_prefixes(prefixes.into_iter().collect(), R::PREFETCH_LIMIT); } + // Execute incoming messages. + for (in_msg, data, tx) in in_msgs_parsed { + Self::execute_in_msg(ctx, in_msg, &data, &tx)?; + } + ctx.set_in_msgs_processed(in_msgs.len()); + // Execute the batch. let mut results = Vec::with_capacity(batch.len()); for (index, (tx_size, tx)) in txs.into_iter().enumerate() { @@ -525,7 +603,7 @@ impl transaction::dispatcher::Dispatcher for Dispatche &self, rt_ctx: transaction::Context<'_>, batch: &mut TxnBatch, - _in_msgs: &[roothash::IncomingMessage], + in_msgs: &[roothash::IncomingMessage], ) -> Result { let cfg = R::SCHEDULE_CONTROL.unwrap(); // Must succeed otherwise we wouldn't be here. let mut tx_reject_hashes = Vec::new(); @@ -533,6 +611,19 @@ impl transaction::dispatcher::Dispatcher for Dispatche let mut result = self.execute_batch_common( rt_ctx, |ctx| -> Result, RuntimeError> { + // Execute incoming messages. + for in_msg in in_msgs { + let data = Self::decode_in_msg(in_msg).unwrap_or_else(|err| { + warn!(ctx.get_logger("dispatcher"), "incoming message data malformed"; "id" => in_msg.id, "err" => ?err); + IncomingMessageData::noop() + }); + let tx = data.ut.as_ref().and_then(|ut| Self::decode_tx(ctx, ut).map_err(|err| { + warn!(ctx.get_logger("dispatcher"), "incoming message transaction malformed"; "id" => in_msg.id, "err" => ?err); + }).ok()); + Self::execute_in_msg(ctx, in_msg, &data, &tx)?; + } + ctx.set_in_msgs_processed(in_msgs.len()); + // Schedule and execute the batch. // // The idea is to keep scheduling transactions as long as we have some space diff --git a/runtime-sdk/src/modules/core/mod.rs b/runtime-sdk/src/modules/core/mod.rs index f5feb5123b..0895771415 100644 --- a/runtime-sdk/src/modules/core/mod.rs +++ b/runtime-sdk/src/modules/core/mod.rs @@ -16,7 +16,7 @@ use crate::{ dispatcher, module::{self, InvariantHandler as _, Module as _, ModuleInfoHandler as _}, types::{ - token, + in_msg, token, transaction::{ self, AddressSpec, AuthProof, Call, CallFormat, TransactionWeight, UnverifiedTransaction, @@ -112,6 +112,14 @@ pub enum Error { #[error("forbidden in secure build")] #[sdk_error(code = 21)] ForbiddenInSecureBuild, + + #[error("malformed incoming message: {0}")] + #[sdk_error(code = 22)] + MalformedIncomingMessageData(u64, #[source] anyhow::Error), + + #[error("invalid incoming message: {0}")] + #[sdk_error(code = 23)] + InvalidIncomingMessage(#[from] in_msg::Error), } /// Events emitted by the core module. diff --git a/runtime-sdk/src/types/in_msg.rs b/runtime-sdk/src/types/in_msg.rs new file mode 100644 index 0000000000..15c9155d43 --- /dev/null +++ b/runtime-sdk/src/types/in_msg.rs @@ -0,0 +1,39 @@ +use thiserror::Error; + +/// The latest incoming message format version. +pub const LATEST_INCOMING_MESSAGE_VERSION: u16 = 1; + +/// Error. +#[derive(Debug, Error)] +pub enum Error { + #[error("unsupported version")] + UnsupportedVersion, + #[error("malformed incoming message data: {0}")] + MalformedTransaction(anyhow::Error), +} + +/// Roothash incoming message data. +#[derive(Clone, Debug, cbor::Encode, cbor::Decode)] +pub struct IncomingMessageData { + #[cbor(rename = "v")] + pub version: u16, + /// An embedded transaction (UnverifiedTransaction). + /// The transaction doesn't need to be from the same account that sent the message. + pub ut: Option>, +} + +impl IncomingMessageData { + pub fn noop() -> Self { + Self { + version: LATEST_INCOMING_MESSAGE_VERSION, + ut: None, + } + } + + pub fn validate_basic(&self) -> Result<(), Error> { + if self.version != LATEST_INCOMING_MESSAGE_VERSION { + return Err(Error::UnsupportedVersion); + } + Ok(()) + } +} diff --git a/runtime-sdk/src/types/mod.rs b/runtime-sdk/src/types/mod.rs index e2eec6deab..1fd25354de 100644 --- a/runtime-sdk/src/types/mod.rs +++ b/runtime-sdk/src/types/mod.rs @@ -2,6 +2,7 @@ pub mod address; pub mod callformat; +pub mod in_msg; pub mod message; pub mod token; pub mod transaction; From c3391f78b266903b19b0b10a83c186f73f9d8a1f Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 11 Mar 2022 11:22:35 -0800 Subject: [PATCH 02/14] module: add incoming message handler --- runtime-sdk/modules/contracts/src/lib.rs | 1 + runtime-sdk/modules/evm/src/lib.rs | 2 + runtime-sdk/src/dispatcher.rs | 8 +-- runtime-sdk/src/module.rs | 51 +++++++++++++++++++ runtime-sdk/src/modules/accounts/mod.rs | 2 + runtime-sdk/src/modules/consensus/mod.rs | 2 + .../src/modules/consensus_accounts/mod.rs | 5 ++ runtime-sdk/src/modules/core/mod.rs | 2 + runtime-sdk/src/modules/core/test.rs | 1 + runtime-sdk/src/modules/rewards/mod.rs | 2 + runtime-sdk/src/runtime.rs | 5 +- runtime-sdk/src/types/address.rs | 6 +++ .../runtimes/benchmarking/src/runtime/mod.rs | 2 + .../runtimes/simple-keyvalue/src/keyvalue.rs | 1 + 14 files changed, 85 insertions(+), 5 deletions(-) diff --git a/runtime-sdk/modules/contracts/src/lib.rs b/runtime-sdk/modules/contracts/src/lib.rs index f0c858c088..396996d990 100644 --- a/runtime-sdk/modules/contracts/src/lib.rs +++ b/runtime-sdk/modules/contracts/src/lib.rs @@ -715,5 +715,6 @@ impl module::MigrationHandler for Module { } impl module::TransactionHandler for Module {} +impl module::IncomingMessageHandler for Module {} impl module::BlockHandler for Module {} impl module::InvariantHandler for Module {} diff --git a/runtime-sdk/modules/evm/src/lib.rs b/runtime-sdk/modules/evm/src/lib.rs index 2cdb532b82..0fcc3e2491 100644 --- a/runtime-sdk/modules/evm/src/lib.rs +++ b/runtime-sdk/modules/evm/src/lib.rs @@ -683,6 +683,8 @@ impl module::TransactionHandler for Module { } } +impl module::IncomingMessageHandler for Module {} + impl module::BlockHandler for Module { fn end_block(ctx: &mut C) { // Update the list of historic block hashes. diff --git a/runtime-sdk/src/dispatcher.rs b/runtime-sdk/src/dispatcher.rs index 8971efc6a0..93dd357724 100644 --- a/runtime-sdk/src/dispatcher.rs +++ b/runtime-sdk/src/dispatcher.rs @@ -32,7 +32,7 @@ use crate::{ error::{Error as _, RuntimeError}, event::IntoTags, keymanager::{KeyManagerClient, KeyManagerError}, - module::{self, BlockHandler, MethodHandler, TransactionHandler}, + module::{self, BlockHandler, IncomingMessageHandler, MethodHandler, TransactionHandler}, modules, modules::core::API as _, runtime::Runtime, @@ -342,6 +342,7 @@ impl Dispatcher { data: &IncomingMessageData, tx: &Option, ) -> Result<(), RuntimeError> { + R::Modules::execute_in_msg(ctx, in_msg, data, tx)?; if let Some(tx) = tx { let tx_size = match data .ut @@ -367,10 +368,11 @@ impl Dispatcher { /// prefixes for the embedded transaction if there is one. pub fn prefetch_in_msg( prefixes: &mut BTreeSet, - _in_msg: &roothash::IncomingMessage, - _data: &IncomingMessageData, + in_msg: &roothash::IncomingMessage, + data: &IncomingMessageData, tx: &Option, ) -> Result<(), RuntimeError> { + R::Modules::prefetch_in_msg(prefixes, in_msg, data, tx)?; if let Some(tx) = tx { Self::prefetch_tx(prefixes, tx.clone())?; } diff --git a/runtime-sdk/src/module.rs b/runtime-sdk/src/module.rs index fe5623ae47..d2d07f9647 100644 --- a/runtime-sdk/src/module.rs +++ b/runtime-sdk/src/module.rs @@ -7,6 +7,8 @@ use std::{ use cbor::Encode as _; use impl_trait_for_tuples::impl_for_tuples; +use oasis_core_runtime::consensus::roothash; + use crate::{ context::{Context, TxContext}, dispatcher, error, @@ -16,6 +18,7 @@ use crate::{ storage, storage::{Prefix, Store}, types::{ + in_msg::IncomingMessageData, message::MessageResult, transaction::{ self, AuthInfo, Call, Transaction, TransactionWeight, UnverifiedTransaction, @@ -370,6 +373,54 @@ impl TransactionHandler for Tuple { } } +/// Roothash incoming message handler. +pub trait IncomingMessageHandler { + /// Add storage prefixes to prefetch, except for the prefixes for the embedded transaction. The + /// dispatcher will invoke the method handler for the embedded transaction separately. + fn prefetch_in_msg( + _prefixes: &mut BTreeSet, + _in_msg: &roothash::IncomingMessage, + _data: &IncomingMessageData, + _tx: &Option, + ) -> Result<(), error::RuntimeError> { + Ok(()) + } + + /// Execute an incoming message, except for the embedded transaction. The dispatcher will + /// invoke the transaction and method handlers for the embedded transaction separately. + fn execute_in_msg( + _ctx: &mut C, + _in_msg: &roothash::IncomingMessage, + _data: &IncomingMessageData, + _tx: &Option, + ) -> Result<(), error::RuntimeError> { + Ok(()) + } +} + +#[impl_for_tuples(30)] +impl IncomingMessageHandler for Tuple { + fn prefetch_in_msg( + prefixes: &mut BTreeSet, + in_msg: &roothash::IncomingMessage, + data: &IncomingMessageData, + tx: &Option, + ) -> Result<(), error::RuntimeError> { + for_tuples!( #( Tuple::prefetch_in_msg(prefixes, in_msg, data, tx)?; )* ); + Ok(()) + } + + fn execute_in_msg( + ctx: &mut C, + in_msg: &roothash::IncomingMessage, + data: &IncomingMessageData, + tx: &Option, + ) -> Result<(), error::RuntimeError> { + for_tuples!( #( Tuple::execute_in_msg(ctx, in_msg, data, tx)?; )* ); + Ok(()) + } +} + /// Migration handler. pub trait MigrationHandler { /// Genesis state type. diff --git a/runtime-sdk/src/modules/accounts/mod.rs b/runtime-sdk/src/modules/accounts/mod.rs index bda154829b..bd7eac36aa 100644 --- a/runtime-sdk/src/modules/accounts/mod.rs +++ b/runtime-sdk/src/modules/accounts/mod.rs @@ -858,6 +858,8 @@ impl module::TransactionHandler for Module { } } +impl module::IncomingMessageHandler for Module {} + impl module::BlockHandler for Module { fn end_block(ctx: &mut C) { // Determine the fees that are available for disbursement from the last block. diff --git a/runtime-sdk/src/modules/consensus/mod.rs b/runtime-sdk/src/modules/consensus/mod.rs index eff9b3d0f3..3d57aa88df 100644 --- a/runtime-sdk/src/modules/consensus/mod.rs +++ b/runtime-sdk/src/modules/consensus/mod.rs @@ -372,6 +372,8 @@ impl module::MigrationHandler for Module { impl module::TransactionHandler for Module {} +impl module::IncomingMessageHandler for Module {} + impl module::BlockHandler for Module {} impl module::InvariantHandler for Module {} diff --git a/runtime-sdk/src/modules/consensus_accounts/mod.rs b/runtime-sdk/src/modules/consensus_accounts/mod.rs index f16ce0c711..2ab466a827 100644 --- a/runtime-sdk/src/modules/consensus_accounts/mod.rs +++ b/runtime-sdk/src/modules/consensus_accounts/mod.rs @@ -416,6 +416,11 @@ impl { } +impl + module::IncomingMessageHandler for Module +{ +} + impl module::BlockHandler for Module { diff --git a/runtime-sdk/src/modules/core/mod.rs b/runtime-sdk/src/modules/core/mod.rs index 0895771415..28cadacae3 100644 --- a/runtime-sdk/src/modules/core/mod.rs +++ b/runtime-sdk/src/modules/core/mod.rs @@ -645,6 +645,8 @@ impl module::TransactionHandler for Module { } } +impl module::IncomingMessageHandler for Module {} + impl module::MigrationHandler for Module { type Genesis = Genesis; diff --git a/runtime-sdk/src/modules/core/test.rs b/runtime-sdk/src/modules/core/test.rs index 552229729c..abfbd7bb7b 100644 --- a/runtime-sdk/src/modules/core/test.rs +++ b/runtime-sdk/src/modules/core/test.rs @@ -211,6 +211,7 @@ impl crate::module::MethodHandler for GasWasterModule { } } +impl module::IncomingMessageHandler for GasWasterModule {} impl module::BlockHandler for GasWasterModule {} impl module::TransactionHandler for GasWasterModule {} impl module::MigrationHandler for GasWasterModule { diff --git a/runtime-sdk/src/modules/rewards/mod.rs b/runtime-sdk/src/modules/rewards/mod.rs index 2f6a07e3fd..d1b0dc72bd 100644 --- a/runtime-sdk/src/modules/rewards/mod.rs +++ b/runtime-sdk/src/modules/rewards/mod.rs @@ -138,6 +138,8 @@ impl module::MigrationHandler for Module module::TransactionHandler for Module {} +impl module::IncomingMessageHandler for Module {} + impl module::BlockHandler for Module { fn end_block(ctx: &mut C) { let epoch = ctx.epoch(); diff --git a/runtime-sdk/src/runtime.rs b/runtime-sdk/src/runtime.rs index 2f828c5708..b6e0fce011 100644 --- a/runtime-sdk/src/runtime.rs +++ b/runtime-sdk/src/runtime.rs @@ -17,8 +17,8 @@ use crate::{ crypto, dispatcher, keymanager::{KeyManagerClient, TrustedPolicySigners}, module::{ - BlockHandler, InvariantHandler, MethodHandler, MigrationHandler, ModuleInfoHandler, - TransactionHandler, + BlockHandler, IncomingMessageHandler, InvariantHandler, MethodHandler, MigrationHandler, + ModuleInfoHandler, TransactionHandler, }, modules, storage, }; @@ -43,6 +43,7 @@ pub trait Runtime { type Modules: TransactionHandler + MigrationHandler + MethodHandler + + IncomingMessageHandler + BlockHandler + InvariantHandler + ModuleInfoHandler; diff --git a/runtime-sdk/src/types/address.rs b/runtime-sdk/src/types/address.rs index d4ff0a340f..09e6be284b 100644 --- a/runtime-sdk/src/types/address.rs +++ b/runtime-sdk/src/types/address.rs @@ -250,6 +250,12 @@ impl From
for ConsensusAddress { } } +impl From<&ConsensusAddress> for Address { + fn from(addr: &ConsensusAddress) -> Address { + Address::from_bytes(addr.as_ref()).unwrap() + } +} + #[cfg(test)] mod test { use super::*; diff --git a/tests/runtimes/benchmarking/src/runtime/mod.rs b/tests/runtimes/benchmarking/src/runtime/mod.rs index a0cc649f07..76fe530e9b 100644 --- a/tests/runtimes/benchmarking/src/runtime/mod.rs +++ b/tests/runtimes/benchmarking/src/runtime/mod.rs @@ -203,6 +203,8 @@ impl module::MigrationHandler for Module module::IncomingMessageHandler for Module {} + impl module::TransactionHandler for Module {} impl module::BlockHandler for Module {} diff --git a/tests/runtimes/simple-keyvalue/src/keyvalue.rs b/tests/runtimes/simple-keyvalue/src/keyvalue.rs index 50a4c8079a..6fea08b5f9 100644 --- a/tests/runtimes/simple-keyvalue/src/keyvalue.rs +++ b/tests/runtimes/simple-keyvalue/src/keyvalue.rs @@ -153,6 +153,7 @@ impl sdk::module::TransactionHandler for Module { } } +impl sdk::module::IncomingMessageHandler for Module {} impl sdk::module::BlockHandler for Module {} impl sdk::module::InvariantHandler for Module {} From 3c99554c0507a961b593feff4677125c109c77a1 Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 18 Mar 2022 15:35:02 -0700 Subject: [PATCH 03/14] modules/accounts: mint_into_fee_accumulator --- runtime-sdk/src/modules/accounts/mod.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/runtime-sdk/src/modules/accounts/mod.rs b/runtime-sdk/src/modules/accounts/mod.rs index bd7eac36aa..de98a0cf76 100644 --- a/runtime-sdk/src/modules/accounts/mod.rs +++ b/runtime-sdk/src/modules/accounts/mod.rs @@ -212,6 +212,12 @@ pub trait API { amount: &token::BaseUnits, ) -> Result<(), modules::core::Error>; + /// Mint new tokens, directly crediting the fee accumulator. + fn mint_into_fee_accumulator( + ctx: &mut C, + amount: &token::BaseUnits, + ) -> Result<(), Error>; + /// Move amount from fee accumulator into address. fn move_from_fee_accumulator( ctx: &mut C, @@ -587,6 +593,24 @@ impl API for Module { Ok(()) } + fn mint_into_fee_accumulator( + ctx: &mut C, + amount: &token::BaseUnits, + ) -> Result<(), Error> { + if ctx.is_simulation() { + return Ok(()); + } + + ctx.value::(CONTEXT_KEY_FEE_ACCUMULATOR) + .or_default() + .add(amount); + + // Increase total supply. + Self::inc_total_supply(ctx.runtime_state(), amount)?; + + Ok(()) + } + fn move_from_fee_accumulator( ctx: &mut C, to: Address, From 879f012d997e81dabd13b6ee9ad38e32d8ce74df Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 11 Mar 2022 12:56:30 -0800 Subject: [PATCH 04/14] modules/consensus_accounts: handle incoming message tokens --- .../src/modules/consensus_accounts/mod.rs | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/runtime-sdk/src/modules/consensus_accounts/mod.rs b/runtime-sdk/src/modules/consensus_accounts/mod.rs index 2ab466a827..1f46f57775 100644 --- a/runtime-sdk/src/modules/consensus_accounts/mod.rs +++ b/runtime-sdk/src/modules/consensus_accounts/mod.rs @@ -4,10 +4,11 @@ //! while keeping track of amount deposited per account. use std::{collections::BTreeSet, convert::TryInto}; +use num_traits::Zero; use once_cell::sync::Lazy; use thiserror::Error; -use oasis_core_runtime::consensus::staking::Account as ConsensusAccount; +use oasis_core_runtime::consensus::{roothash, staking::Account as ConsensusAccount}; use oasis_runtime_sdk_macros::{handler, sdk_derive}; use crate::{ @@ -20,9 +21,10 @@ use crate::{ storage::Prefix, types::{ address::Address, + in_msg::IncomingMessageData, message::{MessageEvent, MessageEventHookInvocation}, token, - transaction::AuthInfo, + transaction::{AuthInfo, Transaction}, }, }; @@ -419,6 +421,40 @@ impl impl module::IncomingMessageHandler for Module { + fn prefetch_in_msg( + _prefixes: &mut BTreeSet, + _in_msg: &roothash::IncomingMessage, + _data: &IncomingMessageData, + _tx: &Option, + ) -> Result<(), error::RuntimeError> { + // todo: their account + Ok(()) + } + + fn execute_in_msg( + ctx: &mut C, + in_msg: &roothash::IncomingMessage, + _data: &IncomingMessageData, + _tx: &Option, + ) -> Result<(), error::RuntimeError> { + if !in_msg.fee.is_zero() { + let amount = token::BaseUnits( + Consensus::amount_from_consensus(ctx, (&in_msg.fee).try_into().unwrap()).unwrap(), + Consensus::consensus_denomination(ctx).unwrap(), + ); + Accounts::mint_into_fee_accumulator(ctx, &amount).unwrap(); + // TODO: Emit event that fee has been paid. + } + if !in_msg.tokens.is_zero() { + let amount = token::BaseUnits( + Consensus::amount_from_consensus(ctx, (&in_msg.tokens).try_into().unwrap()) + .unwrap(), + Consensus::consensus_denomination(ctx).unwrap(), + ); + Accounts::mint(ctx, (&in_msg.caller).into(), &amount).unwrap(); + } + Ok(()) + } } impl module::BlockHandler From c3a024e5d26de7b825c1c5b0eec06d3da1f35a80 Mon Sep 17 00:00:00 2001 From: Warren He Date: Wed, 16 Mar 2022 13:33:32 -0700 Subject: [PATCH 05/14] modules/core: add incoming messages gas limit parameter --- runtime-sdk/src/modules/core/mod.rs | 10 ++++++++++ runtime-sdk/src/modules/core/test.rs | 7 +++++++ tests/runtimes/benchmarking/src/lib.rs | 1 + tests/runtimes/simple-consensus/src/lib.rs | 1 + tests/runtimes/simple-contracts/src/lib.rs | 1 + tests/runtimes/simple-evm/src/lib.rs | 1 + tests/runtimes/simple-keyvalue/src/lib.rs | 1 + tests/runtimes/simple-keyvalue/src/test.rs | 1 + 8 files changed, 23 insertions(+) diff --git a/runtime-sdk/src/modules/core/mod.rs b/runtime-sdk/src/modules/core/mod.rs index 28cadacae3..596e6e66e4 100644 --- a/runtime-sdk/src/modules/core/mod.rs +++ b/runtime-sdk/src/modules/core/mod.rs @@ -145,6 +145,7 @@ pub struct GasCosts { #[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)] pub struct Parameters { pub max_batch_gas: u64, + pub max_in_msgs_gas: u64, pub max_tx_signers: u32, pub max_multisig_signers: u32, pub gas_costs: GasCosts, @@ -169,6 +170,9 @@ pub trait API { /// Returns the remaining batch-wide gas. fn remaining_batch_gas(ctx: &mut C) -> u64; + /// Returns the remaining batch-wide gas that can be used for roothash incoming messages. + fn remaining_in_msgs_gas(ctx: &mut C) -> u64; + /// Return the remaining tx-wide gas. fn remaining_tx_gas(ctx: &mut C) -> u64; @@ -300,6 +304,12 @@ impl API for Module { batch_gas_limit.saturating_sub(*batch_gas_used) } + fn remaining_in_msgs_gas(ctx: &mut C) -> u64 { + let in_msgs_gas_limit = Self::params(ctx.runtime_state()).max_in_msgs_gas; + let batch_gas_used = ctx.value::(CONTEXT_KEY_GAS_USED).or_default(); + in_msgs_gas_limit.saturating_sub(*batch_gas_used) + } + fn remaining_tx_gas(ctx: &mut C) -> u64 { let gas_limit = ctx.tx_auth_info().fee.gas; let gas_used = ctx.tx_value::(CONTEXT_KEY_GAS_USED).or_default(); diff --git a/runtime-sdk/src/modules/core/test.rs b/runtime-sdk/src/modules/core/test.rs index abfbd7bb7b..d143b78be8 100644 --- a/runtime-sdk/src/modules/core/test.rs +++ b/runtime-sdk/src/modules/core/test.rs @@ -31,6 +31,7 @@ fn test_use_gas() { ctx.runtime_state(), Parameters { max_batch_gas: BLOCK_MAX_GAS, + max_in_msgs_gas: BLOCK_MAX_GAS / 4, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: Default::default(), @@ -126,6 +127,7 @@ fn test_query_min_gas_price() { ctx.runtime_state(), Parameters { max_batch_gas: 10000, + max_in_msgs_gas: 2500, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: Default::default(), @@ -243,6 +245,7 @@ impl Runtime for GasWasterRuntime { super::Genesis { parameters: Parameters { max_batch_gas: u64::MAX, + max_in_msgs_gas: u64::MAX, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: super::GasCosts { @@ -373,6 +376,7 @@ fn test_approve_unverified_tx() { ctx.runtime_state(), Parameters { max_batch_gas: u64::MAX, + max_in_msgs_gas: u64::MAX, max_tx_signers: 2, max_multisig_signers: 2, gas_costs: Default::default(), @@ -587,6 +591,7 @@ fn test_check_weights() { ctx.runtime_state(), Parameters { max_batch_gas: u64::MAX, + max_in_msgs_gas: u64::MAX, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: super::GasCosts { @@ -650,6 +655,7 @@ fn test_min_gas_price() { ctx.runtime_state(), Parameters { max_batch_gas: u64::MAX, + max_in_msgs_gas: u64::MAX, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: super::GasCosts { @@ -830,6 +836,7 @@ fn test_gas_used_events() { ctx.runtime_state(), Parameters { max_batch_gas: 1_000_000, + max_in_msgs_gas: 250_000, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: Default::default(), diff --git a/tests/runtimes/benchmarking/src/lib.rs b/tests/runtimes/benchmarking/src/lib.rs index 37f202f74b..2e23f76209 100644 --- a/tests/runtimes/benchmarking/src/lib.rs +++ b/tests/runtimes/benchmarking/src/lib.rs @@ -47,6 +47,7 @@ impl sdk::Runtime for Runtime { modules::core::Genesis { parameters: modules::core::Parameters { max_batch_gas: 10_000_000, + max_in_msgs_gas: 2_500_000, max_tx_signers: 8, max_multisig_signers: 8, // These are free, in order to simplify benchmarking. diff --git a/tests/runtimes/simple-consensus/src/lib.rs b/tests/runtimes/simple-consensus/src/lib.rs index 2e1c493ebd..61fcf9a0db 100644 --- a/tests/runtimes/simple-consensus/src/lib.rs +++ b/tests/runtimes/simple-consensus/src/lib.rs @@ -65,6 +65,7 @@ impl sdk::Runtime for Runtime { modules::core::Genesis { parameters: modules::core::Parameters { max_batch_gas: 10_000, + max_in_msgs_gas: 2_500, max_tx_signers: 8, max_multisig_signers: 8, // These are free, in order to simplify testing. diff --git a/tests/runtimes/simple-contracts/src/lib.rs b/tests/runtimes/simple-contracts/src/lib.rs index ba572f33ff..64111473e5 100644 --- a/tests/runtimes/simple-contracts/src/lib.rs +++ b/tests/runtimes/simple-contracts/src/lib.rs @@ -89,6 +89,7 @@ impl sdk::Runtime for Runtime { modules::core::Genesis { parameters: modules::core::Parameters { max_batch_gas: 10_000_000, + max_in_msgs_gas: 2_500_000, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: modules::core::GasCosts { diff --git a/tests/runtimes/simple-evm/src/lib.rs b/tests/runtimes/simple-evm/src/lib.rs index 65f0684af5..f2f92661db 100644 --- a/tests/runtimes/simple-evm/src/lib.rs +++ b/tests/runtimes/simple-evm/src/lib.rs @@ -69,6 +69,7 @@ impl sdk::Runtime for Runtime { modules::core::Genesis { parameters: modules::core::Parameters { max_batch_gas: 1_000_000, + max_in_msgs_gas: 250_000, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: modules::core::GasCosts { diff --git a/tests/runtimes/simple-keyvalue/src/lib.rs b/tests/runtimes/simple-keyvalue/src/lib.rs index 80c4d77934..66c946439e 100644 --- a/tests/runtimes/simple-keyvalue/src/lib.rs +++ b/tests/runtimes/simple-keyvalue/src/lib.rs @@ -145,6 +145,7 @@ impl sdk::Runtime for Runtime { modules::core::Genesis { parameters: modules::core::Parameters { max_batch_gas: 2_000, + max_in_msgs_gas: 500, max_tx_signers: 8, max_multisig_signers: 8, gas_costs: modules::core::GasCosts { diff --git a/tests/runtimes/simple-keyvalue/src/test.rs b/tests/runtimes/simple-keyvalue/src/test.rs index 0a6ad2a25d..228179251d 100644 --- a/tests/runtimes/simple-keyvalue/src/test.rs +++ b/tests/runtimes/simple-keyvalue/src/test.rs @@ -16,6 +16,7 @@ fn test_impl_for_tuple() { ctx.runtime_state(), core::Parameters { max_batch_gas: u64::MAX, + max_in_msgs_gas: u64::MAX, max_tx_signers: 1, max_multisig_signers: 1, gas_costs: Default::default(), From a03c2edfbe6a44717a388aa8ebb8c44fe4b5de44 Mon Sep 17 00:00:00 2001 From: Warren He Date: Wed, 16 Mar 2022 15:11:28 -0700 Subject: [PATCH 06/14] dispatcher: watch incoming messages gas --- runtime-sdk/src/dispatcher.rs | 49 ++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/runtime-sdk/src/dispatcher.rs b/runtime-sdk/src/dispatcher.rs index 93dd357724..df0de1a740 100644 --- a/runtime-sdk/src/dispatcher.rs +++ b/runtime-sdk/src/dispatcher.rs @@ -614,17 +614,58 @@ impl transaction::dispatcher::Dispatcher for Dispatche rt_ctx, |ctx| -> Result, RuntimeError> { // Execute incoming messages. + let in_msgs_gas_limit = R::Core::remaining_in_msgs_gas(ctx); + let mut in_msgs_processed = 0usize; for in_msg in in_msgs { let data = Self::decode_in_msg(in_msg).unwrap_or_else(|err| { warn!(ctx.get_logger("dispatcher"), "incoming message data malformed"; "id" => in_msg.id, "err" => ?err); IncomingMessageData::noop() }); - let tx = data.ut.as_ref().and_then(|ut| Self::decode_tx(ctx, ut).map_err(|err| { - warn!(ctx.get_logger("dispatcher"), "incoming message transaction malformed"; "id" => in_msg.id, "err" => ?err); - }).ok()); + let tx = match data.ut.as_ref() { + Some(ut) => { + match Self::decode_tx(ctx, ut) { + Ok(tx) => { + let remaining_gas = R::Core::remaining_in_msgs_gas(ctx); + if remaining_gas < cfg.min_remaining_gas { + // This next message has a transaction, but we won't have + // enough gas to execute it, so leave it for the next + // round and stop. + break; + } else if tx.auth_info.fee.gas > in_msgs_gas_limit { + // The transaction is too large to execute under our + // current parameters, so skip over it. + warn!(ctx.get_logger("dispatcher"), "incoming message transaction fee gas exceeds round gas limit"; + "id" => in_msg.id, + "tx_gas" => tx.auth_info.fee.gas, + "in_msgs_gas_limit" => in_msgs_gas_limit, + ); + // Actually don't skip the message entirely, just don't + // execute the transaction. + None + } else if tx.auth_info.fee.gas > remaining_gas { + // The transaction is too large to execute in this round, + // so leave it for the next round and stop. + break; + } else { + Some(tx) + } + } + Err(err) => { + warn!(ctx.get_logger("dispatcher"), "incoming message transaction malformed"; + "id" => in_msg.id, + "err" => ?err, + ); + None + } + } + } + None => None, + }; + Self::execute_in_msg(ctx, in_msg, &data, &tx)?; + in_msgs_processed += 1; } - ctx.set_in_msgs_processed(in_msgs.len()); + ctx.set_in_msgs_processed(in_msgs_processed); // Schedule and execute the batch. // From c85b0001430c67b28f3df14776cd478f043d212a Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 25 Mar 2022 16:07:33 -0700 Subject: [PATCH 07/14] dispatcher: add note about incoming messages events --- runtime-sdk/src/dispatcher.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime-sdk/src/dispatcher.rs b/runtime-sdk/src/dispatcher.rs index df0de1a740..b532c31f63 100644 --- a/runtime-sdk/src/dispatcher.rs +++ b/runtime-sdk/src/dispatcher.rs @@ -359,6 +359,7 @@ impl Dispatcher { }; // Use the ID as index. let index = in_msg.id.try_into().unwrap(); + // todo: put result tags in block Self::execute_tx(ctx, tx_size, tx.clone(), index)?; } Ok(()) From 4e640a3b9d9cf392d83af237f0734d13afc78bdf Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Mar 2022 16:40:31 -0700 Subject: [PATCH 08/14] go/testing: expose test accounts' consensus signer --- client-sdk/go/testing/testing.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/client-sdk/go/testing/testing.go b/client-sdk/go/testing/testing.go index 01fe03f941..4d4da5c7d2 100644 --- a/client-sdk/go/testing/testing.go +++ b/client-sdk/go/testing/testing.go @@ -5,6 +5,7 @@ import ( "golang.org/x/crypto/sha3" + coreSignature "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" memorySigner "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature" @@ -21,15 +22,20 @@ type TestKey struct { // EthAddress is the corresponding Ethereum address if the key is secp256k1. EthAddress [20]byte + + // ConsensusSigner is the signer for the consensus API if the key is ed25519. + ConsensusSigner coreSignature.Signer } func newEd25519TestKey(seed string) TestKey { - signer := ed25519.WrapSigner(memorySigner.NewTestSigner(seed)) + consensusSigner := memorySigner.NewTestSigner(seed) + signer := ed25519.WrapSigner(consensusSigner) sigspec := types.NewSignatureAddressSpecEd25519(signer.Public().(ed25519.PublicKey)) return TestKey{ - Signer: signer, - Address: types.NewAddress(sigspec), - SigSpec: sigspec, + Signer: signer, + Address: types.NewAddress(sigspec), + SigSpec: sigspec, + ConsensusSigner: consensusSigner, } } From 6c35cb23ab947738d18c1dc57ffe9b7f22fa249b Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Mar 2022 17:02:43 -0700 Subject: [PATCH 09/14] go/types: add incoming message data types --- client-sdk/go/types/inmsg.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 client-sdk/go/types/inmsg.go diff --git a/client-sdk/go/types/inmsg.go b/client-sdk/go/types/inmsg.go new file mode 100644 index 0000000000..7a7bc1f5ba --- /dev/null +++ b/client-sdk/go/types/inmsg.go @@ -0,0 +1,32 @@ +package types + +import ( + "fmt" + + "github.com/oasisprotocol/oasis-core/go/common/cbor" +) + +// LatestIncomingMessageVersion is the latest incoming message format version. +const LatestIncomingMessageVersion = 1 + +type IncomingMessageData struct { + cbor.Versioned + + // UnverifiedTransaction is an embedded transaction (UnverifiedTransaction). + // The transaction doesn't need to be from the same account that sent the message. + UnverifiedTransaction *[]byte `json:"ut"` +} + +func (d *IncomingMessageData) ValidateBasic() error { + if d.V != LatestIncomingMessageVersion { + return fmt.Errorf("incoming message data: unsupported version") + } + return nil +} + +func NoopIncomingMessageData() *IncomingMessageData { + return &IncomingMessageData{ + Versioned: cbor.NewVersioned(LatestIncomingMessageVersion), + UnverifiedTransaction: nil, + } +} From 3e381c3582bb0ac323489c537dd59d9ab82819c6 Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 18 Mar 2022 15:31:03 -0700 Subject: [PATCH 10/14] go/client: transaction builder GetUnverifiedTransaction --- client-sdk/go/client/transaction.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client-sdk/go/client/transaction.go b/client-sdk/go/client/transaction.go index b9c5f2ffd9..f1d271de96 100644 --- a/client-sdk/go/client/transaction.go +++ b/client-sdk/go/client/transaction.go @@ -101,6 +101,11 @@ func (tb *TransactionBuilder) AppendSign(ctx context.Context, signer signature.S return tb.ts.AppendSign(rtInfo.ChainContext, signer) } +//GetUnverifiedTransaction returns the underlying signed transaction. +func (tb *TransactionBuilder) GetUnverifiedTransaction() *types.UnverifiedTransaction { + return tb.ts.UnverifiedTransaction() +} + // SubmitTx submits a transaction to the runtime transaction scheduler and waits for transaction // execution results. func (tb *TransactionBuilder) SubmitTx(ctx context.Context, rsp interface{}) error { From 1bf2e17ffc4dee352015aee768baad25b98d1b66 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 24 Mar 2022 16:31:51 -0700 Subject: [PATCH 11/14] tests: enable incoming messages --- tests/e2e/runtime.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/runtime.go b/tests/e2e/runtime.go index bef02c9191..42ff9f7fcc 100644 --- a/tests/e2e/runtime.go +++ b/tests/e2e/runtime.go @@ -189,6 +189,7 @@ func (sc *RuntimeScenario) Fixture() (*oasis.NetworkFixture, error) { TxnScheduler: registry.TxnSchedulerParameters{ MaxBatchSize: 1000, MaxBatchSizeBytes: 16 * 1024 * 1024, // 16 MB. + MaxInMessages: 128, BatchFlushTimeout: 1 * time.Second, ProposerTimeout: 30, }, From 3b4511982b2bb0e24b795d1ee8c1ad1e577a4241 Mon Sep 17 00:00:00 2001 From: Warren He Date: Wed, 23 Mar 2022 16:06:04 -0700 Subject: [PATCH 12/14] (experimental) tests/e2e: bump oasis-core --- tests/e2e/go.mod | 6 +++--- tests/e2e/go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index 4fc3f5a9d9..228f3fe3b4 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -22,7 +22,7 @@ replace ( require ( github.com/btcsuite/btcd v0.22.0-beta github.com/oasisprotocol/curve25519-voi v0.0.0-20211219162838-e9a669f65da9 - github.com/oasisprotocol/oasis-core/go v0.2200.2 + github.com/oasisprotocol/oasis-core/go v0.2200.3-0.20220323214631-e9ee3cef8d62 github.com/oasisprotocol/oasis-sdk/client-sdk/go v0.0.0-00010101000000-000000000000 google.golang.org/grpc v1.45.0 ) @@ -101,7 +101,7 @@ require ( github.com/libp2p/go-conn-security-multistream v0.3.0 // indirect github.com/libp2p/go-eventbus v0.2.1 // indirect github.com/libp2p/go-flow-metrics v0.0.3 // indirect - github.com/libp2p/go-libp2p v0.18.0-rc6 // indirect + github.com/libp2p/go-libp2p v0.18.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.1.0 // indirect github.com/libp2p/go-libp2p-blankhost v0.3.0 // indirect github.com/libp2p/go-libp2p-core v0.14.0 // indirect @@ -113,7 +113,7 @@ require ( github.com/libp2p/go-libp2p-pnet v0.2.0 // indirect github.com/libp2p/go-libp2p-pubsub v0.6.1 // indirect github.com/libp2p/go-libp2p-quic-transport v0.16.1 // indirect - github.com/libp2p/go-libp2p-resource-manager v0.1.3 // indirect + github.com/libp2p/go-libp2p-resource-manager v0.1.5 // indirect github.com/libp2p/go-libp2p-swarm v0.10.2 // indirect github.com/libp2p/go-libp2p-tls v0.3.1 // indirect github.com/libp2p/go-libp2p-transport-upgrader v0.7.1 // indirect diff --git a/tests/e2e/go.sum b/tests/e2e/go.sum index 7b2cd6f96e..b66150d269 100644 --- a/tests/e2e/go.sum +++ b/tests/e2e/go.sum @@ -585,8 +585,8 @@ github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVh github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p v0.18.0-rc6 h1:IR6TVPYGo1wDY0tY61gyPQVxK1koOkXh49ejVfAnH7A= -github.com/libp2p/go-libp2p v0.18.0-rc6/go.mod h1:oOUOAlBrm1L0+jxT10h2TMUMTDz6pV3EvmkJ3beDYGQ= +github.com/libp2p/go-libp2p v0.18.0 h1:moKKKG875KNGsCjZxTIFB75ihHiVjFeWg5I4aR1pDLk= +github.com/libp2p/go-libp2p v0.18.0/go.mod h1:+veaZ9z1SZQhmc5PW78jvnnxZ89Mgvmh4cggO11ETmw= github.com/libp2p/go-libp2p-asn-util v0.1.0 h1:rABPCO77SjdbJ/eJ/ynIo8vWICy1VEnL5JAxJbQLo1E= github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I= github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= @@ -630,8 +630,8 @@ github.com/libp2p/go-libp2p-quic-transport v0.13.0/go.mod h1:39/ZWJ1TW/jx1iFkKzz github.com/libp2p/go-libp2p-quic-transport v0.16.0/go.mod h1:1BXjVMzr+w7EkPfiHkKnwsWjPjtfaNT0q8RS3tGDvEQ= github.com/libp2p/go-libp2p-quic-transport v0.16.1 h1:N/XqYXHurphPLDfXYhll8NyqzdZYQqAF4GIr7+SmLV8= github.com/libp2p/go-libp2p-quic-transport v0.16.1/go.mod h1:1BXjVMzr+w7EkPfiHkKnwsWjPjtfaNT0q8RS3tGDvEQ= -github.com/libp2p/go-libp2p-resource-manager v0.1.3 h1:Umf0tW6WNXSb6Uoma0YT56azB5iikL/aeGAP7s7+f5o= -github.com/libp2p/go-libp2p-resource-manager v0.1.3/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y= +github.com/libp2p/go-libp2p-resource-manager v0.1.5 h1:7J6t9KLFS0MxXDTfqA6rwfVCZl/yLQnXW5LpZjHAANI= +github.com/libp2p/go-libp2p-resource-manager v0.1.5/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y= github.com/libp2p/go-libp2p-swarm v0.8.0/go.mod h1:sOMp6dPuqco0r0GHTzfVheVBh6UEL0L1lXUZ5ot2Fvc= github.com/libp2p/go-libp2p-swarm v0.10.0/go.mod h1:71ceMcV6Rg/0rIQ97rsZWMzto1l9LnNquef+efcRbmA= github.com/libp2p/go-libp2p-swarm v0.10.2 h1:UaXf+CTq6Ns1N2V1EgqJ9Q3xaRsiN7ImVlDMpirMAWw= @@ -854,8 +854,8 @@ github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2 github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= github.com/oasisprotocol/ed25519 v0.0.0-20210127160119-f7017427c1ea h1:r4MhxgqQ1bed0fQhRINBM50Rbwsgma6oEjG4zQ444Lw= github.com/oasisprotocol/ed25519 v0.0.0-20210127160119-f7017427c1ea/go.mod h1:IZbb50w3AB72BVobEF6qG93NNSrTw/V2QlboxqSu3Xw= -github.com/oasisprotocol/oasis-core/go v0.2200.2 h1:is4ilDlTcXPlBq4OAkpXAY4ryKKmvXkAbiw8KCJYQTc= -github.com/oasisprotocol/oasis-core/go v0.2200.2/go.mod h1:GS8OwL3mFgpd+YcXnD7yUu7Fnk+IOaxHcfSGzfBZbZY= +github.com/oasisprotocol/oasis-core/go v0.2200.3-0.20220323214631-e9ee3cef8d62 h1:6hw5m/8S/Mxp0mvD5J+paLX5yi37FrHPHwB9RvYXnns= +github.com/oasisprotocol/oasis-core/go v0.2200.3-0.20220323214631-e9ee3cef8d62/go.mod h1:5j0o8vu87SRgO0QM4YZpiOEqLpWdOGHF+7hgOrbQRro= github.com/oasisprotocol/safeopen v0.0.0-20200528085122-e01cfdfc7661 h1:MB73kGMtuMGS+6VDoU/mitzff7Cu+aZo9ta5wabuxVA= github.com/oasisprotocol/safeopen v0.0.0-20200528085122-e01cfdfc7661/go.mod h1:SwBxaVibf6Sr2IZ6M3WnUue0yp8dPLAo1riQRNQ60+g= github.com/oasisprotocol/tendermint v0.34.9-oasis2 h1:wVFJjLnmen7QXpIAznbiGHOeVrX5oNtJMUk0rrpSmG8= From 9a86431e4b0dafbfd6040bffaef1a86e366b0ce6 Mon Sep 17 00:00:00 2001 From: Warren He Date: Thu, 17 Mar 2022 17:03:05 -0700 Subject: [PATCH 13/14] tests: incoming message tests --- tests/e2e/inmsgstest.go | 182 ++++++++++++++++++++++++++++++++++++++++ tests/e2e/scenarios.go | 2 +- 2 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/inmsgstest.go diff --git a/tests/e2e/inmsgstest.go b/tests/e2e/inmsgstest.go new file mode 100644 index 0000000000..7ef12bdb48 --- /dev/null +++ b/tests/e2e/inmsgstest.go @@ -0,0 +1,182 @@ +package main + +import ( + "context" + "fmt" + "time" + + "google.golang.org/grpc" + + "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/logging" + "github.com/oasisprotocol/oasis-core/go/common/quantity" + consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" + "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction" + roothash "github.com/oasisprotocol/oasis-core/go/roothash/api" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" + + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/client" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/modules/accounts" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/testing" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types" +) + +func makeMintCheck(owner types.Address, amount types.BaseUnits) func(e client.DecodedEvent) bool { + return func(e client.DecodedEvent) bool { + ae, ok := e.(*accounts.Event) + if !ok { + return false + } + if ae.Mint == nil { + return false + } + if !ae.Mint.Owner.Equal(owner) { + return false + } + if ae.Mint.Amount.Amount.Cmp(&amount.Amount) != 0 { + return false + } + if ae.Mint.Amount.Denomination != amount.Denomination { + return false + } + return true + } +} + +func makeRuntimeTransferCheck(from types.Address, to types.Address, amount types.BaseUnits) func(e client.DecodedEvent) bool { + return func(e client.DecodedEvent) bool { + ae, ok := e.(*accounts.Event) + if !ok { + return false + } + if ae.Transfer == nil { + return false + } + if !ae.Transfer.From.Equal(from) { + return false + } + if !ae.Transfer.To.Equal(to) { + return false + } + if ae.Transfer.Amount.Amount.Cmp(&amount.Amount) != 0 { + return false + } + if ae.Transfer.Amount.Denomination != amount.Denomination { + return false + } + return true + } +} + +func IncomingMessagesTest(sc *RuntimeScenario, log *logging.Logger, conn *grpc.ClientConn, rtc client.RuntimeClient) error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + cons := consensus.NewConsensusClient(conn) + stakingClient := cons.Staking() + ch, sub, err := stakingClient.WatchEvents(ctx) + defer sub.Close() + if err != nil { + return fmt.Errorf("staking client watch events: %w", err) + } + + consDenomination := types.Denomination("TEST") + + ac := accounts.NewV1(rtc) + + acCh, err := rtc.WatchEvents(ctx, []client.EventDecoder{ac}, false) + if err != nil { + return fmt.Errorf("runtime client watch events: %w", err) + } + + runtimeAddr := staking.NewRuntimeAddress(runtimeID) + + log.Warn("0: get alice consensus starting balance") + aliceConsensusAccount, err := cons.Staking().Account(ctx, &staking.OwnerQuery{ + Height: consensus.HeightLatest, + Owner: testing.Alice.Address.ConsensusAddress(), + }) + if err != nil { + return err + } + aliceConsensusExpectedBalance := aliceConsensusAccount.General.Balance + log.Warn("0.5: get alice runtime starting balance") + aliceRuntimeBalances, err := ac.Balances(ctx, client.RoundLatest, testing.Alice.Address) + if err != nil { + return err + } + aliceRuntimeExpectedBalance := aliceRuntimeBalances.Balances[consDenomination] + + // Message with transfer. + tb := ac.Transfer(testing.Bob.Address, types.NewBaseUnits(*quantity.NewFromUint64(10_000), consDenomination)) + tb.AppendAuthSignature(testing.Alice.SigSpec, 2) + if err = tb.AppendSign(ctx, testing.Alice.Signer); err != nil { + return fmt.Errorf("msg 1 embedded transfer append sign: %w", err) + } + ut := cbor.Marshal(tb.GetUnverifiedTransaction()) + signedTx, err := transaction.Sign(testing.Alice.ConsensusSigner, roothash.NewSubmitMsgTx(0, &transaction.Fee{ + Gas: 2000, + }, &roothash.SubmitMsg{ + ID: runtimeID, + Tag: 0, + Fee: *quantity.NewFromUint64(1), + Tokens: *quantity.NewFromUint64(12), + Data: cbor.Marshal(types.IncomingMessageData{ + Versioned: cbor.NewVersioned(types.LatestIncomingMessageVersion), + UnverifiedTransaction: &ut, + }), + })) + if err != nil { + return fmt.Errorf("msg 1 submit sign: %w", err) + } + if err = cons.SubmitTx(ctx, signedTx); err != nil { + return fmt.Errorf("msg 1 submit: %w", err) + } + // 1 fee + 12 tokens + if err = aliceConsensusExpectedBalance.Sub(quantity.NewFromUint64(13)); err != nil { + return fmt.Errorf("msg 1 decreasing expected consensus balance: %w", err) + } + // 12_000 tokens - 10_000 transferred + if err = aliceRuntimeExpectedBalance.Add(quantity.NewFromUint64(2_000)); err != nil { + return fmt.Errorf("msg 1 increasing expected runtime balance: %w", err) + } + + log.Warn("1: alice get consensus balance") + aliceConsensusAccount, err = cons.Staking().Account(ctx, &staking.OwnerQuery{ + Height: consensus.HeightLatest, + Owner: testing.Alice.Address.ConsensusAddress(), + }) + // todo: figure out what consensus balance should be. 1 million minus something from previous tests + if aliceConsensusAccount.General.Balance.Cmp(&aliceConsensusExpectedBalance) != 0 { + return fmt.Errorf("after message 1: alice consensus balance expected %v actual %v", aliceConsensusExpectedBalance, aliceConsensusAccount.General.Balance) + } + if err = ensureRuntimeEvent(log, acCh, makeMintCheck(testing.Alice.Address, types.NewBaseUnits(*quantity.NewFromUint64(12_000), consDenomination))); err != nil { + return fmt.Errorf("after msg 1 wait for mint event: %w", err) + } + // todo: events from inside embedded tx are lost + //if err = ensureRuntimeEvent(log, acCh, makeRuntimeTransferCheck(testing.Alice.Address, testing.Bob.Address, transferAmount)); err != nil { + // return fmt.Errorf("after msg 1 wait for transfer event: %w", err) + //} + log.Warn("1.5: alice get runtime balance") + aliceRuntimeBalances, err = ac.Balances(ctx, client.RoundLatest, testing.Alice.Address) + if err != nil { + return err + } + aliceRuntimeBalance := aliceRuntimeBalances.Balances[consDenomination] + if aliceRuntimeBalance.Cmp(&aliceRuntimeExpectedBalance) != 0 { + return fmt.Errorf("after message 1: alice runtime balance expected %v actual %v", aliceRuntimeExpectedBalance, aliceRuntimeBalances.Balances[consDenomination]) + } + + // %%% + _ = ch + _ = runtimeAddr + + // todo: test other cases + // - embedded transfer, different sender: should execute + // - malformed data field: funds should work + // - invalid transaction: funds should work + // - failed transaction: funds should work + // - too much gas: funds should work + + return nil +} diff --git a/tests/e2e/scenarios.go b/tests/e2e/scenarios.go index 448b6a3a6b..c3e367f120 100644 --- a/tests/e2e/scenarios.go +++ b/tests/e2e/scenarios.go @@ -34,7 +34,7 @@ var ( }) // SimpleConsensusRuntime is the simple-consensus runtime test. - SimpleConsensusRuntime *RuntimeScenario = NewRuntimeScenario("test-runtime-simple-consensus", []RunTestFunction{SimpleConsensusTest, ConsensusAccountsParametersTest}) + SimpleConsensusRuntime *RuntimeScenario = NewRuntimeScenario("test-runtime-simple-consensus", []RunTestFunction{SimpleConsensusTest, ConsensusAccountsParametersTest, IncomingMessagesTest}) // SimpleEVMRuntime is the simple-evm runtime test. SimpleEVMRuntime *RuntimeScenario = NewRuntimeScenario("test-runtime-simple-evm", []RunTestFunction{ From 05ed1c94336e1d085970cab7a6a929ea943dc32d Mon Sep 17 00:00:00 2001 From: Warren He Date: Fri, 25 Mar 2022 16:58:58 -0700 Subject: [PATCH 14/14] (experimental) dispatcher: diagnostic logs --- runtime-sdk/src/dispatcher.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/runtime-sdk/src/dispatcher.rs b/runtime-sdk/src/dispatcher.rs index b532c31f63..c2ffc50a74 100644 --- a/runtime-sdk/src/dispatcher.rs +++ b/runtime-sdk/src/dispatcher.rs @@ -342,7 +342,9 @@ impl Dispatcher { data: &IncomingMessageData, tx: &Option, ) -> Result<(), RuntimeError> { + warn!(ctx.get_logger("dispatcher"), "incoming message executing"; "id" => in_msg.id); // %%% R::Modules::execute_in_msg(ctx, in_msg, data, tx)?; + warn!(ctx.get_logger("dispatcher"), "incoming message modules done"; "id" => in_msg.id); // %%% if let Some(tx) = tx { let tx_size = match data .ut @@ -360,7 +362,11 @@ impl Dispatcher { // Use the ID as index. let index = in_msg.id.try_into().unwrap(); // todo: put result tags in block - Self::execute_tx(ctx, tx_size, tx.clone(), index)?; + let result = Self::execute_tx(ctx, tx_size, tx.clone(), index)?; + let result_parsed = cbor::from_slice::(&result.output).unwrap(); + warn!(ctx.get_logger("dispatcher"), "incoming message transaction done"; "id" => in_msg.id, "result_parsed" => ?result_parsed); // %%% + } else { + warn!(ctx.get_logger("dispatcher"), "incoming message no transaction"; "id" => in_msg.id); // %%% } Ok(()) }