Merge branch 'master' into chain-db-interface

This commit is contained in:
coffeepots 2018-08-29 17:23:25 +01:00 committed by GitHub
commit e59d019a03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 295 additions and 156 deletions

View File

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

View File

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

View File

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

View File

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