diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index f64ce7e..ff51fc6 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -13,6 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + + - name: Install CapnProto + run: sudo apt-get install capnproto + - name: Install Rust run: | rustup toolchain install nightly --component llvm-tools-preview rustfmt clippy diff --git a/CHANGELOG.md b/CHANGELOG.md index 778566a..0108efc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Adding storage API to read from the storage file and write into storage file - Updating Kelk version to 0.3.0 in test-contract - Updating Wasmer version to 3.1.0 +- Refactoring Tanour Server to call Tanour executor using CapnProto APIs ## 0.1.0 diff --git a/Cargo.toml b/Cargo.toml index ed8a812..d643466 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = [ "tanour", - # "tanour-server", + "tanour-server", ] exclude = ["test-contract"] diff --git a/tanour-server/Cargo.toml b/tanour-server/Cargo.toml index 5a009ed..c4e8716 100644 --- a/tanour-server/Cargo.toml +++ b/tanour-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tanour-server" -version = "0.1.0" +version = "0.2.0" authors = ["Pactus blockchain "] edition = "2021" @@ -14,13 +14,19 @@ path = "src/main.rs" capnpc = { git = "https://github.com/capnproto/capnproto-rust" } [dependencies] -capnp = { git = "https://github.com/capnproto/capnproto-rust" } +capnp = { git = "https://github.com/capnproto/capnproto-rust" } capnp-rpc = { git = "https://github.com/capnproto/capnproto-rust" } futures = "0.3.0" -tokio = { version = "0.2.0", features = ["time", "sync", "rt-util", "rt-core", "net", "macros"]} +tokio = { version = "0.2.0", features = [ + "time", + "sync", + "rt-util", + "rt-core", + "net", + "macros", +] } tokio-util = { version = "0.3.0", features = ["compat"] } -tanour = { path = "../tanour" } -primitive-types = "0.7.2" +tanour = { version = "0.2.0" } async-std = "1.5.0" log = "0.4" simple_logger = "1.4.0" diff --git a/tanour-server/src/adaptor.rs b/tanour-server/src/adaptor.rs new file mode 100644 index 0000000..9b51f6b --- /dev/null +++ b/tanour-server/src/adaptor.rs @@ -0,0 +1,76 @@ +use crate::tanour_capnp; + +use log::debug; + +use tanour::{blockchain_api::BlockchainAPI, Address}; + +unsafe impl Send for tanour_capnp::provider::Client {} + +pub struct BlockchainAdaptor { + client: tanour_capnp::provider::Client, +} + +impl BlockchainAdaptor { + pub fn new(client: tanour_capnp::provider::Client) -> Self { + BlockchainAdaptor { client } + } +} + +impl BlockchainAPI for BlockchainAdaptor { + fn page_size(&self) -> Result { + let req = self.client.page_size_request(); + + let handle = async move { + debug!("Try ot call `page_size` method in client"); + let result = req.send().promise.await.unwrap(); //TODO: no unwrap + result.get().unwrap().get_size() //TODO: no unwrap + }; + + Ok(futures::executor::block_on(handle)) + } + + fn read_page(&self, page_no: u32) -> Result, tanour::error::Error> { + let mut req = self.client.read_page_request(); + req.get().set_page_no(page_no); + + let handle = async move { + debug!("Try ot call `read_page` method in client"); + let result = req.send().promise.await.unwrap(); //TODO: no unwrap + result.get().unwrap().get_data().unwrap().to_vec() //TODO: no unwrap + }; + + Ok(futures::executor::block_on(handle)) //TODO: no unwrap + } + + fn write_page(&self, page_no: u32, data: &[u8]) -> Result<(), tanour::error::Error> { + let mut req = self.client.write_page_request(); + req.get().set_page_no(page_no); + req.get().set_data(data); + + let handle = async move { + debug!("Try ot call `write_page` method in client"); + let result = req.send().promise.await.unwrap(); //TODO: no unwrap + result.get().unwrap(); //TODO: no unwrap + }; + + futures::executor::block_on(handle); + Ok(()) //TODO: no unwrap + } + + fn exist(&self, address: &Address) -> Result { + let mut req = self.client.exists_request(); + req.get().set_address(address); + + let handle = async move { + debug!("Try ot call `exists` method in client"); + let result = req.send().promise.await.unwrap(); //TODO: no unwrap + result.get().unwrap().get_exist() //TODO: no unwrap + }; + + Ok(futures::executor::block_on(handle)) + } + + fn current_block_number(&self) -> u32 { + todo!() + } +} diff --git a/tanour-server/src/build.rs b/tanour-server/src/build.rs index a0e1247..bfaf627 100644 --- a/tanour-server/src/build.rs +++ b/tanour-server/src/build.rs @@ -1,4 +1,6 @@ - fn main() { - ::capnpc::CompilerCommand::new().file("tanour.capnp").run().unwrap(); -} \ No newline at end of file + ::capnpc::CompilerCommand::new() + .file("tanour.capnp") + .run() + .unwrap(); +} diff --git a/tanour-server/src/executor_impl.rs b/tanour-server/src/executor_impl.rs index c6ff8b7..6568849 100644 --- a/tanour-server/src/executor_impl.rs +++ b/tanour-server/src/executor_impl.rs @@ -1,77 +1,56 @@ -use crate::provider_adaptor::ProviderAdaptor; +use crate::adaptor::BlockchainAdaptor; use crate::tanour_capnp; use crate::tanour_capnp::executor; use capnp::capability::Promise; use capnp::Error; use capnp_rpc::pry; -use log::debug; -use primitive_types::{H256, U256}; -use tanour::Address; +use tanour::address_from_bytes; +use tanour::contract::Params; use tokio::sync::oneshot; use tokio::sync::oneshot::error::TryRecvError; -impl<'a> From> - for Result -{ - fn from(reader: tanour_capnp::transaction::Reader<'a>) -> Self { - let sender = Address::from_slice(reader.get_sender()?); - let value = U256::from_little_endian(reader.get_value()?); - let gas = U256::from_little_endian(reader.get_gas()?); - let gas_price = U256::from_little_endian(reader.get_gas_price()?); - let args = reader.get_args()?.to_vec(); - let action = match reader.get_action().which()? { - tanour_capnp::transaction::action::Create(create) => { - let code = create.get_code()?.to_vec(); - let salt = H256::from_slice(create.get_salt()?); - tanour::transaction::Action::Create(code, salt) - } - tanour_capnp::transaction::action::Call(call) => { - let address = Address::from_slice(call.get_address()?); - tanour::transaction::Action::Call(address) - } - }; - - Ok(tanour::transaction::Transaction { - sender: sender, - value: value, - gas: gas, - gas_price: gas_price, - action: action, - args: args, - }) - } -} - -pub struct ExecutorImpl {} - -impl ExecutorImpl { - pub fn new() -> Self { - ExecutorImpl {} - } -} - -unsafe impl Send for tanour_capnp::provider::Client {} -//unsafe impl Sync for tanour_capnp::provider::Client {} +pub struct ExecutorImpl; +// TODO: ??? why ??? +#[allow(clippy::async_yields_async)] impl executor::Server for ExecutorImpl { fn execute( &mut self, params: executor::ExecuteParams, mut results: executor::ExecuteResults, ) -> Promise<(), Error> { - let provider_client = pry!(pry!(params.get()).get_provider()); - let transaction = pry!(pry!(pry!(params.get()).get_transaction()).into()); let (tx, mut rx) = oneshot::channel(); - tokio::task::spawn(async move { - debug!("provider: {:?}", std::thread::current().id()); - let mut adaptor = ProviderAdaptor::new(provider_client); - - let result = tanour::execute::execute(&mut adaptor, &transaction).unwrap(); - - tx.send(result).unwrap(); + tokio::task::spawn_local(async move { + let provider_client = pry!(pry!(params.get()).get_provider()); + let transaction = pry!(pry!(params.get()).get_transaction()); + let adaptor = BlockchainAdaptor::new(provider_client); + let msg = pry!(transaction.get_args()); + let address = address_from_bytes(pry!(transaction.get_address())); + let code = pry!(transaction.get_code()); + let params = Params { + memory_limit_page: 1000, + metering_limit: 11100, + }; + + let mut contract = + tanour::contract::Contract::new(Box::new(adaptor), &address, code, params).unwrap(); // TODO: no unwrap + + let res = match pry!(transaction.get_action().which()) { + tanour_capnp::transaction::action::Instantiate(_) => { + contract.call_instantiate(msg).unwrap() // TODO: no unwrap + } + tanour_capnp::transaction::action::Process(_) => { + contract.call_process(msg).unwrap() // TODO: no unwrap + } + tanour_capnp::transaction::action::Query(_) => { + contract.call_query(msg).unwrap() // TODO: no unwrap + } + }; + + tx.send(res).unwrap(); // TODO: no unwrap + Promise::<(), Error>::ok(()) }); - debug!("executor: {:?}", std::thread::current().id()); Promise::from_future(async move { loop { @@ -79,31 +58,19 @@ impl executor::Server for ExecutorImpl { match msg { Err(TryRecvError::Empty) => {} Err(e) => { - return Err(Error::failed(format!("{}", e))); + return Err(Error::failed(format!("{e}"))); } Ok(result_data) => { - tokio::time::delay_for(std::time::Duration::from_millis(10 as u64)).await; - - let mut tmp = Vec::new(); - tmp.resize(32, 0); + tokio::time::delay_for(std::time::Duration::from_millis(10_u64)).await; let mut builder = results.get().get_result_data().unwrap(); - - result_data.gas_left.to_little_endian(&mut tmp); - builder.set_gas_left(&tmp); - builder.set_data(&result_data.data); - builder.set_contract(&result_data.contract.as_bytes()); - - // TODO: Implement it later - //builder.set_logs(); + builder.set_data(&result_data); break; } }; - //print!("."); - tokio::task::yield_now().await; - //tokio::time::delay_for(std::time::Duration::from_millis(10 as u64)).await; + tokio::task::yield_now().await } Ok(()) diff --git a/tanour-server/src/main.rs b/tanour-server/src/main.rs index 2451888..d0b1a39 100644 --- a/tanour-server/src/main.rs +++ b/tanour-server/src/main.rs @@ -1,14 +1,14 @@ pub mod tanour_capnp { include!(concat!(env!("OUT_DIR"), "/tanour_capnp.rs")); } +mod adaptor; mod executor_impl; -mod provider_adaptor; use capnp_rpc::{rpc_twoparty_capnp, twoparty, RpcSystem}; -use tanour_capnp::executor; use executor_impl::ExecutorImpl; use futures::{AsyncReadExt, FutureExt, TryFutureExt}; use std::net::ToSocketAddrs; +use tanour_capnp::executor; use tokio::net::TcpListener; #[tokio::main] @@ -30,7 +30,7 @@ pub async fn main() -> Result<(), Box> { tokio::task::LocalSet::new() .run_until(async move { let mut listener = TcpListener::bind(&addr).await?; - let executor_impl = ExecutorImpl::new(); + let executor_impl = ExecutorImpl {}; let executor: executor::Client = capnp_rpc::new_client(executor_impl); loop { @@ -47,9 +47,7 @@ pub async fn main() -> Result<(), Box> { let rpc_system = RpcSystem::new(Box::new(network), Some(executor.clone().client)); tokio::task::spawn_local(Box::pin( - rpc_system - .map_err(|e| println!("error: {:?}", e)) - .map(|_| ()), + rpc_system.map_err(|e| println!("error: {e:?}")).map(|_| ()), )); } }) diff --git a/tanour-server/src/provider_adaptor.rs b/tanour-server/src/provider_adaptor.rs deleted file mode 100644 index 5190350..0000000 --- a/tanour-server/src/provider_adaptor.rs +++ /dev/null @@ -1,185 +0,0 @@ -use crate::tanour_capnp; -use log::debug; -use primitive_types::{H256, U256}; -use tanour::provider_api::ProviderAPI; -use tanour::Address; -struct Error { - pub failed: String, -} - -impl From<::capnp::Error> for Error { - fn from(error: ::capnp::Error) -> Self { - Error { - failed: error.description, - } - } -} -impl From for tanour::error::Error { - fn from(error: Error) -> Self { - tanour::error::Error::Other { msg: error.failed } - } -} - -pub struct ProviderAdaptor { - client: tanour_capnp::provider::Client, -} - -impl ProviderAdaptor { - pub fn new(client: tanour_capnp::provider::Client) -> Self { - ProviderAdaptor { client } - } -} - -impl ProviderAPI for ProviderAdaptor { - fn exist(&self, address: &Address) -> bool { - let mut request = self.client.exist_request(); - { - request.get().set_address(address.as_bytes()); - } - - let handle = async move { - debug!("Try ot call `exist` method in client"); - let result = request.send().promise.await?; - let exist = result.get()?.get_exist(); - - Ok(exist) - }; - let ret: Result = futures::executor::block_on(handle); - match ret { - Ok(exist) => exist, - Err(_) => false, - } - } - - fn account(&self, address: &Address) -> Result { - let mut request = self.client.account_request(); - { - request.get().set_address(address.as_bytes()); - } - let handle = async move { - debug!("Try ot call `account` method in client"); - let result = request.send().promise.await?; - let account = result.get()?.get_account()?; - - Ok(StateAccount { - nonce: U256::from_little_endian(account.get_nonce()?), - balance: U256::from_little_endian(account.get_balance()?), - code: account.get_code()?.to_vec(), - }) - }; - - futures::executor::block_on(handle).map_err(|e: Error| e.into()) - } - - fn create_contract( - &mut self, - address: &Address, - code: &Vec, - ) -> Result<(), tanour::error::Error> { - let mut request = self.client.create_contract_request(); - { - request.get().set_address(address.as_bytes()); - request.get().set_code(code); - } - let handle = async move { - debug!("Try ot call `create_contract` method in client"); - request.send().promise.await?; - - Ok(()) - }; - - futures::executor::block_on(handle).map_err(|e: Error| e.into()) - } - - fn update_account( - &mut self, - address: &Address, - balance: &U256, - nonce: &U256, - ) -> Result<(), tanour::error::Error> { - let mut request = self.client.update_account_request(); - { - let mut tmp = Vec::new(); - tmp.resize(32, 0); - - request.get().set_address(address.as_bytes()); - - balance.to_little_endian(&mut tmp); - request.get().set_balance(&tmp); - - nonce.to_little_endian(&mut tmp); - request.get().set_nonce(&tmp); - } - let handle = async move { - debug!("Try ot call `update_account` method in client"); - request.send().promise.await?; - - Ok(()) - }; - - futures::executor::block_on(handle).map_err(|e: Error| e.into()) - } - - fn storage_at(&self, address: &Address, key: &H256) -> Result { - let mut request = self.client.storage_at_request(); - { - request.get().set_address(address.as_bytes()); - request.get().set_key(key.as_bytes()); - } - let handle = async move { - debug!("Try ot call `storage_at` method in client"); - let result = request.send().promise.await?; - let storage = result.get()?.get_storage()?; - - Ok(H256::from_slice(storage)) - }; - - futures::executor::block_on(handle).map_err(|e: Error| e.into()) - } - - fn set_storage( - &mut self, - address: &Address, - key: &H256, - value: &H256, - ) -> Result<(), tanour::error::Error> { - let mut request = self.client.set_storage_request(); - { - request.get().set_address(address.as_bytes()); - request.get().set_key(key.as_bytes()); - request.get().set_value(value.as_bytes()); - } - let handle = async move { - debug!("Try ot call `set_storage` method in client"); - request.send().promise.await?; - - Ok(()) - }; - - futures::executor::block_on(handle).map_err(|e: Error| e.into()) - } - - fn block_hash(&self, _num: u64) -> Result { - Ok(H256::zero()) - } - - fn timestamp(&self) -> u64 { - 0 - } - - fn block_number(&self) -> u64 { - 0 - } - - fn block_author(&self) -> Result { - Err(tanour::error::Error::NotSupported) - } - - fn difficulty(&self) -> Result { - Err(tanour::error::Error::NotSupported) - } - - fn gas_limit(&self) -> Result { - Ok(U256::zero()) - } -} diff --git a/tanour-server/tanour.capnp b/tanour-server/tanour.capnp index a59818c..7c76b3f 100644 --- a/tanour-server/tanour.capnp +++ b/tanour-server/tanour.capnp @@ -11,29 +11,20 @@ struct Transaction { value @1: UInt64; gas @2: UInt64; gasPrice @3: UInt64; + address @4: Data; + code @5: Data; action: union{ - create: group { - code @4: Data; - salt @5: Data; - } - call: group { - address @6: Data; - } + instantiate @6: Void; + process @7: Void; + query @8: Void; } - args @7: Data; -} - -struct LogEntry { - address @0: Data; - topics @1: List(Data); - data @2: List(Int8); + args @9: Data; } struct ResultData { gasLeft @0: UInt64; data @1: Data; contract @2: Data; - logs @3: List(LogEntry); } interface Executor { @@ -41,14 +32,9 @@ interface Executor { } interface Provider { - exists @0 ( address: Data ) -> (exist: Bool); - account @1 ( address: Data ) -> (account: Account); - updateAccount @2 ( address: Data, balance: UInt64, sequence: UInt64 ) -> (); - createContract @3 ( address: Data, code: Data ) -> (); - getStorage @4 ( address: Data, key: Data ) -> (storage: Data); - setStorage @5 ( address: Data, key: Data, value: Data ) -> (); - timestamp @6 ( ) -> (timestamp: UInt64); - blockNumber @7 ( ) -> (number: UInt64); - blockHash @8 ( blockNo: UInt64 ) -> (hash: Data); - gasLimit @9 ( ) -> (gasLimit: UInt64); + pageSize @0 ( ) -> (size: UInt32); + readPage @1 ( pageNo: UInt32 ) -> (data: Data); + writePage @2 ( pageNo: UInt32, data: Data ) -> (); + exists @3 ( address: Data ) -> (exist: Bool); + account @4 ( address: Data ) -> (account: Account); } diff --git a/tanour/Cargo.toml b/tanour/Cargo.toml index 9adef0f..76b5fc4 100644 --- a/tanour/Cargo.toml +++ b/tanour/Cargo.toml @@ -15,10 +15,10 @@ wasmer = { version = "3.1", default-features = false, features = [ wasmer-middlewares = "3.1" thiserror = "1.0" hex = "0.4" -minicbor = { version = "0.18", features = ["std"] } mockall = "0.10" [dev-dependencies] +minicbor = { version = "0.18", features = ["std"] } simple_logger = "1.4" wat = "1" test_contract = { path = "../test-contract" } diff --git a/tanour/src/blockchain_api.rs b/tanour/src/blockchain_api.rs index 684d2e1..7921a57 100644 --- a/tanour/src/blockchain_api.rs +++ b/tanour/src/blockchain_api.rs @@ -4,8 +4,9 @@ use mockall::{automock, predicate::*}; #[automock] pub trait BlockchainAPI: Send + 'static { - fn read_storage(&self, offset: u32, length: u32) -> Result>; - fn write_storage(&mut self, offset: u32, data: &[u8]) -> Result<()>; + fn page_size(&self) -> Result; + fn read_page(&self, page_no: u32) -> Result>; + fn write_page(&self, page_no: u32, data: &[u8]) -> Result<()>; fn exist(&self, address: &Address) -> Result; // TODO: maybe better we return a block_info, including hash, time, number and proposer address fn current_block_number(&self) -> u32; diff --git a/tanour/src/contract.rs b/tanour/src/contract.rs index 7676f68..1ac9f88 100644 --- a/tanour/src/contract.rs +++ b/tanour/src/contract.rs @@ -1,21 +1,19 @@ use crate::blockchain_api::BlockchainAPI; -use crate::error::{Error, Result}; +use crate::error::Result; use crate::executor::Executor; use crate::memory::Pointer; use crate::provider::ProviderAdaptor; use crate::{wasmer, Address}; -use minicbor::{Decode, Encode}; use std::sync::{Arc, Mutex}; -const PAGE_SIZE: u32 = 1024 * 1024; // 1 MB - #[derive(Debug)] pub struct ResultData { pub gas_left: u64, pub data: Vec, } +// TODO: rename me, it is confusing with ExecuteParams #[derive(Debug)] pub struct Params { pub memory_limit_page: u32, @@ -27,8 +25,6 @@ pub struct Contract { executor: Box, // State of the contract _state: Arc>, - // internal buffer for decoding messages, because minicbor is zero-copy. - buffer: Vec, // Contract's address _address: Address, } @@ -40,7 +36,7 @@ impl Contract { code: &[u8], params: Params, ) -> Result { - let provider = Arc::new(Mutex::new(ProviderAdaptor::new(api, PAGE_SIZE))); + let provider = Arc::new(Mutex::new(ProviderAdaptor::new(api)?)); let executor = wasmer::WasmerExecutor::new( code, params.memory_limit_page, @@ -51,48 +47,34 @@ impl Contract { Ok(Contract { executor: Box::new(executor), _state: provider, - buffer: Vec::new(), _address: *address, }) } - fn call_exported_fn<'a, E: Encode<()>, D: Decode<'a, ()>>( - &'a mut self, - msg: E, - fname: &str, - ) -> Result { - let param_data = minicbor::to_vec(msg).map_err(|original| Error::SerializationError { - msg: format!("{original}"), - })?; - let size = param_data.len() as u32; + fn call_exported_fn(&mut self, fname: &str, data: &[u8]) -> Result> { + let size = data.len() as u32; let ptr_64 = self.allocate(size)?; let ptr = Pointer::from_u64(ptr_64); - self.executor.write_ptr(&ptr, ¶m_data)?; + self.executor.write_ptr(&ptr, data)?; let res_ptr_64 = self.executor.call_fn_1(fname, ptr_64)?; self.deallocate(ptr_64)?; // Decoding result (result to pointer) let res_ptr = Pointer::from_u64(res_ptr_64); - self.buffer = self.executor.read_ptr(&res_ptr)?; - minicbor::decode(&self.buffer).map_err(|original| Error::SerializationError { - msg: format!("{original}"), - }) + self.executor.read_ptr(&res_ptr) } - pub fn call_instantiate<'a, E: Encode<()>, D: Decode<'a, ()>>( - &'a mut self, - msg: E, - ) -> Result { - self.call_exported_fn(msg, "instantiate") + pub fn call_instantiate(&mut self, encoded_arg: &[u8]) -> Result> { + self.call_exported_fn("instantiate", encoded_arg) } - pub fn call_process<'a, E: Encode<()>, D: Decode<'a, ()>>(&'a mut self, msg: E) -> Result { - self.call_exported_fn(msg, "process") + pub fn call_process(&mut self, encoded_arg: &[u8]) -> Result> { + self.call_exported_fn("process", encoded_arg) } - pub fn call_query<'a, E: Encode<()>, D: Decode<'a, ()>>(&'a mut self, msg: E) -> Result { - self.call_exported_fn(msg, "query") + pub fn call_query(&mut self, encoded_arg: &[u8]) -> Result> { + self.call_exported_fn("query", encoded_arg) } fn allocate(&self, size: u32) -> Result { diff --git a/tanour/src/error.rs b/tanour/src/error.rs index 298121d..dc69313 100644 --- a/tanour/src/error.rs +++ b/tanour/src/error.rs @@ -14,10 +14,10 @@ pub enum Error { #[error("Memory error: {msg}")] MemoryError { msg: String }, - #[error("Serialization error: {msg}")] - SerializationError { msg: String }, - #[error("io error: {0}")] IOError(#[from] std::io::Error), + + #[error("NetworkError error: {}", msg)] + NetworkError { msg: String }, } pub type Result = std::result::Result; diff --git a/tanour/src/executor.rs b/tanour/src/executor.rs index ce23187..d404e50 100644 --- a/tanour/src/executor.rs +++ b/tanour/src/executor.rs @@ -18,8 +18,10 @@ pub trait Executor { // Get the remaining points (metering) fn remaining_points(&self) -> Result; + // Get the consumed points (metering) fn consumed_points(&self) -> Result; + // Check if all points are consumed (metering) fn exhausted(&self) -> Result; } diff --git a/tanour/src/lib.rs b/tanour/src/lib.rs index 80d4f82..e602978 100644 --- a/tanour/src/lib.rs +++ b/tanour/src/lib.rs @@ -12,6 +12,12 @@ pub const ADDRESS_SIZE: usize = 21; pub type Address = [u8; ADDRESS_SIZE]; +pub fn address_from_bytes(d: &[u8]) -> Address { + let mut addr: Address = [0u8; ADDRESS_SIZE]; + addr.copy_from_slice(d); + addr +} + #[cfg(test)] pub fn address_from_hex(s: &str) -> Address { let mut addr: Address = [0u8; ADDRESS_SIZE]; diff --git a/tanour/src/provider.rs b/tanour/src/provider.rs index 089348b..c7059e5 100644 --- a/tanour/src/provider.rs +++ b/tanour/src/provider.rs @@ -20,12 +20,12 @@ pub struct ProviderAdaptor { } impl ProviderAdaptor { - pub fn new(api: Box, page_size: u32) -> Self { - ProviderAdaptor { - api, - page_size, + pub fn new(api: Box) -> Result { + Ok(ProviderAdaptor { + page_size: api.page_size()?, pages: HashMap::new(), - } + api, + }) } fn read_page(&mut self, page_no: u32) -> Result<&mut Page> { println!("fn: read_page, page_no: {page_no}"); @@ -38,7 +38,7 @@ impl ProviderAdaptor { "Try to read the storage. offset: {offset}, page_size: {}", self.page_size ); - let bytes = self.api.read_storage(offset, self.page_size)?; + let bytes = self.api.read_page(page_no)?; let page = Page::new(offset, self.page_size, bytes); v.insert(page) } diff --git a/tanour/src/provider_test.rs b/tanour/src/provider_test.rs index db04b2a..ccc0da4 100644 --- a/tanour/src/provider_test.rs +++ b/tanour/src/provider_test.rs @@ -1,3 +1,5 @@ +use std::vec; + use crate::blockchain_api::MockBlockchainAPI; use super::*; @@ -5,12 +7,9 @@ use super::*; #[test] fn test_read() { let mut api = Box::new(MockBlockchainAPI::new()); - api.expect_read_storage().returning(|_, len| { - let mut d = Vec::new(); - d.resize(len as usize, 0); - Ok(d) - }); - let mut provider = ProviderAdaptor::new(api, 5); + api.expect_page_size().returning(|| Ok(256)); + api.expect_read_page().returning(|_| Ok(vec![0; 256])); + let mut provider = ProviderAdaptor::new(api).unwrap(); let data = provider.read_storage(3, 12).expect("Reading failed"); assert_eq!(data, vec![0; 12]); @@ -19,12 +18,9 @@ fn test_read() { #[test] fn test_write() { let mut api = Box::new(MockBlockchainAPI::new()); - api.expect_read_storage().returning(|_, len| { - let mut d = Vec::new(); - d.resize(len as usize, 0); - Ok(d) - }); - let mut provider = ProviderAdaptor::new(api, 5); + api.expect_page_size().returning(|| Ok(256)); + api.expect_read_page().returning(|_| Ok(vec![0; 256])); + let mut provider = ProviderAdaptor::new(api).unwrap(); let data = vec![1, 2, 3]; provider.write_storage(3, &data).expect("Writing failed"); diff --git a/tanour/src/wasmer/executor.rs b/tanour/src/wasmer/executor.rs index a27864c..8ff4b24 100644 --- a/tanour/src/wasmer/executor.rs +++ b/tanour/src/wasmer/executor.rs @@ -124,8 +124,8 @@ impl executor::Executor for WasmerExecutor { let result = self.call_function(name, &[val])?; match result.first() { - Some(val) => Err(Error::RuntimeError { - msg: format!("Invalid return value for {name}: {val:?}"), + Some(_) => Err(Error::RuntimeError { + msg: format!("expecting no return value for {name}"), }), None => Ok(()), } @@ -139,7 +139,7 @@ impl executor::Executor for WasmerExecutor { Some(val) => match val { Value::I64(i64) => Ok(*i64 as u64), _ => Err(Error::RuntimeError { - msg: format!("Invalid return value for {name}"), + msg: format!("expecting return value for {name}"), }), }, None => Err(Error::RuntimeError { diff --git a/tanour/tests/contract_test.rs b/tanour/tests/contract_test.rs index cb9710f..0bc07c3 100644 --- a/tanour/tests/contract_test.rs +++ b/tanour/tests/contract_test.rs @@ -14,11 +14,8 @@ fn make_test_contract(wat: &[u8], memory_limit_page: u32, metering_limit: u64) - }; let mut api = Box::new(MockBlockchainAPI::new()); - api.expect_read_storage().returning(|_, len| { - let mut d = Vec::new(); - d.resize(len as usize, 0); - Ok(d) - }); + api.expect_page_size().returning(|| Ok(256)); + api.expect_read_page().returning(|_| Ok(vec![0; 256])); Contract::new(api, &address, &code, params).unwrap() } @@ -28,11 +25,13 @@ fn test_call_process() { let wat = include_bytes!("../../test-contract/wasm/test_contract.wasm"); let mut contract = make_test_contract(wat, 16, 10000); - let _: Result<(), Error> = contract.call_instantiate(InstantiateMsg {}).unwrap(); + let arg = InstantiateMsg {}; + let data = minicbor::to_vec(arg).unwrap(); + contract.call_instantiate(&data).unwrap(); - let msg = ProcMsg::Null; - let res: Result<(), Error> = contract.call_process(&msg).unwrap(); - assert!(res.is_ok()); + let arg = ProcMsg::Null; + let encoded_arg = minicbor::to_vec(arg).unwrap(); + contract.call_process(&encoded_arg).unwrap(); assert_eq!(contract.consumed_points().unwrap(), 9526); } @@ -40,13 +39,26 @@ fn test_call_process() { fn test_read_write_storage() { let wat = include_bytes!("../../test-contract/wasm/test_contract.wasm"); let mut contract = make_test_contract(wat, 16, 100000); - let _: Result<(), Error> = contract.call_instantiate(InstantiateMsg {}).unwrap(); - let msg = "hello world!".to_string(); - let _: Result<(), Error> = contract.call_process(&ProcMsg::SetMessage { msg }).unwrap(); + let arg = InstantiateMsg {}; + let encoded_arg = minicbor::to_vec(arg).unwrap(); + let encoded_res = contract.call_instantiate(&encoded_arg).unwrap(); + let res = minicbor::decode::>(&encoded_res).unwrap(); + assert!(res.is_ok()); + + let arg = ProcMsg::SetMessage { + msg: "hello world!".to_string(), + }; + let encoded_arg = minicbor::to_vec(arg).unwrap(); + let encoded_res = contract.call_process(&encoded_arg).unwrap(); + let res = minicbor::decode::>(&encoded_res).unwrap(); + assert!(res.is_ok()); assert_eq!(contract.consumed_points().unwrap(), 12350); - let res: Result = contract.call_query(&QueryMsg::GetMessage).unwrap(); + let encoded_arg = QueryMsg::GetMessage; + let data = minicbor::to_vec(encoded_arg).unwrap(); + let encoded_res = contract.call_query(&data).unwrap(); + let res = minicbor::decode::>(&encoded_res).unwrap(); assert_eq!(res.unwrap(), QueryRsp::String("hello world!".to_string()),); assert_eq!(contract.consumed_points().unwrap(), 18119); assert!(!contract.exhausted().unwrap()); @@ -57,10 +69,18 @@ fn test_hash_blake2b() { let wat = include_bytes!("../../test-contract/wasm/test_contract.wasm"); let mut contract = make_test_contract(wat, 16, 100000); - let _: Result<(), Error> = contract.call_instantiate(InstantiateMsg {}).unwrap(); + let arg = InstantiateMsg {}; + let encoded_arg = minicbor::to_vec(arg).unwrap(); + let encoded_res = contract.call_instantiate(&encoded_arg).unwrap(); + let res = minicbor::decode::>(&encoded_res).unwrap(); + assert!(res.is_ok()); - let data = "zarb".as_bytes().to_vec(); - let res: Result = contract.call_query(&QueryMsg::Hasher { data }).unwrap(); + let arg = QueryMsg::Hasher { + data: "zarb".as_bytes().to_vec(), + }; + let encoded_arg = minicbor::to_vec(arg).unwrap(); + let encoded_res = contract.call_query(&encoded_arg).unwrap(); + let res = minicbor::decode::>(&encoded_res).unwrap(); assert_eq!( res.unwrap(), QueryRsp::Data(