Skip to content

Commit

Permalink
fix copyFrom for odd lengths
Browse files Browse the repository at this point in the history
  • Loading branch information
arnetheduck committed Sep 28, 2024
1 parent 899c0f7 commit 13f7102
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 22 deletions.
7 changes: 2 additions & 5 deletions eth/common/addresses.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,13 @@ template data*(v: Address): array[20, byte] =
template `data=`*[N: static int](a: FixedBytes[N], b: array[N, byte]) =
assign(distinctBase(a), b)

func copyFrom*(T: type Address, v: openArray[byte], start = 0): T =
template copyFrom*(T: type Address, v: openArray[byte], start = 0): T =
## Copy up to N bytes from the given openArray, starting at `start` and
## filling any missing bytes with zero.
##
## This is a lenient function in that `v` may contain both fewer and more
## bytes than N and start might be out of bounds.
if v.len > 0:
assign(
result.data, v.toOpenArray(min(start, v.len), min(start + sizeof(T), v.len()) - 1)
)
Address(Bytes20.copyFrom(v, start))

template default*(_: type Address): Address =
# Avoid bad codegen where fixed bytes are zeroed byte-by-byte at call site
Expand Down
12 changes: 7 additions & 5 deletions eth/common/base.nim
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ func copyFrom*[N: static int](T: type FixedBytes[N], v: openArray[byte], start =
##
## This is a lenient function in that `v` may contain both fewer and more
## bytes than N and start might be out of bounds.
if v.len > 0:
assign(
result.data, v.toOpenArray(min(start, v.len), min(start + sizeof(T), v.len()) - 1)
)
if v.len > start:
let n = min(N, v.len - start)
assign(result.data.toOpenArray(0, n - 1), v.toOpenArray(start, start + n - 1))

template default*[N](T: type FixedBytes[N]): T =
# Avoid bad codegen where fixed bytes are zeroed byte-by-byte at call site
Expand All @@ -97,13 +96,16 @@ func hash*[N: static int](v: FixedBytes[N]): Hash {.inline.} =
result !& tmp

func toHex*(v: FixedBytes): string =
## Convert to lowercase hex without 0x prefix
toHex(v.data)

func to0xHex*(v: FixedBytes): string =
## Convert to lowercase hex with 0x prefix
to0xHex(v.data)

func `$`*(v: FixedBytes): string =
# There's a strong tradition of including 0x in the execution layer
## Convert the given value to a string representation suitable for presentation
## To convert to a specific string encoding, use `toHex`, `to0xHex` etc
to0xHex(v)

func fromHex*(T: type FixedBytes, c: openArray[char]): T {.raises: [ValueError].} =
Expand Down
7 changes: 2 additions & 5 deletions eth/common/hashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,13 @@ template data*(v: Hash32): array[32, byte] =
template `data=`*(a: Hash32, b: array[32, byte]) =
assign(distinctBase(a), b)

func copyFrom*(T: type Hash32, v: openArray[byte], start = 0): T =
template copyFrom*(T: type Hash32, v: openArray[byte], start = 0): T =
## Copy up to N bytes from the given openArray, starting at `start` and
## filling any missing bytes with zero.
##
## This is a lenient function in that `v` may contain both fewer and more
## bytes than N and start might be out of bounds.
if v.len > 0:
assign(
result.data, v.toOpenArray(min(start, v.len), min(start + sizeof(T), v.len()) - 1)
)
Hash32(Bytes32.copyFrom(v, start))

template default*(_: type Hash32): Hash32 =
# Avoid bad codegen where fixed bytes are zeroed byte-by-byte at call site
Expand Down
45 changes: 38 additions & 7 deletions tests/common/test_eth_types.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{.used.}

import
unittest2, std/strutils,
unittest2,
std/strutils,
serialization/testing/generic_suite,
../../eth/common/[eth_types, eth_types_json_serialization],
../../eth/common/eth_types_rlp
Expand All @@ -15,8 +16,8 @@ func `==`*(lhs, rhs: BlockHashOrNumber): bool =
else:
lhs.number == rhs.number

const
testHash = hash32"0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588"
const testHash =
hash32"0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588"

suite "BlockHashOrNumber":
test "construction":
Expand Down Expand Up @@ -47,13 +48,14 @@ suite "BlockHashOrNumber":
echo "A longer hash should not produce the value ", x

test "serialization":
const hash = hash32"0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588"
const hash =
hash32"0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588"

Json.roundtripTest BlockHashOrNumber(isHash: true, hash: hash),
"\"0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588\""
"\"0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588\""

Json.roundtripTest BlockHashOrNumber(isHash: false, number: 1209231231),
"\"1209231231\""
"\"1209231231\""

suite "Block encodings":
test "EIP-4399 prevRandao field":
Expand All @@ -76,6 +78,14 @@ suite "Block encodings":
check dh.parentBeaconBlockRoot.get == testHash

suite "Address":
test "Bytes conversion":
let bytes =
bytes32"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
check:
bytes.to(Address) == address"ccddeeff00112233445566778899aabbccddeeff"
bytes.to(Address).to(Bytes32) ==
bytes32"000000000000000000000000ccddeeff00112233445566778899aabbccddeeff"

test "EIP-55 checksum":
let
cases = [
Expand All @@ -88,7 +98,7 @@ suite "Address":
"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB",
"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb",
]
fails = cases[4..7]
fails = cases[4 .. 7]

# https://eips.ethereum.org/EIPS/eip-55#test-cases
for s in cases:
Expand All @@ -98,3 +108,24 @@ suite "Address":
for s in fails:
check:
not Address.hasValidChecksum(s.toLowerAscii)

suite "Bytes":
test "copyFrom":
check:
Bytes4.copyFrom([]) == default(Bytes4)
Bytes4.copyFrom([byte 0]) == default(Bytes4)
Bytes4.copyFrom([byte 0, 0, 0, 0, 0]) == default(Bytes4)
Bytes4.copyFrom([byte 1, 0], 1) == default(Bytes4)
Bytes4.copyFrom([byte 1, 1], 2) == default(Bytes4)
Bytes4.copyFrom([byte 1, 1], 20) == default(Bytes4)

test "toHex":
check:
bytes4"0xaabbccdd".toHex == "aabbccdd"
bytes4"0xaabbccdd".to0xHex == "0xaabbccdd"

suite "Hashes":
test "constants":
check:
emptyKeccak256 == keccak256(default(array[0, byte]))
emptyRoot == keccak256([byte 128])

0 comments on commit 13f7102

Please sign in to comment.