This repository was archived by the owner on Aug 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmain.etk
148 lines (124 loc) · 5.63 KB
/
main.etk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# __ ___________ ____
# / // /__ ( __ )( __ )____ __________ ___
# / // /_ / / __ / __ / __ `/ ___/ __ `__ \
# /__ __// / /_/ / /_/ / /_/ (__ ) / / / / /
# /_/ /_/\____/\____/\__,_/____/_/ /_/ /_/
#
# This is an implementation of EIP-4788's predeploy contract. It implements two
# ring buffers to create bounded beacon root lookup. The first ring buffer is a
# timestamp % buflen -> timestamp mapping. This is used to ensure timestamp
# argument actually matches the stored root and isn't different dividend. The
# second ring buffer store the beacon root. It's also keyed by timestamp %
# buflen and the shifted right by buflen so the two don't overlap.
#
# The ring buffers can be visualized as follows:
#
# buflen = 10
# |--------------|--------------|
# 0 10 20
# timestamps beacon roots
#
# To get the corresponding beacon root for a specific timestamp, simply add
# buflen to the timestamp's index in the first ring buffer. The sum will be
# the storage slot in the second ring buffer where it is stored.
# -----------------------------------------------------------------------------
# MACROS ----------------------------------------------------------------------
# -----------------------------------------------------------------------------
# buflen returns the HISTORY_BUFFER_LENGTH as defined in the EIP.
%def buflen()
8191
%end
# sysaddr is the address which calls the contract to submit a new root.
%def sysaddr()
0xfffffffffffffffffffffffffffffffffffffffe
%end
# do_revert sets up and then executes a revert(0,0) operation.
%macro do_revert()
push0 # [0]
push0 # [0, 0]
revert # []
%end
# -----------------------------------------------------------------------------
# MACROS END ------------------------------------------------------------------
# -----------------------------------------------------------------------------
start:
# Protect the submit routine by verifying the caller is equal to
# sysaddr().
caller # [caller]
push20 sysaddr() # [sysaddr, caller]
eq # [sysaddr == caller]
push1 submit # [submit_lbl, sysaddr == caller]
jumpi # []
# Fallthrough if addresses don't match -- this means the caller intends
# to read a root.
# Check if calldata is equal to 32 bytes.
push1 32 # [32]
calldatasize # [calldatasize, 32]
eq # [calldatasize == 32]
# Jump to continue if length-check passed, otherwise revert.
push1 loadtime # [loadtime_lbl, calldatasize == 32]
jumpi # []
%do_revert() # []
loadtime:
jumpdest
# Load input timestamp.
push0 # [0]
calldataload # [input_timestamp]
dup1 # [input_timestamp, input_timestamp]
# Verify input timestamp is non-zero.
iszero # [input_timestamp == 0, input_timestamp]
push1 throw # [throw_lbl, input_timestamp == 0, input_timestamp]
jumpi # [input_timestamp]
# Compute the timestamp index and load from storage.
push3 buflen() # [buflen, input_timestamp]
dup2 # [input_timestamp, buflen, input_timestamp]
mod # [time_index, input_timestamp]
swap1 # [input_timestamp, time_index]
dup2 # [time_index, input_timestamp, time_index]
sload # [stored_timestamp, input_timestamp, time_index]
# Verify stored timestamp matches input timestamp. It's possible these
# don't match if the slot has been overwritten by the ring buffer or if
# the timestamp input wasn't a valid previous timestamp.
eq # [stored_timestamp == input_timestamp, time_index]
push1 loadroot # [loadroot_lbl, input == timestamp, time_index]
jumpi # [time_index]
%do_revert() # []
loadroot:
# Extend index to get root index.
jumpdest # [time_index]
push3 buflen() # [buflen, time_index]
add # [root_index]
sload # [root]
# Write the retrieved root to memory so it can be returned.
push0 # [0, root]
mstore # []
# Return the root.
push1 32 # [size]
push0 # [offset, size]
return # []
throw:
# Reverts current execution with no return data.
jumpdest
%do_revert()
submit:
jumpdest # []
# Calculate the index the timestamp should be stored at, e.g.
# time_index = (time % buflen).
push3 buflen() # [buflen]
timestamp # [time, buflen]
mod # [time % buflen]
# Write timestamp into storage slot at time_index.
timestamp # [time, time_index]
dup2 # [time_index, time, time_index]
sstore # [time_index]
# Get root from calldata and write into root_index. No validation is
# done on the input root. Becuase the routine is protected by a caller
# check against sysaddr(), it's okay to assume the value is correctly
# given.
push0 # [0, time_index]
calldataload # [root, time_index]
swap1 # [time_index, root]
push3 buflen() # [buflen, time_index, root]
add # [root_index, root]
sstore # []
stop # []