Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Commit e6bdb8d

Browse files
authored
Add PUSH0. (#510)
1 parent 9f36600 commit e6bdb8d

File tree

13 files changed

+196
-82
lines changed

13 files changed

+196
-82
lines changed

bus-mapping/src/circuit_input_builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ impl<'a> CircuitInputBuilder {
497497
state_ref.block_ctx.rwc.0,
498498
state_ref.call().map(|c| c.call_id).unwrap_or(0),
499499
state_ref.call_ctx()?.memory.len(),
500-
if geth_step.op.is_push() {
500+
if geth_step.op.is_push_with_data() {
501501
format!("{:?}", geth_trace.struct_logs[index + 1].stack.last())
502502
} else if geth_step.op.is_call_without_value() {
503503
format!(

bus-mapping/src/evm/opcodes.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,12 @@ type FnGenAssociatedOps = fn(
153153
) -> Result<Vec<ExecStep>, Error>;
154154

155155
fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
156-
if opcode_id.is_push() {
156+
if opcode_id.is_push_with_data() {
157157
return StackOnlyOpcode::<0, 1>::gen_associated_ops;
158158
}
159159

160160
match opcode_id {
161+
OpcodeId::PUSH0 => StackOnlyOpcode::<0, 0>::gen_associated_ops,
161162
OpcodeId::STOP => Stop::gen_associated_ops,
162163
OpcodeId::ADD => StackOnlyOpcode::<2, 1>::gen_associated_ops,
163164
OpcodeId::MUL => StackOnlyOpcode::<2, 1>::gen_associated_ops,

eth-types/src/bytecode.rs

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ impl Bytecode {
9494
self
9595
}
9696

97-
/// Push
97+
/// Push, value is useless for `PUSH0`
9898
pub fn push<T: ToWord>(&mut self, n: u8, value: T) -> &mut Self {
99-
debug_assert!((1..=32).contains(&n), "invalid push");
99+
debug_assert!((..=32).contains(&n), "invalid push");
100100
let value = value.to_word();
101101

102102
// Write the op code
@@ -163,7 +163,7 @@ impl Bytecode {
163163
pub fn append_asm(&mut self, op: &str) -> Result<(), Error> {
164164
match OpcodeWithData::from_str(op)? {
165165
OpcodeWithData::Opcode(op) => self.write_op(op),
166-
OpcodeWithData::Push(n, value) => self.push(n, value),
166+
OpcodeWithData::PushWithData(n, value) => self.push(n, value),
167167
};
168168
Ok(())
169169
}
@@ -174,7 +174,7 @@ impl Bytecode {
174174
OpcodeWithData::Opcode(opcode) => {
175175
self.write_op(opcode);
176176
}
177-
OpcodeWithData::Push(n, word) => {
177+
OpcodeWithData::PushWithData(n, word) => {
178178
self.push(n, word);
179179
}
180180
}
@@ -196,18 +196,18 @@ impl Bytecode {
196196
/// An ASM entry
197197
#[derive(Clone, PartialEq, Eq)]
198198
pub enum OpcodeWithData {
199-
/// A non-push opcode
199+
/// A `PUSH0` or non-push opcode
200200
Opcode(OpcodeId),
201-
/// A push opcode
202-
Push(u8, Word),
201+
/// A `PUSH1` .. `PUSH32` opcode
202+
PushWithData(u8, Word),
203203
}
204204

205205
impl OpcodeWithData {
206206
/// get the opcode
207207
pub fn opcode(&self) -> OpcodeId {
208208
match self {
209209
OpcodeWithData::Opcode(op) => *op,
210-
OpcodeWithData::Push(n, _) => OpcodeId::push_n(*n).expect("valid push size"),
210+
OpcodeWithData::PushWithData(n, _) => OpcodeId::push_n(*n).expect("valid push size"),
211211
}
212212
}
213213
}
@@ -218,31 +218,37 @@ impl FromStr for OpcodeWithData {
218218
#[allow(clippy::manual_range_contains)]
219219
fn from_str(op: &str) -> Result<Self, Self::Err> {
220220
let err = || Error::InvalidAsmError(op.to_string());
221+
221222
if let Some(push) = op.strip_prefix("PUSH") {
222223
let n_value: Vec<_> = push.splitn(3, ['(', ')']).collect();
223224
let n = n_value[0].parse::<u8>().map_err(|_| err())?;
224-
if n < 1 || n > 32 {
225+
if n > 32 {
225226
return Err(err());
226227
}
227-
let value = if n_value[1].starts_with("0x") {
228-
Word::from_str_radix(&n_value[1][2..], 16)
229-
} else {
230-
Word::from_str_radix(n_value[1], 10)
228+
229+
// Parse `PUSH0` below only for shanghai (otherwise as an invalid opcode).
230+
if n > 0 {
231+
let value = if n_value[1].starts_with("0x") {
232+
Word::from_str_radix(&n_value[1][2..], 16)
233+
} else {
234+
Word::from_str_radix(n_value[1], 10)
235+
}
236+
.map_err(|_| err())?;
237+
238+
return Ok(OpcodeWithData::PushWithData(n, value));
231239
}
232-
.map_err(|_| err())?;
233-
Ok(OpcodeWithData::Push(n, value))
234-
} else {
235-
let opcode = OpcodeId::from_str(op).map_err(|_| err())?;
236-
Ok(OpcodeWithData::Opcode(opcode))
237240
}
241+
242+
let opcode = OpcodeId::from_str(op).map_err(|_| err())?;
243+
Ok(OpcodeWithData::Opcode(opcode))
238244
}
239245
}
240246

241247
impl ToString for OpcodeWithData {
242248
fn to_string(&self) -> String {
243249
match self {
244250
OpcodeWithData::Opcode(opcode) => format!("{:?}", opcode),
245-
OpcodeWithData::Push(n, word) => format!("PUSH{}({})", n, word),
251+
OpcodeWithData::PushWithData(n, word) => format!("PUSH{}({})", n, word),
246252
}
247253
}
248254
}
@@ -255,13 +261,16 @@ impl<'a> Iterator for BytecodeIterator<'a> {
255261
fn next(&mut self) -> Option<Self::Item> {
256262
self.0.next().map(|byte| {
257263
let op = OpcodeId::from(byte.value);
258-
if op.is_push() {
259-
let n = op.data_len();
264+
let n = op.data_len();
265+
if n > 0 {
266+
assert!(op.is_push_with_data());
267+
260268
let mut value = vec![0u8; n];
261269
for value_byte in value.iter_mut() {
262270
*value_byte = self.0.next().unwrap().value;
263271
}
264-
OpcodeWithData::Push(n as u8, Word::from(value.as_slice()))
272+
273+
OpcodeWithData::PushWithData(n as u8, Word::from(value.as_slice()))
265274
} else {
266275
OpcodeWithData::Opcode(op)
267276
}
@@ -277,7 +286,7 @@ impl From<Vec<u8>> for Bytecode {
277286
while let Some(byte) = input_iter.next() {
278287
let op = OpcodeId::from(*byte);
279288
code.write_op(op);
280-
if op.is_push() {
289+
if op.is_push_with_data() {
281290
let n = op.postfix().expect("opcode with postfix");
282291
for _ in 0..n {
283292
match input_iter.next() {
@@ -315,14 +324,14 @@ macro_rules! bytecode_internal {
315324
($code:ident, ) => {};
316325
// PUSHX op codes
317326
($code:ident, $x:ident ($v:expr) $($rest:tt)*) => {{
318-
debug_assert!($crate::evm_types::OpcodeId::$x.is_push(), "invalid push");
327+
debug_assert!($crate::evm_types::OpcodeId::$x.is_push_with_data(), "invalid push");
319328
let n = $crate::evm_types::OpcodeId::$x.postfix().expect("opcode with postfix");
320329
$code.push(n, $v);
321330
$crate::bytecode_internal!($code, $($rest)*);
322331
}};
323332
// Default opcode without any inputs
324333
($code:ident, $x:ident $($rest:tt)*) => {{
325-
debug_assert!(!$crate::evm_types::OpcodeId::$x.is_push(), "invalid push");
334+
debug_assert!(!$crate::evm_types::OpcodeId::$x.is_push_with_data(), "invalid push");
326335
$code.write_op($crate::evm_types::OpcodeId::$x);
327336
$crate::bytecode_internal!($code, $($rest)*);
328337
}};
@@ -338,6 +347,13 @@ macro_rules! bytecode_internal {
338347
}};
339348
}
340349

350+
impl Bytecode {
351+
/// Helper function for `PUSH0`
352+
pub fn op_push0(&mut self) -> &mut Self {
353+
self.push(0, Word::zero())
354+
}
355+
}
356+
341357
macro_rules! impl_push_n {
342358
($($push_n:ident, $n:expr)*) => {
343359
#[allow(missing_docs)]
@@ -538,6 +554,8 @@ mod tests {
538554
#[test]
539555
fn test_bytecode_roundtrip() {
540556
let code = bytecode! {
557+
PUSH0
558+
POP
541559
PUSH8(0x123)
542560
POP
543561
PUSH24(0x321)
@@ -571,4 +589,26 @@ mod tests {
571589

572590
assert_eq!(code.code, code2.code);
573591
}
592+
593+
#[cfg(feature = "shanghai")]
594+
#[test]
595+
fn test_asm_disasm_for_shanghai() {
596+
let code = bytecode! {
597+
PUSH0
598+
POP
599+
PUSH1(5)
600+
PUSH2(0xa)
601+
MUL
602+
STOP
603+
};
604+
let mut code2 = Bytecode::default();
605+
code.iter()
606+
.map(|op| op.to_string())
607+
.map(|op| OpcodeWithData::from_str(&op).unwrap())
608+
.for_each(|op| {
609+
code2.append_op(op);
610+
});
611+
612+
assert_eq!(code.code, code2.code);
613+
}
574614
}

eth-types/src/evm_types/opcode_ids.rs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ pub enum OpcodeId {
9595
JUMPDEST,
9696

9797
// PUSHn
98+
/// `PUSH0`
99+
PUSH0,
98100
/// `PUSH1`
99101
PUSH1,
100102
/// `PUSH2`
@@ -315,11 +317,22 @@ pub enum OpcodeId {
315317
}
316318

317319
impl OpcodeId {
320+
#[cfg(feature = "shanghai")]
321+
/// Returns `true` if the `OpcodeId` is a `PUSHn` (including `PUSH0`).
322+
pub fn is_push(&self) -> bool {
323+
self.as_u8() >= Self::PUSH0.as_u8() && self.as_u8() <= Self::PUSH32.as_u8()
324+
}
325+
#[cfg(not(feature = "shanghai"))]
318326
/// Returns `true` if the `OpcodeId` is a `PUSHn`.
319327
pub fn is_push(&self) -> bool {
320328
self.as_u8() >= Self::PUSH1.as_u8() && self.as_u8() <= Self::PUSH32.as_u8()
321329
}
322330

331+
/// Returns `true` if the `OpcodeId` is a `PUSH1` .. `PUSH32` (excluding `PUSH0`).
332+
pub fn is_push_with_data(&self) -> bool {
333+
self.as_u8() >= Self::PUSH1.as_u8() && self.as_u8() <= Self::PUSH32.as_u8()
334+
}
335+
323336
/// ..
324337
pub fn is_call_with_value(&self) -> bool {
325338
matches!(self, Self::CALL | Self::CALLCODE)
@@ -412,6 +425,7 @@ impl OpcodeId {
412425
OpcodeId::PC => 0x58u8,
413426
OpcodeId::MSIZE => 0x59u8,
414427
OpcodeId::JUMPDEST => 0x5bu8,
428+
OpcodeId::PUSH0 => 0x5fu8,
415429
OpcodeId::PUSH1 => 0x60u8,
416430
OpcodeId::PUSH2 => 0x61u8,
417431
OpcodeId::PUSH3 => 0x62u8,
@@ -590,6 +604,7 @@ impl OpcodeId {
590604
OpcodeId::MSIZE => GasCost::QUICK,
591605
OpcodeId::GAS => GasCost::QUICK,
592606
OpcodeId::JUMPDEST => GasCost::ONE,
607+
OpcodeId::PUSH0 => GasCost::QUICK,
593608
OpcodeId::PUSH1 => GasCost::FASTEST,
594609
OpcodeId::PUSH2 => GasCost::FASTEST,
595610
OpcodeId::PUSH3 => GasCost::FASTEST,
@@ -744,6 +759,7 @@ impl OpcodeId {
744759
OpcodeId::MSIZE => (1, 1024),
745760
OpcodeId::GAS => (1, 1024),
746761
OpcodeId::JUMPDEST => (0, 1024),
762+
OpcodeId::PUSH0 => (1, 1024),
747763
OpcodeId::PUSH1 => (1, 1024),
748764
OpcodeId::PUSH2 => (1, 1024),
749765
OpcodeId::PUSH3 => (1, 1024),
@@ -850,8 +866,10 @@ impl OpcodeId {
850866

851867
/// Returns PUSHn opcode from parameter n.
852868
pub fn push_n(n: u8) -> Result<Self, Error> {
853-
if (1..=32).contains(&n) {
854-
Ok(OpcodeId::from(OpcodeId::PUSH1.as_u8() + n - 1))
869+
let op = OpcodeId::from(OpcodeId::PUSH0.as_u8().checked_add(n).unwrap_or_default());
870+
871+
if op.is_push() {
872+
Ok(op)
855873
} else {
856874
Err(Error::InvalidOpConversion)
857875
}
@@ -860,7 +878,7 @@ impl OpcodeId {
860878
/// If operation has postfix returns it, otherwise None.
861879
pub fn postfix(&self) -> Option<u8> {
862880
if self.is_push() {
863-
Some(self.as_u8() - OpcodeId::PUSH1.as_u8() + 1)
881+
Some(self.as_u8() - OpcodeId::PUSH0.as_u8())
864882
} else if self.is_dup() {
865883
Some(self.as_u8() - OpcodeId::DUP1.as_u8() + 1)
866884
} else if self.is_swap() {
@@ -873,10 +891,10 @@ impl OpcodeId {
873891
}
874892

875893
/// Returns number of bytes used by immediate data. This is > 0 only for
876-
/// push opcodes.
894+
/// `PUSH1` .. `PUSH32` opcodes.
877895
pub fn data_len(&self) -> usize {
878-
if self.is_push() {
879-
(self.as_u8() - OpcodeId::PUSH1.as_u8() + 1) as usize
896+
if self.is_push_with_data() {
897+
(self.as_u8() - OpcodeId::PUSH0.as_u8()) as usize
880898
} else {
881899
0
882900
}
@@ -946,6 +964,8 @@ impl From<u8> for OpcodeId {
946964
0x58u8 => OpcodeId::PC,
947965
0x59u8 => OpcodeId::MSIZE,
948966
0x5bu8 => OpcodeId::JUMPDEST,
967+
#[cfg(feature = "shanghai")]
968+
0x5fu8 => OpcodeId::PUSH0,
949969
0x60u8 => OpcodeId::PUSH1,
950970
0x61u8 => OpcodeId::PUSH2,
951971
0x62u8 => OpcodeId::PUSH3,
@@ -1100,6 +1120,10 @@ impl FromStr for OpcodeId {
11001120
"PC" => OpcodeId::PC,
11011121
"MSIZE" => OpcodeId::MSIZE,
11021122
"JUMPDEST" => OpcodeId::JUMPDEST,
1123+
#[cfg(feature = "shanghai")]
1124+
"PUSH0" => OpcodeId::PUSH0,
1125+
#[cfg(not(feature = "shanghai"))]
1126+
"PUSH0" => OpcodeId::INVALID(0x5f),
11031127
"PUSH1" => OpcodeId::PUSH1,
11041128
"PUSH2" => OpcodeId::PUSH2,
11051129
"PUSH3" => OpcodeId::PUSH3,
@@ -1167,7 +1191,6 @@ impl FromStr for OpcodeId {
11671191
"RETURN" => OpcodeId::RETURN,
11681192
"REVERT" => OpcodeId::REVERT,
11691193
"INVALID" => OpcodeId::INVALID(0xfe),
1170-
"PUSH0" => OpcodeId::INVALID(0x5f),
11711194
"SHA3" | "KECCAK256" => OpcodeId::SHA3,
11721195
"ADDRESS" => OpcodeId::ADDRESS,
11731196
"BALANCE" => OpcodeId::BALANCE,
@@ -1248,20 +1271,27 @@ mod opcode_ids_tests {
12481271

12491272
#[test]
12501273
fn push_n() {
1251-
assert!(matches!(OpcodeId::push_n(1), Ok(OpcodeId::PUSH1)));
1252-
assert!(matches!(OpcodeId::push_n(10), Ok(OpcodeId::PUSH10)));
1274+
#[cfg(feature = "shanghai")]
1275+
assert!(matches!(OpcodeId::push_n(0), Ok(OpcodeId::PUSH0)));
1276+
#[cfg(not(feature = "shanghai"))]
12531277
assert!(matches!(
1254-
OpcodeId::push_n(100),
1278+
OpcodeId::push_n(0),
12551279
Err(Error::InvalidOpConversion)
12561280
));
1281+
assert!(matches!(OpcodeId::push_n(1), Ok(OpcodeId::PUSH1)));
1282+
assert!(matches!(OpcodeId::push_n(10), Ok(OpcodeId::PUSH10)));
12571283
assert!(matches!(
1258-
OpcodeId::push_n(0),
1284+
OpcodeId::push_n(100),
12591285
Err(Error::InvalidOpConversion)
12601286
));
12611287
}
12621288

12631289
#[test]
12641290
fn postfix() {
1291+
#[cfg(feature = "shanghai")]
1292+
assert_eq!(OpcodeId::PUSH0.postfix(), Some(0));
1293+
#[cfg(not(feature = "shanghai"))]
1294+
assert_eq!(OpcodeId::PUSH0.postfix(), None);
12651295
assert_eq!(OpcodeId::PUSH1.postfix(), Some(1));
12661296
assert_eq!(OpcodeId::PUSH10.postfix(), Some(10));
12671297
assert_eq!(OpcodeId::LOG2.postfix(), Some(2));
@@ -1270,6 +1300,7 @@ mod opcode_ids_tests {
12701300

12711301
#[test]
12721302
fn data_len() {
1303+
assert_eq!(OpcodeId::PUSH0.data_len(), 0);
12731304
assert_eq!(OpcodeId::PUSH1.data_len(), 1);
12741305
assert_eq!(OpcodeId::PUSH10.data_len(), 10);
12751306
assert_eq!(OpcodeId::LOG2.data_len(), 0);

0 commit comments

Comments
 (0)