Merge pull request #115 from status-im/moreRpcs2

More rpcs2
This commit is contained in:
coffeepots 2018-08-21 14:48:15 +01:00 committed by GitHub
commit 5ac1949870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 210 additions and 38 deletions

View File

@ -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.

View File

@ -10,6 +10,17 @@
## This module implements the Ethereum hexadecimal string formats for JSON
## See: https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding
#[
Note:
The following types are converted to hex strings when marshalled to JSON:
* EthAddress
* ref EthAddress
* Hash256
* UInt256
* openArray[seq]
* ref BloomFilter
]#
import eth_common/eth_types, stint, byteutils, nimcrypto
type
@ -142,6 +153,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)

View File

@ -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:
@ -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,31 +121,32 @@ 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)
if body == nil:
raise newException(ValueError, "Cannot find hash")
result = body.transactions.len
rpcsrv.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> int:
@ -150,7 +156,10 @@ 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)
if body == nil:
raise newException(ValueError, "Cannot find hash")
result = body.transactions.len
rpcsrv.rpc("eth_getUncleCountByBlockHash") do(data: HexDataStr) -> int:
@ -158,9 +167,10 @@ 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)
if body == nil:
raise newException(ValueError, "Cannot find hash")
result = body.uncles.len
rpcsrv.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string) -> int:
@ -171,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:
@ -180,9 +192,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
@ -261,7 +273,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.
@ -270,8 +284,11 @@ 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)
if body == nil:
raise newException(ValueError, "Cannot find hash")
populateBlockObject(header, body)
rpcsrv.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject:
@ -283,14 +300,52 @@ 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 =
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)
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)
# 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.
@ -298,41 +353,122 @@ 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)
if body == nil:
raise newException(ValueError, "Cannot find hash")
let
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.
##
## 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)
if body == nil:
raise newException(ValueError, "Cannot find hash")
let
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
#[
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)
if body == nil:
raise newException(ValueError, "Cannot find hash")
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:
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)
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: 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)
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.
# See: https://github.com/status-im/nim-json-rpc/issues/29
@ -374,7 +510,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.
##
@ -386,6 +521,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.

View File

@ -59,21 +59,21 @@ 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
hash*: Hash256 # hash of the transaction.
nonce*: int64 # TODO: Is int? 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*: HexDataStr # the data send along with the transaction.
input*: Blob # the data send along with the transaction.
LogObject* = object
# Returned to user
@ -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*: Hash256 # hash of the transaction.
transactionIndex*: int # integer of the transactions index position in the block.
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*: 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*: Hash256 # post-transaction stateroot (pre Byzantium).
status*: int # 1 = success, 0 = failure.