Skip to content

Commit

Permalink
Changes for code quality
Browse files Browse the repository at this point in the history
  • Loading branch information
kwannoel committed Jul 20, 2021
1 parent b5449cf commit ec6916e
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 57 deletions.
108 changes: 68 additions & 40 deletions evm-instructions.ss
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
(export #t)
(import
:gerbil/gambit/bits :gerbil/gambit/bytes :gerbil/gambit/exact
:gerbil/gambit/bits :gerbil/gambit/bytes
:std/misc/number :std/sugar :std/misc/list :std/format
:clan/base :clan/number :clan/with-id :std/srfi/1
:clan/base :clan/number :std/srfi/1
:clan/poo/object (only-in :clan/poo/mop Type)
:clan/crypto/secp256k1
./assembly ./ethereum ./types ./evm-runtime)
(only-in :std/srfi/141 floor/)
./assembly ./ethereum ./evm-runtime)

;; --------------------------------
;; General purpose EVM instructions
Expand All @@ -20,60 +20,77 @@
;; -------------
;; Byte encoding
;; -------------
;; Instructions encode bytes as big-endian.
;; Instructions encode bytes as big-endian, left-padding them.
;;
;; For example, given the value: 0x00ff,
;; After PUSH: Stack will have "0x00" on top, followed by "0xff".
;; After STORE: Memory location will have "0x00" at offset, "0xff" at offset+1.
;; For example, given an array of two bytes: #u8(#x11 #xff).
;;
;; After &push/any-size:
;; The topmost word on the EVM Stack will be:
;; #x00000000000000000000000000000000000000000000000000000000000011ff
;;
;; After (&mstore/free/any-size 2):
;; Memory location will have "0x11" at offset, "0xff" at offset+1.
;;
;; For larger bytes (larger than a EVM word),
;; we partition them into smaller chunks,
;; less than or equal to a EVM word.
;; We use the notation: "part[n]", to denote the partition index.
;; E.g. 65-bytes -> | 32-bytes | 32-bytes | 1-byte |
;; | part0 | part1 | part2 |

;; | part0 | part1 | part2 |
(defrule (EVM-WORD-SIZE) 32)

;; ----------------------
;; Instruction defintions
;; ----------------------
;; -----------------------
;; Instruction definitions
;; -----------------------

;; For contracts, to store values in memory,
;; we first have to marshal them to the stack.
;; This instruction serves that purpose.
;;
;; This allows us to PUSH a non-empty sequence of bytes
;; which are variably sized onto the stack.
;; PUSH[1-32] instructions cannot handle more than 32 bytes,
;; which this instruction supports.
;; NOTE: bytes are stored compactly without padding.
;;
;; stack input: -
;; stack output: part0 part1 ... partn
(def (&push/any-size bytes (start 0) (len (u8vector-length bytes)))
(assert! (<= 1 len) (format "size: ~d not in range: [1,∞]")) ; TODO: provide a concrete upper bound
(def end (min len (+ start 32)))
;; (Thunk part0 part1 ... partn <-) <- Bytes
(def (&push/any-size bytes)
(def total-bytes (u8vector-length bytes))
(assert-bytes-at-least! total-bytes 1)
(def start 0)
(&push-1/any-size bytes start total-bytes))

(def (&push-1/any-size bytes start total-bytes)
(def end (min total-bytes (+ start (EVM-WORD-SIZE))))
(when (< start end)
(let ()
(def bytes<=32 (subu8vector bytes start end))
(def n-bytes (u8vector-length bytes<=32))
(&begin (&push/any-size bytes end) [&push-bytes bytes<=32])
)))
(def bytes<=evm-word-size (subu8vector bytes start end))
(&begin (&push-1/any-size bytes end total-bytes) [&push-bytes bytes<=evm-word-size]))))

;; Helper function - Make list of word-sizes
;; E.g. (sizes/word-size<-size 65 32) -> [32 32 1]
(def (sizes/word-size<-size size (word-size 32))
(def n-words (truncate (/ size word-size)))
(def (sizes/word-size<-size size (word-size (EVM-WORD-SIZE)))
(defvalues (n-words rem) (floor/ size word-size))
(def words (make-list n-words word-size))
(def rem (modulo size word-size))
(if (eq? rem 0) words [words ... rem])
)
(if (zero? rem) words [words ... rem]))

;; NOTE: Uses brk@ for offset via &brk-cons, dependent on EVM memory layout.
;; stack input: part0 part1 ... partn
;; stack output: -
(def (&mstore/free/any-size size)
(assert! (<= 1 size) (format "size: ~d not in range: [1,∞]")) ; TODO: provide a concrete upper bound
(def sizes/base-32 (sizes/word-size<-size size))
(&begin (map &brk-cons sizes/base-32) ...)
(assert-bytes-at-least! size 1)
(def sizes/base/evm-word-size (sizes/word-size<-size size))
(&begin (map &brk-cons sizes/base/evm-word-size) ...)
)

;; Helper function - Make list of relative offsets and sizes for partitions.
;; E.g. (offsets-and-sizes<-size 65) -> [[0 32] [32 32] [64 1]]
;; List [RelativeOffset Size] <- Nat
;; E.g. (offsets-and-sizes<-size 65) -> [[0 (EVM-WORD-SIZE)] [32 32] [64 1]]
;; (ListOf (List RelativeOffset Size)) <- Nat
(def (offsets-and-sizes<-size size)
(def sizes (sizes/word-size<-size size))
(def relative-offsets (iota (length sizes) 0 32))
(def relative-offsets (iota (length sizes) 0 (EVM-WORD-SIZE)))
(zip relative-offsets sizes))

;; Given relative-offset and size,
Expand All @@ -83,33 +100,44 @@
;;
;; stack input: start-offset
;; stack output: start-offset bytes[offset:end]
;; where offset = offset+relative-offset
;; where offset = start-offset+relative-offset
;; end = offset+size
;; (Thunk start-offset bytes[offset:end] <- start-offset) <- (List RelativeOffset Size)
(def &mload-1/any-size
(match <>
([relative-offset size]
(&begin ; start-offset
DUP1 relative-offset ADD ; offset start-offset
(&mload size) SWAP1)))) ; start-offset bytes[offset:end]
(&begin ; start-offset
DUP1 #|start-offset|# relative-offset ADD ; offset start-offset
(&mload size) SWAP1)))) ; start-offset bytes[offset:end]

;; stack input: start-offset
;; stack output: part0 part1 ... partn
;; (Thunk part0 ... partn <- start-offset) <- Size
(def (&mload/any-size size)
(assert! (<= 0 size) (format "size: ~d not in range: [1,∞]")) ; TODO: provide a concrete upper bound
(assert-bytes-at-least! size 0)
(match (offsets-and-sizes<-size size)
;; = 0 bytes
([] (&begin POP 0)
([] (&begin POP)
)
;; > 0 bytes
([[_ start-size] . rest]
(&begin
(map &mload-1/any-size (reverse rest)) ...
(&mload start-size)))
)
)
(&mload start-size)))))

;; stack in: -
;; mem in: part0 part1 ... partn
;; stack out: part0 part1 ... partn
(def (&mloadat/any-size offset length-size)
(&begin offset (&mload/any-size length-size)))

;; ----------------
;; Shared Utilities
;; ----------------

;; FIXME: there should be some upper bound for the length of bytes,
;; where it will be unfeasible due to stack/memory/gas constraints to push/store/load bytes.
(def (assert-bytes-at-least! total-bytes lower-bound)
(assert!
(<= lower-bound total-bytes)
(format "total bytes: ~d should be more than ~d" total-bytes lower-bound)))
63 changes: 51 additions & 12 deletions t/100-evm-instructions-integrationtest.ss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
../testing ../evm-instructions.ss
./10-json-rpc-integrationtest)

;; Initializes free memory pointer
(def &init-brk
(&begin #x20 (&mstoreat brk@ 32)))

;; Stores free memory pointer at free memory location for returning
(def &store-brk
(&begin (&mloadat brk@) DUP1 MSTORE))
Expand All @@ -14,8 +18,8 @@
(test-suite "integration tests for evm instructions"
(test-case "EVM-type: &mstore/free/any-size, size = 1"
(evm-test [] (&begin
#x20 (&mstoreat brk@ 32)
(&push/any-size #u8(1)) ; stack: 1
&init-brk
(&push/any-size #u8(1))
(&mstore/free/any-size 1)
&store-brk
)
Expand All @@ -24,6 +28,46 @@
result-in-memory?: #t
result-start: #x20))

(test-case "EVM-type: &mstore/free/any-size, size = 1, left-padded to EVM-Word size"
(evm-test [] (&begin
&init-brk
(&push/any-size #u8(1))
(&mstore/free/any-size 32)
&store-brk
)
[[Bytes32 . #u8(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
)]
[UInt256 . 64]]
result-in-memory?: #t
result-start: #x20))

(test-case "EVM-type: &mstore/free/any-size, size = 2"
(evm-test [] (&begin
&init-brk
(&push/any-size #u8(#x11 #xff))
(&mstore/free/any-size 2)
&store-brk
)
[[Bytes2 . #u8(#x11 #xff)]
[UInt256 . 34]]
result-in-memory?: #t
result-start: #x20))

(test-case "EVM-type: &mstore/free/any-size, size = 4, left-padded to EVM-Word size"
(evm-test [] (&begin
&init-brk
(&push/any-size #u8(1 2 3 4))
(&mstore/free/any-size 32)
&store-brk
)
[[Bytes32 . #u8(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4
)]
[UInt256 . 64]]
result-in-memory?: #t
result-start: #x20))

(test-case "EVM-type: &mstore/free/any-size, size = 65"
(def 65-bytes (list->u8vector (make-list 65 65)))
(u8vector-set! 65-bytes 0 66)
Expand All @@ -35,7 +79,7 @@
(def 65-bytes/64-65 (subu8vector 65-bytes 64 65))

(evm-test [] (&begin
#x20 (&mstoreat brk@ 32)
&init-brk
(&push/any-size 65-bytes)
(&mstore/free/any-size 65)
&store-brk
Expand All @@ -46,8 +90,7 @@
[UInt256 . 97] ; 32 (free mem ptr) + 65 (str65) = 97
]
result-in-memory?: #t
result-start: #x20)
)
result-start: #x20))

(test-case "EVM-type &mload/free/any-size, size = 65"
(def 65-bytes (list->u8vector (make-list 65 65)))
Expand All @@ -60,8 +103,7 @@
(def 65-bytes/64-65 (subu8vector 65-bytes 64 65))

(evm-test [] (&begin
;; init freememptr
#x20 (&mstoreat brk@ 32) ; -
&init-brk

;; Store str65 in mem
(&push/any-size 65-bytes) ; bytes[0-32] bytes[32-64] bytes[64-65]
Expand Down Expand Up @@ -103,8 +145,5 @@
(&mload 32) SWAP1 ; -- offset bytes[32-64] bytes[64-65]

;; Load first string segment
(&mload 32) ; -- bytes[0-32] bytes[32-64] bytes[64-65]
)))
(check-equal? &load65/actual &load65/expected)
)
))
(&mload 32)))) ; -- bytes[0-32] bytes[32-64] bytes[64-65]
(check-equal? &load65/actual &load65/expected))))
3 changes: 1 addition & 2 deletions t/80-evm-eval-integrationtest.ss
Original file line number Diff line number Diff line change
Expand Up @@ -274,5 +274,4 @@
(def maxUInt256 (- (expt 2 256) 1))
(evm-test [] (&begin maxUInt256 0 (&mstore 32))
[[UInt256 . maxUInt256]]
result-in-memory?: #t))
))
result-in-memory?: #t))))
4 changes: 1 addition & 3 deletions t/evm-instructions-test.ss
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,4 @@
[(subu8vector 65-bytes 64 65) ; bytes/64-65: 68
(subu8vector 65-bytes 32 64) ; bytes/32-64: 67 65 65 ... 65
(subu8vector 65-bytes 0 32) ; bytes/0-32: 66 65 65 ... 65
])
)
))
]))))

0 comments on commit ec6916e

Please sign in to comment.