Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make BlockNumber distinct #137

Merged
merged 12 commits into from
Mar 16, 2024
21 changes: 13 additions & 8 deletions tests/test_contracts.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2018-2023 Status Research & Development GmbH
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -213,8 +213,9 @@ suite "Contracts":
receipt = await web3.deployContract(MetaCoinCode)
cc = receipt.contractAddress.get

let deployedAtBlock = distinctBase(receipt.blockNumber)
echo "Deployed MetaCoin contract: ", cc, " at block ", deployedAtBlock
let deployedAtBlock = receipt.blockNumber
echo "Deployed MetaCoin contract: ", cc, " at block ",
distinctBase(deployedAtBlock)

let ns = web3.contractSender(MetaCoin, cc)

Expand All @@ -225,7 +226,8 @@ suite "Contracts":
fromAddr, toAddr: Address, value: UInt256)
{.raises: [], gcsafe.}:
try:
echo "onTransfer: ", fromAddr, " transferred ", value.toHex, " to ", toAddr
echo "onTransfer: ", fromAddr, " transferred ", value.toHex,
" to ", toAddr
inc notificationsReceived
assert(fromAddr == web3.defaultAccount)
assert((notificationsReceived == 1 and value == 50.u256) or
Expand All @@ -238,12 +240,15 @@ suite "Contracts":

let balNow = await ns.getBalance(web3.defaultAccount).call()
echo "getbalance (now): ", balNow.toHex
let balNew = await ns.getBalance(web3.defaultAccount).call(blockNumber = deployedAtBlock)
let balNew = await ns.getBalance(web3.defaultAccount).call(
blockNumber = deployedAtBlock)
echo "getbalance (after creation): ", balNew.toHex

# Let's try to get the balance at a point in time where the contract was not deployed yet:
# Let's try to get the balance at a point in time where the contract
# was not deployed yet:
try:
let balFirst = await ns.getBalance(web3.defaultAccount).call(blockNumber = 1'u64)
let balFirst = await ns.getBalance(web3.defaultAccount).call(
blockNumber = 1.BlockNumber)
echo "getbalance (first block): ", balFirst.toHex
except CatchableError as err:
echo "getbalance (first block): ", err.msg
Expand All @@ -261,7 +266,7 @@ suite "Contracts":
echo "transfers: ", await ns.getJsonLogs(
Transfer,
fromBlock = some(blockId(deployedAtBlock)),
toBlock = some(blockId(1000'u64)))
toBlock = some(blockId(1000.BlockNumber)))

await notifFut
await s.unsubscribe()
Expand Down
4 changes: 2 additions & 2 deletions tests/test_deposit_contract.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2018-2023 Status Research & Development GmbH
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -43,7 +43,7 @@ suite "Deposit contract":

var fut = newFuture[void]()

let options = FilterOptions(fromBlock: some(blockId(0)))
let options = FilterOptions(fromBlock: some(blockId(0.BlockNumber)))
let s = await ns.subscribe(DepositEvent, options) do (
pubkey: DynamicBytes[0, 48], withdrawalCredentials: DynamicBytes[0, 32], amount: DynamicBytes[0, 8], signature: DynamicBytes[0, 96], merkleTreeIndex: DynamicBytes[0, 8])
{.raises: [], gcsafe.}:
Expand Down
77 changes: 44 additions & 33 deletions tests/test_json_marshalling.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ proc rand[M,N](_: type DynamicBytes[M,N]): DynamicBytes[M,N] =
proc rand(_: type Address): Address =
discard randomBytes(distinctBase result)

proc rand(_: type Quantity): Quantity =
proc rand[T: Quantity | BlockNumber](_: type T): T =
var res: array[8, byte]
discard randomBytes(res)
result = Quantity(uint64.fromBytesBE(res))
result = T(uint64.fromBytesBE(res))

proc rand(_: type RlpEncodedBytes): RlpEncodedBytes =
discard randomBytes(distinctBase result)
Expand All @@ -57,7 +57,7 @@ proc rand(_: type UInt256): UInt256 =
result = UInt256.fromBytesBE(x)

proc rand(_: type RtBlockIdentifier): RtBlockIdentifier =
RtBlockIdentifier(kind: bidNumber, number: rand(Quantity).uint64)
RtBlockIdentifier(kind: bidNumber, number: rand(BlockNumber))

proc rand(_: type PayloadExecutionStatus): PayloadExecutionStatus =
var x: array[1, byte]
Expand Down Expand Up @@ -98,32 +98,37 @@ template checkRandomObject(T: type) =

suite "JSON-RPC Quantity":
test "Valid":
for (validQuantityStr, validQuantity) in [
("0x0", Quantity 0),
("0x123", Quantity 291),
("0x1234", Quantity 4660)]:
let validQuantityJson = JrpcConv.encode(validQuantityStr)
let resQuantity = JrpcConv.decode(validQuantityJson, Quantity)
let resUInt256 = JrpcConv.decode(validQuantityJson, UInt256)
let resUInt256Ref = JrpcConv.decode(validQuantityJson, ref UInt256)

check:
JrpcConv.decode(validQuantityJson, Quantity) == validQuantity
JrpcConv.encode(validQuantity) == validQuantityJson
resQuantity == validQuantity
resUInt256 == validQuantity.distinctBase.u256
resUInt256Ref[] == validQuantity.distinctBase.u256

test "Invalid Quantity/UInt256/ref UInt256":
template checkType(typeName: typedesc): untyped =
for (validStr, validValue) in [
("0x0", typeName 0),
("0x123", typeName 291),
("0x1234", typeName 4660)]:
let
validJson = JrpcConv.encode(validStr)
res = JrpcConv.decode(validJson, typeName)
resUInt256 = JrpcConv.decode(validJson, UInt256)
resUInt256Ref = JrpcConv.decode(validJson, ref UInt256)

check:
JrpcConv.decode(validJson, typeName) == validValue
JrpcConv.encode(validValue) == validJson
res == validValue
resUInt256 == validValue.distinctBase.u256
resUInt256Ref[] == validValue.distinctBase.u256

checkType(Quantity)
checkType(BlockNumber)

test "Invalid Quantity/BlockNumber/UInt256/ref UInt256":
# TODO once https://github.com/status-im/nimbus-eth2/pull/3850 addressed,
# re-add "0x0400" test case as invalid.
for invalidStr in [
"", "1234", "01234", "x1234", "0x", "ff"]:
template checkInvalids(typeName: untyped) =
var resQuantity: `typeName`
var res: `typeName`
try:
let jsonBytes = JrpcConv.encode(invalidStr)
resQuantity = JrpcConv.decode(jsonBytes, `typeName`)
res = JrpcConv.decode(jsonBytes, `typeName`)
echo `typeName`, " ", invalidStr
check: false
except SerializationError:
Expand All @@ -132,6 +137,7 @@ suite "JSON-RPC Quantity":
check: false

checkInvalids(Quantity)
checkInvalids(BlockNumber)
checkInvalids(UInt256)
checkInvalids(ref UInt256)

Expand Down Expand Up @@ -189,11 +195,11 @@ suite "JSON-RPC Quantity":
checkRandomObject(GetPayloadResponse)

test "check blockId":
let a = RtBlockIdentifier(kind: bidNumber, number: 77.uint64)
let a = RtBlockIdentifier(kind: bidNumber, number: 77.BlockNumber)
let x = JrpcConv.encode(a)
let c = JrpcConv.decode(x, RtBlockIdentifier)
check c.kind == bidNumber
check c.number == 77
check c.number == 77.BlockNumber

let d = JrpcConv.decode("\"10\"", RtBlockIdentifier)
check d.kind == bidAlias
Expand All @@ -210,15 +216,20 @@ suite "JSON-RPC Quantity":
check c.kind == slkNull

test "quantity parser and writer":
let a = JrpcConv.decode("\"0x016345785d8a0000\"", Quantity)
check a.uint64 == 100_000_000_000_000_000'u64
let b = JrpcConv.encode(a)
check b == "\"0x16345785d8a0000\""

let x = JrpcConv.decode("\"0xFFFF_FFFF_FFFF_FFFF\"", Quantity)
check x.uint64 == 0xFFFF_FFFF_FFFF_FFFF_FFFF'u64
let y = JrpcConv.encode(x)
check y == "\"0xffffffffffffffff\""
template checkType(typeName: typedesc): untyped =
block:
let a = JrpcConv.decode("\"0x016345785d8a0000\"", typeName)
check a.uint64 == 100_000_000_000_000_000'u64
let b = JrpcConv.encode(a)
check b == "\"0x16345785d8a0000\""

let x = JrpcConv.decode("\"0xFFFF_FFFF_FFFF_FFFF\"", typeName)
check x.uint64 == 0xFFFF_FFFF_FFFF_FFFF_FFFF'u64
let y = JrpcConv.encode(x)
check y == "\"0xffffffffffffffff\""

checkType(Quantity)
checkType(BlockNumber)

test "AccessListResult":
var z: AccessListResult
Expand Down
4 changes: 2 additions & 2 deletions tests/test_logs.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2018-2023 Status Research & Development GmbH
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -74,7 +74,7 @@ suite "Logs":
let notifFut = newFuture[void]()
var notificationsReceived = 0

let options = FilterOptions(fromBlock: some(blockId(0)))
let options = FilterOptions(fromBlock: some(blockId(0.BlockNumber)))
let s = await ns.subscribe(MyEvent, options) do (
sender: Address, value: UInt256)
{.raises: [], gcsafe.}:
Expand Down
50 changes: 32 additions & 18 deletions web3.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2019-2023 Status Research & Development GmbH
# Copyright (c) 2019-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -50,7 +50,7 @@ type
gas*: uint64
gasPrice*: int
chainId*: Option[ChainId]
blockNumber*: uint64
blockNumber*: BlockNumber

Sender*[T] = ContractInstance[T, Web3SenderImpl]
AsyncSender*[T] = ContractInstance[T, Web3AsyncSenderImpl]
Expand Down Expand Up @@ -354,29 +354,31 @@ proc send*[T](c: ContractInvocation[T, Web3SenderImpl],
gasPrice = 0): Future[TxHash] =
sendData(c.sender.web3, c.sender.contractAddress, c.sender.web3.defaultAccount, c.data, value, gas, gasPrice, some(chainId))

proc callAux(web3: Web3,
contractAddress: Address,
defaultAccount: Address,
data: seq[byte],
value = 0.u256,
gas = 3000000'u64,
blockNumber = high(uint64)): Future[seq[byte]] {.async.} =
proc callAux(
web3: Web3,
contractAddress: Address,
defaultAccount: Address,
data: seq[byte],
value = 0.u256,
gas = 3000000'u64,
blockNumber = high(BlockNumber)): Future[seq[byte]] {.async.} =
var cc: EthCall
cc.data = some(data)
cc.source = some(defaultAccount)
cc.to = some(contractAddress)
cc.gas = some(Quantity(gas))
cc.value = some(value)
result =
if blockNumber != high(uint64):
if blockNumber != high(BlockNumber):
await web3.provider.eth_call(cc, blockId(blockNumber))
else:
await web3.provider.eth_call(cc, "latest")

proc call*[T](c: ContractInvocation[T, Web3SenderImpl],
value = 0.u256,
gas = 3000000'u64,
blockNumber = high(uint64)): Future[T] {.async.} =
proc call*[T](
c: ContractInvocation[T, Web3SenderImpl],
value = 0.u256,
gas = 3000000'u64,
blockNumber = high(BlockNumber)): Future[T] {.async.} =
let response = await callAux(c.sender.web3, c.sender.contractAddress, c.sender.web3.defaultAccount, c.data, value, gas, blockNumber)
if response.len > 0:
discard decode(response, 0, 0, result)
Expand Down Expand Up @@ -436,17 +438,29 @@ proc createMutableContractInvocation*(sender: Web3SenderImpl, ReturnType: typede
proc createImmutableContractInvocation*(sender: Web3SenderImpl, ReturnType: typedesc, data: sink seq[byte]): ContractInvocation[ReturnType, Web3SenderImpl] {.inline.} =
ContractInvocation[ReturnType, Web3SenderImpl](sender: sender, data: data)

proc contractInstance*(web3: Web3, T: typedesc, toAddress: Address): AsyncSender[T] =
AsyncSender[T](sender: Web3AsyncSenderImpl(web3: web3, contractAddress: toAddress, defaultAccount: web3.defaultAccount, gas: 3000000, blockNumber: uint64.high))
proc contractInstance*(
web3: Web3, T: typedesc, toAddress: Address): AsyncSender[T] =
AsyncSender[T](
sender: Web3AsyncSenderImpl(
web3: web3,
contractAddress: toAddress,
defaultAccount: web3.defaultAccount,
gas: 3000000,
blockNumber: BlockNumber.high))

proc createMutableContractInvocation*(sender: Web3AsyncSenderImpl, ReturnType: typedesc, data: sink seq[byte]) {.async.} =
assert(sender.gas > 0)
let h = await sendData(sender.web3, sender.contractAddress, sender.defaultAccount, data, sender.value, sender.gas, sender.gasPrice, sender.chainId)
let receipt = await sender.web3.getMinedTransactionReceipt(h)
discard receipt

proc createImmutableContractInvocation*(sender: Web3AsyncSenderImpl, ReturnType: typedesc, data: sink seq[byte]): Future[ReturnType] {.async.} =
let response = await callAux(sender.web3, sender.contractAddress, sender.defaultAccount, data, sender.value, sender.gas, sender.blockNumber)
proc createImmutableContractInvocation*(
sender: Web3AsyncSenderImpl,
ReturnType: typedesc,
data: sink seq[byte]): Future[ReturnType] {.async.} =
let response = await callAux(
sender.web3, sender.contractAddress, sender.defaultAccount, data,
sender.value, sender.gas, sender.blockNumber)
if response.len > 0:
discard decode(response, 0, 0, result)
else:
Expand Down
17 changes: 12 additions & 5 deletions web3/conversions.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# nim-web3
# Copyright (c) 2019-2023 Status Research & Development GmbH
# Copyright (c) 2019-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
Expand Down Expand Up @@ -198,8 +198,9 @@ proc writeValue*[F: CommonJsonFlavors](w: var JsonWriter[F], v: RlpEncodedBytes)
{.gcsafe, raises: [IOError].} =
writeHexValue w, distinctBase(v)

proc writeValue*[F: CommonJsonFlavors](w: var JsonWriter[F], v: Quantity | BlockNumber)
{.gcsafe, raises: [IOError].} =
proc writeValue*[F: CommonJsonFlavors](
w: var JsonWriter[F], v: Quantity | BlockNumber
) {.gcsafe, raises: [IOError].} =
w.stream.write "\"0x"
w.stream.toHex(distinctBase v)
w.stream.write "\""
Expand Down Expand Up @@ -243,6 +244,11 @@ proc readValue*[F: CommonJsonFlavors](r: var JsonReader[F], val: var Quantity)
wrapValueError:
val = Quantity fromHex[uint64](hexStr)

proc readValue*[F: CommonJsonFlavors](
r: var JsonReader[F],
val: var BlockNumber) {.gcsafe, raises: [IOError, JsonReaderError].} =
r.readValue(distinctBase(val, recursive = false))

proc readValue*[F: CommonJsonFlavors](r: var JsonReader[F], val: var PayloadExecutionStatus)
{.gcsafe, raises: [IOError, JsonReaderError].} =
const enumStrings = static: getEnumStringTable(PayloadExecutionStatus)
Expand Down Expand Up @@ -300,7 +306,8 @@ proc readValue*(r: var JsonReader[JrpcConv], val: var RtBlockIdentifier)
let hexStr = r.parseString()
wrapValueError:
if valid(hexStr):
val = RtBlockIdentifier(kind: bidNumber, number: fromHex[uint64](hexStr))
val = RtBlockIdentifier(
kind: bidNumber, number: BlockNumber fromHex[uint64](hexStr))
else:
val = RtBlockIdentifier(kind: bidAlias, alias: hexStr)

Expand Down Expand Up @@ -385,7 +392,7 @@ proc writeValue*(w: var JsonWriter[JrpcConv], v: Opt[seq[ReceiptObject]])
else:
w.writeValue JsonString("null")

func `$`*(v: Quantity): string {.inline.} =
func `$`*(v: Quantity | BlockNumber): string {.inline.} =
encodeQuantity(v.uint64)

func `$`*(v: TypedTransaction): string {.inline.} =
Expand Down
Loading