Skip to content

Commit

Permalink
update for latest Optional spec
Browse files Browse the repository at this point in the history
EIP-6475 was bumped to emit an additional `0x01` byte in the `Some` case
of optionals to allow nesting `Optional` and `Optional[List]` and to
provide compatibility with `Union` serialization. Note that the `None`
case is still serialized as empty.

https://eips.ethereum.org/EIPS/eip-6475
ethereum/EIPs#6945
  • Loading branch information
etan-status committed Apr 27, 2023
1 parent 3f8946a commit 604d199
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 9 deletions.
3 changes: 2 additions & 1 deletion ssz_serialization.nim
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ proc writeVarSizeType(w: var SszWriter, value: auto) {.raises: [Defect, IOError]
writeSeq(w, bytes value)
elif value is OptionalType:
if value.isSome:
w.writeValue 1'u8
w.writeValue value.get
elif value is object|tuple|array:
when isCaseObject(type(value)):
Expand Down Expand Up @@ -243,7 +244,7 @@ func sszSize*(value: auto): int {.gcsafe, raises:[].} =

elif T is OptionalType:
if value.isSome:
sszSize(value.unsafeGet)
1 + sszSize(value.unsafeGet)
else:
0

Expand Down
7 changes: 6 additions & 1 deletion ssz_serialization/codec.nim
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,13 @@ proc readSszValue*[T](input: openArray[byte],
else:
val = Opt.none(E)
else:
var isSome: uint8
readSszValue(input.toOpenArray(0, 0), isSome)
if isSome != 1:
raiseMalformedSszError(
T, "Unexpected isSome " & $isSome & " (expected: 1)")
var v: E
readSszValue(input, v)
readSszValue(input.toOpenArray(1, input.len - 1), v)
when val is Option:
val = options.some(v)
else:
Expand Down
51 changes: 44 additions & 7 deletions tests/test_ssz_optional.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ proc doTest[T](name: string, value: Option[T] | Opt[T]) =
test name:
const isUnsupported =
when T is object:
when T.isCaseObject():
when T is OptionalType:
false
elif T.isCaseObject():
true
else:
false
Expand All @@ -36,10 +38,14 @@ proc doTest[T](name: string, value: Option[T] | Opt[T]) =
value.hash_tree_root(2.GeneralizedIndex).get == zeroHashes[0]
value.hash_tree_root(3.GeneralizedIndex).get == zeroHashes[0]
else:
let v = value.unsafeGet
let
v = value.unsafeGet
encoded = SSZ.encode(value)
check:
SSZ.encode(value) == SSZ.encode(v)
sszSize(value) == SSZ.encode(value).len
encoded.len >= 1
encoded[0] == 0x01
encoded[1 ..< encoded.len] == SSZ.encode(v)
sszSize(value) == encoded.len
value.hash_tree_root() == List[T, 1](@[v]).hash_tree_root()
value.hash_tree_root(1.GeneralizedIndex).get == value.hash_tree_root()
value.hash_tree_root(2.GeneralizedIndex).get == v.hash_tree_root()
Expand Down Expand Up @@ -91,6 +97,19 @@ suite "SSZ Optional":
testCase "boolean - Some",
Opt.some(bool(true))

testCase "Optional - None (1)",
Opt.none(Opt[uint64])
testCase "Optional - None (2)",
Opt.none(Option[uint64])
testCase "Optional - Some(None) (1)",
Opt.some(Opt.none(uint64))
testCase "Optional - Some(None) (2)",
Opt.some(options.none(uint64))
testCase "Optional - Some(Some) (1)",
Opt.some(Opt.some(uint64(64)))
testCase "Optional - Some(Some) (2)",
Opt.some(options.some(uint64(64)))

type Foo = object
a: uint64
b: Opt[uint32]
Expand All @@ -117,11 +136,29 @@ suite "SSZ Optional":
testCase "List - None (1)",
Opt.none(List[uint64, 1])
testCase "List - Some (1)",
Opt.some(List[uint64, 1](@[uint64(64)]))
Opt.some(List[uint64, 1](@[]))
testCase "List - Some (2)",
Opt.some(List[Foo, 1](@[Foo(a: 64, b: Opt.some(uint32(32)))]))
Opt.some(List[uint64, 1](@[uint64(64)]))
testCase "List - Some (3)",
Opt.some(List[Foo, 1](@[Foo(a: 64, b: Opt.some(uint32(32)), c: options.some(uint16(16)))]))
Opt.some(List[uint64, 5](@[uint64(64), 64]))
testCase "List - None (2)",
Opt.none(List[Opt[uint64], 9])
testCase "List - None (3)",
Opt.none(List[Option[uint64], 9])
testCase "List - Some (4)",
Opt.some(List[Opt[uint64], 9](@[
Opt.none(uint64), Opt.some(uint64(64))]))
testCase "List - Some (5)",
Opt.some(List[Option[uint64], 9](@[
options.none(uint64), options.some(uint64(64))]))
testCase "List - Some (6)",
Opt.some(List[Foo, 1](@[Foo(a: 64, b: Opt.some(uint32(32)))]))
testCase "List - Some (7)",
Opt.some(List[Opt[Foo], 1](@[Opt[Foo].some(
Foo(a: 64, b: Opt.some(uint32(32)), c: options.some(uint16(16))))]))
testCase "List - Some (8)",
Opt.some(List[Option[Foo], 1](@[options.some(
Foo(a: 64, b: Opt.some(uint32(32)), c: options.some(uint16(16))))]))

testCase "Bitvector - None (1)",
Opt.none(BitArray[1])
Expand Down

0 comments on commit 604d199

Please sign in to comment.