Skip to content

Commit d6e477a

Browse files
authored
feat: unchecked ext trait and with_parts_unchecked (#22)
1 parent 7ff62e4 commit d6e477a

File tree

4 files changed

+296
-76
lines changed

4 files changed

+296
-76
lines changed

src/evm.rs

Lines changed: 47 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
use crate::{
22
states::EvmBlockComplete, BasicContext, Block, BlockComplete, BlockContext, Cfg, ErroredState,
3-
EvmErrored, EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsNextBlock, EvmNeedsTx, EvmReady,
4-
EvmTransacted, HasCfg, HasOutputs, NeedsBlock, NeedsCfg, NeedsNextBlock, NeedsTx, Ready,
5-
TransactedState, Tx,
3+
EvmErrored, EvmExtUnchecked, EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsNextBlock, EvmNeedsTx,
4+
EvmReady, EvmTransacted, HasCfg, HasContext, HasOutputs, NeedsBlock, NeedsCfg, NeedsNextBlock,
5+
NeedsTx, Ready, TransactedState, Tx,
66
};
7-
use alloy_consensus::constants::KECCAK_EMPTY;
87
use alloy_primitives::{Address, Bytes, U256};
98
use revm::{
109
db::{states::bundle_state::BundleRetention, BundleState},
1110
primitives::{
12-
Account, AccountInfo, AccountStatus, Bytecode, EVMError, EvmState, EvmStorageSlot,
13-
ExecutionResult, InvalidTransaction, ResultAndState, SpecId,
11+
AccountInfo, Bytecode, EVMError, EvmState, ExecutionResult, InvalidTransaction,
12+
ResultAndState, SpecId,
1413
},
1514
Database, DatabaseCommit, DatabaseRef, Evm, State,
1615
};
@@ -252,21 +251,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
252251
address: Address,
253252
f: F,
254253
) -> Result<AccountInfo, Db::Error> {
255-
let db = self.inner_mut_unchecked().db_mut();
256-
257-
let mut info = db.basic(address)?.unwrap_or_default();
258-
let old = info.clone();
259-
f(&mut info);
260-
261-
// Make a new account with the modified info
262-
let mut acct = Account { info, status: AccountStatus::Touched, ..Default::default() };
263-
acct.mark_touch();
264-
265-
// Create a state object with the modified account.
266-
let state = [(address, acct)].iter().cloned().collect();
267-
self.commit_unchecked(state);
268-
269-
Ok(old)
254+
self.inner.modify_account(address, f)
270255
}
271256

272257
/// Set the nonce of an account, returning the previous nonce. This is a
@@ -276,7 +261,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
276261
address: Address,
277262
nonce: u64,
278263
) -> Result<u64, Db::Error> {
279-
self.try_modify_account_unchecked(address, |info| info.nonce = nonce).map(|info| info.nonce)
264+
self.inner.set_nonce(address, nonce)
280265
}
281266

282267
/// Increment the nonce of an account, returning the previous nonce. This is
@@ -285,17 +270,15 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
285270
/// If the nonce is already at the maximum value, it will not be
286271
/// incremented.
287272
pub fn try_increment_nonce_unchecked(&mut self, address: Address) -> Result<u64, Db::Error> {
288-
self.try_modify_account_unchecked(address, |info| info.nonce = info.nonce.saturating_add(1))
289-
.map(|info| info.nonce)
273+
self.inner.increment_nonce(address)
290274
}
291275

292276
/// Decrement the nonce of an account, returning the previous nonce. This is
293277
/// a low-level API, and is not intended for general use.
294278
///
295279
/// If the nonce is already 0, it will not be decremented.
296280
pub fn try_decrement_nonce_unchecked(&mut self, address: Address) -> Result<u64, Db::Error> {
297-
self.try_modify_account_unchecked(address, |info| info.nonce = info.nonce.saturating_sub(1))
298-
.map(|info| info.nonce)
281+
self.inner.decrement_nonce(address)
299282
}
300283

301284
/// Set the EVM storage at a slot. This is a low-level API, and is not
@@ -306,22 +289,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
306289
slot: U256,
307290
value: U256,
308291
) -> Result<U256, Db::Error> {
309-
let db = self.inner_mut_unchecked().db_mut();
310-
let info = db.basic(address)?.unwrap_or_default();
311-
let old = db.storage(address, slot)?;
312-
313-
let change = EvmStorageSlot::new_changed(old, value);
314-
315-
// Make a new account with the modified storage
316-
let storage = [(slot, change)].iter().cloned().collect();
317-
let mut acct = Account { storage, info, ..Default::default() };
318-
acct.mark_touch();
319-
320-
// Create a state object with the modified account.
321-
let state = [(address, acct)].iter().cloned().collect();
322-
self.commit_unchecked(state);
323-
324-
Ok(old)
292+
self.inner.set_storage(address, slot, value)
325293
}
326294

327295
/// Set the bytecode at a specific address, returning the previous bytecode
@@ -332,24 +300,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
332300
address: Address,
333301
bytecode: Bytecode,
334302
) -> Result<Option<Bytecode>, Db::Error> {
335-
let db = self.inner_mut_unchecked().db_mut();
336-
let mut info = db.basic(address)?.unwrap_or_default();
337-
338-
let old = if info.code_hash != KECCAK_EMPTY {
339-
Some(db.code_by_hash(info.code_hash)?)
340-
} else {
341-
None
342-
};
343-
344-
info.code_hash = if bytecode.is_empty() { KECCAK_EMPTY } else { bytecode.hash_slow() };
345-
info.code = Some(bytecode);
346-
347-
let mut acct = Account { info, ..Default::default() };
348-
acct.mark_touch();
349-
let state = [(address, acct)].iter().cloned().collect();
350-
self.commit_unchecked(state);
351-
352-
Ok(old)
303+
self.inner.set_bytecode(address, bytecode)
353304
}
354305

355306
/// Increase the balance of an account. Returns the previous balance. This
@@ -362,10 +313,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
362313
address: Address,
363314
amount: U256,
364315
) -> Result<U256, Db::Error> {
365-
self.try_modify_account_unchecked(address, |info| {
366-
info.balance = info.balance.saturating_add(amount)
367-
})
368-
.map(|info| info.balance)
316+
self.inner.increase_balance(address, amount)
369317
}
370318

371319
/// Decrease the balance of an account. Returns the previous balance. This
@@ -377,10 +325,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
377325
address: Address,
378326
amount: U256,
379327
) -> Result<U256, Db::Error> {
380-
self.try_modify_account_unchecked(address, |info| {
381-
info.balance = info.balance.saturating_sub(amount)
382-
})
383-
.map(|info| info.balance)
328+
self.inner.decrease_balance(address, amount)
384329
}
385330

386331
/// Set the balance of an account. Returns the previous balance. This is a
@@ -390,8 +335,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev
390335
address: Address,
391336
amount: U256,
392337
) -> Result<U256, Db::Error> {
393-
self.try_modify_account_unchecked(address, |info| info.balance = amount)
394-
.map(|info| info.balance)
338+
self.inner.set_balance(address, amount)
395339
}
396340
}
397341

@@ -798,6 +742,8 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: NeedsBlock>
798742
}
799743
}
800744

745+
// --- HAS OUTPUTS
746+
801747
impl<'a, Ext, Db: Database + DatabaseCommit, Missing: HasOutputs>
802748
Trevm<'a, Ext, State<Db>, Missing>
803749
{
@@ -820,6 +766,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, Missing: HasOutputs>
820766
bundle
821767
}
822768
}
769+
// --- BLOCK COMPLETE
823770

824771
impl<'a, Ext, Db: Database + DatabaseCommit, C> EvmBlockComplete<'a, Ext, Db, C> {
825772
/// Destructure the EVM and return the block context and the EVM ready for
@@ -834,6 +781,36 @@ impl<'a, Ext, Db: Database + DatabaseCommit, C> EvmBlockComplete<'a, Ext, Db, C>
834781
}
835782
}
836783

784+
// --- HAS CONTEXT
785+
786+
impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasContext>
787+
Trevm<'a, Ext, Db, TrevmState>
788+
{
789+
/// Get a reference to the block context.
790+
pub fn block_context(&self) -> &TrevmState::Context {
791+
self.state.context()
792+
}
793+
794+
/// Get a mutable reference to the block context.
795+
pub fn block_context_mut(&mut self) -> &mut TrevmState::Context {
796+
self.state.context_mut()
797+
}
798+
799+
/// Apply a closure to the components. This allows for modifying the block
800+
/// context and the EVM in a single operation, by (e.g.) passing the EVM
801+
/// to a block context method.
802+
///
803+
/// This is a low-level API and is not intended for general use.
804+
pub fn with_parts_unchecked<F>(self, f: F) -> Self
805+
where
806+
F: FnOnce(&mut TrevmState, &mut Evm<'a, Ext, Db>),
807+
{
808+
let Trevm { mut inner, mut state } = self;
809+
f(&mut state, &mut inner);
810+
Trevm { inner, state }
811+
}
812+
}
813+
837814
// --- NEEDS FIRST TX
838815

839816
impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext<Ext, Db>> EvmNeedsTx<'a, Ext, Db, C> {

src/ext.rs

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
use std::collections::HashMap;
2+
3+
use alloy_primitives::{Address, B256, U256};
4+
use revm::{
5+
primitives::{Account, AccountInfo, Bytecode, EvmStorageSlot},
6+
Database, DatabaseCommit,
7+
};
8+
9+
/// Extension trait for [`revm::Evm`] with convenience functions for reading
10+
/// and modifying state.
11+
pub trait EvmExtUnchecked<Db: Database> {
12+
/// Get a mutable reference to the database.
13+
fn db_mut_ext(&mut self) -> &mut Db;
14+
15+
/// Load an account from the database.
16+
fn account(&mut self, address: Address) -> Result<Account, Db::Error> {
17+
let info = self.db_mut_ext().basic(address)?;
18+
let created = info.is_none();
19+
let mut acct = Account { info: info.unwrap_or_default(), ..Default::default() };
20+
if created {
21+
acct.mark_created();
22+
}
23+
Ok(acct)
24+
}
25+
26+
/// Load a storage slot from an account.
27+
fn storage(&mut self, address: Address, index: U256) -> Result<U256, Db::Error> {
28+
self.db_mut_ext().storage(address, index)
29+
}
30+
31+
/// Load the bytecode for a contract. Returns empty bytecode if the account
32+
/// does not currently have contract code.
33+
fn bytecode(&mut self, code_hash: B256) -> Result<Bytecode, Db::Error> {
34+
self.db_mut_ext().code_by_hash(code_hash)
35+
}
36+
37+
/// Modify multiple accounts with a closure and commit the modified
38+
/// accounts.
39+
fn modify_accounts<I, F>(
40+
&mut self,
41+
changes: I,
42+
f: F,
43+
) -> Result<HashMap<Address, AccountInfo>, Db::Error>
44+
where
45+
I: IntoIterator<Item = Address>,
46+
F: Fn(&mut AccountInfo),
47+
Db: DatabaseCommit,
48+
{
49+
let mut state = HashMap::default();
50+
let mut old = HashMap::default();
51+
for account in changes.into_iter() {
52+
let mut acct = self.account(account)?;
53+
old.insert(account, acct.info.clone());
54+
f(&mut acct.info);
55+
acct.mark_touch();
56+
state.insert(account, acct);
57+
}
58+
self.db_mut_ext().commit(state);
59+
Ok(old)
60+
}
61+
62+
/// Modify an account with a closure and commit the modified account.
63+
fn modify_account<F>(&mut self, address: Address, f: F) -> Result<AccountInfo, Db::Error>
64+
where
65+
F: FnOnce(&mut AccountInfo),
66+
Db: DatabaseCommit,
67+
{
68+
let mut acct = self.account(address)?;
69+
let old = acct.info.clone();
70+
f(&mut acct.info);
71+
acct.mark_touch();
72+
let changes = [(address, acct)].into_iter().collect();
73+
self.db_mut_ext().commit(changes);
74+
Ok(old)
75+
}
76+
77+
/// Set the storage value of an account, returning the previous value.
78+
fn set_storage(&mut self, address: Address, index: U256, value: U256) -> Result<U256, Db::Error>
79+
where
80+
Db: DatabaseCommit,
81+
{
82+
let mut acct = self.account(address)?;
83+
let old = self.storage(address, index)?;
84+
85+
let change = EvmStorageSlot::new_changed(old, value);
86+
acct.storage.insert(index, change);
87+
acct.mark_touch();
88+
89+
let changes = [(address, acct)].into_iter().collect();
90+
self.db_mut_ext().commit(changes);
91+
Ok(old)
92+
}
93+
94+
/// Set the bytecode for an account, returns the previous bytecode.
95+
fn set_bytecode(
96+
&mut self,
97+
address: Address,
98+
bytecode: Bytecode,
99+
) -> Result<Option<Bytecode>, Db::Error>
100+
where
101+
Db: DatabaseCommit,
102+
{
103+
let mut acct = self.account(address)?;
104+
let old = self.db_mut_ext().code_by_hash(acct.info.code_hash).map(|old| {
105+
if old.is_empty() {
106+
None
107+
} else {
108+
Some(old)
109+
}
110+
})?;
111+
acct.info.code_hash = bytecode.hash_slow();
112+
acct.info.code = Some(bytecode);
113+
acct.mark_touch();
114+
115+
let changes = [(address, acct)].into_iter().collect();
116+
self.db_mut_ext().commit(changes);
117+
Ok(old)
118+
}
119+
120+
/// Increase the balance of an account, returning the previous balance.
121+
fn increase_balance(&mut self, address: Address, amount: U256) -> Result<U256, Db::Error>
122+
where
123+
Db: DatabaseCommit,
124+
{
125+
self.modify_account(address, |info| info.balance = info.balance.saturating_add(amount))
126+
.map(|info| info.balance)
127+
}
128+
129+
/// Decrease the balance of an account, returning the previous balance.
130+
fn decrease_balance(&mut self, address: Address, amount: U256) -> Result<U256, Db::Error>
131+
where
132+
Db: DatabaseCommit,
133+
{
134+
self.modify_account(address, |info| info.balance = info.balance.saturating_sub(amount))
135+
.map(|info| info.balance)
136+
}
137+
138+
/// Set the balance of an account, returning the previous balance.
139+
fn set_balance(&mut self, address: Address, amount: U256) -> Result<U256, Db::Error>
140+
where
141+
Db: DatabaseCommit,
142+
{
143+
self.modify_account(address, |info| info.balance = amount).map(|info| info.balance)
144+
}
145+
146+
/// Set the nonce of an account, returning the previous nonce.
147+
fn set_nonce(&mut self, address: Address, nonce: u64) -> Result<u64, Db::Error>
148+
where
149+
Db: DatabaseCommit,
150+
{
151+
self.modify_account(address, |info| info.nonce = nonce).map(|info| info.nonce)
152+
}
153+
154+
/// Increment the nonce of an account, returning the new nonce.
155+
fn increment_nonce(&mut self, address: Address) -> Result<u64, Db::Error>
156+
where
157+
Db: DatabaseCommit,
158+
{
159+
self.modify_account(address, |info| info.nonce = info.nonce.saturating_add(1))
160+
.map(|info| info.nonce)
161+
}
162+
163+
/// Decrement the nonce of an account, returning the new nonce.
164+
fn decrement_nonce(&mut self, address: Address) -> Result<u64, Db::Error>
165+
where
166+
Db: DatabaseCommit,
167+
{
168+
self.modify_account(address, |info| info.nonce = info.nonce.saturating_sub(1))
169+
.map(|info| info.nonce)
170+
}
171+
}
172+
173+
impl<Ext, Db: Database> EvmExtUnchecked<Db> for revm::Evm<'_, Ext, Db> {
174+
fn db_mut_ext(&mut self) -> &mut Db {
175+
self.db_mut()
176+
}
177+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,9 @@
402402
mod evm;
403403
pub use evm::Trevm;
404404

405+
mod ext;
406+
pub use ext::EvmExtUnchecked;
407+
405408
mod fill;
406409
pub use fill::{Block, Cfg, NoopBlock, NoopCfg, Tx};
407410

0 commit comments

Comments
 (0)