Skip to content

Commit c1d2ca0

Browse files
committed
runtime-sdk/modules: Add revm-based EVM module
1 parent 39ce172 commit c1d2ca0

16 files changed

Lines changed: 4786 additions & 48 deletions

File tree

Cargo.lock

Lines changed: 665 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ members = [
77
# Runtime SDK Modules.
88
"runtime-sdk/modules/contracts",
99
"runtime-sdk/modules/evm",
10+
"runtime-sdk/modules/evm-new",
1011

1112
# Smart Contract SDK.
1213
"contract-sdk",
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[package]
2+
name = "oasis-runtime-sdk-evm-new"
3+
description = "New EVM module for the Oasis Runtime SDK."
4+
version = "0.6.0"
5+
authors = ["Oasis Protocol Foundation <info@oasisprotocol.org>"]
6+
edition = "2021"
7+
license = "Apache-2.0"
8+
9+
[dependencies]
10+
cbor = { version = "0.5.1", package = "oasis-cbor" }
11+
oasis-runtime-sdk = { path = "../.." }
12+
13+
# Third party.
14+
anyhow = "1.0"
15+
base64 = "0.22.1"
16+
blake3 = { version = "~1.5.1", features = ["traits-preview"] }
17+
thiserror = "1.0"
18+
hex = "0.4.2"
19+
k256 = "0.13.1"
20+
sha2 = "0.10.8"
21+
sha3 = { version = "0.10", default-features = false }
22+
once_cell = "1.8.0"
23+
x25519-dalek = "2.0.1"
24+
hmac = "0.12.1"
25+
rand_core = { version = "0.6.4", default-features = false }
26+
27+
# Ethereum.
28+
ethabi = { version = "18.0.0", default-features = false, features = ["std"] }
29+
ethereum = "0.15"
30+
revm = { git = "https://github.com/bluealloy/revm", tag = "v55", default-features = false, features = ["std", "serde", "optional_eip3607"] }
31+
fixed-hash = "0.8.0"
32+
primitive-types = { version = "0.12", default-features = false, features = ["rlp", "num-traits"] }
33+
rlp = "0.5.2"
34+
uint = "0.9.1"
35+
36+
# Fuzzing.
37+
honggfuzz = "0.5.56"
38+
serde = { version = "1.0.203", features = ["derive"], optional = true }
39+
serde_json = { version = "1.0.116", features = ["raw_value"], optional = true }
40+
41+
[dev-dependencies]
42+
criterion = "0.5.1"
43+
oasis-runtime-sdk = { path = "../..", features = ["test"] }
44+
rand = "0.8.5"
45+
serde = { version = "1.0.203", features = ["derive"] }
46+
serde_json = { version = "1.0.116", features = ["raw_value"] }
47+
ethabi = { version = "18.0.0", default-features = false, features = ["std", "full-serde"] }
48+
49+
[features]
50+
default = []
51+
test = ["serde", "serde_json"]
52+
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
use revm::{
2+
primitives::{Account, AccountInfo, Address, Bytecode, B256, KECCAK_EMPTY, U256},
3+
Database, DatabaseCommit,
4+
};
5+
use std::{collections::HashMap, vec::Vec};
6+
7+
use std::marker::PhantomData;
8+
9+
use oasis_runtime_sdk::{
10+
context::Context, core::common::crypto::hash::Hash, modules::accounts::API as _,
11+
state::CurrentState, types::token, Runtime,
12+
};
13+
14+
use crate::{state, types, Config};
15+
16+
pub struct OasisDB<'ctx, C: Context, Cfg: Config> {
17+
ctx: &'ctx C,
18+
_cfg: PhantomData<Cfg>,
19+
}
20+
21+
impl<'ctx, C: Context, Cfg: Config> OasisDB<'ctx, C, Cfg> {
22+
pub fn new(ctx: &'ctx C) -> Self {
23+
Self {
24+
ctx,
25+
_cfg: PhantomData,
26+
}
27+
}
28+
}
29+
30+
// Implement read-only parts of the database.
31+
impl<'ctx, C: Context, Cfg: Config> Database for OasisDB<'ctx, C, Cfg> {
32+
type Error = String;
33+
34+
/// Get basic account information.
35+
fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
36+
// Derive SDK account address from the Ethereum address.
37+
let sdk_address = Cfg::map_address(address);
38+
39+
print!("*** {:#?}", address);
40+
41+
// Fetch balance and nonce from SDK accounts. Note that these can never fail.
42+
let balance =
43+
<C::Runtime as Runtime>::Accounts::get_balance(sdk_address, Cfg::TOKEN_DENOMINATION)
44+
.unwrap();
45+
let nonce = <C::Runtime as Runtime>::Accounts::get_nonce(sdk_address).unwrap();
46+
47+
// Fetch code for this address from storage.
48+
let code = CurrentState::with_store(|store| {
49+
let codes = state::codes(store);
50+
51+
if let Some(code) = codes.get::<_, Vec<u8>>(address) {
52+
if !code.is_empty() {
53+
Some(Bytecode::new_raw(code.into()))
54+
} else {
55+
None
56+
}
57+
} else {
58+
None
59+
}
60+
});
61+
62+
// Calculate hash of code if it exists.
63+
let code_hash = match code {
64+
None => KECCAK_EMPTY,
65+
Some(ref bc) => bc.hash_slow(),
66+
};
67+
68+
println!(": {:#?} {:#?}", balance, nonce);
69+
70+
Ok(Some(AccountInfo {
71+
// XXX: The nonce needs a proper fix like: https://github.com/oasisprotocol/oasis-sdk/commit/eda6e0d67c2b2664182a0d60408875af32562a7f
72+
nonce, //: nonce.saturating_sub(1),
73+
balance: U256::from(balance),
74+
code,
75+
code_hash,
76+
}))
77+
}
78+
79+
/// Get account code by its hash (unimplemented).
80+
fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
81+
println!("###### code_by_hash called ######");
82+
Err("getting code by hash is not supported".to_string())
83+
}
84+
85+
/// Get storage value of address at index.
86+
fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
87+
let address: types::H160 = address.into_array().into();
88+
let index: types::H256 = index.to_be_bytes().into();
89+
90+
let res: types::H256 = state::with_storage::<Cfg, _, _, _>(self.ctx, &address, |store| {
91+
store.get(index).unwrap_or_default()
92+
});
93+
Ok(U256::from_be_bytes(res.into()))
94+
}
95+
96+
/// Get block hash by block number.
97+
fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
98+
CurrentState::with_store(|store| {
99+
let block_hashes = state::block_hashes(store);
100+
101+
if let Some(hash) = block_hashes.get::<_, Hash>(&number.to_be_bytes()) {
102+
Ok(B256::from_slice(hash.as_ref()))
103+
} else {
104+
Ok(B256::default())
105+
}
106+
})
107+
}
108+
}
109+
110+
// Implement committing.
111+
impl<'ctx, C: Context, Cfg: Config> DatabaseCommit for OasisDB<'ctx, C, Cfg> {
112+
fn commit(&mut self, changes: HashMap<Address, Account>) {
113+
for (address, account) in changes {
114+
if !account.is_touched() {
115+
continue;
116+
}
117+
118+
// Derive SDK account address from the Ethereum address.
119+
let sdk_address = Cfg::map_address(address);
120+
121+
println!(
122+
"### {:#?}: {:?} {:?}",
123+
address, account.info.balance, account.info.nonce
124+
);
125+
126+
// Update account's balance, nonce, and code (if any).
127+
<C::Runtime as Runtime>::Accounts::set_balance(
128+
sdk_address,
129+
&token::BaseUnits::new(account.info.balance.to::<u128>(), Cfg::TOKEN_DENOMINATION),
130+
);
131+
132+
// XXX
133+
//<C::Runtime as Runtime>::Accounts::set_nonce(sdk_address, account.info.nonce);
134+
135+
if account.info.code.is_some() {
136+
let code = account.info.code.unwrap().bytecode().to_vec();
137+
CurrentState::with_store(|store| {
138+
let mut codes = state::codes(store);
139+
if !code.is_empty() {
140+
codes.insert(address, code);
141+
} else {
142+
codes.remove(address);
143+
}
144+
});
145+
} else {
146+
CurrentState::with_store(|store| {
147+
let mut codes = state::codes(store);
148+
codes.remove(address);
149+
});
150+
}
151+
152+
// Apply account's storage changes.
153+
let storage_changes = account
154+
.storage
155+
.into_iter()
156+
.map(|(key, value)| (key, value.present_value()));
157+
for (key, value) in storage_changes {
158+
let index: types::H256 = key.to_be_bytes().into();
159+
let val: types::H256 = value.to_be_bytes().into();
160+
161+
if value == U256::default() {
162+
state::with_storage::<Cfg, _, _, _>(
163+
self.ctx,
164+
&address.into_array().into(),
165+
|store| {
166+
store.remove(index);
167+
},
168+
);
169+
} else {
170+
state::with_storage::<Cfg, _, _, _>(
171+
self.ctx,
172+
&address.into_array().into(),
173+
|store| {
174+
store.insert(index, val);
175+
},
176+
);
177+
}
178+
}
179+
}
180+
}
181+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use oasis_runtime_sdk::types::{
2+
address::SignatureAddressSpec,
3+
transaction::{AddressSpec, AuthInfo, CallerAddress},
4+
};
5+
6+
use crate::{types::H160, Error};
7+
8+
pub fn from_sigspec(spec: &SignatureAddressSpec) -> Result<H160, Error> {
9+
match spec {
10+
SignatureAddressSpec::Secp256k1Eth(pk) => Ok(H160::from_slice(&pk.to_eth_address())),
11+
_ => Err(Error::InvalidSignerType),
12+
}
13+
}
14+
15+
pub fn from_tx_auth_info(ai: &AuthInfo) -> Result<H160, Error> {
16+
match &ai.signer_info[0].address_spec {
17+
AddressSpec::Signature(spec) => from_sigspec(spec),
18+
AddressSpec::Internal(CallerAddress::EthAddress(address)) => Ok(address.into()),
19+
_ => Err(Error::InvalidSignerType),
20+
}
21+
}

0 commit comments

Comments
 (0)