Test JSON-RPC Provider with polling

This commit is contained in:
Mark Spanbroek 2023-06-27 15:05:15 +02:00 committed by markspanbroek
parent 0322ae1451
commit 88d60b14b0

View File

@ -7,126 +7,123 @@ import pkg/stew/byteutils
import ../../examples import ../../examples
import ../../miner import ../../miner
suite "JsonRpcProvider": for url in ["ws://localhost:8545", "http://localhost:8545"]:
var provider: JsonRpcProvider suite "JsonRpcProvider (" & url & ")":
setup: var provider: JsonRpcProvider
provider = JsonRpcProvider.new("ws://localhost:8545")
test "can be instantiated with a default URL": setup:
discard JsonRpcProvider.new() provider = JsonRpcProvider.new(url)
test "can be instantiated with an HTTP URL": test "can be instantiated with a default URL":
discard JsonRpcProvider.new("http://localhost:8545") discard JsonRpcProvider.new()
test "can be instantiated with a websocket URL": test "lists all accounts":
discard JsonRpcProvider.new("ws://localhost:8545") let accounts = await provider.listAccounts()
check accounts.len > 0
test "lists all accounts": test "sends raw messages to the provider":
let accounts = await provider.listAccounts() let response = await provider.send("evm_mine")
check accounts.len > 0 check response == %"0x0"
test "sends raw messages to the provider": test "returns block number":
let response = await provider.send("evm_mine") let blocknumber1 = await provider.getBlockNumber()
check response == %"0x0" discard await provider.send("evm_mine")
let blocknumber2 = await provider.getBlockNumber()
check blocknumber2 > blocknumber1
test "returns block number": test "returns block":
let blocknumber1 = await provider.getBlockNumber() let block1 = !await provider.getBlock(BlockTag.earliest)
discard await provider.send("evm_mine") let block2 = !await provider.getBlock(BlockTag.latest)
let blocknumber2 = await provider.getBlockNumber() check block1.hash != block2.hash
check blocknumber2 > blocknumber1 check !block1.number < !block2.number
check block1.timestamp < block2.timestamp
test "returns block": test "subscribes to new blocks":
let block1 = !await provider.getBlock(BlockTag.earliest) let oldBlock = !await provider.getBlock(BlockTag.latest)
let block2 = !await provider.getBlock(BlockTag.latest) var newBlock: Block
check block1.hash != block2.hash let blockHandler = proc(blck: Block) {.async.} = newBlock = blck
check !block1.number < !block2.number let subscription = await provider.subscribe(blockHandler)
check block1.timestamp < block2.timestamp discard await provider.send("evm_mine")
check eventually newBlock.number.isSome
check !newBlock.number > !oldBlock.number
check newBlock.timestamp > oldBlock.timestamp
check newBlock.hash != oldBlock.hash
await subscription.unsubscribe()
test "subscribes to new blocks": test "can send a transaction":
let oldBlock = !await provider.getBlock(BlockTag.latest) let signer = provider.getSigner()
var newBlock: Block let transaction = Transaction.example
let blockHandler = proc(blck: Block) {.async.} = newBlock = blck let populated = await signer.populateTransaction(transaction)
let subscription = await provider.subscribe(blockHandler)
discard await provider.send("evm_mine")
check !newBlock.number > !oldBlock.number
check newBlock.timestamp > oldBlock.timestamp
check newBlock.hash != oldBlock.hash
await subscription.unsubscribe()
test "can send a transaction": let txResp = await signer.sendTransaction(populated)
let signer = provider.getSigner() check txResp.hash.len == 32
let transaction = Transaction.example check UInt256.fromHex("0x" & txResp.hash.toHex) > 0
let populated = await signer.populateTransaction(transaction)
let txResp = await signer.sendTransaction(populated) test "can wait for a transaction to be confirmed":
check txResp.hash.len == 32 let signer = provider.getSigner()
check UInt256.fromHex("0x" & txResp.hash.toHex) > 0 let transaction = Transaction.example
let populated = await signer.populateTransaction(transaction)
test "can wait for a transaction to be confirmed": # must not be awaited so we can get newHeads inside of .wait
let signer = provider.getSigner() let futMined = provider.mineBlocks(5)
let transaction = Transaction.example
let populated = await signer.populateTransaction(transaction)
# must not be awaited so we can get newHeads inside of .wait
let futMined = provider.mineBlocks(5)
let receipt = await signer.sendTransaction(populated).confirm(3)
let endBlock = await provider.getBlockNumber()
check receipt.blockNumber.isSome # was eventually mined
# >= 3 because more blocks may have been mined by the time the
# check in `.wait` was done.
# +1 for the block the tx was mined in
check (endBlock - !receipt.blockNumber) + 1 >= 3
await futMined
test "waiting for block to be mined times out":
# must not be awaited so we can get newHeads inside of .wait
let futMined = provider.mineBlocks(7)
let startBlock = await provider.getBlockNumber()
let response = TransactionResponse(hash: TransactionHash.example,
provider: provider)
try:
discard await response.confirm(wantedConfirms = 2,
timeoutInBlocks = 5)
await futMined
except EthersError as e:
check e.msg == "Transaction was not mined in 5 blocks"
let receipt = await signer.sendTransaction(populated).confirm(3)
let endBlock = await provider.getBlockNumber() let endBlock = await provider.getBlockNumber()
# >= 5 because more blocks may have been mined by the time the check receipt.blockNumber.isSome # was eventually mined
# >= 3 because more blocks may have been mined by the time the
# check in `.wait` was done. # check in `.wait` was done.
# +1 for including the start block # +1 for the block the tx was mined in
check (endBlock - startBlock) + 1 >= 5 # +1 including start block check (endBlock - !receipt.blockNumber) + 1 >= 3
if not futMined.completed and not futMined.finished: await futMined
test "Conversion: missing block number in Block isNone": await futMined
var blkJson = %*{ test "waiting for block to be mined times out":
"subscription": "0x20",
"result":{ # must not be awaited so we can get newHeads inside of .wait
"number": newJNull(), let futMined = provider.mineBlocks(7)
"hash":"0x2d7d68c8f48b4213d232a1f12cab8c9fac6195166bb70a5fb21397984b9fe1c7",
"timestamp":"0x6285c293" let startBlock = await provider.getBlockNumber()
let response = TransactionResponse(hash: TransactionHash.example,
provider: provider)
try:
discard await response.confirm(wantedConfirms = 2,
timeoutInBlocks = 5)
await futMined
except EthersError as e:
check e.msg == "Transaction was not mined in 5 blocks"
let endBlock = await provider.getBlockNumber()
# >= 5 because more blocks may have been mined by the time the
# check in `.wait` was done.
# +1 for including the start block
check (endBlock - startBlock) + 1 >= 5 # +1 including start block
if not futMined.completed and not futMined.finished: await futMined
test "Conversion: missing block number in Block isNone":
var blkJson = %*{
"subscription": "0x20",
"result":{
"number": newJNull(),
"hash":"0x2d7d68c8f48b4213d232a1f12cab8c9fac6195166bb70a5fb21397984b9fe1c7",
"timestamp":"0x6285c293"
}
} }
}
var blk = Block.fromJson(blkJson["result"]) var blk = Block.fromJson(blkJson["result"])
check blk.number.isNone check blk.number.isNone
blkJson["result"]["number"] = newJString("") blkJson["result"]["number"] = newJString("")
blk = Block.fromJson(blkJson["result"]) blk = Block.fromJson(blkJson["result"])
check blk.number.isSome check blk.number.isSome
check blk.number.get.isZero check blk.number.get.isZero
test "Conversion: missing block hash in Block isNone": test "Conversion: missing block hash in Block isNone":
@ -142,132 +139,132 @@ suite "JsonRpcProvider":
var blk = Block.fromJson(blkJson["result"]) var blk = Block.fromJson(blkJson["result"])
check blk.hash.isNone check blk.hash.isNone
test "Conversion: missing block number in TransactionReceipt isNone": test "Conversion: missing block number in TransactionReceipt isNone":
var txReceiptJson = %*{ var txReceiptJson = %*{
"sender": newJNull(), "sender": newJNull(),
"to": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "to": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
"contractAddress": newJNull(), "contractAddress": newJNull(),
"transactionIndex": "0x0", "transactionIndex": "0x0",
"gasUsed": "0x10db1", "gasUsed": "0x10db1",
"logsBloom": "0x00000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000840020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000", "logsBloom": "0x00000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000840020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000",
"blockHash": "0x7b00154e06fe4f27a87208eba220efb4dbc52f7429549a39a17bba2e0d98b960", "blockHash": "0x7b00154e06fe4f27a87208eba220efb4dbc52f7429549a39a17bba2e0d98b960",
"transactionHash": "0xa64f07b370cbdcce381ec9bfb6c8004684341edfb6848fd418189969d4b9139c", "transactionHash": "0xa64f07b370cbdcce381ec9bfb6c8004684341edfb6848fd418189969d4b9139c",
"logs": [ "logs": [
{ {
"data": "0x0000000000000000000000000000000000000000000000000000000000000064", "data": "0x0000000000000000000000000000000000000000000000000000000000000064",
"topics": [ "topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000",
"0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8" "0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8"
] ]
} }
], ],
"blockNumber": newJNull(), "blockNumber": newJNull(),
"cumulativeGasUsed": "0x10db1", "cumulativeGasUsed": "0x10db1",
"status": "0000000000000001" "status": "0000000000000001"
} }
var txReceipt = TransactionReceipt.fromJson(txReceiptJson) var txReceipt = TransactionReceipt.fromJson(txReceiptJson)
check txReceipt.blockNumber.isNone check txReceipt.blockNumber.isNone
txReceiptJson["blockNumber"] = newJString("") txReceiptJson["blockNumber"] = newJString("")
txReceipt = TransactionReceipt.fromJson(txReceiptJson) txReceipt = TransactionReceipt.fromJson(txReceiptJson)
check txReceipt.blockNumber.isSome check txReceipt.blockNumber.isSome
check txReceipt.blockNumber.get.isZero check txReceipt.blockNumber.get.isZero
test "Conversion: missing block hash in TransactionReceipt isNone": test "Conversion: missing block hash in TransactionReceipt isNone":
var txReceiptJson = %*{ var txReceiptJson = %*{
"sender": newJNull(), "sender": newJNull(),
"to": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "to": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
"contractAddress": newJNull(), "contractAddress": newJNull(),
"transactionIndex": "0x0", "transactionIndex": "0x0",
"gasUsed": "0x10db1", "gasUsed": "0x10db1",
"logsBloom": "0x00000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000840020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000", "logsBloom": "0x00000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000840020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000020000000000000000000000000000000000001000000000000000000000000000000",
"blockHash": newJNull(), "blockHash": newJNull(),
"transactionHash": "0xa64f07b370cbdcce381ec9bfb6c8004684341edfb6848fd418189969d4b9139c", "transactionHash": "0xa64f07b370cbdcce381ec9bfb6c8004684341edfb6848fd418189969d4b9139c",
"logs": [ "logs": [
{ {
"data": "0x0000000000000000000000000000000000000000000000000000000000000064", "data": "0x0000000000000000000000000000000000000000000000000000000000000064",
"topics": [ "topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000",
"0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8" "0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8"
] ]
} }
], ],
"blockNumber": newJNull(), "blockNumber": newJNull(),
"cumulativeGasUsed": "0x10db1", "cumulativeGasUsed": "0x10db1",
"status": "0000000000000001" "status": "0000000000000001"
} }
var txReceipt = TransactionReceipt.fromJson(txReceiptJson) var txReceipt = TransactionReceipt.fromJson(txReceiptJson)
check txReceipt.blockHash.isNone check txReceipt.blockHash.isNone
test "confirmations calculated correctly": test "confirmations calculated correctly":
# when receipt block number is higher than current block number, # when receipt block number is higher than current block number,
# should return 0 # should return 0
check confirmations(2.u256, 1.u256) == 0.u256 check confirmations(2.u256, 1.u256) == 0.u256
# Same receipt and current block counts as one confirmation # Same receipt and current block counts as one confirmation
check confirmations(1.u256, 1.u256) == 1.u256 check confirmations(1.u256, 1.u256) == 1.u256
check confirmations(1.u256, 2.u256) == 2.u256 check confirmations(1.u256, 2.u256) == 2.u256
test "checks if transation has been mined correctly": test "checks if transation has been mined correctly":
var receipt: TransactionReceipt var receipt: TransactionReceipt
var currentBlock = 1.u256 var currentBlock = 1.u256
var wantedConfirms = 1 var wantedConfirms = 1
let blockHash = hexToByteArray[32]( let blockHash = hexToByteArray[32](
"0x7b00154e06fe4f27a87208eba220efb4dbc52f7429549a39a17bba2e0d98b960" "0x7b00154e06fe4f27a87208eba220efb4dbc52f7429549a39a17bba2e0d98b960"
).some ).some
# missing blockHash # missing blockHash
receipt = TransactionReceipt( receipt = TransactionReceipt(
blockNumber: 1.u256.some blockNumber: 1.u256.some
) )
check not receipt.hasBeenMined(currentBlock, wantedConfirms) check not receipt.hasBeenMined(currentBlock, wantedConfirms)
# missing block number # missing block number
receipt = TransactionReceipt( receipt = TransactionReceipt(
blockHash: blockHash blockHash: blockHash
) )
check not receipt.hasBeenMined(currentBlock, wantedConfirms) check not receipt.hasBeenMined(currentBlock, wantedConfirms)
# block number is 0 # block number is 0
receipt = TransactionReceipt( receipt = TransactionReceipt(
blockNumber: 0.u256.some blockNumber: 0.u256.some
) )
check not receipt.hasBeenMined(currentBlock, wantedConfirms) check not receipt.hasBeenMined(currentBlock, wantedConfirms)
# not enough confirms # not enough confirms
receipt = TransactionReceipt( receipt = TransactionReceipt(
blockNumber: 1.u256.some blockNumber: 1.u256.some
) )
check not receipt.hasBeenMined(currentBlock, wantedConfirms) check not receipt.hasBeenMined(currentBlock, wantedConfirms)
# success # success
receipt = TransactionReceipt( receipt = TransactionReceipt(
blockNumber: 1.u256.some, blockNumber: 1.u256.some,
blockHash: blockHash blockHash: blockHash
) )
currentBlock = int.high.u256 currentBlock = int.high.u256
wantedConfirms = int.high wantedConfirms = int.high
check receipt.hasBeenMined(currentBlock, wantedConfirms) check receipt.hasBeenMined(currentBlock, wantedConfirms)
test "raises JsonRpcProviderError when something goes wrong": test "raises JsonRpcProviderError when something goes wrong":
let provider = JsonRpcProvider.new("http://invalid.") let provider = JsonRpcProvider.new("http://invalid.")
expect JsonRpcProviderError: expect JsonRpcProviderError:
discard await provider.listAccounts() discard await provider.listAccounts()
expect JsonRpcProviderError: expect JsonRpcProviderError:
discard await provider.send("evm_mine") discard await provider.send("evm_mine")
expect JsonRpcProviderError: expect JsonRpcProviderError:
discard await provider.getBlockNumber() discard await provider.getBlockNumber()
expect JsonRpcProviderError: expect JsonRpcProviderError:
discard await provider.getBlock(BlockTag.latest) discard await provider.getBlock(BlockTag.latest)
expect JsonRpcProviderError: expect JsonRpcProviderError:
discard await provider.subscribe(proc(_: Block) {.async.} = discard) discard await provider.subscribe(proc(_: Block) {.async.} = discard)
expect JsonRpcProviderError: expect JsonRpcProviderError:
discard await provider.getSigner().sendTransaction(Transaction.example) discard await provider.getSigner().sendTransaction(Transaction.example)