diff --git a/eth/common/eth_types.nim b/eth/common/eth_types.nim index 28fbd93..840a7c6 100644 --- a/eth/common/eth_types.nim +++ b/eth/common/eth_types.nim @@ -1,5 +1,6 @@ import - stew/endians2, options, times, chronicles, + strutils, + stew/[endians2, byteutils], options, times, chronicles, stint, nimcrypto/[keccak, hash], eth/rlp, eth/trie/[trie_defs, db] export @@ -125,6 +126,7 @@ type receiptRoot*: Hash256 blockNumber*: BlockNumber + # TODO: Make BlockNumber a uint64 and deprecate either this or BlockHashOrNumber HashOrNum* = object case isHash*: bool of true: @@ -132,6 +134,13 @@ type else: number*: BlockNumber + BlockHashOrNumber* = object + case isHash*: bool + of true: + hash*: Hash256 + else: + number*: uint64 + BlocksRequest* = object startBlock*: HashOrNum maxResults*, skip*: uint @@ -301,6 +310,24 @@ proc read*(rlp: var Rlp, T: typedesc[HashOrStatus]): T {.inline.} = raise newException(RlpTypeMismatch, "HashOrStatus expected, but the source RLP is not a blob of right size.") +func init*(T: type BlockHashOrNumber, str: string): T + {.raises: [ValueError, Defect].} = + if str.startsWith "0x": + if str.len != sizeof(result.hash.data) * 2 + 2: + raise newException(ValueError, "Block hash has incorrect length") + + result.isHash = true + hexToByteArray(str, result.hash.data) + else: + result.isHash = false + result.number = parseBiggestUInt str + +func `$`*(x: BlockHashOrNumber): string = + if x.isHash: + "0x" & x.hash.data.toHex + else: + $x.number + proc append*(rlpWriter: var RlpWriter, value: HashOrStatus) {.inline.} = if value.isHash: rlpWriter.append(value.hash) diff --git a/eth/common/eth_types_json_serialization.nim b/eth/common/eth_types_json_serialization.nim index 24a910f..6b027cf 100644 --- a/eth/common/eth_types_json_serialization.nim +++ b/eth/common/eth_types_json_serialization.nim @@ -2,6 +2,9 @@ import times, net, json_serialization, nimcrypto/[hash, utils], eth_types +export + json_serialization + {.push raises: [SerializationError, IOError, Defect].} proc writeValue*(w: var JsonWriter, a: MDigest) = @@ -42,3 +45,12 @@ proc writeValue*(w: var JsonWriter, value: HashOrNum) = w.writeField("number", value.number) w.endRecord() +proc writeValue*(w: var JsonWriter, value: BlockHashOrNumber) = + w.writeValue $value + +proc readValue*(r: var JsonReader, value: var BlockHashOrNumber) = + try: + value = init(BlockHashOrNumber, r.readValue(string)) + except ValueError: + r.raiseUnexpectedValue("A hex-encoded block hash or a decimal block number expected") + diff --git a/tests/common/test_eth_types.nim b/tests/common/test_eth_types.nim new file mode 100644 index 0000000..ebdf10c --- /dev/null +++ b/tests/common/test_eth_types.nim @@ -0,0 +1,51 @@ +import + unittest, + nimcrypto/hash, + serialization/testing/generic_suite, + ../../eth/common/[eth_types, eth_types_json_serialization] + +func `==`*(lhs, rhs: BlockHashOrNumber): bool = + if lhs.isHash != rhs.isHash: + return false + + if lhs.isHash: + lhs.hash == rhs.hash + else: + lhs.number == rhs.number + +suite "BlockHashOrNumber": + test "construction": + expect ValueError: + var x = BlockHashOrNumber.init "" + echo "An empty string should not produce the value ", x + + let x1 = BlockHashOrNumber.init "0" + check((not x1.isHash) and x1.number == 0) + + let x2 = BlockHashOrNumber.init "1241328" + check((not x1.isHash) and x2.number == 1241328) + + expect ValueError: + var x = BlockHashOrNumber.init "0x" + echo "An empty hash should not produce the value ", x + + expect ValueError: + var x = BlockHashOrNumber.init "0xff11" + echo "A shorter hash should not produce the value ", x + + expect ValueError: + var x = BlockHashOrNumber.init "0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c58z" + echo "An invalid hash should not produce the value ", x + + expect ValueError: + var x = BlockHashOrNumber.init "0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c58811" + echo "A longer hash should not produce the value ", x + + test "serialization": + let hash = Hash256.fromHex "0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588" + + Json.roundtripTest BlockHashOrNumber(isHash: true, hash: hash), + "\"0x7a64245f7f95164f6176d90bd4903dbdd3e5433d555dd1385e81787f9672c588\"" + + Json.roundtripTest BlockHashOrNumber(isHash: false, number: 1209231231), + "\"1209231231\""