From c94b02f9552d426dea7d39c42abfd1051b799803 Mon Sep 17 00:00:00 2001 From: Nick Hynes Date: Mon, 9 May 2022 21:52:30 +0800 Subject: [PATCH] evm: make confidentiality a config option --- runtime-sdk/modules/evm/Cargo.toml | 3 - runtime-sdk/modules/evm/src/backend.rs | 26 +++++-- runtime-sdk/modules/evm/src/lib.rs | 3 + runtime-sdk/modules/evm/src/raw_tx.rs | 2 + runtime-sdk/modules/evm/src/state.rs | 36 ++-------- runtime-sdk/modules/evm/src/test.rs | 94 +++++++++++++++++--------- runtime-sdk/src/storage/empty.rs | 63 ----------------- runtime-sdk/src/storage/mod.rs | 2 - 8 files changed, 93 insertions(+), 136 deletions(-) delete mode 100644 runtime-sdk/src/storage/empty.rs diff --git a/runtime-sdk/modules/evm/Cargo.toml b/runtime-sdk/modules/evm/Cargo.toml index 72548dfa26..917b152a11 100644 --- a/runtime-sdk/modules/evm/Cargo.toml +++ b/runtime-sdk/modules/evm/Cargo.toml @@ -31,6 +31,3 @@ fixed-hash = "0.7.0" primitive-types = { version = "0.10.1", default-features = false, features = ["rlp", "num-traits"] } rlp = "0.5.1" uint = "0.9.1" - -[features] -confidential = [] # Use confidential storage by default. diff --git a/runtime-sdk/modules/evm/src/backend.rs b/runtime-sdk/modules/evm/src/backend.rs index f8c9ea1fb7..acfcbf8a1e 100644 --- a/runtime-sdk/modules/evm/src/backend.rs +++ b/runtime-sdk/modules/evm/src/backend.rs @@ -20,6 +20,23 @@ pub struct Vicinity { pub origin: H160, } +/// This macro is like `fn with_storage(ctx, addr, f: FnOnce(impl Storage) -> T) ->T` +/// that chooses public/confidential storage, if that such a function were possible to +/// write without the compiler complaining about unspecified generic type errors. +macro_rules! with_storage { + ($ctx:expr, $addr:expr, |$store:ident| $handler:expr) => { + if Cfg::CONFIDENTIAL { + #[allow(unused_mut)] + let mut $store = state::confidential_storage($ctx, $addr); + $handler + } else { + #[allow(unused_mut)] + let mut $store = state::public_storage($ctx, $addr); + $handler + } + }; +} + /// Backend for the evm crate that enables the use of our storage. pub struct Backend<'ctx, C: Context, Cfg: Config> { vicinity: Vicinity, @@ -125,8 +142,7 @@ impl<'ctx, C: Context, Cfg: Config> EVMBackend for Backend<'ctx, C, Cfg> { let idx: H256 = index.into(); let mut ctx = self.ctx.borrow_mut(); - let store = state::storage(*ctx, &address); - let res: H256 = store.get(&idx).unwrap_or_default(); + let res: H256 = with_storage!(*ctx, &address, |store| store.get(&idx).unwrap_or_default()); res.into() } @@ -241,11 +257,11 @@ impl<'c, C: Context, Cfg: Config> ApplyBackendResult for Backend<'c, C, Cfg> { let idx: H256 = index.into(); let val: H256 = value.into(); - let mut store = state::storage(*self.ctx.get_mut(), &addr); + let ctx = self.ctx.get_mut(); if value == primitive_types::H256::default() { - store.remove(&idx); + with_storage!(*ctx, &addr, |store| store.remove(&idx)); } else { - store.insert(&idx, val); + with_storage!(*ctx, &addr, |store| store.insert(&idx, val)); } } } diff --git a/runtime-sdk/modules/evm/src/lib.rs b/runtime-sdk/modules/evm/src/lib.rs index 69a0952307..abea747b44 100644 --- a/runtime-sdk/modules/evm/src/lib.rs +++ b/runtime-sdk/modules/evm/src/lib.rs @@ -55,6 +55,9 @@ pub trait Config: 'static { /// Token denomination used as the native EVM token. const TOKEN_DENOMINATION: token::Denomination; + /// Whether to use confidential storage by default, and transaction data encryption. + const CONFIDENTIAL: bool = false; + /// Maps an Ethereum address into an SDK account address. fn map_address(address: primitive_types::H160) -> Address { Address::new( diff --git a/runtime-sdk/modules/evm/src/raw_tx.rs b/runtime-sdk/modules/evm/src/raw_tx.rs index 3ebaa6d619..ab9d4808cd 100644 --- a/runtime-sdk/modules/evm/src/raw_tx.rs +++ b/runtime-sdk/modules/evm/src/raw_tx.rs @@ -209,6 +209,7 @@ mod test { use super::decode; + #[allow(clippy::too_many_arguments)] fn decode_expect_call( raw: &str, expected_chain_id: Option, @@ -241,6 +242,7 @@ mod test { assert_eq!(tx.auth_info.fee.gas, expected_gas_limit); } + #[allow(clippy::too_many_arguments)] fn decode_expect_create( raw: &str, expected_chain_id: Option, diff --git a/runtime-sdk/modules/evm/src/state.rs b/runtime-sdk/modules/evm/src/state.rs index e4faa4f363..7c3fa1556c 100644 --- a/runtime-sdk/modules/evm/src/state.rs +++ b/runtime-sdk/modules/evm/src/state.rs @@ -9,34 +9,16 @@ pub const STORAGES: &[u8] = &[0x02]; /// Prefix for Ethereum block hashes (only for last BLOCK_HASH_WINDOW_SIZE blocks /// excluding current) storage in our storage (maps Round -> H256). pub const BLOCK_HASHES: &[u8] = &[0x03]; -#[cfg(feature = "confidential")] /// Prefix for Ethereum account storage in our confidential storage (maps H160||H256 -> H256). pub const CONFIDENTIAL_STORAGES: &[u8] = &[0x04]; -#[cfg(feature = "confidential")] /// Confidential store key pair ID domain separation context base. pub const CONFIDENTIAL_STORE_KEY_PAIR_ID_CONTEXT_BASE: &[u8] = b"oasis-runtime-sdk/evm: state"; -#[cfg(feature = "confidential")] const CONTEXT_KEY_CONFIDENTIAL_STORE_INSTANCE_COUNT: &str = "evm.ConfidentialStoreCounter"; /// The number of hash blocks that can be obtained from the current blockchain. pub const BLOCK_HASH_WINDOW_SIZE: u64 = 256; -/// Get a typed store for the given address' storage. -pub fn storage<'a, C: Context>( - ctx: &'a mut C, - address: &'a H160, -) -> storage::TypedStore { - #[cfg(feature = "confidential")] - { - confidential_storage(ctx, address) - } - #[cfg(not(feature = "confidential"))] - { - public_storage(ctx, address) - } -} - pub fn public_storage<'a, C: Context>( ctx: &'a mut C, address: &'a H160, @@ -46,26 +28,20 @@ pub fn public_storage<'a, C: Context>( )) } -#[cfg(feature = "confidential")] pub fn confidential_storage<'a, C: Context>( ctx: &'a mut C, address: &'a H160, ) -> storage::TypedStore> { - fn empty_store() -> storage::TypedStore> { - storage::TypedStore::new(Box::new(storage::EmptyStore::new())) - } - let kmgr_client = match ctx.key_manager() { - Some(kmgr_client) => kmgr_client, - None => return empty_store(), - }; + let kmgr_client = ctx + .key_manager() + .expect("key manager must be available to use confidential storage"); let key_id = oasis_runtime_sdk::keymanager::get_key_pair_id(&[ CONFIDENTIAL_STORE_KEY_PAIR_ID_CONTEXT_BASE, address.as_ref(), ]); - let keypair = match kmgr_client.get_or_create_keys(key_id) { - Ok(keypair) => keypair, - Err(_) => return empty_store(), - }; + let keypair = kmgr_client + .get_or_create_keys(key_id) + .expect("unable to retrieve confidential storage keys"); let confidential_key = keypair.state_key; // These values are used to derive the confidential store nonce: diff --git a/runtime-sdk/modules/evm/src/test.rs b/runtime-sdk/modules/evm/src/test.rs index be75aa47b2..a63877c867 100644 --- a/runtime-sdk/modules/evm/src/test.rs +++ b/runtime-sdk/modules/evm/src/test.rs @@ -37,7 +37,17 @@ impl Config for EVMConfig { const TOKEN_DENOMINATION: Denomination = Denomination::NATIVE; } -type EVM = EVMModule; +struct ConfidentialEVMConfig; + +impl Config for ConfidentialEVMConfig { + type Accounts = Accounts; + + const CHAIN_ID: u64 = 0x5afe; + + const TOKEN_DENOMINATION: Denomination = Denomination::NATIVE; + + const CONFIDENTIAL: bool = true; +} fn load_erc20() -> Vec { Vec::from_hex( @@ -83,8 +93,7 @@ fn test_evm_caller_addr_derivation() { assert_eq!(derived, expected); } -#[test] -fn test_evm_calls() { +fn do_test_evm_calls() { let mut mock = mock::Mock::default(); let mut ctx = mock.create_ctx(); @@ -120,7 +129,7 @@ fn test_evm_calls() { }, ); - EVM::init( + EVMModule::::init( &mut ctx, Genesis { parameters: Default::default(), @@ -137,7 +146,7 @@ fn test_evm_calls() { method: "evm.Create".to_owned(), body: cbor::to_value(types::Create { value: 0.into(), - init_code: erc20.clone(), + init_code: erc20, }), }, auth_info: transaction::AuthInfo { @@ -157,11 +166,11 @@ fn test_evm_calls() { let erc20_addr = ctx.with_tx(0, create_tx, |mut tx_ctx, call| { let addr = H160::from_slice( - &EVM::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + &EVMModule::::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("create should succeed"), ); - EVM::check_invariants(&mut tx_ctx).expect("invariants should hold"); + EVMModule::::check_invariants(&mut tx_ctx).expect("invariants should hold"); tx_ctx.commit(); @@ -197,10 +206,10 @@ fn test_evm_calls() { Accounts::authenticate_tx(&mut ctx, &call_name_tx).unwrap(); let erc20_name = ctx.with_tx(0, call_name_tx, |mut tx_ctx, call| { - let name = EVM::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + let name = EVMModule::::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("call name should succeed"); - EVM::check_invariants(&mut tx_ctx).expect("invariants should hold"); + EVMModule::::check_invariants(&mut tx_ctx).expect("invariants should hold"); tx_ctx.commit(); @@ -211,19 +220,29 @@ fn test_evm_calls() { assert_eq!(erc20_name[64..68], vec![0x54, 0x65, 0x73, 0x74]); // "Test". } +#[test] +fn test_evm_calls() { + do_test_evm_calls::(); +} + +#[test] +fn test_c10l_evm_calls() { + do_test_evm_calls::(); +} + struct CoreConfig; impl core::Config for CoreConfig {} /// EVM test runtime. -struct EVMRuntime; +struct EVMRuntime(C); -impl Runtime for EVMRuntime { +impl Runtime for EVMRuntime { const VERSION: Version = Version::new(0, 0, 0); type Core = Core; - type Modules = (Core, Accounts, EVM); + type Modules = (Core, Accounts, EVMModule); fn genesis_state() -> ::Genesis { ( @@ -258,12 +277,11 @@ impl Runtime for EVMRuntime { } } -#[test] -fn test_evm_runtime() { +fn do_test_evm_runtime() { let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx_for_runtime::(context::Mode::ExecuteTx); + let mut ctx = mock.create_ctx_for_runtime::>(context::Mode::ExecuteTx); - EVMRuntime::migrate(&mut ctx); + EVMRuntime::::migrate(&mut ctx); let erc20 = load_erc20(); @@ -291,15 +309,15 @@ fn test_evm_runtime() { }, }; // Run authentication handler to simulate nonce increments. - ::Modules::authenticate_tx(&mut ctx, &create_tx).unwrap(); + as Runtime>::Modules::authenticate_tx(&mut ctx, &create_tx).unwrap(); let erc20_addr = ctx.with_tx(0, create_tx, |mut tx_ctx, call| { let addr = H160::from_slice( - &EVM::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + &EVMModule::::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("create should succeed"), ); - EVM::check_invariants(&mut tx_ctx).expect("invariants should hold"); + EVMModule::::check_invariants(&mut tx_ctx).expect("invariants should hold"); tx_ctx.commit(); @@ -330,17 +348,17 @@ fn test_evm_runtime() { }, }; // Run authentication handler to simulate nonce increments. - ::Modules::authenticate_tx(&mut ctx, &out_of_gas_create).unwrap(); + as Runtime>::Modules::authenticate_tx(&mut ctx, &out_of_gas_create).unwrap(); ctx.with_tx(0, out_of_gas_create.clone(), |mut tx_ctx, call| { - EVM::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + EVMModule::::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect_err("call transfer should fail"); }); // CheckTx should not fail. ctx.with_child(context::Mode::CheckTx, |mut check_ctx| { check_ctx.with_tx(0, out_of_gas_create, |mut tx_ctx, call| { - let rsp = EVM::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + let rsp = EVMModule::::tx_create(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("call should succeed with empty result"); assert_eq!( @@ -377,15 +395,15 @@ fn test_evm_runtime() { }, }; // Run authentication handler to simulate nonce increments. - ::Modules::authenticate_tx(&mut ctx, &call_name_tx).unwrap(); + as Runtime>::Modules::authenticate_tx(&mut ctx, &call_name_tx).unwrap(); // Test transaction call in simulate mode. ctx.with_child(context::Mode::SimulateTx, |mut sim_ctx| { let erc20_name = sim_ctx.with_tx(0, call_name_tx.clone(), |mut tx_ctx, call| { - let name = EVM::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + let name = EVMModule::::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("call name should succeed"); - EVM::check_invariants(&mut tx_ctx).expect("invariants should hold"); + EVMModule::::check_invariants(&mut tx_ctx).expect("invariants should hold"); tx_ctx.commit(); @@ -397,10 +415,10 @@ fn test_evm_runtime() { }); let erc20_name = ctx.with_tx(0, call_name_tx.clone(), |mut tx_ctx, call| { - let name = EVM::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + let name = EVMModule::::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("call name should succeed"); - EVM::check_invariants(&mut tx_ctx).expect("invariants should hold"); + EVMModule::::check_invariants(&mut tx_ctx).expect("invariants should hold"); tx_ctx.commit(); @@ -444,13 +462,13 @@ fn test_evm_runtime() { }, }; // Run authentication handler to simulate nonce increments. - ::Modules::authenticate_tx(&mut ctx, &call_transfer_tx).unwrap(); + as Runtime>::Modules::authenticate_tx(&mut ctx, &call_transfer_tx).unwrap(); let transfer_ret = ctx.with_tx(0, call_transfer_tx.clone(), |mut tx_ctx, call| { - let ret = EVM::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + let ret = EVMModule::::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("call transfer should succeed"); - EVM::check_invariants(&mut tx_ctx).expect("invariants should hold"); + EVMModule::::check_invariants(&mut tx_ctx).expect("invariants should hold"); tx_ctx.commit(); @@ -485,17 +503,17 @@ fn test_evm_runtime() { }, }, }; - ::Modules::authenticate_tx(&mut ctx, &out_of_gas_tx).unwrap(); + as Runtime>::Modules::authenticate_tx(&mut ctx, &out_of_gas_tx).unwrap(); ctx.with_tx(0, out_of_gas_tx.clone(), |mut tx_ctx, call| { - EVM::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + EVMModule::::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect_err("call transfer should fail"); }); // CheckTx should not fail. ctx.with_child(context::Mode::CheckTx, |mut check_ctx| { check_ctx.with_tx(0, out_of_gas_tx, |mut tx_ctx, call| { - let rsp = EVM::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) + let rsp = EVMModule::::tx_call(&mut tx_ctx, cbor::from_value(call.body).unwrap()) .expect("call should succeed with empty result"); assert_eq!( @@ -507,6 +525,16 @@ fn test_evm_runtime() { }); } +#[test] +fn test_evm_runtime() { + do_test_evm_runtime::(); +} + +#[test] +fn test_c10l_evm_runtime() { + do_test_evm_runtime::(); +} + #[test] fn test_revert_reason_decoding() { let long_reason = vec![0x61; 1050]; diff --git a/runtime-sdk/src/storage/empty.rs b/runtime-sdk/src/storage/empty.rs deleted file mode 100644 index 50e3155a1d..0000000000 --- a/runtime-sdk/src/storage/empty.rs +++ /dev/null @@ -1,63 +0,0 @@ -use oasis_core_runtime::storage::mkvs; - -/// A key-value store that is always empty. Useful for testing, -/// and when there is no better store is not available. -#[derive(Default)] -pub struct EmptyStore; - -impl EmptyStore { - pub fn new() -> Self { - Self::default() - } -} - -impl super::Store for EmptyStore { - fn get(&self, _key: &[u8]) -> Option> { - None - } - - fn insert(&mut self, _key: &[u8], _value: &[u8]) {} - - fn remove(&mut self, _key: &[u8]) {} - - fn iter(&self) -> Box { - Box::new(EmptyStoreIter) - } -} - -#[derive(Clone, Copy, Debug)] -struct EmptyStoreIter; - -impl std::iter::Iterator for EmptyStoreIter { - type Item = (Vec, Vec); - - fn next(&mut self) -> Option { - None - } -} - -impl mkvs::Iterator for EmptyStoreIter { - fn set_prefetch(&mut self, _prefetch: usize) {} - - fn is_valid(&self) -> bool { - true - } - - fn error(&self) -> &Option { - &None - } - - fn rewind(&mut self) {} - - fn seek(&mut self, _key: &[u8]) {} - - fn get_key(&self) -> &Option { - &None - } - - fn get_value(&self) -> &Option> { - &None - } - - fn next(&mut self) {} -} diff --git a/runtime-sdk/src/storage/mod.rs b/runtime-sdk/src/storage/mod.rs index 89944d10a7..8800a6e33e 100644 --- a/runtime-sdk/src/storage/mod.rs +++ b/runtime-sdk/src/storage/mod.rs @@ -2,7 +2,6 @@ use oasis_core_runtime::storage::mkvs::Iterator; pub mod confidential; -mod empty; mod hashed; mod mkvs; mod overlay; @@ -72,7 +71,6 @@ impl Store for Box { } pub use confidential::{ConfidentialStore, Error as ConfidentialStoreError}; -pub use empty::EmptyStore; pub use hashed::HashedStore; pub use mkvs::MKVSStore; pub use overlay::OverlayStore;