Skip to content

Commit

Permalink
Finish the first memory impl and tests, stack tests, fixes validation
Browse files Browse the repository at this point in the history
  • Loading branch information
alehander92 committed Feb 6, 2018
1 parent 04b1228 commit 211d46e
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
nimcache/
runner
*_test
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Nimbus

An Ethereum 2.0 Sharding Client for Resource-Restricted Devices.

https://docs.google.com/document/d/14u65XVNLOd83cq3t7wNC9UPweZ6kPWvmXwRTWWn0diQ/edit#
10 changes: 8 additions & 2 deletions src/constants.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ proc int256*(i: int): Int256 =
i.initBigInt

template i256*(i: int): Int256 =
i.int256
i.initBigInt

template i256*(i: Int256): Int256 =
i

template getInt*(i: int): int =
i

# TODO
# We'll have a fast fixed i256, for now this works
Expand Down Expand Up @@ -77,7 +83,7 @@ mapOp(`xor`)
proc `abs`*(a: Int256): Int256 =
if a >= 0.i256: a else: -a

proc `getInt`*(a: Int256): int =
template `getInt`*(a: Int256): int =
a.limbs[0].int

let
Expand Down
38 changes: 23 additions & 15 deletions src/validation.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


import
strformat,
errors, constants, bigints
Expand All @@ -8,27 +6,37 @@ proc validateCanonicalAddress*(value: string, title: string = "Value") =
# TODO
if false: #len(value) != 20:
raise newException(ValidationError,
fmt"{title} {value} is not a valid canonical address")
&"{title} {value} is not a valid canonical address")




proc validateGte*(value: Int256 | int, minimum: int, title: string = "Value") =
if value.i256 < minimum.i256:
raise newException(ValidationError,
&"{title} {value} is not greater than or equal to {minimum}")

proc validateGt*(value: Int256 | int, minimum: int, title: string = "Value") =
if value.i256 <= minimum.i256:
raise newException(ValidationError,
&"{title} {value} is not greater than {minimum}")

proc validateGte*(value: Int256, minimum: int, title: string = "Value") =
if value <= minimum.int256:
proc validateLength*[T](values: seq[T], size: int) =
if values.len != size:
raise newException(ValidationError,
fmt"{title} {value} is not greater than or equal to {minimum}")
&"seq expected {size} len, got {values.len}")

proc validateGte*(value: int, minimum: int, title: string = "Value") =
if value <= minimum:
proc validateLte*(value: Int256 | int, maximum: int, title: string = "Value") =
if value.i256 > maximum.i256:
raise newException(ValidationError,
fmt"{title} {value} is not greater than or equal to {minimum}")
&"{title} {value} is not less or equal to {maximum}")

proc validateGt*(value: Int256, minimum: int, title: string = "Value") =
if value < minimum.int256:
proc validateLt*(value: Int256 | int, maximum: int, title: string = "Value") =
if value.i256 >= maximum.i256:
raise newException(ValidationError,
fmt"{title} {value} is not greater than or equal to {minimum}")
&"{title} {value} is not less than {maximum}")

proc validateGt*(value: int, minimum: int, title: string = "Value") =
if value < minimum:
proc validateStackItem*(value: string) =
if value.len > 32:
raise newException(ValidationError,
fmt"{title} {value} is not greater than or equal to {minimum}")
&"Invalid stack item: expected 32 bytes, got {value.len}: value is {value}")
16 changes: 14 additions & 2 deletions src/vm/memory.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ proc newMemory*: Memory =
result.logger = logging.getLogger("evm.vm.memory.Memory")

proc len*(memory: Memory): int =
result = len(memory.bytes)
result = memory.bytes.len

proc extend*(memory: var Memory; startPosition: Int256; size: Int256) =
if size == 0:
Expand All @@ -32,7 +32,19 @@ proc read*(memory: var Memory, startPosition: Int256, size: Int256): seq[byte] =
result = memory.bytes[startPosition.getInt ..< (startPosition + size).getInt]

proc write*(memory: var Memory, startPosition: Int256, size: Int256, value: seq[byte]) =
echo value
if size == 0:
return
validateGte(startPosition, 0)
validateGte(size, 0)
validateLength(value, size.getInt)
validateLte(startPosition + size, memory.len)

let index = memory.len
if memory.len.i256 < startPosition + size:
memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPosition + size).getInt)) # TODO: better logarithmic scaling?

for z, b in value:
memory.bytes[z + startPosition.getInt] = b

template write*(memory: var Memory, startPosition: Int256, size: Int256, value: cstring) =
memory.write(startPosition, size, value.toBytes)
15 changes: 11 additions & 4 deletions src/vm/stack.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,31 @@ proc len*(stack: Stack): int =
proc push*(stack: var Stack; value: Value) =
## Push an item onto the stack
ensureStackLimit()
if value.kind == VInt:
validateGte(value.i, 0)
else:
validateStackItem(value.b)

stack.values.add(value)

proc push*(stack: var Stack; value: int) =
## Push an integer onto the stack
ensureStackLimit()

validateGte(value, 0)

stack.values.add(Value(kind: VInt, i: value.int256))

proc push*(stack: var Stack; value: Int256) =
## Push an integer onto the stack
ensureStackLimit()
validateGte(value, 0)

stack.values.add(Value(kind: VInt, i: value))

proc push*(stack: var Stack; value: string) =
## Push a binary onto the stack
ensureStackLimit()
validateStackItem(value)

stack.values.add(Value(kind: VBinary, b: value))

Expand Down Expand Up @@ -159,10 +166,10 @@ proc swap*(stack: var Stack; position: int) =
raise newException(InsufficientStack,
&"Insufficient stack items for SWAP{position}")

proc dup*(stack: var Stack; position: int) =
proc dup*(stack: var Stack; position: int | Int256) =
## Perform a DUP operation on the stack
if position < len(stack) + 1:
stack.push(stack.values[^position])
if (position != 0 and position.getInt < stack.len + 1) or (position == 0 and position.getInt < stack.len):
stack.push(stack.values[^position.getInt])
else:
raise newException(InsufficientStack,
&"Insufficient stack items for DUP{position}")
Expand Down
8 changes: 8 additions & 0 deletions src/vm/value.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ proc vint*(i: Int256): Value =
proc vbinary*(b: string): Value =
Value(kind: VBinary, b: b)

proc `==`*(a: Value, b: Value): bool =
if a.kind != b.kind:
return false
case a.kind:
of VInt:
a.i == b.i
of VBinary:
a.b == b.b
61 changes: 61 additions & 0 deletions tests/memory_test.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import unittest, macros, strformat, strutils, sequtils, constants, opcode_values, errors, vm / memory, bigints

proc memory32: Memory =
result = newMemory()
result.extend(0.i256, 32.i256)

proc memory128: Memory =
result = newMemory()
result.extend(0.i256, 128.i256)

suite "memory":
test "write":
var mem = memory32()
# Test that write creates 32byte string == value padded with zeros
mem.write(startPosition = 0.i256, size = 4.i256, value = @[1.byte, 0.byte, 1.byte, 0.byte])
check(mem.bytes == @[1.byte, 0.byte, 1.byte, 0.byte].concat(repeat(0.byte, 28)))

test "write rejects invalid position":
expect(ValidationError):
var mem = memory32()
mem.write(startPosition = -1.i256, size = 2.i256, value = @[1.byte, 0.byte])
expect(ValidationError):
var mem = memory32()
mem.write(startPosition = pow(2.i256, 256), size = 2.i256, value = @[1.byte, 0.byte])

test "write rejects invalid size":
expect(ValidationError):
var mem = memory32()
mem.write(startPosition = 0.i256, size = -1.i256, value = @[1.byte, 0.byte])
expect(ValidationError):
var mem = memory32()
mem.write(startPosition = 0.i256, size = pow(2.i256, 256), value = @[1.byte, 0.byte])

test "write rejects invalid value":
expect(ValidationError):
var mem = memory32()
mem.write(startPosition = 0.i256, size = 4.i256, value = @[1.byte, 0.byte])

test "write rejects valyes beyond memory size":
expect(ValidationError):
var mem = memory128()
mem.write(startPosition = 128.i256, size = 4.i256, value = @[1.byte, 0.byte, 1.byte, 0.byte])

test "extends appropriately extends memory":
var mem = newMemory()
# Test extends to 32 byte array: 0 < (start_position + size) <= 32
mem.extend(startPosition = 0.i256, size = 10.i256)
check(mem.bytes == repeat(0.byte, 32))
# Test will extend past length if params require: 32 < (start_position + size) <= 64
mem.extend(startPosition = 28.i256, size = 32.i256)
check(mem.bytes == repeat(0.byte, 64))
# Test won't extend past length unless params require: 32 < (start_position + size) <= 64
mem.extend(startPosition = 48.i256, size = 10.i256)
check(mem.bytes == repeat(0.byte, 64))

test "read returns correct bytes":
var mem = memory32()
mem.write(startPosition = 5.i256, size = 4.i256, value = @[1.byte, 0.byte, 1.byte, 0.byte])
check(mem.read(startPosition = 5.i256, size = 4.i256) == @[1.byte, 0.byte, 1.byte, 0.byte])
check(mem.read(startPosition = 6.i256, size = 4.i256) == @[0.byte, 1.byte, 0.byte, 0.byte])
check(mem.read(startPosition = 1.i256, size = 3.i256) == @[0.byte, 0.byte, 0.byte])
77 changes: 77 additions & 0 deletions tests/stack_test.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import unittest, macros, strformat, strutils, sequtils, constants, opcode_values, errors, vm / [stack, value], bigints

suite "stack":
test "push only valid":
for value in @[0.vint, (pow(2.i256, 256) - 1.i256).vint, "ves".vbinary]:
var stack = newStack()
stack.push(value)
check(stack.values == @[value])

for value in @[(-1).vint, (-2).vint, "yzyzyzyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz".vbinary]:
var stack = newStack()
expect(ValidationError):
stack.push(value)

test "push does not allow stack to exceed 1024":
var stack = newStack()
for z in 0 .. < 1024:
stack.push(z)
check(stack.len == 1024)
expect(FullStack):
stack.push(1025)

test "dup does not allow stack to exceed 1024":
var stack = newStack()
stack.push(1.i256)
for z in 0 ..< 1023:
stack.dup(1.i256)
check(stack.len == 1024)
expect(FullStack):
stack.dup(1.i256)

test "pop returns latest stack item":
var stack = newStack()
for element in @[1.vint, 2.vint, 3.vint]:
stack.push(element)
check(stack.popInt == 3)

stack = newStack()
for element in @["1".vbinary]:
stack.push(element)
check(stack.popBinary == "1")


test "swap correct":
var stack = newStack()
for z in 0 ..< 5:
stack.push(z)
check(stack.values == @[0.vint, 1.vint, 2.vint, 3.vint, 4.vint])
stack.swap(3)
check(stack.values == @[0.vint, 4.vint, 2.vint, 3.vint, 1.vint])
stack.swap(1)
check(stack.values == @[0.vint, 4.vint, 2.vint, 1.vint, 3.vint])

test "dup correct":
var stack = newStack()
for z in 0 ..< 5:
stack.push(z)
check(stack.values == @[0.vint, 1.vint, 2.vint, 3.vint, 4.vint])
stack.dup(1)
check(stack.values == @[0.vint, 1.vint, 2.vint, 3.vint, 4.vint, 4.vint])
stack.dup(5)
check(stack.values == @[0.vint, 1.vint, 2.vint, 3.vint, 4.vint, 4.vint, 1.vint])

test "pop raises InsufficientStack appropriately":
var stack = newStack()
expect(InsufficientStack):
discard stack.popInt()

test "swap raises InsufficientStack appropriately":
var stack = newStack()
expect(InsufficientStack):
stack.swap(0)

test "dup raises InsufficientStack appropriately":
var stack = newStack()
expect(InsufficientStack):
stack.dup(0)
5 changes: 5 additions & 0 deletions tests/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@

nim c tests/code_stream_test.nim
nim c tests/gas_meter_test.nim
nim c tests/memory_test.nim
nim c tests/stack_test.nim
./tests/code_stream_test
./tests/gas_meter_test
./tests/memory_test
./tests/stack_test

0 comments on commit 211d46e

Please sign in to comment.