Skip to content

Commit 24f9256

Browse files
authored
feat: send bytecode with call input (#2963)
* feat: send bytecode with call input * load acc first * inline calls * use * never inline few fn * inline load code * load account * inline only * now we can do unsafe transfer * handle same address transfer, and zero balance * rm get_many_mut and optimize inlining * revert inlines * inline load_account_info_skip_cold_load * use old transfer * use transfer_loaded * never inline call helper * bytecode is always some in CallInput
1 parent 8972bc2 commit 24f9256

File tree

10 files changed

+174
-62
lines changed

10 files changed

+174
-62
lines changed

crates/context/interface/src/journaled_state.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ pub trait JournalTr {
125125
balance: U256,
126126
) -> Result<Option<TransferError>, <Self::Database as Database>::Error>;
127127

128+
/// Transfers the balance from one account to another. Assume form and to are loaded.
129+
fn transfer_loaded(
130+
&mut self,
131+
from: Address,
132+
to: Address,
133+
balance: U256,
134+
) -> Option<TransferError>;
135+
128136
/// Increments the balance of the account.
129137
fn caller_accounting_journal_entry(
130138
&mut self,

crates/context/src/journal.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,16 @@ impl<DB: Database, ENTRY: JournalEntryTr> JournalTr for Journal<DB, ENTRY> {
195195
self.inner.transfer(&mut self.database, from, to, balance)
196196
}
197197

198+
#[inline]
199+
fn transfer_loaded(
200+
&mut self,
201+
from: Address,
202+
to: Address,
203+
balance: U256,
204+
) -> Option<TransferError> {
205+
self.inner.transfer_loaded(from, to, balance)
206+
}
207+
198208
#[inline]
199209
fn touch_account(&mut self, address: Address) {
200210
self.inner.touch(address);
@@ -304,6 +314,7 @@ impl<DB: Database, ENTRY: JournalEntryTr> JournalTr for Journal<DB, ENTRY> {
304314
self.inner.finalize()
305315
}
306316

317+
#[inline]
307318
fn sload_skip_cold_load(
308319
&mut self,
309320
address: Address,
@@ -315,6 +326,7 @@ impl<DB: Database, ENTRY: JournalEntryTr> JournalTr for Journal<DB, ENTRY> {
315326
.sload(&mut self.database, address, key, skip_cold_load)
316327
}
317328

329+
#[inline]
318330
fn sstore_skip_cold_load(
319331
&mut self,
320332
address: Address,

crates/context/src/journal/inner.rs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,59 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
315315
self.journal.push(ENTRY::nonce_changed(address));
316316
}
317317

318+
/// Transfers balance from two accounts. Returns error if sender balance is not enough.
319+
///
320+
/// # Panics
321+
///
322+
/// Panics if from or to are not loaded.
323+
#[inline]
324+
pub fn transfer_loaded(
325+
&mut self,
326+
from: Address,
327+
to: Address,
328+
balance: U256,
329+
) -> Option<TransferError> {
330+
if from == to {
331+
let from_balance = self.state.get_mut(&to).unwrap().info.balance;
332+
// Check if from balance is enough to transfer the balance.
333+
if balance > from_balance {
334+
return Some(TransferError::OutOfFunds);
335+
}
336+
return None;
337+
}
338+
339+
if balance.is_zero() {
340+
Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
341+
return None;
342+
}
343+
344+
// sub balance from
345+
let from_account = self.state.get_mut(&from).unwrap();
346+
// no need to touch caller account.
347+
// Self::touch_account(&mut self.journal, from, from_account);
348+
let from_balance = &mut from_account.info.balance;
349+
let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
350+
return Some(TransferError::OutOfFunds);
351+
};
352+
*from_balance = from_balance_decr;
353+
354+
// add balance to
355+
let to_account = self.state.get_mut(&to).unwrap();
356+
Self::touch_account(&mut self.journal, to, to_account);
357+
let to_balance = &mut to_account.info.balance;
358+
let Some(to_balance_incr) = to_balance.checked_add(balance) else {
359+
// Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc.
360+
return Some(TransferError::OverflowPayment);
361+
};
362+
*to_balance = to_balance_incr;
363+
364+
// add journal entry
365+
self.journal
366+
.push(ENTRY::balance_transfer(from, to, balance));
367+
368+
None
369+
}
370+
318371
/// Transfers balance from two accounts. Returns error if sender balance is not enough.
319372
#[inline]
320373
pub fn transfer<DB: Database>(
@@ -349,10 +402,10 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
349402
Self::touch_account(&mut self.journal, to, to_account);
350403
let to_balance = &mut to_account.info.balance;
351404
let Some(to_balance_incr) = to_balance.checked_add(balance) else {
405+
// Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc.
352406
return Ok(Some(TransferError::OverflowPayment));
353407
};
354408
*to_balance = to_balance_incr;
355-
// Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc.
356409

357410
self.journal
358411
.push(ENTRY::balance_transfer(from, to, balance));
@@ -600,7 +653,9 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
600653
// load delegate code if account is EIP-7702
601654
if let Some(Bytecode::Eip7702(code)) = &account.info.code {
602655
let address = code.address();
603-
let delegate_account = self.load_account(db, address)?;
656+
let delegate_account = self
657+
.load_account_optional(db, address, true, [], false)
658+
.map_err(JournalLoadError::unwrap_db_error)?;
604659
account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
605660
}
606661

crates/handler/src/execution.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,36 @@ use context_interface::Transaction;
22
use interpreter::{
33
CallInput, CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, FrameInput,
44
};
5-
use primitives::TxKind;
5+
use primitives::{TxKind, B256};
6+
use state::Bytecode;
67
use std::boxed::Box;
78

89
/// Creates the first [`FrameInput`] from the transaction, spec and gas limit.
9-
pub fn create_init_frame(tx: &impl Transaction, gas_limit: u64) -> FrameInput {
10+
#[inline]
11+
pub fn create_init_frame(
12+
tx: &impl Transaction,
13+
bytecode: Option<(Bytecode, B256)>,
14+
gas_limit: u64,
15+
) -> FrameInput {
1016
let input = tx.input().clone();
1117

1218
match tx.kind() {
13-
TxKind::Call(target_address) => FrameInput::Call(Box::new(CallInputs {
14-
input: CallInput::Bytes(input),
15-
gas_limit,
16-
target_address,
17-
bytecode_address: target_address,
18-
caller: tx.caller(),
19-
value: CallValue::Transfer(tx.value()),
20-
scheme: CallScheme::Call,
21-
is_static: false,
22-
return_memory_offset: 0..0,
23-
})),
19+
TxKind::Call(target_address) => {
20+
let (bytecode, bytecode_hash) = bytecode.unwrap_or_default();
21+
FrameInput::Call(Box::new(CallInputs {
22+
input: CallInput::Bytes(input),
23+
gas_limit,
24+
target_address,
25+
bytecode_address: target_address,
26+
bytecode,
27+
bytecode_hash,
28+
caller: tx.caller(),
29+
value: CallValue::Transfer(tx.value()),
30+
scheme: CallScheme::Call,
31+
is_static: false,
32+
return_memory_offset: 0..0,
33+
}))
34+
}
2435
TxKind::Create => FrameInput::Create(Box::new(CreateInputs {
2536
caller: tx.caller(),
2637
scheme: CreateScheme::Create,

crates/handler/src/frame.rs

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,6 @@ impl EthFrame<EthInterpreter> {
163163
return return_result(InstructionResult::CallTooDeep);
164164
}
165165

166-
// Make account warm and loaded.
167-
let _ = ctx
168-
.journal_mut()
169-
.load_account_delegated(inputs.bytecode_address)?;
170-
171166
// Create subroutine checkpoint
172167
let checkpoint = ctx.journal_mut().checkpoint();
173168

@@ -177,7 +172,7 @@ impl EthFrame<EthInterpreter> {
177172
// Target will get touched even if balance transferred is zero.
178173
if let Some(i) =
179174
ctx.journal_mut()
180-
.transfer(inputs.caller, inputs.target_address, value)?
175+
.transfer_loaded(inputs.caller, inputs.target_address, value)
181176
{
182177
ctx.journal_mut().checkpoint_revert(checkpoint);
183178
return return_result(i.into());
@@ -206,21 +201,8 @@ impl EthFrame<EthInterpreter> {
206201
})));
207202
}
208203

209-
let account = ctx
210-
.journal_mut()
211-
.load_account_code(inputs.bytecode_address)?;
212-
213-
let mut code_hash = account.info.code_hash();
214-
let mut bytecode = account.info.code.clone().unwrap_or_default();
215-
216-
if let Bytecode::Eip7702(eip7702_bytecode) = bytecode {
217-
let account = &ctx
218-
.journal_mut()
219-
.load_account_code(eip7702_bytecode.delegated_address)?
220-
.info;
221-
bytecode = account.code.clone().unwrap_or_default();
222-
code_hash = account.code_hash();
223-
}
204+
let bytecode = inputs.bytecode.clone();
205+
let bytecode_hash = inputs.bytecode_hash;
224206

225207
// Returns success if bytecode is empty.
226208
if bytecode.is_empty() {
@@ -236,7 +218,7 @@ impl EthFrame<EthInterpreter> {
236218
FrameInput::Call(inputs),
237219
depth,
238220
memory,
239-
ExtBytecode::new_with_hash(bytecode, code_hash),
221+
ExtBytecode::new_with_hash(bytecode, bytecode_hash),
240222
interpreter_input,
241223
is_static,
242224
ctx.cfg().spec().into(),
@@ -275,12 +257,6 @@ impl EthFrame<EthInterpreter> {
275257
return return_error(InstructionResult::CallTooDeep);
276258
}
277259

278-
// Prague EOF
279-
// TODO(EOF)
280-
// if spec.is_enabled_in(OSAKA) && inputs.init_code.starts_with(&EOF_MAGIC_BYTES) {
281-
// return return_error(InstructionResult::CreateInitCodeStartingEF00);
282-
// }
283-
284260
// Fetch balance of caller.
285261
let caller_info = &mut context.journal_mut().load_account(inputs.caller)?.data.info;
286262

crates/handler/src/handler.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use context_interface::{
1313
use interpreter::interpreter_action::FrameInit;
1414
use interpreter::{Gas, InitialAndFloorGas, SharedMemory};
1515
use primitives::U256;
16+
use state::Bytecode;
1617

1718
/// Trait for errors that can occur during EVM execution.
1819
///
@@ -290,13 +291,34 @@ pub trait Handler {
290291
evm: &mut Self::Evm,
291292
gas_limit: u64,
292293
) -> Result<FrameInit, Self::Error> {
293-
let memory =
294-
SharedMemory::new_with_buffer(evm.ctx().local().shared_memory_buffer().clone());
295-
let ctx = evm.ctx_ref();
294+
let ctx = evm.ctx_mut();
295+
let memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone());
296+
297+
let (tx, journal) = ctx.tx_journal_mut();
298+
let bytecode = if let Some(&to) = tx.kind().to() {
299+
let account = &journal.load_account_code(to)?.info;
300+
301+
if let Some(Bytecode::Eip7702(eip7702_bytecode)) = &account.code {
302+
let delegated_address = eip7702_bytecode.delegated_address;
303+
let account = &journal.load_account_code(delegated_address)?.info;
304+
Some((
305+
account.code.clone().unwrap_or_default(),
306+
account.code_hash(),
307+
))
308+
} else {
309+
Some((
310+
account.code.clone().unwrap_or_default(),
311+
account.code_hash(),
312+
))
313+
}
314+
} else {
315+
None
316+
};
317+
296318
Ok(FrameInit {
297319
depth: 0,
298320
memory,
299-
frame_input: execution::create_init_frame(ctx.tx(), gas_limit),
321+
frame_input: execution::create_init_frame(tx, bytecode, gas_limit),
300322
})
301323
}
302324

crates/interpreter/src/instructions/contract.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub fn call<WIRE: InterpreterTypes, H: Host + ?Sized>(
127127
else {
128128
return;
129129
};
130-
let Some(gas_limit) =
130+
let Some((gas_limit, bytecode, bytecode_hash)) =
131131
load_acc_and_calc_gas(&mut context, to, has_transfer, true, local_gas_limit)
132132
else {
133133
return;
@@ -144,6 +144,8 @@ pub fn call<WIRE: InterpreterTypes, H: Host + ?Sized>(
144144
target_address: to,
145145
caller: context.interpreter.input.target_address(),
146146
bytecode_address: to,
147+
bytecode,
148+
bytecode_hash,
147149
value: CallValue::Transfer(value),
148150
scheme: CallScheme::Call,
149151
is_static: context.interpreter.runtime_flag.is_static(),
@@ -169,7 +171,7 @@ pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
169171
return;
170172
};
171173

172-
let Some(gas_limit) =
174+
let Some((gas_limit, bytecode, bytecode_hash)) =
173175
load_acc_and_calc_gas(&mut context, to, has_transfer, false, local_gas_limit)
174176
else {
175177
return;
@@ -186,6 +188,8 @@ pub fn call_code<WIRE: InterpreterTypes, H: Host + ?Sized>(
186188
target_address: context.interpreter.input.target_address(),
187189
caller: context.interpreter.input.target_address(),
188190
bytecode_address: to,
191+
bytecode,
192+
bytecode_hash,
189193
value: CallValue::Transfer(value),
190194
scheme: CallScheme::CallCode,
191195
is_static: context.interpreter.runtime_flag.is_static(),
@@ -211,7 +215,8 @@ pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
211215
return;
212216
};
213217

214-
let Some(gas_limit) = load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
218+
let Some((gas_limit, bytecode, bytecode_hash)) =
219+
load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
215220
else {
216221
return;
217222
};
@@ -227,6 +232,8 @@ pub fn delegate_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
227232
target_address: context.interpreter.input.target_address(),
228233
caller: context.interpreter.input.caller_address(),
229234
bytecode_address: to,
235+
bytecode,
236+
bytecode_hash,
230237
value: CallValue::Apparent(context.interpreter.input.call_value()),
231238
scheme: CallScheme::DelegateCall,
232239
is_static: context.interpreter.runtime_flag.is_static(),
@@ -252,7 +259,8 @@ pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
252259
return;
253260
};
254261

255-
let Some(gas_limit) = load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
262+
let Some((gas_limit, bytecode, bytecode_hash)) =
263+
load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)
256264
else {
257265
return;
258266
};
@@ -268,6 +276,8 @@ pub fn static_call<WIRE: InterpreterTypes, H: Host + ?Sized>(
268276
target_address: to,
269277
caller: context.interpreter.input.target_address(),
270278
bytecode_address: to,
279+
bytecode,
280+
bytecode_hash,
271281
value: CallValue::Transfer(U256::ZERO),
272282
scheme: CallScheme::StaticCall,
273283
is_static: true,

0 commit comments

Comments
 (0)