Skip to content

Commit

Permalink
ssz: switch to little-endian
Browse files Browse the repository at this point in the history
  • Loading branch information
arnetheduck committed Jan 22, 2019
1 parent a9c026b commit 523a990
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 24 deletions.
36 changes: 18 additions & 18 deletions beacon_chain/ssz.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ from milagro_crypto import getRaw, fromRaw
# toBytesSSZ convert simple fixed-length types to their SSZ wire representation
func toBytesSSZ(x: SomeInteger): array[sizeof(x), byte] =
## Convert directly to bytes the size of the int. (e.g. ``uint16 = 2 bytes``)
## All integers are serialized as **big endian**.
## All integers are serialized as **little endian**.

when x.sizeof == 8: bigEndian64(result.addr, x.unsafeAddr)
elif x.sizeof == 4: bigEndian32(result.addr, x.unsafeAddr)
elif x.sizeof == 2: bigEndian16(result.addr, x.unsafeAddr)
when x.sizeof == 8: littleEndian64(result.addr, x.unsafeAddr)
elif x.sizeof == 4: littleEndian32(result.addr, x.unsafeAddr)
elif x.sizeof == 2: littleEndian16(result.addr, x.unsafeAddr)
elif x.sizeof == 1: copyMem(result.addr, x.unsafeAddr, sizeof(result))
else: {.fatal: "Unsupported type serialization: " & $(type(x)).name.}

func toBytesSSZ(x: Uint24): array[3, byte] =
## Integers are all encoded as bigendian and not padded
## Integers are all encoded as little endian and not padded
let v = x.uint32
result[2] = byte(v and 0xff)
result[0] = byte(v and 0xff)
result[1] = byte((v shr 8) and 0xff)
result[0] = byte((v shr 16) and 0xff)
result[2] = byte((v shr 16) and 0xff)

func toBytesSSZ(x: bool): array[1, byte] =
[if x: 1'u8 else: 0'u8]
Expand Down Expand Up @@ -74,7 +74,7 @@ func sszLen(v: seq | array): int =
# there's enough data in the buffer
func fromBytesSSZUnsafe(T: typedesc[SomeInteger], data: pointer): T =
## Convert directly to bytes the size of the int. (e.g. ``uint16 = 2 bytes``)
## All integers are serialized as **big endian**.
## All integers are serialized as **little endian**.
## TODO: Assumes data points to a sufficiently large buffer

# TODO: any better way to get a suitably aligned buffer in nim???
Expand All @@ -83,9 +83,9 @@ func fromBytesSSZUnsafe(T: typedesc[SomeInteger], data: pointer): T =
var alignedBuf = cast[ptr byte](tmp.addr)
copyMem(alignedBuf, data, result.sizeof)

when result.sizeof == 8: bigEndian64(result.addr, alignedBuf)
elif result.sizeof == 4: bigEndian32(result.addr, alignedBuf)
elif result.sizeof == 2: bigEndian16(result.addr, alignedBuf)
when result.sizeof == 8: littleEndian64(result.addr, alignedBuf)
elif result.sizeof == 4: littleEndian32(result.addr, alignedBuf)
elif result.sizeof == 2: littleEndian16(result.addr, alignedBuf)
elif result.sizeof == 1: copyMem(result.addr, alignedBuf, sizeof(result))
else: {.fatal: "Unsupported type deserialization: " & $(type(result)).name.}

Expand All @@ -95,12 +95,12 @@ func fromBytesSSZUnsafe(T: typedesc[bool], data: pointer): T =
fromBytesSSZUnsafe(uint8, data) != 0

func fromBytesSSZUnsafe(T: typedesc[Uint24], data: pointer): T =
## Integers are all encoded as bigendian and not padded
## Integers are all encoded as littleendian and not padded
var tmp: uint32
let p = cast[ptr UncheckedArray[byte]](data)
tmp = tmp or uint32(p[2])
tmp = tmp or uint32(p[0])
tmp = tmp or uint32(p[1]) shl 8
tmp = tmp or uint32(p[0]) shl 16
tmp = tmp or uint32(p[2]) shl 16
result = tmp.Uint24

func fromBytesSSZUnsafe(T: typedesc[EthAddress], data: pointer): T =
Expand Down Expand Up @@ -212,7 +212,7 @@ func serialize[T: not enum](dest: var seq[byte], src: T) =

# Write size (we only know it once we've serialized the object!)
var objLen = dest.len() - lenPos - 4
bigEndian32(dest[lenPos].addr, objLen.addr)
littleEndian32(dest[lenPos].addr, objLen.addr)

# ################### Core functions ###################################

Expand Down Expand Up @@ -262,12 +262,12 @@ func merkleHash[T](lst: openArray[T]): array[32, byte]

func hash_tree_root*(x: SomeInteger | bool): array[sizeof(x), byte] =
## Convert directly to bytes the size of the int. (e.g. ``uint16 = 2 bytes``)
## All integers are serialized as **big endian**.
## All integers are serialized as **little endian**.
toBytesSSZ(x)

func hash_tree_root*(x: Uint24): array[3, byte] =
## Convert directly to bytes the size of the int. (e.g. ``uint16 = 2 bytes``)
## All integers are serialized as **big endian**.
## All integers are serialized as **little endian**.
toBytesSSZ(x)

func hash_tree_root*(x: EthAddress): array[sizeof(x), byte] =
Expand Down Expand Up @@ -333,7 +333,7 @@ func merkleHash[T](lst: openArray[T]): array[32, byte] =
# Store length of list (to compensate for non-bijectiveness of padding)
var dataLen: array[32, byte]
var lstLen = uint64(len(lst))
bigEndian64(dataLen[32-8].addr, lstLen.addr)
littleEndian64(dataLen[32-8].addr, lstLen.addr)

# Divide into chunks
var chunkz: seq[seq[byte]]
Expand Down
12 changes: 6 additions & 6 deletions tests/test_ssz.nim
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ suite "Simple serialization":
)

var expected_ser = @[
byte 0, 0, 0, 67, # length
byte 67, 0, 0, 0, # length
5,
'\xFF'.ord, '\xFF'.ord, '\xFF'.ord, '\xFD'.ord,
0xFD, 0xFF, 0xFF, 0xFF,
]
expected_ser &= EthAddress.filled(byte 35)
expected_ser &= MDigest[256].filled(byte 35).data
expected_ser &= [byte 0, 0, 0, 3, 'c'.ord, 'o'.ord, 'w'.ord]
expected_ser &= [byte 0, 0, 79]
expected_ser &= [byte 3, 0, 0, 0, 'c'.ord, 'o'.ord, 'w'.ord]
expected_ser &= [byte 79, 0, 0]

test "Object deserialization":
let deser = expected_ser.deserialize(Foo).get()
Expand Down Expand Up @@ -119,5 +119,5 @@ suite "Tree hashing":
check: hash_tree_root(bb).len > 0

test "Hash integer":
check: hash_tree_root(0x01'u32) == [0'u8, 0, 0, 1] # big endian!
check: hash_tree_root(Uint24(0x01)) == [0'u8, 0, 1] # big endian!
check: hash_tree_root(0x01'u32) == [1'u8, 0, 0, 0] # little endian!
check: hash_tree_root(Uint24(0x01)) == [1'u8, 0, 0] # little endian!

0 comments on commit 523a990

Please sign in to comment.