Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,20 @@ build: $(GEAS)
mkdir -p bytecode/factory
$(GEAS) -a -no-nl -o bytecode/factory/main.hex src/factory/main.eas

# 8272
mkdir -p bytecode/recent_root
$(GEAS) -a -no-nl -o bytecode/recent_root/main.hex src/recent_root/main.eas
$(GEAS) -a -no-nl -o bytecode/recent_root/ctor.hex src/recent_root/ctor.eas

# test helper
mkdir -p bytecode/fake_expo_test
$(GEAS) -a -no-nl -o bytecode/fake_expo_test/main.hex src/common/fake_expo_test.eas

# 8272 test shim: the same runtime with SLOTNUM read from NUMBER, so it runs
# under the prague EVM and a test picks the slot with vm.roll.
sed -E 's/^( +)slotnum( |$$)/\1number \2/' src/recent_root/main.eas > test/recent_root_shim.eas
$(GEAS) -a -no-nl -o test/recent_root_shim.hex test/recent_root_shim.eas

checksums: build
shasum -a 256 -c checksums.txt

Expand Down
1 change: 1 addition & 0 deletions bytecode/recent_root/ctor.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
61014080600a5f395ff3346100ba57366040146100c05736604836066100ba5780156100ba5761048081116100ba574b60005b602081013560c01c828110156100ba5780830361200011156100ba577f8f42481679c8e6fefa040974b3c905e0ce3f2e464ba93acdb074a41181617efc60005260488260203760686000207fbdc897da2177d260ff5f4be5d4b2aad43f89c3347a305b584fa5a2546d053daa60005290611fff1660c01b60405260486000205414156100ba5760480182811061002857005b60006000fd5b33600052602060006020376034600c20807f8f42481679c8e6fefa040974b3c905e0ce3f2e464ba93acdb074a41181617efc6040524b606852606052602060206088376068604020817fbdc897da2177d260ff5f4be5d4b2aad43f89c3347a305b584fa5a2546d053daa60a852611fff4b1660d05260c852604860a8205500
1 change: 1 addition & 0 deletions bytecode/recent_root/main.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
346100ba57366040146100c05736604836066100ba5780156100ba5761048081116100ba574b60005b602081013560c01c828110156100ba5780830361200011156100ba577f8f42481679c8e6fefa040974b3c905e0ce3f2e464ba93acdb074a41181617efc60005260488260203760686000207fbdc897da2177d260ff5f4be5d4b2aad43f89c3347a305b584fa5a2546d053daa60005290611fff1660c01b60405260486000205414156100ba5760480182811061002857005b60006000fd5b33600052602060006020376034600c20807f8f42481679c8e6fefa040974b3c905e0ce3f2e464ba93acdb074a41181617efc6040524b606852606052602060206088376068604020817fbdc897da2177d260ff5f4be5d4b2aad43f89c3347a305b584fa5a2546d053daa60a852611fff4b1660d05260c852604860a8205500
2 changes: 2 additions & 0 deletions checksums.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ ea3eebcbb9549c4c158fbf92b77bccb250909095c824846ecbff6e4f4829249e bytecode/conso
b7ff005f1ad4e7adc56859f2fc3876b42e9216def18000c5373b69629adca7a6 bytecode/execution_hash/ctor.hex
1676499334d750e5cc9e6bd9bbaf8b78a901b675b1ea1c8c8dbcde2fbc7751b4 bytecode/execution_hash/main.hex
e48ada9a3056d5a2a52de5c078229849c3392e6a6c8e87ebb2c4b65bf1045e6d bytecode/factory/main.hex
04c7897a143652c7617d9db3d96cb241d8dcef01691c1bd796505fadb07d5e68 bytecode/recent_root/ctor.hex
bb235cf81ca86be59cc597e1868f2144db2e5c4cbeabbd624708a1a28d14eca6 bytecode/recent_root/main.hex
cc660e9bde40daf93521f879909ecf7b3b207524356f8e87b888e68a44181f65 bytecode/withdrawals/ctor.hex
39c5522e72643757cc35d4cffc36848b2ab860d5ad38a18590b93513d550ba9f bytecode/withdrawals/main.hex
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ src = 'src'
out = 'out'
ffi = true
libs = ['lib']
fs_permissions = [{ access = "read", path = "./bytecode" }]
fs_permissions = [{ access = "read", path = "./bytecode" }, { access = "read", path = "./test" }]
4 changes: 4 additions & 0 deletions scripts/addr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ case $1 in
echo "searching for execution hash deployment data "
bin/nick search --score=$score --initcode="0x$(cat bytecode/execution_hash/ctor.hex)" --prefix=0x0000 --suffix=0xcccc
;;
recent_root|rr|8272)
echo "searching for recent root deployment data "
bin/nick search --score=$score --initcode="0x$(cat bytecode/recent_root/ctor.hex)" --prefix=0x0000 --suffix=0x8272 --gaslimit=$gaslimit
;;
builder_deposits)
echo "searching for builder_deposits deployment data "
bin/nick search --score=$score --initcode="0x$(cat bytecode/builder_deposits/ctor.hex)" --prefix=0x8282 --suffix=0xdddd --gaslimit=$gaslimit
Expand Down
10 changes: 10 additions & 0 deletions src/recent_root/ctor.eas
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
;; Copy and return the runtime code.
push len(code) ; [len]
dup1 ; [len, len]
push @code ; [codeOffset, len, len]
push 0 ; [dest, codeOffset, len, len]
codecopy ; [len]
push 0 ; [ptr, len]
return ; []

#bytes code: assemble("main.eas")
205 changes: 205 additions & 0 deletions src/recent_root/main.eas
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
;;; RECENT_ROOT_CODE — EIP-8272 recent-root predeploy runtime.
;;;
;;; Calls with nonzero value revert. Calldata length selects the operation.
;;;
;;; Write, len == 64: calldata = salt(32) || root(32)
;;;
;;; source_id = keccak256(msg.sender || salt)
;;; entry_hash = keccak256(ENTRY_DOMAIN || source_id || uint64_be(SLOTNUM) || root)
;;; storage_key = keccak256(STORAGE_DOMAIN || source_id || uint64_be(SLOTNUM % 8192))
;;; storage[storage_key] = entry_hash
;;;
;;; Validation, len == n * 72 with 1 <= n <= 16: calldata is n tuples
;;; source_id(32) || uint64_be(slot)(8) || root(32). Every tuple must satisfy
;;;
;;; slot < SLOTNUM
;;; SLOTNUM - slot <= 8191
;;; storage[keccak256(STORAGE_DOMAIN || source_id || uint64_be(slot % 8192))]
;;; == keccak256(ENTRY_DOMAIN || source_id || uint64_be(slot) || root)
;;;
;;; or the call reverts. A passing call returns no data and changes no state.
;;; Any other length reverts; 64 is not a multiple of 72, so the two are disjoint.
;;;
;;; Only a write in RECENT_ROOT_ADDRESS's own storage context is a recent-root
;;; write. In a static context the SSTORE fails; under DELEGATECALL/CALLCODE, or
;;; a call to an EIP-7702 account delegated here, the SSTORE lands in the
;;; executing account's own storage, so no explicit guard is needed for either.

#pragma target "amsterdam"

;;; ENTRY_DOMAIN = keccak256("RECENT_ROOT_ENTRY")
#define ENTRY_DOMAIN = 0x8f42481679c8e6fefa040974b3c905e0ce3f2e464ba93acdb074a41181617efc
;;; STORAGE_DOMAIN = keccak256("RECENT_ROOT_STORAGE")
#define STORAGE_DOMAIN = 0xbdc897da2177d260ff5f4be5d4b2aad43f89c3347a305b584fa5a2546d053daa
;;; RECENT_ROOT_LENGTH - 1, the ring-buffer index mask (RECENT_ROOT_LENGTH = 8192).
#define RING_MASK = 0x1fff
;;; RECENT_ROOT_LENGTH, one past the largest allowed reference age.
#define RING_LENGTH = 0x2000
;;; RECENT_ROOT_TUPLE_BYTES.
#define TUPLE_BYTES = 72
;;; MAX_RECENT_ROOT_REFERENCES * RECENT_ROOT_TUPLE_BYTES.
#define MAX_VALIDATION_BYTES = 1152

;; Reject any call value.
callvalue ; [value]
push2 @fail ; [@fail, value]
jumpi ; []

;; A 64-byte call is a write.
calldatasize ; [len]
push1 0x40 ; [64, len]
eq ; [len == 64]
push2 @write ; [@write, len == 64]
jumpi ; []

;; Otherwise the length must be n * 72 with 1 <= n <= 16.
calldatasize ; [len]
push1 TUPLE_BYTES ; [72, len]
calldatasize ; [len, 72, len]
mod ; [len % 72, len]
push2 @fail ; [@fail, len % 72, len]
jumpi ; [len]
dup1 ; [len, len]
iszero ; [len == 0, len]
push2 @fail ; [@fail, len == 0, len]
jumpi ; [len]
push2 MAX_VALIDATION_BYTES ; [1152, len]
dup2 ; [len, 1152, len]
gt ; [len > 1152, len]
push2 @fail ; [@fail, len > 1152, len]
jumpi ; [len]

slotnum ; [cur, len]
push1 0x00 ; [off, cur, len]

validate:
;; slot = uint64_be at calldata[off+32..off+40)
push1 0x20 ; [32, off, cur, len]
dup2 ; [off, 32, off, cur, len]
add ; [off+32, off, cur, len]
calldataload ; [word, off, cur, len]
push1 0xc0 ; [192, word, off, cur, len]
shr ; [slot, off, cur, len]

;; slot < cur
dup3 ; [cur, slot, off, cur, len]
dup2 ; [slot, cur, slot, off, cur, len]
lt ; [slot < cur, slot, off, cur, len]
iszero ; [slot >= cur, slot, off, cur, len]
push2 @fail ; [@fail, slot >= cur, slot, off, cur, len]
jumpi ; [slot, off, cur, len]

;; cur - slot < 8192, which with slot < cur is cur - slot <= 8191
dup1 ; [slot, slot, off, cur, len]
dup4 ; [cur, slot, slot, off, cur, len]
sub ; [cur - slot, slot, off, cur, len]
push2 RING_LENGTH ; [8192, cur - slot, slot, off, cur, len]
gt ; [8192 > cur - slot, slot, off, cur, len]
iszero ; [too old, slot, off, cur, len]
push2 @fail ; [@fail, too old, slot, off, cur, len]
jumpi ; [slot, off, cur, len]

;; entry_hash preimage at mem[0x00..0x68):
;; ENTRY_DOMAIN(32) || source_id(32) || uint64_be(slot)(8) || root(32),
;; the last three being the tuple exactly as it sits in calldata.
push ENTRY_DOMAIN ; [ENTRY_DOMAIN, slot, off, cur, len]
push1 0x00 ; [0, ENTRY_DOMAIN, slot, off, cur, len]
mstore ; [slot, off, cur, len]
push1 TUPLE_BYTES ; [72, slot, off, cur, len]
dup3 ; [off, 72, slot, off, cur, len]
push1 0x20 ; [0x20, off, 72, slot, off, cur, len]
calldatacopy ; [slot, off, cur, len] mem[0x20..0x68) = tuple
push1 0x68 ; [104, slot, off, cur, len]
push1 0x00 ; [0, 104, slot, off, cur, len]
keccak256 ; [entry_hash, slot, off, cur, len]

;; storage_key preimage at mem[0x00..0x48):
;; STORAGE_DOMAIN(32) || source_id(32) || uint64_be(slot % 8192)(8).
;; source_id is still at mem[0x20..0x40).
push STORAGE_DOMAIN ; [STORAGE_DOMAIN, entry_hash, slot, off, cur, len]
push1 0x00 ; [0, STORAGE_DOMAIN, entry_hash, slot, off, cur, len]
mstore ; [entry_hash, slot, off, cur, len]
swap1 ; [slot, entry_hash, off, cur, len]
push2 RING_MASK ; [0x1fff, slot, entry_hash, off, cur, len]
and ; [i, entry_hash, off, cur, len]
push1 0xc0 ; [192, i, entry_hash, off, cur, len]
shl ; [i << 192, entry_hash, off, cur, len]
push1 0x40 ; [0x40, i << 192, entry_hash, off, cur, len]
mstore ; [entry_hash, off, cur, len] mem[0x40..0x48) = uint64_be(i)
push1 0x48 ; [72, entry_hash, off, cur, len]
push1 0x00 ; [0, 72, entry_hash, off, cur, len]
keccak256 ; [storage_key, entry_hash, off, cur, len]
sload ; [stored, entry_hash, off, cur, len]
eq ; [match, off, cur, len]
iszero ; [mismatch, off, cur, len]
push2 @fail ; [@fail, mismatch, off, cur, len]
jumpi ; [off, cur, len]

;; next tuple
push1 TUPLE_BYTES ; [72, off, cur, len]
add ; [off', cur, len]
dup3 ; [len, off', cur, len]
dup2 ; [off', len, off', cur, len]
lt ; [off' < len, off', cur, len]
push2 @validate ; [@validate, off' < len, off', cur, len]
jumpi ; [off', cur, len]
stop

fail:
push1 0x00 ; [0]
push1 0x00 ; [0, 0]
revert ; []

write:
;; source_id = keccak256(caller || salt), laid out at mem[0x0c..0x40).
caller ; [caller]
push1 0x00 ; [0, caller]
mstore ; [] mem[0x00..0x20] = caller (address in low 20 bytes)
push1 0x20 ; [0x20]
push1 0x00 ; [0, 0x20]
push1 0x20 ; [0x20, 0, 0x20]
calldatacopy ; [] mem[0x20..0x40] = salt
push1 0x34 ; [52]
push1 0x0c ; [0x0c, 52]
keccak256 ; [source_id]

;; entry_hash preimage at mem[0x40..0xa8):
;; ENTRY_DOMAIN(32) || source_id(32) || uint64_be(SLOTNUM)(8) || root(32).
;; The slot word is stored first so the source_id word overwrites all of it
;; but its low eight bytes.
dup1 ; [source_id, source_id]
push ENTRY_DOMAIN ; [ENTRY_DOMAIN, source_id, source_id]
push1 0x40 ; [0x40, ENTRY_DOMAIN, source_id, source_id]
mstore ; [source_id, source_id] mem[0x40..0x60] = ENTRY_DOMAIN
slotnum ; [slot, source_id, source_id]
push1 0x68 ; [0x68, slot, source_id, source_id]
mstore ; [source_id, source_id] mem[0x80..0x88] = uint64_be(slot)
push1 0x60 ; [0x60, source_id, source_id]
mstore ; [source_id] mem[0x60..0x80] = source_id
push1 0x20 ; [0x20, source_id]
push1 0x20 ; [0x20, 0x20, source_id]
push1 0x88 ; [0x88, 0x20, 0x20, source_id]
calldatacopy ; [source_id] mem[0x88..0xa8] = root
push1 0x68 ; [104, source_id]
push1 0x40 ; [0x40, 104, source_id]
keccak256 ; [entry_hash, source_id]

;; storage_key preimage at mem[0xa8..0xf0):
;; STORAGE_DOMAIN(32) || source_id(32) || uint64_be(SLOTNUM % 8192)(8).
dup2 ; [source_id, entry_hash, source_id]
push STORAGE_DOMAIN ; [STORAGE_DOMAIN, source_id, entry_hash, source_id]
push1 0xa8 ; [0xa8, STORAGE_DOMAIN, source_id, entry_hash, source_id]
mstore ; [source_id, entry_hash, source_id] mem[0xa8..0xc8] = STORAGE_DOMAIN
push2 RING_MASK ; [0x1fff, source_id, entry_hash, source_id]
slotnum ; [slot, 0x1fff, source_id, entry_hash, source_id]
and ; [i, source_id, entry_hash, source_id]
push1 0xd0 ; [0xd0, i, source_id, entry_hash, source_id]
mstore ; [source_id, entry_hash, source_id] mem[0xe8..0xf0] = uint64_be(i)
push1 0xc8 ; [0xc8, source_id, entry_hash, source_id]
mstore ; [entry_hash, source_id] mem[0xc8..0xe8] = source_id
push1 0x48 ; [72, entry_hash, source_id]
push1 0xa8 ; [0xa8, 72, entry_hash, source_id]
keccak256 ; [storage_key, entry_hash, source_id]

sstore ; [source_id] storage[storage_key] = entry_hash
stop
Loading