mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-11 12:54:13 +00:00
Merge branch 'master' into chain-db-interface
This commit is contained in:
commit
e59d019a03
@ -9,12 +9,12 @@
|
||||
import strutils, nimcrypto, eth_common, stint, eth_trie/[memdb, types]
|
||||
import
|
||||
json_rpc/server, ../vm_state, ../logging, ../db/[db_chain, state_db],
|
||||
../constants, ../config
|
||||
../constants, ../config, hexstrings
|
||||
|
||||
proc setupCommonRPC*(server: RpcServer) =
|
||||
server.rpc("web3_clientVersion") do() -> string:
|
||||
result = NimbusIdent
|
||||
|
||||
server.rpc("web3_sha3") do(data: string) -> string:
|
||||
var rawdata = nimcrypto.fromHex(data)
|
||||
server.rpc("web3_sha3") do(data: HexDataStr) -> string:
|
||||
var rawdata = nimcrypto.fromHex(data.string)
|
||||
result = "0x" & $keccak_256.digest(rawdata)
|
||||
|
@ -17,6 +17,7 @@
|
||||
* ref EthAddress
|
||||
* Hash256
|
||||
* UInt256
|
||||
* seq[byte]
|
||||
* openArray[seq]
|
||||
* ref BloomFilter
|
||||
]#
|
||||
@ -26,13 +27,12 @@ import eth_common/eth_types, stint, byteutils, nimcrypto
|
||||
type
|
||||
HexQuantityStr* = distinct string
|
||||
HexDataStr* = distinct string
|
||||
EthAddressStr* = distinct string # Same as HexDataStr but must be less <= 20 bytes
|
||||
EthHashStr* = distinct string # Same as HexDataStr but must be exactly 32 bytes
|
||||
EthAddressStr* = distinct string # Same as HexDataStr but must be less <= 20 bytes
|
||||
EthHashStr* = distinct string # Same as HexDataStr but must be exactly 32 bytes
|
||||
WhisperIdentityStr* = distinct string # 60 bytes
|
||||
HexStrings = HexQuantityStr | HexDataStr | EthAddressStr | EthHashStr | WhisperIdentityStr
|
||||
|
||||
func len*(quantity: HexQuantityStr): int = quantity.string.len
|
||||
func len*(data: HexDataStr): int = data.string.len
|
||||
func len*(data: EthAddressStr): int = data.string.len
|
||||
func len*(data: EthHashStr): int = data.string.len
|
||||
template len*(value: HexStrings): int = value.string.len
|
||||
|
||||
# Hex validation
|
||||
|
||||
@ -91,11 +91,17 @@ func isValidEthHash*(value: string): bool =
|
||||
# TODO: Allow shorter hashes (pad with zeros) for convenience?
|
||||
result = value.len == 66 and value.isValidHexData
|
||||
|
||||
func isValidWhisperIdentity*(value: string): bool =
|
||||
# 60 bytes for WhisperIdentity plus "0x"
|
||||
# TODO: Are the HexData constratins applicable to Whisper identities?
|
||||
result = value.len == 122 and value.isValidHexData
|
||||
|
||||
const
|
||||
SInvalidQuantity = "Invalid hex quantity format for Ethereum"
|
||||
SInvalidData = "Invalid hex data format for Ethereum"
|
||||
SInvalidAddress = "Invalid address format for Ethereum"
|
||||
SInvalidHash = "Invalid hash format for Ethereum"
|
||||
SInvalidWhisperIdentity = "Invalid format for whisper identity"
|
||||
|
||||
proc validateHexQuantity*(value: string) {.inline.} =
|
||||
if unlikely(not value.isValidHexQuantity):
|
||||
@ -113,6 +119,10 @@ proc validateHashStr*(value: string) {.inline.} =
|
||||
if unlikely(not value.isValidEthHash):
|
||||
raise newException(ValueError, SInvalidHash & ": " & value)
|
||||
|
||||
proc validateWhisperIdentity*(value: string) {.inline.} =
|
||||
if unlikely(not value.isValidWhisperIdentity):
|
||||
raise newException(ValueError, SInvalidWhisperIdentity & ": " & value)
|
||||
|
||||
# Initialisation
|
||||
|
||||
proc hexQuantityStr*(value: string): HexQuantityStr {.inline.} =
|
||||
@ -131,21 +141,16 @@ proc ethHashStr*(value: string): EthHashStr {.inline.} =
|
||||
value.validateHashStr
|
||||
result = value.EthHashStr
|
||||
|
||||
proc whisperIdentity*(value: string): WhisperIdentityStr {.inline.} =
|
||||
value.validateWhisperIdentity
|
||||
result = value.WhisperIdentityStr
|
||||
|
||||
# Converters for use in RPC
|
||||
|
||||
import json
|
||||
from json_rpc/rpcserver import expect
|
||||
|
||||
proc `%`*(value: HexQuantityStr): JsonNode =
|
||||
result = %(value.string)
|
||||
|
||||
proc `%`*(value: HexDataStr): JsonNode =
|
||||
result = %(value.string)
|
||||
|
||||
proc `%`*(value: EthAddressStr): JsonNode =
|
||||
result = %(value.string)
|
||||
|
||||
proc `%`*(value: EthHashStr): JsonNode =
|
||||
proc `%`*(value: HexStrings): JsonNode =
|
||||
result = %(value.string)
|
||||
|
||||
# Overloads to support expected representation of hex data
|
||||
@ -162,8 +167,8 @@ proc `%`*(value: Hash256): JsonNode =
|
||||
proc `%`*(value: UInt256): JsonNode =
|
||||
result = %("0x" & value.toString)
|
||||
|
||||
proc `%`*(value: openArray[seq]): JsonNode =
|
||||
result = %("0x" & value.toHex)
|
||||
proc `%`*(value: WhisperIdentity): JsonNode =
|
||||
result = %("0x" & byteutils.toHex(value))
|
||||
|
||||
proc `%`*(value: ref BloomFilter): JsonNode =
|
||||
result = %("0x" & toHex[256](value[]))
|
||||
@ -171,7 +176,6 @@ proc `%`*(value: ref BloomFilter): JsonNode =
|
||||
# Marshalling from JSON to Nim types that includes format checking
|
||||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var HexQuantityStr) =
|
||||
# Note that '0x' is stripped after validation
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if not hexStr.isValidHexQuantity:
|
||||
@ -179,7 +183,6 @@ proc fromJson*(n: JsonNode, argName: string, result: var HexQuantityStr) =
|
||||
result = hexStr.hexQuantityStr
|
||||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var HexDataStr) =
|
||||
# Note that '0x' is stripped after validation
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if not hexStr.isValidHexData:
|
||||
@ -187,7 +190,6 @@ proc fromJson*(n: JsonNode, argName: string, result: var HexDataStr) =
|
||||
result = hexStr.hexDataStr
|
||||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var EthAddressStr) =
|
||||
# Note that '0x' is stripped after validation
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if not hexStr.isValidEthAddress:
|
||||
@ -195,10 +197,16 @@ proc fromJson*(n: JsonNode, argName: string, result: var EthAddressStr) =
|
||||
result = hexStr.EthAddressStr
|
||||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var EthHashStr) =
|
||||
# Note that '0x' is stripped after validation
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if not hexStr.isValidEthHash:
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" is not valid as an Ethereum hash \"" & hexStr & "\"")
|
||||
result = hexStr.EthHashStr
|
||||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var WhisperIdentityStr) =
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if not hexStr.isValidWhisperIdentity:
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" is not valid as a Whisper identity \"" & hexStr & "\"")
|
||||
result = hexStr.WhisperIdentityStr
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
# those terms.
|
||||
import
|
||||
nimcrypto, json_rpc/rpcserver, eth_p2p, hexstrings, strutils, stint,
|
||||
../config, ../vm_state, ../constants, eth_trie/[memdb, types],
|
||||
../config, ../vm_state, ../constants, eth_trie/[memdb, types], eth_keys,
|
||||
../db/[db_chain, state_db, storage_types], eth_common, rpc_types, byteutils,
|
||||
ranges/typedranges, times, ../utils/header, rlp
|
||||
|
||||
@ -33,6 +33,52 @@ func toHash(value: array[32, byte]): Hash256 {.inline.} =
|
||||
func strToHash(value: string): Hash256 {.inline.} =
|
||||
result = hexToPaddedByteArray[32](value).toHash
|
||||
|
||||
func hash(transaction: Transaction): Hash256 =
|
||||
# Hash transaction without signature
|
||||
type
|
||||
TransHashObj = object
|
||||
accountNonce: uint64
|
||||
gasPrice: GasInt
|
||||
gasLimit: GasInt
|
||||
to: EthAddress
|
||||
value: UInt256
|
||||
payload: Blob
|
||||
return TransHashObj(
|
||||
accountNonce: transaction.accountNonce,
|
||||
gasPrice: transaction.gasPrice,
|
||||
gasLimit: transaction.gasLimit,
|
||||
to: transaction.to,
|
||||
value: transaction.value,
|
||||
payload: transaction.payload
|
||||
).rlpHash
|
||||
|
||||
proc toSignature(transaction: Transaction): Signature =
|
||||
var bytes: array[65, byte]
|
||||
bytes[0..31] = cast[array[32, byte]](transaction.R)
|
||||
bytes[32..63] = cast[array[32, byte]](transaction.S)
|
||||
#[
|
||||
TODO: In the yellow paper:
|
||||
It is assumed that v is the ‘recovery id’, a 1 byte value
|
||||
specifying the sign and finiteness of the curve point; this
|
||||
value is in the range of [27,30].
|
||||
Does this need to be checked that it is [0, 1] and inc by 27?
|
||||
]#
|
||||
# TODO: Ugly casting below, is there a better way/helper func?
|
||||
bytes[64] = (cast[uint64](transaction.V.data.lo) and 0xff'u64).uint8
|
||||
initSignature(bytes)
|
||||
|
||||
proc getSender(transaction: Transaction): EthAddress =
|
||||
## Find the address the transaction was sent from.
|
||||
let
|
||||
txHash = transaction.hash # hash without signature
|
||||
sig = transaction.toSignature()
|
||||
pubKey = recoverKeyFromSignature(sig, txHash)
|
||||
result = pubKey.toCanonicalAddress()
|
||||
|
||||
template balance(addressDb: AccountStateDb, address: EthAddress): GasInt =
|
||||
# TODO: Account balance u256 but GasInt is int64?
|
||||
cast[GasInt](addressDb.get_balance(address).data.lo)
|
||||
|
||||
func headerFromTag(chain:BaseChainDB, blockTag: string): BlockHeader =
|
||||
let tag = blockTag.toLowerAscii
|
||||
case tag
|
||||
@ -102,14 +148,14 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
## Returns integer of the current block number the client is on.
|
||||
result = chain.getCanonicalHead().blockNumber
|
||||
|
||||
rpcsrv.rpc("eth_getBalance") do(data: EthAddressStr, quantityTag: string) -> int:
|
||||
rpcsrv.rpc("eth_getBalance") do(data: EthAddressStr, quantityTag: string) -> GasInt:
|
||||
## Returns the balance of the account of given address.
|
||||
##
|
||||
## data: address to check for balance.
|
||||
## 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
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
accountDb = getAccountDbFromTag(quantityTag)
|
||||
addrBytes = strToAddress(data.string)
|
||||
balance = accountDb.get_balance(addrBytes)
|
||||
|
||||
@ -123,7 +169,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, 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
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
accountDb = getAccountDbFromTag(quantityTag)
|
||||
addrBytes = strToAddress(data.string)
|
||||
storage = accountDb.getStorage(addrBytes, quantity.u256)
|
||||
if storage[1]:
|
||||
@ -137,7 +183,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
## Returns integer of the number of transactions send from this address.
|
||||
let
|
||||
addrBytes = data.string.strToAddress()
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
accountDb = getAccountDbFromTag(quantityTag)
|
||||
result = accountDb.getNonce(addrBytes)
|
||||
|
||||
rpcsrv.rpc("eth_getBlockTransactionCountByHash") do(data: HexDataStr) -> int:
|
||||
@ -179,12 +225,17 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, 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
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
accountDb = getAccountDbFromTag(quantityTag)
|
||||
addrBytes = strToAddress(data.string)
|
||||
storage = accountDb.getCode(addrBytes)
|
||||
# Easier to return the string manually here rather than expect ByteRange to be marshalled
|
||||
result = byteutils.toHex(storage.toOpenArray).HexDataStr
|
||||
|
||||
template sign(privateKey: PrivateKey, message: string): string =
|
||||
# TODO: Is message length encoded as bytes or characters?
|
||||
let msgData = "\x19Ethereum Signed Message:\n" & $message.len & message
|
||||
$signMessage(privateKey, msgData)
|
||||
|
||||
rpcsrv.rpc("eth_sign") do(data: EthAddressStr, message: HexDataStr) -> HexDataStr:
|
||||
## The sign method calculates an Ethereum specific signature with: sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).
|
||||
## By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature.
|
||||
@ -194,7 +245,9 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
## data: address.
|
||||
## message: message to sign.
|
||||
## Returns signature.
|
||||
discard
|
||||
let accountDb = getAccountDb(true)
|
||||
var privateKey: PrivateKey # TODO: Get from key store
|
||||
result = ("0x" & sign(privateKey, message.string)).HexDataStr
|
||||
|
||||
rpcsrv.rpc("eth_sendTransaction") do(obj: EthSend) -> HexDataStr:
|
||||
## Creates new message call transaction or a contract creation, if the data field contains code.
|
||||
@ -220,7 +273,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
## Returns the return value of executed contract.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("eth_estimateGas") do(call: EthCall, quantityTag: string) -> HexDataStr: # TODO: Int or U/Int256?
|
||||
rpcsrv.rpc("eth_estimateGas") do(call: EthCall, quantityTag: string) -> GasInt:
|
||||
## Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.
|
||||
## The transaction will not be added to the blockchain. Note that the estimate may be significantly more than
|
||||
## the amount of gas actually used by the transaction, for a variety of reasons including EVM mechanics and node performance.
|
||||
@ -231,10 +284,8 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
discard
|
||||
|
||||
func populateBlockObject(header: BlockHeader, blockBody: BlockBody): BlockObject =
|
||||
result.number = new BlockNumber
|
||||
result.number[] = header.blockNumber
|
||||
result.hash = new Hash256
|
||||
result.hash[] = header.hash
|
||||
result.number = some(header.blockNumber)
|
||||
result.hash = some(header.hash)
|
||||
result.parentHash = header.parentHash
|
||||
result.nonce = header.nonce.toUint
|
||||
|
||||
@ -247,7 +298,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
startIdx += 32
|
||||
result.sha3Uncles = keccak256.digest(rawData)
|
||||
|
||||
result.logsBloom = nil # TODO: Create bloom filter for logs
|
||||
result.logsBloom = some(header.bloom)
|
||||
result.transactionsRoot = header.txRoot
|
||||
result.stateRoot = header.stateRoot
|
||||
result.receiptsRoot = header.receiptRoot
|
||||
@ -264,7 +315,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
for i in 0 ..< blockBody.uncles.len:
|
||||
result.uncles[i] = blockBody.uncles[i].hash
|
||||
|
||||
rpcsrv.rpc("eth_getBlockByHash") do(data: HexDataStr, fullTransactions: bool) -> BlockObject:
|
||||
rpcsrv.rpc("eth_getBlockByHash") do(data: HexDataStr, fullTransactions: bool) -> Option[BlockObject]:
|
||||
## Returns information about a block by hash.
|
||||
##
|
||||
## data: Hash of a block.
|
||||
@ -273,9 +324,9 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
let
|
||||
h = data.string.strToHash
|
||||
header = chain.getBlockHeader(h)
|
||||
populateBlockObject(header, getBlockBody(h))
|
||||
result = some(populateBlockObject(header, getBlockBody(h)))
|
||||
|
||||
rpcsrv.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject:
|
||||
rpcsrv.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> Option[BlockObject]:
|
||||
## Returns information about a block by block number.
|
||||
##
|
||||
## quantityTag: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
|
||||
@ -283,26 +334,29 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
## Returns BlockObject or nil when no block was found.
|
||||
let
|
||||
header = chain.headerFromTag(quantityTag)
|
||||
populateBlockObject(header, getBlockBody(header.hash))
|
||||
result = some(populateBlockObject(header, getBlockBody(header.hash)))
|
||||
|
||||
proc populateTransactionObject(transaction: Transaction, txIndex: int64, blockHeader: BlockHeader, blockHash: Hash256): TransactionObject =
|
||||
let
|
||||
vmState = newBaseVMState(blockHeader, chain)
|
||||
accountDb = vmState.chaindb.getStateDb(blockHash, true)
|
||||
address = transaction.getSender()
|
||||
txCount = accountDb.getNonce(address)
|
||||
txHash = transaction.rlpHash
|
||||
accountGas = accountDb.balance(address)
|
||||
|
||||
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.blockHash = some(blockHash)
|
||||
result.blockNumber = some(blockHeader.blockNumber)
|
||||
result.transactionIndex = some(txIndex)
|
||||
result.source = transaction.getSender()
|
||||
result.to = some(transaction.to)
|
||||
result.value = transaction.value
|
||||
result.gasPrice = transaction.gasPrice
|
||||
result.gas = gas
|
||||
result.gas = accountGas
|
||||
result.input = transaction.payload
|
||||
|
||||
|
||||
rpcsrv.rpc("eth_getTransactionByHash") do(data: HexDataStr) -> TransactionObject:
|
||||
## Returns the information about a transaction requested by transaction hash.
|
||||
##
|
||||
@ -314,15 +368,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
header = chain.getBlockHeader(txDetails.blockNumber)
|
||||
blockHash = chain.getBlockHash(txDetails.blockNumber)
|
||||
transaction = getBlockBody(blockHash).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)
|
||||
populateTransactionObject(transaction, txDetails.index, header, blockHash)
|
||||
|
||||
rpcsrv.rpc("eth_getTransactionByBlockHashAndIndex") do(data: HexDataStr, quantity: int) -> TransactionObject:
|
||||
## Returns information about a transaction by block hash and transaction index position.
|
||||
@ -334,16 +380,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
blockHash = data.string.strToHash()
|
||||
header = chain.getBlockHeader(blockHash)
|
||||
transaction = getBlockBody(blockHash).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)
|
||||
|
||||
populateTransactionObject(transaction, quantity, header, blockHash)
|
||||
|
||||
rpcsrv.rpc("eth_getTransactionByBlockNumberAndIndex") do(quantityTag: string, quantity: int) -> TransactionObject:
|
||||
## Returns information about a transaction by block number and transaction index position.
|
||||
@ -354,38 +391,24 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
header = chain.headerFromTag(quantityTag)
|
||||
blockHash = header.hash
|
||||
transaction = getBlockBody(blockHash).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)
|
||||
populateTransactionObject(transaction, quantity, header, blockHash)
|
||||
|
||||
# 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 =
|
||||
proc populateReceipt(receipt: Receipt, cumulativeGas: GasInt, 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
|
||||
result.sender = transaction.getSender()
|
||||
result.to = some(transaction.to)
|
||||
result.cumulativeGasUsed = cumulativeGas
|
||||
result.gasUsed = receipt.gasUsed
|
||||
# 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.contractAddress = none(EthAddress)
|
||||
result.logs = receipt.logs
|
||||
result.logsBloom = blockHeader.bloom
|
||||
# post-transaction stateroot (pre Byzantium).
|
||||
result.root = blockHeader.stateRoot
|
||||
# TODO: Respond to success/failure
|
||||
# 1 = success, 0 = failure.
|
||||
result.status = 1
|
||||
|
||||
@ -399,13 +422,16 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
txDetails = chain.getTransactionKey(h)
|
||||
header = chain.getBlockHeader(txDetails.blockNumber)
|
||||
body = getBlockBody(header.hash)
|
||||
var idx = 0
|
||||
var
|
||||
idx = 0
|
||||
cumulativeGas: GasInt
|
||||
for receipt in chain.getReceipts(header, Receipt):
|
||||
cumulativeGas += receipt.gasUsed
|
||||
if idx == txDetails.index:
|
||||
return populateReceipt(receipt, body.transactions[txDetails.index], txDetails.index, header)
|
||||
return populateReceipt(receipt, cumulativeGas, body.transactions[txDetails.index], txDetails.index, header)
|
||||
idx.inc
|
||||
|
||||
rpcsrv.rpc("eth_getUncleByBlockHashAndIndex") do(data: HexDataStr, quantity: int) -> BlockObject:
|
||||
rpcsrv.rpc("eth_getUncleByBlockHashAndIndex") do(data: HexDataStr, quantity: int) -> Option[BlockObject]:
|
||||
## Returns information about a uncle of a block by hash and uncle index position.
|
||||
##
|
||||
## data: hash of block.
|
||||
@ -417,9 +443,9 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
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)
|
||||
result = some(populateBlockObject(uncle, body))
|
||||
|
||||
rpcsrv.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: int) -> BlockObject:
|
||||
rpcsrv.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: int) -> Option[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.
|
||||
@ -431,11 +457,8 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
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)
|
||||
result = some(populateBlockObject(uncle, body))
|
||||
|
||||
# FilterOptions requires more layout planning.
|
||||
# See: https://github.com/status-im/nim-json-rpc/issues/29
|
||||
#[
|
||||
rpcsrv.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> int:
|
||||
## Creates a filter object, based on filter options, to notify when the state changes (logs).
|
||||
## To check if the state has changed, call eth_getFilterChanges.
|
||||
@ -449,7 +472,6 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
## filterOptions: settings for this filter.
|
||||
## Returns integer filter id.
|
||||
discard
|
||||
]#
|
||||
|
||||
rpcsrv.rpc("eth_newBlockFilter") do() -> int:
|
||||
## Creates a filter in the node, to notify when a new block arrives.
|
||||
@ -473,31 +495,29 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
|
||||
## Returns true if the filter was successfully uninstalled, otherwise false.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("eth_getFilterChanges") do(filterId: int) -> seq[LogObject]:
|
||||
rpcsrv.rpc("eth_getFilterChanges") do(filterId: int) -> seq[FilterLog]:
|
||||
## Polling method for a filter, which returns an list of logs which occurred since last poll.
|
||||
##
|
||||
## filterId: the filter id.
|
||||
result = @[]
|
||||
|
||||
rpcsrv.rpc("eth_getFilterLogs") do(filterId: int) -> seq[LogObject]:
|
||||
rpcsrv.rpc("eth_getFilterLogs") do(filterId: int) -> seq[FilterLog]:
|
||||
## filterId: the filter id.
|
||||
## Returns a list of all logs matching filter with given id.
|
||||
result = @[]
|
||||
|
||||
#[
|
||||
rpcsrv.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]:
|
||||
rpcsrv.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[FilterLog]:
|
||||
## filterOptions: settings for this filter.
|
||||
## Returns a list of all logs matching a given filter object.
|
||||
result = @[]
|
||||
]#
|
||||
|
||||
rpcsrv.rpc("eth_getWork") do() -> seq[HexDataStr]:
|
||||
rpcsrv.rpc("eth_getWork") do() -> array[3, UInt256]:
|
||||
## Returns the hash of the current block, the seedHash, and the boundary condition to be met ("target").
|
||||
## Returned list has the following properties:
|
||||
## DATA, 32 Bytes - current block header pow-hash.
|
||||
## DATA, 32 Bytes - the seed hash used for the DAG.
|
||||
## DATA, 32 Bytes - the boundary condition ("target"), 2^256 / difficulty.
|
||||
result = @[]
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("eth_submitWork") do(nonce: int64, powHash: HexDataStr, mixDigest: HexDataStr) -> bool:
|
||||
## Used for submitting a proof-of-work solution.
|
||||
|
@ -1,9 +1,9 @@
|
||||
import eth_common, hexstrings
|
||||
import eth_common, hexstrings, options
|
||||
|
||||
#[
|
||||
Notes:
|
||||
* Some of the types suppose 'null' when there is no appropriate value.
|
||||
To allow for this, currently these values are refs so the JSON transform can convert to `JNull`.
|
||||
To allow for this, you can use Option[T] or use refs so the JSON transform can convert to `JNull`.
|
||||
* Parameter objects from users must have their data verified so will use EthAddressStr instead of EthAddres, for example
|
||||
* Objects returned to the user can use native Nimbus types, where hexstrings provides converters to hex strings.
|
||||
This is because returned arrays in JSON is
|
||||
@ -39,14 +39,14 @@ type
|
||||
|
||||
## A block object, or null when no block was found
|
||||
## Note that this includes slightly different information from eth_common.BlockHeader
|
||||
BlockObject* = ref object
|
||||
BlockObject* = object
|
||||
# Returned to user
|
||||
number*: ref BlockNumber # the block number. null when its pending block.
|
||||
hash*: ref Hash256 # hash of the block. null when its pending block.
|
||||
number*: Option[BlockNumber] # the block number. null when its pending block.
|
||||
hash*: Option[Hash256] # hash of the block. null when its pending block.
|
||||
parentHash*: Hash256 # hash of the parent block.
|
||||
nonce*: uint64 # hash of the generated proof-of-work. null when its pending block.
|
||||
sha3Uncles*: Hash256 # SHA3 of the uncles data in the block.
|
||||
logsBloom*: ref BloomFilter # the bloom filter for the logs of the block. null when its pending block.
|
||||
logsBloom*: Option[BloomFilter] # the bloom filter for the logs of the block. null when its pending block.
|
||||
transactionsRoot*: Hash256 # the root of the transaction trie of the block.
|
||||
stateRoot*: Hash256 # the root of the final state trie of the block.
|
||||
receiptsRoot*: Hash256 # the root of the receipts trie of the block.
|
||||
@ -63,45 +63,82 @@ type
|
||||
|
||||
TransactionObject* = object # A transaction object, or null when no transaction was found:
|
||||
# Returned to user
|
||||
hash*: Hash256 # hash of the transaction.
|
||||
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*: 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.
|
||||
hash*: Hash256 # hash of the transaction.
|
||||
nonce*: UInt256 # the number of transactions made by the sender prior to this one.
|
||||
blockHash*: Option[Hash256] # hash of the block where this transaction was in. null when its pending.
|
||||
blockNumber*: Option[BlockNumber] # block number where this transaction was in. null when its pending.
|
||||
transactionIndex*: Option[int64] # integer of the transactions index position in the block. null when its pending.
|
||||
source*: EthAddress # address of the sender.
|
||||
to*: Option[EthAddress] # address of the receiver. null when its a contract creation transaction.
|
||||
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.
|
||||
|
||||
LogObject* = object
|
||||
FilterLog* = object
|
||||
# Returned to user
|
||||
removed*: bool # true when the log was removed, due to a chain reorganization. false if its a valid log.
|
||||
logIndex*: ref int # integer of the log index position in the block. null when its pending log.
|
||||
transactionIndex*: ref int # integer of the transactions index position log was created from. null when its pending log.
|
||||
transactionHash*: ref Hash256 # hash of the transactions this log was created from. null when its pending log.
|
||||
blockHash*: ref Hash256 # hash of the block where this log was in. null when its pending. null when its pending log.
|
||||
blockNumber*: ref BlockNumber # the block number where this log was in. null when its pending. null when its pending log.
|
||||
address*: EthAddress # address from which this log originated.
|
||||
data*: seq[Hash256] # contains one or more 32 Bytes non-indexed arguments of the log.
|
||||
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.)
|
||||
removed*: bool # true when the log was removed, due to a chain reorganization. false if its a valid log.
|
||||
logIndex*: Option[int] # integer of the log index position in the block. null when its pending log.
|
||||
transactionIndex*: Option[int] # integer of the transactions index position log was created from. null when its pending log.
|
||||
transactionHash*: Option[Hash256] # hash of the transactions this log was created from. null when its pending log.
|
||||
blockHash*: Option[Hash256] # hash of the block where this log was in. null when its pending. null when its pending log.
|
||||
blockNumber*: Option[BlockNumber] # the block number where this log was in. null when its pending. null when its pending log.
|
||||
address*: EthAddress # address from which this log originated.
|
||||
data*: seq[Hash256] # contains one or more 32 Bytes non-indexed arguments of the log.
|
||||
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.
|
||||
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*: Option[EthAddress] # address of the receiver. null when its a contract creation transaction.
|
||||
cumulativeGasUsed*: GasInt # the total amount of gas used when this transaction was executed in the block.
|
||||
gasUsed*: GasInt # the amount of gas used by this specific transaction alone.
|
||||
contractAddress*: Option[EthAddress] # the contract address created, if the transaction was a contract creation, otherwise null.
|
||||
logs*: seq[Log] # 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.
|
||||
|
||||
FilterDataKind* = enum fkItem, fkList
|
||||
FilterData* = object
|
||||
# Difficult to process variant objects in input data, as kind is immutable.
|
||||
# TODO: This might need more work to handle "or" options
|
||||
kind*: FilterDataKind
|
||||
items*: seq[FilterData]
|
||||
item*: UInt256
|
||||
|
||||
FilterOptions* = object
|
||||
# Parameter from user
|
||||
fromBlock*: string # (optional, default: "latest") integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions.
|
||||
toBlock*: string # (optional, default: "latest") integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions.
|
||||
address*: EthAddress # (optional) contract address or a list of addresses from which logs should originate.
|
||||
topics*: seq[FilterData] # (optional) list of DATA topics. Topics are order-dependent. Each topic can also be a list of DATA with "or" options.
|
||||
|
||||
WhisperPost* = object
|
||||
# Parameter from user
|
||||
source*: WhisperIdentityStr # (optional) the identity of the sender.
|
||||
to*: WhisperIdentityStr # (optional) the identity of the receiver. When present whisper will encrypt the message so that only the receiver can decrypt it.
|
||||
topics*: seq[HexDataStr] # list of DATA topics, for the receiver to identify messages.
|
||||
payload*: HexDataStr # the payload of the message.
|
||||
priority*: int # integer of the priority in a rang from.
|
||||
ttl*: int # integer of the time to live in seconds.
|
||||
|
||||
WhisperIdentity = array[60, byte]
|
||||
|
||||
WhisperMessage* = object
|
||||
# Returned to user
|
||||
hash*: Hash256 # the hash of the message.
|
||||
source*: WhisperIdentity # the sender of the message, if a sender was specified.
|
||||
to*: WhisperIdentity # the receiver of the message, if a receiver was specified.
|
||||
expiry*: int # integer of the time in seconds when this message should expire.
|
||||
ttl*: int # integer of the time the message should float in the system in seconds.
|
||||
sent*: int # integer of the unix timestamp when the message was sent.
|
||||
topics*: seq[UInt256] # list of DATA topics the message contained.
|
||||
payload*: Blob # the payload of the message.
|
||||
workProved*: int # integer of the work this message required before it was send.
|
||||
|
74
nimbus/rpc/whisper.nim
Normal file
74
nimbus/rpc/whisper.nim
Normal file
@ -0,0 +1,74 @@
|
||||
import json_rpc/rpcserver, rpc_types, stint, hexstrings, eth_common
|
||||
|
||||
proc setupWhisperRPC*(rpcsrv: RpcServer) =
|
||||
rpcsrv.rpc("shh_version") do() -> string:
|
||||
## Returns string of the current whisper protocol version.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_post") do(message: WhisperPost) -> bool:
|
||||
## Sends a whisper message.
|
||||
##
|
||||
## message: Whisper message to post.
|
||||
## Returns true if the message was send, otherwise false.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_newIdentity") do() -> WhisperIdentity:
|
||||
## Creates new whisper identity in the client.
|
||||
##
|
||||
## Returns the address of the new identiy.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_hasIdentity") do(identity: WhisperIdentityStr) -> bool:
|
||||
## Checks if the client holds the private keys for a given identity.
|
||||
##
|
||||
## identity: the identity address to check.
|
||||
## Returns true if the client holds the privatekey for that identity, otherwise false.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_newGroup") do() -> WhisperIdentity:
|
||||
## (?) - This has no description information in the RPC wiki.
|
||||
##
|
||||
## Returns the address of the new group. (?)
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_addToGroup") do(identity: WhisperIdentityStr) -> bool:
|
||||
## (?) - This has no description information in the RPC wiki.
|
||||
##
|
||||
## identity: the identity address to add to a group (?).
|
||||
## Returns true if the identity was successfully added to the group, otherwise false (?).
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_newFilter") do(filterOptions: FilterOptions, to: WhisperIdentityStr, topics: seq[HexDataStr]) -> int:
|
||||
## Creates filter to notify, when client receives whisper message matching the filter options.
|
||||
##
|
||||
## filterOptions: The filter options:
|
||||
## to: DATA, 60 Bytes - (optional) identity of the receiver. When present it will try to decrypt any incoming message if the client holds the private key to this identity.
|
||||
## topics: Array of DATA - list of DATA topics which the incoming message's topics should match. You can use the following combinations:
|
||||
## [A, B] = A && B
|
||||
## [A, [B, C]] = A && (B || C)
|
||||
## [null, A, B] = ANYTHING && A && B null works as a wildcard
|
||||
## Returns the newly created filter.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_uninstallFilter") do(id: int) -> bool:
|
||||
## Uninstalls a filter with given id.
|
||||
## Should always be called when watch is no longer needed.
|
||||
## Additonally Filters timeout when they aren't requested with shh_getFilterChanges for a period of time.
|
||||
##
|
||||
## id: the filter id.
|
||||
## Returns true if the filter was successfully uninstalled, otherwise false.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_getFilterChanges") do(id: int) -> seq[WhisperMessage]:
|
||||
## Polling method for whisper filters. Returns new messages since the last call of this method.
|
||||
## Note: calling the shh_getMessages method, will reset the buffer for this method, so that you won't receive duplicate messages.
|
||||
##
|
||||
## id: the filter id.
|
||||
discard
|
||||
|
||||
rpcsrv.rpc("shh_getMessages") do(id: int) -> seq[WhisperMessage]:
|
||||
## Get all messages matching a filter. Unlike shh_getFilterChanges this returns all messages.
|
||||
##
|
||||
## id: the filter id.
|
||||
## Returns a list of messages received since last poll.
|
||||
discard
|
Loading…
x
Reference in New Issue
Block a user