diff --git a/.circleci/config.yml b/.circleci/config.yml index 1643d0451..2b54e4bba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ jobs: - run: name: Checkstyle - command: cargo fmt -p ethcore -p ethcore-transaction -p ethkey -p vm -p wasm -p wasmer -- --check + command: cargo fmt -p ethcore -p ethcore-transaction -p ethkey -p vm -p wasm -- --check # Build ethcore for runtime (SGX) - run: @@ -34,7 +34,7 @@ jobs: # Run cargo tests - run: name: Run cargo tests - command: cargo test --package ethcore --package ethcore-transaction --package ethkey --package vm --package wasm --package wasmer --features "ethkey-test" + command: cargo test --package ethcore --package ethcore-transaction --package ethkey --package vm --package wasm --features "ethkey-test" coverage: machine: true diff --git a/Cargo.lock b/Cargo.lock index ed6df846e..a2c678158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,10 +120,10 @@ dependencies = [ [[package]] name = "bcfs" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "blockchain-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blockchain-traits 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "oasis-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "wasi-types 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -232,7 +232,7 @@ dependencies = [ [[package]] name = "blockchain-traits" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "oasis-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -662,7 +662,7 @@ dependencies = [ name = "ethcore" version = "1.12.0" dependencies = [ - "blockchain-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blockchain-traits 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.2.0", "bn 0.4.4 (git+https://github.com/paritytech/bn)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3143,7 +3143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "vm" version = "0.1.0" dependencies = [ - "blockchain-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blockchain-traits 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "elastic-array 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3193,8 +3193,8 @@ name = "wasm" version = "0.1.0" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bcfs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blockchain-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bcfs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blockchain-traits 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "ethcore-logger 1.12.0", @@ -3292,8 +3292,8 @@ name = "wasmer" version = "0.1.0" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bcfs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blockchain-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bcfs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blockchain-traits 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", "ethcore-logger 1.12.0", @@ -3548,7 +3548,7 @@ dependencies = [ "checksum base-x 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bcfs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e0d19b77d1c664adacadb5d9500b2d93f68e3e391601a9ac7870327a811c3b6c" +"checksum bcfs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82e92e079aa6c85865ab857b119a5ac01ca4aaec96ab8c7b5f50095cb28c6053" "checksum bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" "checksum bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7f7f0701772b17de73e4f5cbcb1dd6926f4706cba4c1ab62c5367f8bdc94e1" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" @@ -3560,7 +3560,7 @@ dependencies = [ "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum blockchain-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "404b5282ee3ef9513b884d67e763eff697b5c7a62d4804d3c6ac3a32aa1e500c" +"checksum blockchain-traits 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed24a85db12475368a2780d6d72fbcc0dc1cfeb78005e9eb5b0824ac6bda25ac" "checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" "checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" diff --git a/README.md b/README.md index 25c5d312d..4d9bc5ecb 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,5 @@ Major changes made by Oasis Labs: * Parity runtime modified to run in Intel SGX, using the [Fortanix Rust Enclave Development Platform](https://github.com/fortanix/rust-sgx). * Support for *confidential smart contracts*: transactions encrypted end-to-end and contract state encrypted in storage. * [Blockchain WASI](https://github.com/oasislabs/rfcs/pull/1)-based runtime for WebAssembly contracts. -* Option to use the [Wasmer](https://github.com/wasmerio/wasmer) WebAssembly runtime rather than the standard [wasmi interpreter](https://github.com/oasislabs/wasmi) (*however Wasmer is not supported when running in SGX*). For an example of how to use, see https://github.com/oasislabs/oasis-chain. diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 475805901..463c63f2c 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -7,7 +7,7 @@ version = "1.12.0" authors = ["Parity Technologies "] [dependencies] -blockchain-traits = "0.3" +blockchain-traits = "0.4" bloomchain = { path = "../util/bloomchain" } bn = { git = "https://github.com/paritytech/bn", default-features = false } byteorder = "1.0" diff --git a/ethcore/res/wasi-tests/src/bin/creator.rs b/ethcore/res/wasi-tests/src/bin/creator.rs new file mode 100644 index 000000000..6fe06c5bf --- /dev/null +++ b/ethcore/res/wasi-tests/src/bin/creator.rs @@ -0,0 +1,37 @@ +#![feature(wasi_ext)] + +use std::io::{Read as _, Write as _}; +use std::os::wasi::prelude::FromRawFd; + +#[link(wasm_import_module = "wasi_unstable")] +#[allow(improper_ctypes)] +extern "C" { + #[link_name = "blockchain_create"] + fn __wasi_blockchain_create( + value: *const u128, + code: *const u8, + code_len: u64, + fd: *mut libc::__wasi_fd_t, + ) -> libc::__wasi_errno_t; +} + +fn main() { + let value = 42u128; + let code = include_bytes!("../../target/service/create_ctor.wasm"); + let mut fd = Default::default(); + assert_eq!( + unsafe { + __wasi_blockchain_create( + &value as *const u128, + code.as_ptr(), + code.len() as u64, + &mut fd as *mut _, + ) + }, + libc::__WASI_ESUCCESS + ); + let mut f_out = unsafe { std::fs::File::from_raw_fd(fd) }; + let mut new_addr_bytes = Vec::new(); + f_out.read_to_end(&mut new_addr_bytes).unwrap(); + std::io::stdout().write_all(&new_addr_bytes).unwrap(); +} diff --git a/ethcore/res/wasi-tests/src/bin/xcc_a.rs b/ethcore/res/wasi-tests/src/bin/xcc_a.rs index d25e12e01..acf07c13b 100644 --- a/ethcore/res/wasi-tests/src/bin/xcc_a.rs +++ b/ethcore/res/wasi-tests/src/bin/xcc_a.rs @@ -4,6 +4,7 @@ use std::io::{Read as _, Write as _}; use std::os::wasi::prelude::FromRawFd; #[link(wasm_import_module = "wasi_unstable")] +#[allow(improper_ctypes)] extern "C" { #[link_name = "blockchain_transact"] fn __wasi_blockchain_transact( diff --git a/ethcore/res/wasi-tests/target/service/creator.wasm b/ethcore/res/wasi-tests/target/service/creator.wasm new file mode 100644 index 000000000..a34a345ba Binary files /dev/null and b/ethcore/res/wasi-tests/target/service/creator.wasm differ diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 6775f36eb..9e0050225 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -2770,6 +2770,62 @@ mod tests { ); } + evm_test! {test_wasm_create: test_wasm_create_int} + fn test_wasm_create(factory: Factory) { + let code = include_bytes!("../res/wasi-tests/target/service/creator.wasm").to_vec(); + let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); + + create_wasm_executive!(factory -> (state, exec)); + + exec.state + .add_balance(&sender, &U256::from(0xfffff), CleanupMode::NoEmpty) + .unwrap(); + + let creator_address = deploy_wasm(&mut exec, sender, &code); + + let created_init_balance = 42; + + let created_address = { + let mut params = ActionParams::default(); + params.sender = sender; + params.address = creator_address; + params.gas = U256::from(1_000_000); + params.code = Some(Arc::new(code)); + params.code_address = creator_address; + params.value = ActionValue::transfer(created_init_balance); + + Address::from( + &*exec + .call( + params, + &mut Substate::new(), + BytesRef::Fixed(&mut []), + &mut NoopTracer, + &mut NoopVMTracer, + &mut NoopExtTracer, + ) + .unwrap() + .return_data, + ) + }; + + assert_eq!(state.balance(&creator_address).unwrap(), U256::zero()); + assert_eq!( + state.balance(&created_address).unwrap(), + U256::from(created_init_balance) + ); + + // the following data is set by the constructor of `create_ctor.wasm` + let k = b"message"; + let mut hk = [0u8; 32]; + hk[..k.len()].copy_from_slice(k.as_ref()); + let new_acct_data = state.storage_bytes_at(&created_address, &H256::from(hk)); + assert_eq!( + new_acct_data.as_ref().map(|v| v.as_slice()), + Ok(b"hello".as_ref()) + ); + } + evm_test! {test_wasm_xcc: test_wasm_xcc_int} fn test_wasm_xcc(factory: Factory) { let code_xcc = diff --git a/ethcore/vm/Cargo.toml b/ethcore/vm/Cargo.toml index 8d32a76a0..8950234cf 100644 --- a/ethcore/vm/Cargo.toml +++ b/ethcore/vm/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -blockchain-traits = "0.3" +blockchain-traits = "0.4" oasis-types = "0.3" byteorder = "1.0" ethcore-bytes = { path = "../../util/bytes" } diff --git a/ethcore/wasm/Cargo.toml b/ethcore/wasm/Cargo.toml index 7c8323be8..3714dd4d6 100644 --- a/ethcore/wasm/Cargo.toml +++ b/ethcore/wasm/Cargo.toml @@ -6,8 +6,8 @@ edition = "2018" [dependencies] base64 = "0.10" -bcfs = "0.3" -blockchain-traits = "0.3" +bcfs = "0.4" +blockchain-traits = "0.4" byteorder = "1.0" common-types = { path = "../types" } ethereum-types = { version = "0.3", default-features = false, features = ["std", "serialize"] } diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index 1e5106795..33d7d9ecc 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -22,7 +22,9 @@ use bcfs::BCFS; use blockchain_traits::{KVStore, KVStoreMut, PendingTransaction, TransactionOutcome}; use common_types::{u128_from_u256, u256_from_u128}; use ethereum_types::{Address, H256, U256}; -use vm::{self, CallType, MessageCallResult, ReturnData}; +use vm::{ + self, CallType, ContractCreateResult, CreateContractAddress, MessageCallResult, ReturnData, +}; use wasmi::{self, Error as InterpreterError, MemoryRef, Trap, TrapKind}; pub struct RuntimeContext { @@ -106,6 +108,71 @@ impl<'a> PendingTransaction for Runtime<'a> { &self.args } + fn create(&mut self, value: u128, code: &[u8]) -> Box { + trace!(target: "wasm", "runtime: CREATE(value: {:?}), code {:?}", value, code); + + let mut receipt = Box::new(Receipt { + caller: *eaddr2maddr(&self.context.address), + callee: oasis_types::Address::default(), + gas_used: 0, + outcome: TransactionOutcome::Fatal, + output: ReturnData::empty(), + }); + + if let Err(err) = self.adjusted_charge(|schedule| Some(schedule.create_gas as u64)) { + trace!("CREATE failed adjusted charge {:?}", err); + receipt.outcome = TransactionOutcome::InsufficientGas; + return receipt; + } + + let pre_gas_left = match self.gas_left() { + Ok(gas_left) => gas_left, + Err(_) => { + receipt.outcome = TransactionOutcome::InsufficientGas; + return receipt; + } + }; + + let mut salt = H256::new(); + self.rng.generate_to_slice(salt.as_mut(), None /* aad */); + + let create_result = self.ext.create( + &pre_gas_left.into(), + &u256_from_u128(value), + code, + CreateContractAddress::FromSenderSaltAndCodeHash(salt), + ); + + match create_result { + ContractCreateResult::Created(new_contract_addr, post_gas_left) => { + receipt.outcome = TransactionOutcome::Success; + receipt.callee = *eaddr2maddr(&new_contract_addr); + receipt.gas_used = pre_gas_left - post_gas_left.low_u64(); + // ^ by definition pre_gas_left > post_gas_left so this cannot overflow + if !self.charge_gas(receipt.gas_used) { + receipt.outcome = TransactionOutcome::InsufficientGas; + receipt.gas_used = self.gas_limit; + return receipt; + } + } + ContractCreateResult::Failed => { + receipt.outcome = TransactionOutcome::Fatal; + receipt.gas_used = self.gas_limit; + } + ContractCreateResult::Reverted(post_gas_left, return_data) => { + if !self.charge_gas(receipt.gas_used) { + receipt.outcome = TransactionOutcome::InsufficientGas; + receipt.gas_used = self.gas_limit; + return receipt; + } + receipt.outcome = TransactionOutcome::Aborted; + receipt.gas_used = pre_gas_left - post_gas_left.low_u64(); + receipt.output = return_data; + } + } + return receipt; + } + /// Executes a balance-transferring RPC to `callee` with provided input and value. /// The new transaction will inherit the gas parameters and gas payer of the top level /// transaction. The current account will be set as the sender. @@ -127,6 +194,8 @@ impl<'a> PendingTransaction for Runtime<'a> { if let Err(err) = self.adjusted_charge(|schedule| Some(schedule.call_gas as u64)) { trace!("CALL failed adjusted charge {:?}", err); + receipt.outcome = TransactionOutcome::InsufficientGas; + return receipt; } let pre_gas_left = match self.gas_left() { diff --git a/ethcore/wasm/src/wasi.rs b/ethcore/wasm/src/wasi.rs index 916870966..4fe10c767 100644 --- a/ethcore/wasm/src/wasi.rs +++ b/ethcore/wasm/src/wasi.rs @@ -39,6 +39,16 @@ macro_rules! read_path { }}; } +macro_rules! fetch_bytes { + ($self:ident, $p_input:ident, $input_len:ident) => {{ + let input_len = $input_len as usize; + let input_ptr = $self.memory.get::<_, u8>($p_input, input_len)?.as_ptr(); + std::mem::forget(input_ptr); + unsafe { std::slice::from_raw_parts(input_ptr, input_len) } + // ^ Mutable borrow needed for `self.transact`, but transact doesn't touch linear memory. + }}; +} + #[wasm_macros::wasm_exports] impl<'a> crate::Runtime<'a> { // not part of wasi, but required by parity @@ -465,7 +475,7 @@ impl<'a> crate::Runtime<'a> { let rng_buf_len = rng_buf_blocks * RNG_HASH_BYTES; let mut rng_buf = Vec::with_capacity(rng_buf_len); unsafe { rng_buf.set_len(rng_buf_len) }; - self.rng.generate_to_slice(&mut rng_buf, None); + self.rng.generate_to_slice(&mut rng_buf, None /* aad */); self.memory .get_mut(buf, buf_len)? .copy_from_slice(&rng_buf[..buf_len]); @@ -476,6 +486,33 @@ impl<'a> crate::Runtime<'a> { Ok(ErrNo::Success) // unimplemented(dontneed): there's only one thread } + pub fn blockchain_create( + &mut self, + p_value: P, + p_code: P, + code_len: u64, + p_fd: P, + ) -> crate::Result { + let code = fetch_bytes!(self, p_code, code_len); + let value = self.memory.get_value(p_value)?; + + let receipt = self.create(value, code); + + let fd = bcfs!(self.bcfs.tempfile()); + bcfs!(self.bcfs.pwrite_vectored( + fd, + &[IoSlice::new(receipt.callee().as_ref())], + 0 /* offset */ + )); + self.memory.set_value(p_fd, fd)?; + + Ok(if receipt.reverted() { + ErrNo::ConnAborted + } else { + ErrNo::Success + }) + } + pub fn blockchain_transact( &mut self, p_callee_addr: P, @@ -485,15 +522,11 @@ impl<'a> crate::Runtime<'a> { p_fd: P, ) -> crate::Result { let callee_addr: oasis_types::Address = self.memory.get_value(p_callee_addr)?; - - let input_len = input_len as usize; - let input_ptr = self.memory.get::<_, u8>(p_input, input_len)?.as_ptr(); - std::mem::forget(input_ptr); - let input = unsafe { std::slice::from_raw_parts(input_ptr, input_len) }; - // ^ Mutable borrow needed for `self.transact`, but transact doesn't touch linear memory. - + let input = fetch_bytes!(self, p_input, input_len); let value = self.memory.get_value(p_value)?; + let receipt = self.transact(callee_addr, value, input); + let fd = bcfs!(self.bcfs.tempfile()); bcfs!(self.bcfs.pwrite_vectored( fd, diff --git a/ethcore/wasmer/Cargo.toml b/ethcore/wasmer/Cargo.toml index a5d4b23c4..1bd31b9ec 100644 --- a/ethcore/wasmer/Cargo.toml +++ b/ethcore/wasmer/Cargo.toml @@ -5,9 +5,9 @@ authors = ["Oasis Labs Inc. "] [dependencies] base64 = "0.10" -bcfs = "0.3" +bcfs = "0.4" byteorder = "1.0" -blockchain-traits = "0.3" +blockchain-traits = "0.4" common-types = { path = "../types" } ethereum-types = { version = "0.3", default-features = false, features = ["std", "serialize"] } log = "0.3"