From c429aa02850568eb1b6a8933698c8ecee572dc82 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Thu, 16 Aug 2018 17:41:40 +0100 Subject: [PATCH 01/16] Add ref EthAddress --- nimbus/rpc/hexstrings.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nimbus/rpc/hexstrings.nim b/nimbus/rpc/hexstrings.nim index 7e411aff9..7ccfeacce 100644 --- a/nimbus/rpc/hexstrings.nim +++ b/nimbus/rpc/hexstrings.nim @@ -142,6 +142,9 @@ proc `%`*(value: EthHashStr): JsonNode = proc `%`*(value: EthAddress): JsonNode = result = %("0x" & value.toHex) +proc `%`*(value: ref EthAddress): JsonNode = + result = %("0x" & value[].toHex) + proc `%`*(value: Hash256): JsonNode = result = %("0x" & $value) From 2f9311db6719810df996b2a8b4d5ea9c01c9d9a5 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Thu, 16 Aug 2018 18:25:17 +0100 Subject: [PATCH 02/16] Fixes (eg eth_getTransactionCount) and helper funcs --- nimbus/rpc/p2p.nim | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index e716d66d6..b6e292d88 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -27,11 +27,17 @@ proc `%`*(value: Time): JsonNode = func strToAddress(value: string): EthAddress = hexToPaddedByteArray[20](value) +func toHash(value: array[32, byte]): Hash256 {.inline.} = + result.data = value + +func strToHash(value: string): Hash256 {.inline.} = + result = hexToPaddedByteArray[32](value).toHash + func headerFromTag(chain:BaseChainDB, blockTag: string): BlockHeader = let tag = blockTag.toLowerAscii case tag of "latest": result = chain.getCanonicalHead() - of "earliest": result = chain.getCanonicalBlockHeaderByNumber(GENESIS_BLOCK_NUMBER) + of "earliest": result = chain.getBlockHeader(GENESIS_BLOCK_NUMBER) of "pending": #TODO: Implement get pending block raise newException(ValueError, "Pending tag not yet implemented") @@ -39,13 +45,12 @@ func headerFromTag(chain:BaseChainDB, blockTag: string): BlockHeader = # Raises are trapped and wrapped in JSON when returned to the user. tag.validateHexQuantity let blockNum = stint.fromHex(UInt256, tag) - result = chain.getCanonicalBlockHeaderByNumber(blockNum) + result = chain.getBlockHeader(blockNum) proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = template chain: untyped = BaseChainDB(node.chain) # TODO: Sensible casting proc accountDbFromTag(tag: string, readOnly = true): AccountStateDb = - # Note: This is a read only account let header = chain.headerFromTag(tag) vmState = newBaseVMState(header, chain) @@ -102,9 +107,9 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. ## Returns integer of the current balance in wei. let - account_db = accountDbFromTag(quantityTag) + accountDb = accountDbFromTag(quantityTag) addrBytes = strToAddress(data.string) - balance = account_db.get_balance(addrBytes) + balance = accountDb.get_balance(addrBytes) result = balance.toInt @@ -116,30 +121,29 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. ## Returns: the value at this storage position. let - account_db = accountDbFromTag(quantityTag) + accountDb = accountDbFromTag(quantityTag) addrBytes = strToAddress(data.string) - storage = account_db.getStorage(addrBytes, quantity.u256) + storage = accountDb.getStorage(addrBytes, quantity.u256) if storage[1]: result = storage[0] - rpcsrv.rpc("eth_getTransactionCount") do(data: EthAddressStr, quantityTag: string) -> int: + rpcsrv.rpc("eth_getTransactionCount") do(data: EthAddressStr, quantityTag: string) -> UInt256: ## Returns the number of transactions sent from an address. ## ## data: address. ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. ## Returns integer of the number of transactions send from this address. let - header = chain.headerFromTag(quantityTag) - body = chain.getBlockBody(header.hash) - result = body.transactions.len + addrBytes = data.string.strToAddress() + accountDb = accountDbFromTag(quantityTag) + result = accountDb.getNonce(addrBytes) rpcsrv.rpc("eth_getBlockTransactionCountByHash") do(data: HexDataStr) -> int: ## Returns the number of transactions in a block from a block matching the given block hash. ## ## data: hash of a block ## Returns integer of the number of transactions in this block. - var hashData: Hash256 - hashData.data = hexToPaddedByteArray[32](data.string) + var hashData = strToHash(data.string) let body = chain.getBlockBody(hashData) result = body.transactions.len @@ -150,6 +154,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## Returns integer of the number of transactions in this block. let header = chain.headerFromTag(quantityTag) + accountDb = accountDbFromTag(quantityTag) body = chain.getBlockBody(header.hash) result = body.transactions.len @@ -158,8 +163,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## ## data: hash of a block. ## Returns integer of the number of uncles in this block. - var hashData: Hash256 - hashData.data = hexToPaddedByteArray[32](data.string) + var hashData = strToHash(data.string) let body = chain.getBlockBody(hashData) result = body.uncles.len @@ -180,9 +184,9 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. ## Returns the code from the given address. let - account_db = accountDbFromTag(quantityTag) + accountDb = accountDbFromTag(quantityTag) addrBytes = strToAddress(data.string) - storage = account_db.getCode(addrBytes) + storage = accountDb.getCode(addrBytes) # Easier to return the string manually here rather than expect ByteRange to be marshalled result = byteutils.toHex(storage.toOpenArray).HexDataStr @@ -270,8 +274,9 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## fullTransactions: If true it returns the full transaction objects, if false only the hashes of the transactions. ## Returns BlockObject or nil when no block was found. let - header = chain.getCanonicalHead() - body = chain.getBlockBody(header.hash) + h = data.string.strToHash + header = chain.getBlockHeader(h) + body = chain.getBlockBody(h) populateBlockObject(header, body) rpcsrv.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject: From 7ee928f5fdc8125778a26972ab242615379ee93f Mon Sep 17 00:00:00 2001 From: coffeepots Date: Thu, 16 Aug 2018 20:04:14 +0100 Subject: [PATCH 03/16] Update nonce type --- nimbus/rpc/rpc_types.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimbus/rpc/rpc_types.nim b/nimbus/rpc/rpc_types.nim index ce86d7c2a..601979dc5 100644 --- a/nimbus/rpc/rpc_types.nim +++ b/nimbus/rpc/rpc_types.nim @@ -64,7 +64,7 @@ type TransactionObject* = object # A transaction object, or null when no transaction was found: # Returned to user hash*: Hash256 # hash of the transaction. - nonce*: int64 # TODO: Is int? the number of transactions made by the sender prior to this one. + nonce*: uint64 # the number of transactions made by the sender prior to this one. blockHash*: ref Hash256 # hash of the block where this transaction was in. null when its pending. blockNumber*: ref BlockNumber # block number where this transaction was in. null when its pending. transactionIndex*: ref int64 # integer of the transactions index position in the block. null when its pending. From 691adf0e23c16e4642c78eec17fc67486dc85950 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Thu, 16 Aug 2018 20:25:50 +0100 Subject: [PATCH 04/16] Update TransactionObject to use Blob --- nimbus/rpc/rpc_types.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimbus/rpc/rpc_types.nim b/nimbus/rpc/rpc_types.nim index 601979dc5..88c9f3e8e 100644 --- a/nimbus/rpc/rpc_types.nim +++ b/nimbus/rpc/rpc_types.nim @@ -73,7 +73,7 @@ type value*: int64 # value transferred in Wei. gasPrice*: GasInt # gas price provided by the sender in Wei. gas*: GasInt # gas provided by the sender. - input*: HexDataStr # the data send along with the transaction. + input*: Blob # the data send along with the transaction. LogObject* = object # Returned to user From be0f77870c5f90d911c41b126be8f04d20275624 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 17 Aug 2018 19:52:48 +0100 Subject: [PATCH 05/16] Add getTransactionKey --- nimbus/db/db_chain.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nimbus/db/db_chain.nim b/nimbus/db/db_chain.nim index a1d79ac52..6dc6115ec 100644 --- a/nimbus/db/db_chain.nim +++ b/nimbus/db/db_chain.nim @@ -113,6 +113,11 @@ iterator getBlockTransactionHashes(self: BaseChainDB, blockHeader: BlockHeader): # for encoded_transaction in all_encoded_transactions: # yield keccak(encoded_transaction) +proc getTransactionKey*(self: BaseChainDB, transactionHash: Hash256): tuple[blockNumber: BlockNumber, index: int] {.inline.} = + let + tx = self.db.get(transactionHashToBlockKey(transactionHash).toOpenArray).toRange + key = rlp.decode(tx, TransactionKey) + return (key.blockNumber, key.index) proc removeTransactionFromCanonicalChain(self: BaseChainDB, transactionHash: Hash256) {.inline.} = ## Removes the transaction specified by the given hash from the canonical chain. From 6b49407952a1ba5ad6880bb4489b41a5c26876df Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 17 Aug 2018 19:53:22 +0100 Subject: [PATCH 06/16] Updates to TransactionObject in line with internal types --- nimbus/rpc/rpc_types.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nimbus/rpc/rpc_types.nim b/nimbus/rpc/rpc_types.nim index 88c9f3e8e..b32d2cb61 100644 --- a/nimbus/rpc/rpc_types.nim +++ b/nimbus/rpc/rpc_types.nim @@ -64,13 +64,13 @@ type TransactionObject* = object # A transaction object, or null when no transaction was found: # Returned to user hash*: Hash256 # hash of the transaction. - nonce*: uint64 # the number of transactions made by the sender prior to this one. + nonce*: UInt256 # the number of transactions made by the sender prior to this one. blockHash*: ref Hash256 # hash of the block where this transaction was in. null when its pending. blockNumber*: ref BlockNumber # block number where this transaction was in. null when its pending. transactionIndex*: ref int64 # integer of the transactions index position in the block. null when its pending. source*: EthAddress # address of the sender. to*: ref EthAddress # address of the receiver. null when its a contract creation transaction. - value*: int64 # value transferred in Wei. + value*: UInt256 # value transferred in Wei. gasPrice*: GasInt # gas price provided by the sender in Wei. gas*: GasInt # gas provided by the sender. input*: Blob # the data send along with the transaction. From 7527b52d11291e1617829a14027ba4f96ef8d772 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 17 Aug 2018 19:53:47 +0100 Subject: [PATCH 07/16] eth_getTransactionByHash --- nimbus/rpc/p2p.nim | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index b6e292d88..3d83067fb 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -9,8 +9,8 @@ import nimcrypto, json_rpc/rpcserver, eth_p2p, hexstrings, strutils, stint, ../config, ../vm_state, ../constants, eth_trie/[memdb, types], - ../db/[db_chain, state_db], eth_common, rpc_types, byteutils, - ranges/typedranges, times, ../utils/header + ../db/[db_chain, state_db, storage_types], eth_common, rpc_types, byteutils, + ranges/typedranges, times, ../utils/header, rlp #[ Note: @@ -290,12 +290,45 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = body = chain.getBlockBody(header.hash) populateBlockObject(header, body) + func populateTransactionObject(transaction: Transaction, txHash: Hash256, txCount: UInt256, txIndex: int, blockHeader: BlockHeader, gas: int64): TransactionObject = + result.hash = txHash + result.nonce = txCount + result.blockHash = new Hash256 + result.blockHash[] = blockHeader.hash + result.blockNumber = new BlockNumber + result.blockNumber[] = blockHeader.blockNumber + result.transactionIndex = new int64 + result.transactionIndex[] = txIndex + # TODO: Fetch or calculate `from` address with signature after signing with the private key + #result.source: EthAddress + result.to = new EthAddress + result.to[] = transaction.to + result.value = transaction.value + result.gasPrice = transaction.gasPrice + result.gas = gas + result.input = transaction.payload + rpcsrv.rpc("eth_getTransactionByHash") do(data: HexDataStr) -> TransactionObject: ## Returns the information about a transaction requested by transaction hash. ## ## data: hash of a transaction. ## Returns requested transaction information. - discard + let + h = data.string.strToHash() + txDetails = chain.getTransactionKey(h) + header = chain.getBlockHeader(txDetails.blockNumber) + blockHash = chain.getBlockHash(txDetails.blockNumber) + body = chain.getBlockBody(blockHash) + transaction = body.transactions[txDetails.index] + vmState = newBaseVMState(header, chain) + addressDb = vmState.chaindb.getStateDb(blockHash, true) + # TODO: Get/calculate address for this transaction + address = ZERO_ADDRESS + txCount = addressDb.getNonce(address) + txHash = transaction.rlpHash + # TODO: Fetch account gas + accountGas = 0 + populateTransactionObject(transaction, txHash, txCount, txDetails.index, header, accountGas) rpcsrv.rpc("eth_getTransactionByBlockHashAndIndex") do(data: HexDataStr, quantity: int) -> TransactionObject: ## Returns information about a transaction by block hash and transaction index position. From bc5a4351d39b8c31a6f7a54ead1d67223758a87c Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 17 Aug 2018 20:03:30 +0100 Subject: [PATCH 08/16] eth_getTransactionByBlockHashAndIndex --- nimbus/rpc/p2p.nim | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index 3d83067fb..46fb27835 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -336,7 +336,21 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## data: hash of a block. ## quantity: integer of the transaction index position. ## Returns requested transaction information. - discard + let + blockHash = data.string.strToHash() + body = chain.getBlockBody(blockHash) + header = chain.getBlockHeader(blockHash) + transaction = body.transactions[quantity] + vmState = newBaseVMState(header, chain) + addressDb = vmState.chaindb.getStateDb(blockHash, true) + # TODO: Get/calculate address for this transaction + address = ZERO_ADDRESS + txCount = addressDb.getNonce(address) + txHash = transaction.rlpHash + # TODO: Fetch account gas + accountGas = 0 + populateTransactionObject(transaction, txHash, txCount, quantity, header, accountGas) + rpcsrv.rpc("eth_getTransactionByBlockNumberAndIndex") do(quantityTag: string, quantity: int) -> TransactionObject: ## Returns information about a transaction by block number and transaction index position. From d85c513453cd0427cbabde10a0521fcf9ffa330f Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 17 Aug 2018 20:06:43 +0100 Subject: [PATCH 09/16] eth_getTransactionByBlockNumberAndIndex --- nimbus/rpc/p2p.nim | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index 46fb27835..a96589575 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -357,7 +357,20 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## ## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter. ## quantity: the transaction index position. - discard + let + header = chain.headerFromTag(quantityTag) + blockHash = header.hash + body = chain.getBlockBody(blockHash) + transaction = body.transactions[quantity] + vmState = newBaseVMState(header, chain) + addressDb = vmState.chaindb.getStateDb(blockHash, true) + # TODO: Get/calculate address for this transaction + address = ZERO_ADDRESS + txCount = addressDb.getNonce(address) + txHash = transaction.rlpHash + # TODO: Fetch account gas + accountGas = 0 + populateTransactionObject(transaction, txHash, txCount, quantity, header, accountGas) # Currently defined as a variant type so this might need rethinking # See: https://github.com/status-im/nim-json-rpc/issues/29 From 39e0185cac5c1730f3bb24b0c0c7c9f3f6949821 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 17 Aug 2018 23:51:05 +0100 Subject: [PATCH 10/16] Add receipt object --- nimbus/rpc/rpc_types.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/nimbus/rpc/rpc_types.nim b/nimbus/rpc/rpc_types.nim index b32d2cb61..665081b0e 100644 --- a/nimbus/rpc/rpc_types.nim +++ b/nimbus/rpc/rpc_types.nim @@ -88,3 +88,20 @@ type topics*: array[4, Hash256] # array of 0 to 4 32 Bytes DATA of indexed log arguments. # (In solidity: The first topic is the hash of the signature of the event. # (e.g. Deposit(address,bytes32,uint256)), except you declared the event with the anonymous specifier.) + + ReceiptObject* = object + # A transaction receipt object, or null when no receipt was found: + transactionHash*: UInt256 # hash of the transaction. + transactionIndex*: int # integer of the transactions index position in the block. + blockHash*: UInt256 # hash of the block where this transaction was in. + blockNumber*: int # block number where this transaction was in. + sender*: EthAddress # address of the sender. + to*: ref EthAddress # address of the receiver. null when its a contract creation transaction. + cumulativeGasUsed*: int # the total amount of gas used when this transaction was executed in the block. + gasUsed*: int # the amount of gas used by this specific transaction alone. + contractAddress*: EthAddress # the contract address created, if the transaction was a contract creation, otherwise null. + logs*: seq[LogObject] # TODO: See Wiki for details. list of log objects, which this transaction generated. + logsBloom*: BloomFilter # bloom filter for light clients to quickly retrieve related logs. + root*: UInt256 # post-transaction stateroot (pre Byzantium). + status*: int # 1 = success, 0 = failure. + From 3dad7fbba8a3335ed9e506ce0b1b2f1a8cedb0b4 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Mon, 20 Aug 2018 20:29:40 +0100 Subject: [PATCH 11/16] Update receipt types --- nimbus/rpc/rpc_types.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nimbus/rpc/rpc_types.nim b/nimbus/rpc/rpc_types.nim index 665081b0e..aa7904935 100644 --- a/nimbus/rpc/rpc_types.nim +++ b/nimbus/rpc/rpc_types.nim @@ -91,17 +91,17 @@ type ReceiptObject* = object # A transaction receipt object, or null when no receipt was found: - transactionHash*: UInt256 # hash of the transaction. + transactionHash*: Hash256 # hash of the transaction. transactionIndex*: int # integer of the transactions index position in the block. - blockHash*: UInt256 # hash of the block where this transaction was in. - blockNumber*: int # block number where this transaction was in. + blockHash*: Hash256 # hash of the block where this transaction was in. + blockNumber*: BlockNumber # block number where this transaction was in. sender*: EthAddress # address of the sender. to*: ref EthAddress # address of the receiver. null when its a contract creation transaction. cumulativeGasUsed*: int # the total amount of gas used when this transaction was executed in the block. gasUsed*: int # the amount of gas used by this specific transaction alone. - contractAddress*: EthAddress # the contract address created, if the transaction was a contract creation, otherwise null. + contractAddress*: ref EthAddress # the contract address created, if the transaction was a contract creation, otherwise null. logs*: seq[LogObject] # TODO: See Wiki for details. list of log objects, which this transaction generated. logsBloom*: BloomFilter # bloom filter for light clients to quickly retrieve related logs. - root*: UInt256 # post-transaction stateroot (pre Byzantium). + root*: Hash256 # post-transaction stateroot (pre Byzantium). status*: int # 1 = success, 0 = failure. From c53b2502d7c179195b7e3432779c9d3e66082682 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Mon, 20 Aug 2018 20:42:40 +0100 Subject: [PATCH 12/16] eth_getTransactionReceipt --- nimbus/rpc/p2p.nim | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index a96589575..38aa6fc50 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -374,14 +374,44 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = # Currently defined as a variant type so this might need rethinking # See: https://github.com/status-im/nim-json-rpc/issues/29 - #[ + + proc populateReceipt(receipt: Receipt, transaction: Transaction, txIndex: int, blockHeader: BlockHeader): ReceiptObject = + result.transactionHash = transaction.rlpHash + result.transactionIndex = txIndex + result.blockHash = blockHeader.hash + result.blockNumber = blockHeader.blockNumber + # TODO: Get sender + #result.sender: EthAddress + result.to = new EthAddress + result.to[] = transaction.to + # TODO: Get gas used + #result.cumulativeGasUsed: int + #result.gasUsed: int + # TODO: Get contract address if the transaction was a contract creation. + result.contractAddress = nil + # TODO: See Wiki for details. list of log objects, which this transaction generated. + result.logs = @[] + result.logsBloom = blockHeader.bloom + # post-transaction stateroot (pre Byzantium). + result.root = blockHeader.stateRoot + # 1 = success, 0 = failure. + result.status = 1 + rpcsrv.rpc("eth_getTransactionReceipt") do(data: HexDataStr) -> ReceiptObject: ## Returns the receipt of a transaction by transaction hash. ## ## data: hash of a transaction. ## Returns transaction receipt. - discard - ]# + let + h = data.string.strToHash() + txDetails = chain.getTransactionKey(h) + header = chain.getBlockHeader(txDetails.blockNumber) + body = chain.getBlockBody(h) + var idx = 0 + for receipt in chain.getReceipts(header, Receipt): + if idx == txDetails.index: + return populateReceipt(receipt, body.transactions[txDetails.index], txDetails.index, header) + idx.inc rpcsrv.rpc("eth_getUncleByBlockHashAndIndex") do(data: HexDataStr, quantity: int64) -> BlockObject: ## Returns information about a uncle of a block by hash and uncle index position. @@ -439,7 +469,6 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## Returns true if the filter was successfully uninstalled, otherwise false. discard - #[ rpcsrv.rpc("eth_getFilterChanges") do(filterId: int) -> seq[LogObject]: ## Polling method for a filter, which returns an list of logs which occurred since last poll. ## @@ -451,6 +480,7 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## Returns a list of all logs matching filter with given id. result = @[] + #[ rpcsrv.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]: ## filterOptions: settings for this filter. ## Returns a list of all logs matching a given filter object. From cb23bc0fddfe33f2f64cd72b37f3301def9218f6 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Mon, 20 Aug 2018 20:51:25 +0100 Subject: [PATCH 13/16] Update block object to include hashes in uncles, not full block header --- nimbus/rpc/rpc_types.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimbus/rpc/rpc_types.nim b/nimbus/rpc/rpc_types.nim index aa7904935..050f04e46 100644 --- a/nimbus/rpc/rpc_types.nim +++ b/nimbus/rpc/rpc_types.nim @@ -59,7 +59,7 @@ type gasUsed*: GasInt # the total used gas by all transactions in this block. timestamp*: EthTime # the unix timestamp for when the block was collated. transactions*: seq[Transaction] # list of transaction objects, or 32 Bytes transaction hashes depending on the last given parameter. - uncles*: seq[BlockHeader] # list of uncle hashes. + uncles*: seq[Hash256] # list of uncle hashes. TransactionObject* = object # A transaction object, or null when no transaction was found: # Returned to user From 96eafbf26b93d4b841dc54b5fcb55fc4cab8d7a5 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Mon, 20 Aug 2018 20:52:09 +0100 Subject: [PATCH 14/16] eth_getUncleByBlockHashAndIndex, populate uncle hashes, not full uncles --- nimbus/rpc/p2p.nim | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index 38aa6fc50..043a580a3 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -265,7 +265,9 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = result.gasUsed = header.gasUsed result.timestamp = header.timeStamp result.transactions = blockBody.transactions - result.uncles = blockBody.uncles + result.uncles = @[] + for i in 0 ..< blockBody.uncles.len: + result.uncles[i] = blockBody.uncles[i].hash rpcsrv.rpc("eth_getBlockByHash") do(data: HexDataStr, fullTransactions: bool) -> BlockObject: ## Returns information about a block by hash. @@ -413,13 +415,17 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = return populateReceipt(receipt, body.transactions[txDetails.index], txDetails.index, header) idx.inc - rpcsrv.rpc("eth_getUncleByBlockHashAndIndex") do(data: HexDataStr, quantity: int64) -> BlockObject: + rpcsrv.rpc("eth_getUncleByBlockHashAndIndex") do(data: HexDataStr, quantity: int) -> BlockObject: ## Returns information about a uncle of a block by hash and uncle index position. ## - ## data: hash a block. + ## data: hash of block. ## quantity: the uncle's index position. ## Returns BlockObject or nil when no block was found. - discard + let + blockHash = data.string.strToHash() + body = chain.getBlockBody(blockHash) + uncle = body.uncles[quantity] + result = populateBlockObject(uncle, body) rpcsrv.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: int64) -> BlockObject: # Returns information about a uncle of a block by number and uncle index position. From d14e3de35b931de3f4356b245501044218ab5ff0 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Mon, 20 Aug 2018 21:06:26 +0100 Subject: [PATCH 15/16] eth_getUncleByBlockNumberAndIndex --- nimbus/rpc/p2p.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index 043a580a3..751e41785 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -427,13 +427,17 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = uncle = body.uncles[quantity] result = populateBlockObject(uncle, body) - rpcsrv.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: int64) -> BlockObject: + rpcsrv.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: int) -> BlockObject: # Returns information about a uncle of a block by number and uncle index position. ## ## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter. ## quantity: the uncle's index position. ## Returns BlockObject or nil when no block was found. - discard + let + header = chain.headerFromTag(quantityTag) + body = chain.getBlockBody(header.hash) + uncle = body.uncles[quantity] + result = populateBlockObject(uncle, body) # FilterOptions requires more layout planning. # See: https://github.com/status-im/nim-json-rpc/issues/29 From 2702412431091871a56076be222b7b1112282b2e Mon Sep 17 00:00:00 2001 From: coffeepots Date: Mon, 20 Aug 2018 21:14:30 +0100 Subject: [PATCH 16/16] Add some error checking for missing body hash --- nimbus/rpc/p2p.nim | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index 751e41785..22a57e454 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -145,6 +145,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## Returns integer of the number of transactions in this block. var hashData = strToHash(data.string) let body = chain.getBlockBody(hashData) + if body == nil: + raise newException(ValueError, "Cannot find hash") result = body.transactions.len rpcsrv.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> int: @@ -156,6 +158,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = header = chain.headerFromTag(quantityTag) accountDb = accountDbFromTag(quantityTag) body = chain.getBlockBody(header.hash) + if body == nil: + raise newException(ValueError, "Cannot find hash") result = body.transactions.len rpcsrv.rpc("eth_getUncleCountByBlockHash") do(data: HexDataStr) -> int: @@ -165,6 +169,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = ## Returns integer of the number of uncles in this block. var hashData = strToHash(data.string) let body = chain.getBlockBody(hashData) + if body == nil: + raise newException(ValueError, "Cannot find hash") result = body.uncles.len rpcsrv.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string) -> int: @@ -175,6 +181,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = let header = chain.headerFromTag(quantityTag) body = chain.getBlockBody(header.hash) + if body == nil: + raise newException(ValueError, "Cannot find hash") result = body.uncles.len rpcsrv.rpc("eth_getCode") do(data: EthAddressStr, quantityTag: string) -> HexDataStr: @@ -279,6 +287,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = h = data.string.strToHash header = chain.getBlockHeader(h) body = chain.getBlockBody(h) + if body == nil: + raise newException(ValueError, "Cannot find hash") populateBlockObject(header, body) rpcsrv.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject: @@ -290,6 +300,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = let header = chain.headerFromTag(quantityTag) body = chain.getBlockBody(header.hash) + if body == nil: + raise newException(ValueError, "Cannot find hash") populateBlockObject(header, body) func populateTransactionObject(transaction: Transaction, txHash: Hash256, txCount: UInt256, txIndex: int, blockHeader: BlockHeader, gas: int64): TransactionObject = @@ -321,6 +333,9 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = header = chain.getBlockHeader(txDetails.blockNumber) blockHash = chain.getBlockHash(txDetails.blockNumber) body = chain.getBlockBody(blockHash) + if body == nil: + raise newException(ValueError, "Cannot find hash") + let transaction = body.transactions[txDetails.index] vmState = newBaseVMState(header, chain) addressDb = vmState.chaindb.getStateDb(blockHash, true) @@ -341,6 +356,9 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = let blockHash = data.string.strToHash() body = chain.getBlockBody(blockHash) + if body == nil: + raise newException(ValueError, "Cannot find hash") + let header = chain.getBlockHeader(blockHash) transaction = body.transactions[quantity] vmState = newBaseVMState(header, chain) @@ -363,6 +381,9 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = header = chain.headerFromTag(quantityTag) blockHash = header.hash body = chain.getBlockBody(blockHash) + if body == nil: + raise newException(ValueError, "Cannot find hash") + let transaction = body.transactions[quantity] vmState = newBaseVMState(header, chain) addressDb = vmState.chaindb.getStateDb(blockHash, true) @@ -409,6 +430,8 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = txDetails = chain.getTransactionKey(h) header = chain.getBlockHeader(txDetails.blockNumber) body = chain.getBlockBody(h) + if body == nil: + raise newException(ValueError, "Cannot find hash") var idx = 0 for receipt in chain.getReceipts(header, Receipt): if idx == txDetails.index: @@ -424,7 +447,11 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = let blockHash = data.string.strToHash() body = chain.getBlockBody(blockHash) - uncle = body.uncles[quantity] + if body == nil: + raise newException(ValueError, "Cannot find hash") + if quantity < 0 or quantity >= body.uncles.len: + raise newException(ValueError, "Uncle index out of range") + let uncle = body.uncles[quantity] result = populateBlockObject(uncle, body) rpcsrv.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: int) -> BlockObject: @@ -436,7 +463,11 @@ proc setupP2PRPC*(node: EthereumNode, rpcsrv: RpcServer) = let header = chain.headerFromTag(quantityTag) body = chain.getBlockBody(header.hash) - uncle = body.uncles[quantity] + if body == nil: + raise newException(ValueError, "Cannot find hash") + if quantity < 0 or quantity >= body.uncles.len: + raise newException(ValueError, "Uncle index out of range") + let uncle = body.uncles[quantity] result = populateBlockObject(uncle, body) # FilterOptions requires more layout planning.