nim-ethers/testmodule/providers/jsonrpc/testJsonRpcSigner.nim
Eric b68bea9909
fix: modify unsubscribe cleanup routine and tests (#84)
* fix: modify unsubscribe cleanup routine

Ignore exceptions (other than CancelledError) if uninstallation of the filter fails. If it's the last step in the subscription cleanup, then filter changes for this filter will no longer be polled so if the filter continues to live on in geth for whatever reason, then it doesn't matter.

This includes a number of fixes:
- `CancelledError` is now caught inside of `getChanges`. This was causing conditions during `subscriptions.close`, where the `CancelledError` would get consumed by the `except CatchableError`, if there was an ongoing `poll` happening at the time of close.
- After creating a new filter inside of `getChanges`, the new filter is polled for changes before returning.
- `getChanges` also does not swallow `CatchableError` by returning an empty array, and instead re-raises the error if it is not `filter not found`.
- The tests were simplified by accessing the private fields of `PollingSubscriptions`. That way, there wasn't a race condition for the `newFilterId` counter inside of the mock.
- The `MockRpcHttpServer` was simplified by keeping track of the active filters only, and invalidation simply removes the filter. The tests then only needed to rely on the fact that the filter id changed in the mapping.
- Because of the above changes, we no longer needed to sleep inside of the tests, so the sleeps were removed, and the polling interval could be changed to 1ms, which not only makes the tests faster, but would further highlight any race conditions if present.

* docs: rpc custom port documentation

---------

Co-authored-by: Adam Uhlíř <adam@uhlir.dev>
2024-10-25 14:58:45 +11:00

108 lines
3.8 KiB
Nim

import std/os
import pkg/asynctest
import pkg/ethers
import pkg/stew/byteutils
import ../../examples
suite "JsonRpcSigner":
var provider: JsonRpcProvider
var accounts: seq[Address]
let providerUrl = getEnv("ETHERS_TEST_PROVIDER", "localhost:8545")
setup:
provider = JsonRpcProvider.new("http://" & providerUrl)
accounts = await provider.listAccounts()
teardown:
await provider.close()
test "is connected to the first account of the provider by default":
let signer = provider.getSigner()
check (await signer.getAddress()) == accounts[0]
test "can connect to a different account":
let signer = provider.getSigner(accounts[1])
check (await signer.getAddress()) == accounts[1]
test "can retrieve gas price":
let signer = provider.getSigner()
let gasprice = await signer.getGasPrice()
check gasprice > 0.u256
test "can retrieve transaction count":
let signer = provider.getSigner(accounts[9])
let count = await signer.getTransactionCount(BlockTag.pending)
check count == 0.u256
test "can estimate gas cost of a transaction":
let signer = provider.getSigner()
let estimate = await signer.estimateGas(Transaction.example)
check estimate > 0.u256
test "can retrieve chain id":
let signer = provider.getSigner()
let chainId = await signer.getChainId()
check chainId == 31337.u256 # hardhat chain id
test "can sign messages":
let signer = provider.getSigner()
let message = "hello".toBytes
check (await signer.signMessage(message)).len == 65
test "can populate missing fields in a transaction":
let signer = provider.getSigner()
let transaction = Transaction.example
let populated = await signer.populateTransaction(transaction)
check !populated.sender == await signer.getAddress()
check !populated.gasPrice == await signer.getGasPrice()
check !populated.nonce == await signer.getTransactionCount(BlockTag.pending)
check !populated.gasLimit == await signer.estimateGas(transaction)
check !populated.chainId == await signer.getChainId()
test "populate does not overwrite existing fields":
let signer = provider.getSigner()
var transaction = Transaction.example
transaction.sender = some await signer.getAddress()
transaction.nonce = some UInt256.example
transaction.chainId = some await signer.getChainId()
transaction.gasPrice = some UInt256.example
transaction.gasLimit = some UInt256.example
let populated = await signer.populateTransaction(transaction)
check populated == transaction
test "populate fails when sender does not match signer address":
let signer = provider.getSigner()
var transaction = Transaction.example
transaction.sender = accounts[1].some
expect SignerError:
discard await signer.populateTransaction(transaction)
test "populate fails when chain id does not match":
let signer = provider.getSigner()
var transaction = Transaction.example
transaction.chainId = 0xdeadbeef.u256.some
expect SignerError:
discard await signer.populateTransaction(transaction)
test "concurrent populate calls increment nonce":
let signer = provider.getSigner()
let count = await signer.getTransactionCount(BlockTag.pending)
var transaction1 = Transaction.example
var transaction2 = Transaction.example
var transaction3 = Transaction.example
let populated = await allFinished(
signer.populateTransaction(transaction1),
signer.populateTransaction(transaction2),
signer.populateTransaction(transaction3)
)
transaction1 = await populated[0]
transaction2 = await populated[1]
transaction3 = await populated[2]
check !transaction1.nonce == count
check !transaction2.nonce == count + 1.u256
check !transaction3.nonce == count + 2.u256