mirror of https://github.com/status-im/nim-eth.git
clean up eth_types (#531)
`eth_types` is being imported from many projects and ends up causing long build times due to its extensive import lists - this PR starts cleaning some of that up by moving the chain DB and RLP to their own modules. this PR also moves `keccakHash` to its own module and uses it in many places.
This commit is contained in:
parent
4f0155e626
commit
d31abca010
|
@ -1,4 +1,4 @@
|
|||
import stint, nimcrypto/[keccak, hash]
|
||||
import stint, ./common/eth_hash
|
||||
|
||||
type UInt2048 = StUint[2048]
|
||||
|
||||
|
@ -27,8 +27,8 @@ proc init*(_: type BloomFilter, h: MDigest[256]): BloomFilter =
|
|||
result.incl(h)
|
||||
|
||||
# TODO: The following 2 procs should be one genric, but it doesn't compile. Nim bug?
|
||||
proc incl*(f: var BloomFilter, v: string) = f.incl(keccak256.digest(v))
|
||||
proc incl*(f: var BloomFilter, v: openArray[byte]) = f.incl(keccak256.digest(v))
|
||||
proc incl*(f: var BloomFilter, v: string) = f.incl(keccakHash(v))
|
||||
proc incl*(f: var BloomFilter, v: openArray[byte]) = f.incl(keccakHash(v))
|
||||
|
||||
proc contains*(f: BloomFilter, h: MDigest[256]): bool =
|
||||
for bits in bloomBits(h):
|
||||
|
@ -36,4 +36,4 @@ proc contains*(f: BloomFilter, h: MDigest[256]): bool =
|
|||
return true
|
||||
|
||||
template contains*[T](f: BloomFilter, v: openArray[T]): bool =
|
||||
f.contains(keccak256.digest(v))
|
||||
f.contains(keccakHash(v))
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
import ./common/[eth_types, utils]
|
||||
export eth_types, utils
|
||||
import ./common/[eth_types_rlp, utils]
|
||||
export eth_types_rlp, utils
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
# Copyright (c) 2022 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
chronicles,
|
||||
./eth_types_rlp,
|
||||
../rlp,
|
||||
../trie/db
|
||||
|
||||
export eth_types_rlp, rlp, db
|
||||
|
||||
type
|
||||
AbstractChainDB* = ref object of RootRef
|
||||
|
||||
proc notImplemented(name: string) =
|
||||
debug "Method not implemented", meth = name
|
||||
|
||||
method genesisHash*(db: AbstractChainDB): KeccakHash
|
||||
{.base, gcsafe, raises: [Defect].} =
|
||||
notImplemented("genesisHash")
|
||||
|
||||
method getBlockHeader*(db: AbstractChainDB, b: HashOrNum,
|
||||
output: var BlockHeader): bool {.base, gcsafe, raises: [RlpError, Defect].} =
|
||||
notImplemented("getBlockHeader")
|
||||
|
||||
proc getBlockHeader*(db: AbstractChainDB, hash: KeccakHash): BlockHeaderRef {.gcsafe.} =
|
||||
new result
|
||||
if not db.getBlockHeader(HashOrNum(isHash: true, hash: hash), result[]):
|
||||
return nil
|
||||
|
||||
proc getBlockHeader*(db: AbstractChainDB, b: BlockNumber): BlockHeaderRef {.gcsafe.} =
|
||||
new result
|
||||
if not db.getBlockHeader(HashOrNum(isHash: false, number: b), result[]):
|
||||
return nil
|
||||
|
||||
# Need to add `RlpError` and sometimes `CatchableError` as the implementations
|
||||
# of these methods in nimbus-eth1 will raise these. Using `CatchableError`
|
||||
# because some can raise for errors not know to this repository such as
|
||||
# `CanonicalHeadNotFound`. It would probably be better to use Result.
|
||||
method getBestBlockHeader*(self: AbstractChainDB): BlockHeader
|
||||
{.base, gcsafe, raises: [RlpError, CatchableError, Defect].} =
|
||||
notImplemented("getBestBlockHeader")
|
||||
|
||||
method getSuccessorHeader*(db: AbstractChainDB, h: BlockHeader,
|
||||
output: var BlockHeader, skip = 0'u): bool
|
||||
{.base, gcsafe, raises: [RlpError, Defect].} =
|
||||
notImplemented("getSuccessorHeader")
|
||||
|
||||
method getAncestorHeader*(db: AbstractChainDB, h: BlockHeader,
|
||||
output: var BlockHeader, skip = 0'u): bool
|
||||
{.base, gcsafe, raises: [RlpError, Defect].} =
|
||||
notImplemented("getAncestorHeader")
|
||||
|
||||
method getBlockBody*(db: AbstractChainDB, blockHash: KeccakHash): BlockBodyRef
|
||||
{.base, gcsafe, raises: [Defect].} =
|
||||
notImplemented("getBlockBody")
|
||||
|
||||
method getReceipt*(db: AbstractChainDB, hash: KeccakHash): ReceiptRef {.base, gcsafe.} =
|
||||
notImplemented("getReceipt")
|
||||
|
||||
method getTrieDB*(db: AbstractChainDB): TrieDatabaseRef
|
||||
{.base, gcsafe, raises: [Defect].} =
|
||||
notImplemented("getTrieDB")
|
||||
|
||||
method getCodeByHash*(db: AbstractChainDB, hash: KeccakHash): Blob {.base, gcsafe.} =
|
||||
notImplemented("getCodeByHash")
|
||||
|
||||
method getSetting*(db: AbstractChainDB, key: string): seq[byte] {.base, gcsafe.} =
|
||||
notImplemented("getSetting")
|
||||
|
||||
method setSetting*(db: AbstractChainDB, key: string, val: openArray[byte]) {.base, gcsafe.} =
|
||||
notImplemented("setSetting")
|
||||
|
||||
method getHeaderProof*(db: AbstractChainDB, req: ProofRequest): Blob {.base, gcsafe.} =
|
||||
notImplemented("getHeaderProof")
|
||||
|
||||
method getProof*(db: AbstractChainDB, req: ProofRequest): Blob {.base, gcsafe.} =
|
||||
notImplemented("getProof")
|
||||
|
||||
method getHelperTrieProof*(db: AbstractChainDB, req: HelperTrieProofRequest): Blob {.base, gcsafe.} =
|
||||
notImplemented("getHelperTrieProof")
|
||||
|
||||
method getTransactionStatus*(db: AbstractChainDB, txHash: KeccakHash): TransactionStatusMsg {.base, gcsafe.} =
|
||||
notImplemented("getTransactionStatus")
|
||||
|
||||
method addTransactions*(db: AbstractChainDB, transactions: openArray[Transaction]) {.base, gcsafe.} =
|
||||
notImplemented("addTransactions")
|
||||
|
||||
method persistBlocks*(db: AbstractChainDB, headers: openArray[BlockHeader], bodies: openArray[BlockBody]): ValidationResult {.base, gcsafe.} =
|
||||
notImplemented("persistBlocks")
|
||||
|
||||
method getForkId*(db: AbstractChainDB, n: BlockNumber): ForkID {.base, gcsafe.} =
|
||||
# EIP 2364/2124
|
||||
notImplemented("getForkId")
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright (c) 2022 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
## keccak256 is used across ethereum as the "default" hash function and this
|
||||
## module provides a type and some helpers to produce such hashes
|
||||
|
||||
import
|
||||
nimcrypto/[keccak, hash]
|
||||
|
||||
export
|
||||
keccak.update, keccak.finish, hash
|
||||
|
||||
type
|
||||
KeccakHash* = MDigest[256]
|
||||
## A hash value computed using keccak256
|
||||
## note: this aliases Eth2Digest too, which uses a different hash!
|
||||
|
||||
template withKeccakHash*(body: untyped): KeccakHash =
|
||||
## This little helper will init the hash function and return the sliced
|
||||
## hash:
|
||||
## let hashOfData = withHash: h.update(data)
|
||||
block:
|
||||
var h {.inject.}: keccak256
|
||||
# init(h) # not needed for new instance
|
||||
body
|
||||
finish(h)
|
||||
|
||||
func keccakHash*(input: openArray[byte]): KeccakHash =
|
||||
keccak256.digest(input)
|
||||
func keccakHash*(input: openArray[char]): KeccakHash =
|
||||
keccak256.digest(input)
|
||||
|
||||
func keccakHash*(a, b: openArray[byte]): KeccakHash =
|
||||
withKeccakHash:
|
||||
h.update a
|
||||
h.update b
|
|
@ -0,0 +1,17 @@
|
|||
# Copyright (c) 2022 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
./eth_hash,
|
||||
../rlp
|
||||
|
||||
export eth_hash, rlp
|
||||
|
||||
proc read*(rlp: var Rlp, T: typedesc[MDigest]): T =
|
||||
result.data = rlp.read(type(result.data))
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, a: MDigest) =
|
||||
rlpWriter.append(a.data)
|
|
@ -1,13 +1,22 @@
|
|||
import
|
||||
std/[strutils, options, times],
|
||||
stew/[endians2, byteutils], chronicles, stint, nimcrypto/[keccak, hash],
|
||||
../rlp, ../trie/[trie_defs, db]
|
||||
# Copyright (c) 2022 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
from stew/objects
|
||||
import checkedEnumAssign
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[options, strutils, times],
|
||||
stew/[byteutils, endians2], stint,
|
||||
./eth_hash
|
||||
|
||||
export
|
||||
stint, read, append, KeccakHash, rlp, options, hash.`$`
|
||||
options, stint, eth_hash,
|
||||
times.Time, times.fromUnix, times.toUnix
|
||||
|
||||
type
|
||||
Hash256* = MDigest[256]
|
||||
|
@ -184,8 +193,6 @@ type
|
|||
fromLevel*: uint
|
||||
auxReq*: uint
|
||||
|
||||
AbstractChainDB* = ref object of RootRef
|
||||
|
||||
BlockHeaderRef* = ref BlockHeader
|
||||
BlockBodyRef* = ref BlockBody
|
||||
ReceiptRef* = ref Receipt
|
||||
|
@ -201,8 +208,10 @@ const
|
|||
Eip2930Receipt* = TxEip2930
|
||||
Eip1559Receipt* = TxEip1559
|
||||
|
||||
BLANK_ROOT_HASH* = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest
|
||||
# TODO clean these up
|
||||
EMPTY_ROOT_HASH* = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest
|
||||
EMPTY_UNCLE_HASH* = "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".toDigest
|
||||
EMPTY_CODE_HASH* = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest
|
||||
|
||||
when BlockNumber is int64:
|
||||
## The goal of these templates is to make it easier to switch
|
||||
|
@ -264,8 +273,8 @@ func toUint*(n: BlockNonce): uint64 =
|
|||
proc newAccount*(nonce: AccountNonce = 0, balance: UInt256 = 0.u256): Account =
|
||||
result.nonce = nonce
|
||||
result.balance = balance
|
||||
result.storageRoot = emptyRlpHash
|
||||
result.codeHash = blankStringHash
|
||||
result.storageRoot = EMPTY_ROOT_HASH
|
||||
result.codeHash = EMPTY_CODE_HASH
|
||||
|
||||
proc hasStatus*(rec: Receipt): bool {.inline.} =
|
||||
rec.isHash == false
|
||||
|
@ -286,240 +295,6 @@ func destination*(tx: Transaction): EthAddress =
|
|||
if tx.to.isSome:
|
||||
return tx.to.get
|
||||
|
||||
#
|
||||
# Rlp serialization:
|
||||
#
|
||||
|
||||
proc read*(rlp: var Rlp, T: type StUint): T {.inline.} =
|
||||
if rlp.isBlob:
|
||||
let bytes = rlp.toBytes
|
||||
if bytes.len > 0:
|
||||
# be sure the amount of bytes matches the size of the stint
|
||||
if bytes.len <= sizeof(result):
|
||||
result.initFromBytesBE(bytes)
|
||||
else:
|
||||
raise newException(RlpTypeMismatch, "Unsigned integer expected, but the source RLP has the wrong length")
|
||||
else:
|
||||
result = 0.to(T)
|
||||
else:
|
||||
raise newException(RlpTypeMismatch, "Unsigned integer expected, but the source RLP is a list")
|
||||
|
||||
rlp.skipElem
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: StUint) =
|
||||
if value > 128:
|
||||
let bytes = value.toByteArrayBE
|
||||
let nonZeroBytes = significantBytesBE(bytes)
|
||||
rlpWriter.append bytes.toOpenArray(bytes.len - nonZeroBytes,
|
||||
bytes.len - 1)
|
||||
else:
|
||||
rlpWriter.append(value.truncate(int))
|
||||
|
||||
proc read*(rlp: var Rlp, T: type StInt): T {.inline.} =
|
||||
# The Ethereum Yellow Paper defines the RLP serialization only
|
||||
# for unsigned integers:
|
||||
{.fatal: "RLP serialization of signed integers is not allowed".}
|
||||
discard
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: StInt) =
|
||||
# The Ethereum Yellow Paper defines the RLP serialization only
|
||||
# for unsigned integers:
|
||||
{.fatal: "RLP serialization of signed integers is not allowed".}
|
||||
discard
|
||||
|
||||
proc append*[T](w: var RlpWriter, val: Option[T]) =
|
||||
if val.isSome:
|
||||
w.append(val.get())
|
||||
else:
|
||||
w.append("")
|
||||
|
||||
proc appendTxLegacy(w: var RlpWriter, tx: Transaction) =
|
||||
w.startList(9)
|
||||
w.append(tx.nonce)
|
||||
w.append(tx.gasPrice)
|
||||
w.append(tx.gasLimit)
|
||||
w.append(tx.to)
|
||||
w.append(tx.value)
|
||||
w.append(tx.payload)
|
||||
w.append(tx.V)
|
||||
w.append(tx.R)
|
||||
w.append(tx.S)
|
||||
|
||||
proc appendTxEip2930(w: var RlpWriter, tx: Transaction) =
|
||||
w.append(1)
|
||||
w.startList(11)
|
||||
w.append(tx.chainId.uint64)
|
||||
w.append(tx.nonce)
|
||||
w.append(tx.gasPrice)
|
||||
w.append(tx.gasLimit)
|
||||
w.append(tx.to)
|
||||
w.append(tx.value)
|
||||
w.append(tx.payload)
|
||||
w.append(tx.accessList)
|
||||
w.append(tx.V)
|
||||
w.append(tx.R)
|
||||
w.append(tx.S)
|
||||
|
||||
proc appendTxEip1559(w: var RlpWriter, tx: Transaction) =
|
||||
w.append(2)
|
||||
w.startList(12)
|
||||
w.append(tx.chainId.uint64)
|
||||
w.append(tx.nonce)
|
||||
w.append(tx.maxPriorityFee)
|
||||
w.append(tx.maxFee)
|
||||
w.append(tx.gasLimit)
|
||||
w.append(tx.to)
|
||||
w.append(tx.value)
|
||||
w.append(tx.payload)
|
||||
w.append(tx.accessList)
|
||||
w.append(tx.V)
|
||||
w.append(tx.R)
|
||||
w.append(tx.S)
|
||||
|
||||
proc append*(w: var RlpWriter, tx: Transaction) =
|
||||
case tx.txType
|
||||
of TxLegacy:
|
||||
w.appendTxLegacy(tx)
|
||||
of TxEip2930:
|
||||
w.appendTxEip2930(tx)
|
||||
of TxEip1559:
|
||||
w.appendTxEip1559(tx)
|
||||
|
||||
template read[T](rlp: var Rlp, val: var T)=
|
||||
val = rlp.read(type val)
|
||||
|
||||
proc read[T](rlp: var Rlp, val: var Option[T])=
|
||||
if rlp.blobLen != 0:
|
||||
val = some(rlp.read(T))
|
||||
else:
|
||||
rlp.skipElem
|
||||
|
||||
proc readTxLegacy(rlp: var Rlp, tx: var Transaction)=
|
||||
tx.txType = TxLegacy
|
||||
rlp.tryEnterList()
|
||||
rlp.read(tx.nonce)
|
||||
rlp.read(tx.gasPrice)
|
||||
rlp.read(tx.gasLimit)
|
||||
rlp.read(tx.to)
|
||||
rlp.read(tx.value)
|
||||
rlp.read(tx.payload)
|
||||
rlp.read(tx.V)
|
||||
rlp.read(tx.R)
|
||||
rlp.read(tx.S)
|
||||
|
||||
proc readTxEip2930(rlp: var Rlp, tx: var Transaction)=
|
||||
tx.txType = TxEip2930
|
||||
rlp.tryEnterList()
|
||||
tx.chainId = rlp.read(uint64).ChainId
|
||||
rlp.read(tx.nonce)
|
||||
rlp.read(tx.gasPrice)
|
||||
rlp.read(tx.gasLimit)
|
||||
rlp.read(tx.to)
|
||||
rlp.read(tx.value)
|
||||
rlp.read(tx.payload)
|
||||
rlp.read(tx.accessList)
|
||||
rlp.read(tx.V)
|
||||
rlp.read(tx.R)
|
||||
rlp.read(tx.S)
|
||||
|
||||
proc readTxEip1559(rlp: var Rlp, tx: var Transaction)=
|
||||
tx.txType = TxEip1559
|
||||
rlp.tryEnterList()
|
||||
tx.chainId = rlp.read(uint64).ChainId
|
||||
rlp.read(tx.nonce)
|
||||
rlp.read(tx.maxPriorityFee)
|
||||
rlp.read(tx.maxFee)
|
||||
rlp.read(tx.gasLimit)
|
||||
rlp.read(tx.to)
|
||||
rlp.read(tx.value)
|
||||
rlp.read(tx.payload)
|
||||
rlp.read(tx.accessList)
|
||||
rlp.read(tx.V)
|
||||
rlp.read(tx.R)
|
||||
rlp.read(tx.S)
|
||||
|
||||
proc readTxTyped(rlp: var Rlp, tx: var Transaction) {.inline.} =
|
||||
# EIP-2718: We MUST decode the first byte as a byte, not `rlp.read(int)`.
|
||||
# If decoded with `rlp.read(int)`, bad transaction data (from the network)
|
||||
# or even just incorrectly framed data for other reasons fails with
|
||||
# any of these misleading error messages:
|
||||
# - "Message too large to fit in memory"
|
||||
# - "Number encoded with a leading zero"
|
||||
# - "Read past the end of the RLP stream"
|
||||
# - "Small number encoded in a non-canonical way"
|
||||
# - "Attempt to read an Int value past the RLP end"
|
||||
# - "The RLP contains a larger than expected Int value"
|
||||
if not rlp.isSingleByte:
|
||||
if not rlp.hasData:
|
||||
raise newException(MalformedRlpError,
|
||||
"Transaction expected but source RLP is empty")
|
||||
raise newException(MalformedRlpError,
|
||||
"TypedTransaction type byte is out of range, must be 0x00 to 0x7f")
|
||||
let txType = rlp.getByteValue
|
||||
rlp.position += 1
|
||||
|
||||
var txVal: TxType
|
||||
if checkedEnumAssign(txVal, txType):
|
||||
case txVal:
|
||||
of TxEip2930:
|
||||
rlp.readTxEip2930(tx)
|
||||
return
|
||||
of TxEip1559:
|
||||
rlp.readTxEip1559(tx)
|
||||
return
|
||||
else:
|
||||
discard
|
||||
|
||||
raise newException(UnsupportedRlpError,
|
||||
"TypedTransaction type must be 1 or 2 in this version, got " & $txType)
|
||||
|
||||
|
||||
proc read*(rlp: var Rlp, T: type Transaction): T =
|
||||
# Individual transactions are encoded and stored as either `RLP([fields..])`
|
||||
# for legacy transactions, or `Type || RLP([fields..])`. Both of these
|
||||
# encodings are byte sequences. The part after `Type` doesn't have to be
|
||||
# RLP in theory, but all types so far use RLP. EIP-2718 covers this.
|
||||
if rlp.isList:
|
||||
rlp.readTxLegacy(result)
|
||||
else:
|
||||
rlp.readTxTyped(result)
|
||||
|
||||
proc read*(rlp: var Rlp,
|
||||
T: (type seq[Transaction]) | (type openArray[Transaction])): seq[Transaction] =
|
||||
# In arrays (sequences), transactions are encoded as either `RLP([fields..])`
|
||||
# for legacy transactions, or `RLP(Type || RLP([fields..]))` for all typed
|
||||
# transactions to date. Spot the extra `RLP(..)` blob encoding, to make it
|
||||
# valid RLP inside a larger RLP. EIP-2976 covers this, "Typed Transactions
|
||||
# over Gossip", although it's not very clear about the blob encoding.
|
||||
#
|
||||
# In practice the extra `RLP(..)` applies to all arrays/sequences of
|
||||
# transactions. In principle, all aggregates (objects etc.), but
|
||||
# arrays/sequences are enough. In `eth/65` protocol this is essential for
|
||||
# the correct encoding/decoding of `Transactions`, `NewBlock`, and
|
||||
# `PooledTransactions` network calls. We need a type match on both
|
||||
# `openArray[Transaction]` and `seq[Transaction]` to catch all cases.
|
||||
if not rlp.isList:
|
||||
raise newException(RlpTypeMismatch,
|
||||
"Transaction list expected, but source RLP is not a list")
|
||||
for item in rlp:
|
||||
var tx: Transaction
|
||||
if item.isList:
|
||||
item.readTxLegacy(tx)
|
||||
else:
|
||||
var rr = rlpFromBytes(rlp.read(Blob))
|
||||
rr.readTxTyped(tx)
|
||||
result.add tx
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter,
|
||||
txs: seq[Transaction] | openArray[Transaction]) {.inline.} =
|
||||
# See above about encoding arrays/sequences of transactions.
|
||||
rlpWriter.startList(txs.len)
|
||||
for tx in txs:
|
||||
if tx.txType == TxLegacy:
|
||||
rlpWriter.append(tx)
|
||||
else:
|
||||
rlpWriter.append(rlp.encode(tx))
|
||||
|
||||
func init*(T: type BlockHashOrNumber, str: string): T
|
||||
{.raises: [ValueError, Defect].} =
|
||||
if str.startsWith "0x":
|
||||
|
@ -538,178 +313,9 @@ func `$`*(x: BlockHashOrNumber): string =
|
|||
else:
|
||||
$x.number
|
||||
|
||||
proc append*(w: var RlpWriter, rec: Receipt) =
|
||||
if rec.receiptType in {Eip2930Receipt, Eip1559Receipt}:
|
||||
w.append(rec.receiptType.int)
|
||||
|
||||
w.startList(4)
|
||||
if rec.isHash:
|
||||
w.append(rec.hash)
|
||||
else:
|
||||
w.append(rec.status.uint8)
|
||||
|
||||
w.append(rec.cumulativeGasUsed)
|
||||
w.append(rec.bloom)
|
||||
w.append(rec.logs)
|
||||
|
||||
proc read*(rlp: var Rlp, T: type Receipt): T =
|
||||
if rlp.isList:
|
||||
result.receiptType = LegacyReceipt
|
||||
else:
|
||||
# EIP 2718
|
||||
let recType = rlp.read(int)
|
||||
if recType notin {1, 2}:
|
||||
raise newException(UnsupportedRlpError,
|
||||
"TxType expect 1 or 2 got " & $recType)
|
||||
result.receiptType = ReceiptType(recType)
|
||||
|
||||
rlp.tryEnterList()
|
||||
if rlp.isBlob and rlp.blobLen in {0, 1}:
|
||||
result.isHash = false
|
||||
result.status = rlp.read(uint8) == 1
|
||||
elif rlp.isBlob and rlp.blobLen == 32:
|
||||
result.isHash = true
|
||||
result.hash = rlp.read(Hash256)
|
||||
else:
|
||||
raise newException(RlpTypeMismatch,
|
||||
"HashOrStatus expected, but the source RLP is not a blob of right size.")
|
||||
|
||||
rlp.read(result.cumulativeGasUsed)
|
||||
rlp.read(result.bloom)
|
||||
rlp.read(result.logs)
|
||||
|
||||
proc read*(rlp: var Rlp, T: type Time): T {.inline.} =
|
||||
result = fromUnix(rlp.read(int64))
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: HashOrNum) =
|
||||
case value.isHash
|
||||
of true:
|
||||
rlpWriter.append(value.hash)
|
||||
else:
|
||||
rlpWriter.append(value.number)
|
||||
|
||||
proc read*(rlp: var Rlp, T: type HashOrNum): T =
|
||||
if rlp.blobLen == 32:
|
||||
result = HashOrNum(isHash: true, hash: rlp.read(Hash256))
|
||||
else:
|
||||
result = HashOrNum(isHash: false, number: rlp.read(BlockNumber))
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, t: Time) {.inline.} =
|
||||
rlpWriter.append(t.toUnix())
|
||||
|
||||
proc append*(w: var RlpWriter, h: BlockHeader) =
|
||||
w.startList(if h.fee.isSome: 16 else: 15)
|
||||
for k, v in fieldPairs(h):
|
||||
when k != "fee":
|
||||
w.append(v)
|
||||
if h.fee.isSome:
|
||||
w.append(h.fee.get())
|
||||
|
||||
proc read*(rlp: var Rlp, T: type BlockHeader): T =
|
||||
let len = rlp.listLen
|
||||
|
||||
if len notin {15, 16}:
|
||||
raise newException(UnsupportedRlpError,
|
||||
"BlockHeader elems should be 15 or 16 got " & $len)
|
||||
|
||||
rlp.tryEnterList()
|
||||
for k, v in fieldPairs(result):
|
||||
when k != "fee":
|
||||
v = rlp.read(type v)
|
||||
|
||||
if len == 16:
|
||||
# EIP-1559
|
||||
result.baseFee = rlp.read(UInt256)
|
||||
|
||||
proc rlpHash*[T](v: T): Hash256 =
|
||||
keccak256.digest(rlp.encode(v))
|
||||
|
||||
func blockHash*(h: BlockHeader): KeccakHash {.inline.} = rlpHash(h)
|
||||
|
||||
proc notImplemented(name: string) =
|
||||
debug "Method not implemented", meth = name
|
||||
|
||||
template hasData*(b: Blob): bool = b.len > 0
|
||||
template hasData*(r: EthResourceRefs): bool = r != nil
|
||||
|
||||
template deref*(b: Blob): auto = b
|
||||
template deref*(o: Option): auto = o.get
|
||||
template deref*(r: EthResourceRefs): auto = r[]
|
||||
|
||||
method genesisHash*(db: AbstractChainDB): KeccakHash
|
||||
{.base, gcsafe, raises: [Defect].} =
|
||||
notImplemented("genesisHash")
|
||||
|
||||
method getBlockHeader*(db: AbstractChainDB, b: HashOrNum,
|
||||
output: var BlockHeader): bool {.base, gcsafe, raises: [RlpError, Defect].} =
|
||||
notImplemented("getBlockHeader")
|
||||
|
||||
proc getBlockHeader*(db: AbstractChainDB, hash: KeccakHash): BlockHeaderRef {.gcsafe.} =
|
||||
new result
|
||||
if not db.getBlockHeader(HashOrNum(isHash: true, hash: hash), result[]):
|
||||
return nil
|
||||
|
||||
proc getBlockHeader*(db: AbstractChainDB, b: BlockNumber): BlockHeaderRef {.gcsafe.} =
|
||||
new result
|
||||
if not db.getBlockHeader(HashOrNum(isHash: false, number: b), result[]):
|
||||
return nil
|
||||
|
||||
# Need to add `RlpError` and sometimes `CatchableError` as the implementations
|
||||
# of these methods in nimbus-eth1 will raise these. Using `CatchableError`
|
||||
# because some can raise for errors not know to this repository such as
|
||||
# `CanonicalHeadNotFound`. It would probably be better to use Result.
|
||||
method getBestBlockHeader*(self: AbstractChainDB): BlockHeader
|
||||
{.base, gcsafe, raises: [RlpError, CatchableError, Defect].} =
|
||||
notImplemented("getBestBlockHeader")
|
||||
|
||||
method getSuccessorHeader*(db: AbstractChainDB, h: BlockHeader,
|
||||
output: var BlockHeader, skip = 0'u): bool
|
||||
{.base, gcsafe, raises: [RlpError, Defect].} =
|
||||
notImplemented("getSuccessorHeader")
|
||||
|
||||
method getAncestorHeader*(db: AbstractChainDB, h: BlockHeader,
|
||||
output: var BlockHeader, skip = 0'u): bool
|
||||
{.base, gcsafe, raises: [RlpError, Defect].} =
|
||||
notImplemented("getAncestorHeader")
|
||||
|
||||
method getBlockBody*(db: AbstractChainDB, blockHash: KeccakHash): BlockBodyRef
|
||||
{.base, gcsafe, raises: [Defect].} =
|
||||
notImplemented("getBlockBody")
|
||||
|
||||
method getReceipt*(db: AbstractChainDB, hash: KeccakHash): ReceiptRef {.base, gcsafe.} =
|
||||
notImplemented("getReceipt")
|
||||
|
||||
method getTrieDB*(db: AbstractChainDB): TrieDatabaseRef
|
||||
{.base, gcsafe, raises: [Defect].} =
|
||||
notImplemented("getTrieDB")
|
||||
|
||||
method getCodeByHash*(db: AbstractChainDB, hash: KeccakHash): Blob {.base, gcsafe.} =
|
||||
notImplemented("getCodeByHash")
|
||||
|
||||
method getSetting*(db: AbstractChainDB, key: string): seq[byte] {.base, gcsafe.} =
|
||||
notImplemented("getSetting")
|
||||
|
||||
method setSetting*(db: AbstractChainDB, key: string, val: openArray[byte]) {.base, gcsafe.} =
|
||||
notImplemented("setSetting")
|
||||
|
||||
method getHeaderProof*(db: AbstractChainDB, req: ProofRequest): Blob {.base, gcsafe.} =
|
||||
notImplemented("getHeaderProof")
|
||||
|
||||
method getProof*(db: AbstractChainDB, req: ProofRequest): Blob {.base, gcsafe.} =
|
||||
notImplemented("getProof")
|
||||
|
||||
method getHelperTrieProof*(db: AbstractChainDB, req: HelperTrieProofRequest): Blob {.base, gcsafe.} =
|
||||
notImplemented("getHelperTrieProof")
|
||||
|
||||
method getTransactionStatus*(db: AbstractChainDB, txHash: KeccakHash): TransactionStatusMsg {.base, gcsafe.} =
|
||||
notImplemented("getTransactionStatus")
|
||||
|
||||
method addTransactions*(db: AbstractChainDB, transactions: openArray[Transaction]) {.base, gcsafe.} =
|
||||
notImplemented("addTransactions")
|
||||
|
||||
method persistBlocks*(db: AbstractChainDB, headers: openArray[BlockHeader], bodies: openArray[BlockBody]): ValidationResult {.base, gcsafe.} =
|
||||
notImplemented("persistBlocks")
|
||||
|
||||
method getForkId*(db: AbstractChainDB, n: BlockNumber): ForkID {.base, gcsafe.} =
|
||||
# EIP 2364/2124
|
||||
notImplemented("getForkId")
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
# Copyright (c) 2022 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
"."/[eth_types, eth_hash_rlp],
|
||||
../rlp
|
||||
|
||||
from stew/objects
|
||||
import checkedEnumAssign
|
||||
|
||||
export
|
||||
eth_types, eth_hash_rlp, rlp
|
||||
|
||||
#
|
||||
# Rlp serialization:
|
||||
#
|
||||
|
||||
proc read*(rlp: var Rlp, T: type StUint): T {.inline.} =
|
||||
if rlp.isBlob:
|
||||
let bytes = rlp.toBytes
|
||||
if bytes.len > 0:
|
||||
# be sure the amount of bytes matches the size of the stint
|
||||
if bytes.len <= sizeof(result):
|
||||
result.initFromBytesBE(bytes)
|
||||
else:
|
||||
raise newException(RlpTypeMismatch, "Unsigned integer expected, but the source RLP has the wrong length")
|
||||
else:
|
||||
result = 0.to(T)
|
||||
else:
|
||||
raise newException(RlpTypeMismatch, "Unsigned integer expected, but the source RLP is a list")
|
||||
|
||||
rlp.skipElem
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: StUint) =
|
||||
if value > 128:
|
||||
let bytes = value.toByteArrayBE
|
||||
let nonZeroBytes = significantBytesBE(bytes)
|
||||
rlpWriter.append bytes.toOpenArray(bytes.len - nonZeroBytes,
|
||||
bytes.len - 1)
|
||||
else:
|
||||
rlpWriter.append(value.truncate(int))
|
||||
|
||||
proc read*(rlp: var Rlp, T: type StInt): T {.inline.} =
|
||||
# The Ethereum Yellow Paper defines the RLP serialization only
|
||||
# for unsigned integers:
|
||||
{.fatal: "RLP serialization of signed integers is not allowed".}
|
||||
discard
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: StInt) =
|
||||
# The Ethereum Yellow Paper defines the RLP serialization only
|
||||
# for unsigned integers:
|
||||
{.fatal: "RLP serialization of signed integers is not allowed".}
|
||||
discard
|
||||
|
||||
proc append*[T](w: var RlpWriter, val: Option[T]) =
|
||||
if val.isSome:
|
||||
w.append(val.get())
|
||||
else:
|
||||
w.append("")
|
||||
|
||||
proc appendTxLegacy(w: var RlpWriter, tx: Transaction) =
|
||||
w.startList(9)
|
||||
w.append(tx.nonce)
|
||||
w.append(tx.gasPrice)
|
||||
w.append(tx.gasLimit)
|
||||
w.append(tx.to)
|
||||
w.append(tx.value)
|
||||
w.append(tx.payload)
|
||||
w.append(tx.V)
|
||||
w.append(tx.R)
|
||||
w.append(tx.S)
|
||||
|
||||
proc appendTxEip2930(w: var RlpWriter, tx: Transaction) =
|
||||
w.append(1)
|
||||
w.startList(11)
|
||||
w.append(tx.chainId.uint64)
|
||||
w.append(tx.nonce)
|
||||
w.append(tx.gasPrice)
|
||||
w.append(tx.gasLimit)
|
||||
w.append(tx.to)
|
||||
w.append(tx.value)
|
||||
w.append(tx.payload)
|
||||
w.append(tx.accessList)
|
||||
w.append(tx.V)
|
||||
w.append(tx.R)
|
||||
w.append(tx.S)
|
||||
|
||||
proc appendTxEip1559(w: var RlpWriter, tx: Transaction) =
|
||||
w.append(2)
|
||||
w.startList(12)
|
||||
w.append(tx.chainId.uint64)
|
||||
w.append(tx.nonce)
|
||||
w.append(tx.maxPriorityFee)
|
||||
w.append(tx.maxFee)
|
||||
w.append(tx.gasLimit)
|
||||
w.append(tx.to)
|
||||
w.append(tx.value)
|
||||
w.append(tx.payload)
|
||||
w.append(tx.accessList)
|
||||
w.append(tx.V)
|
||||
w.append(tx.R)
|
||||
w.append(tx.S)
|
||||
|
||||
proc append*(w: var RlpWriter, tx: Transaction) =
|
||||
case tx.txType
|
||||
of TxLegacy:
|
||||
w.appendTxLegacy(tx)
|
||||
of TxEip2930:
|
||||
w.appendTxEip2930(tx)
|
||||
of TxEip1559:
|
||||
w.appendTxEip1559(tx)
|
||||
|
||||
template read[T](rlp: var Rlp, val: var T)=
|
||||
val = rlp.read(type val)
|
||||
|
||||
proc read[T](rlp: var Rlp, val: var Option[T])=
|
||||
if rlp.blobLen != 0:
|
||||
val = some(rlp.read(T))
|
||||
else:
|
||||
rlp.skipElem
|
||||
|
||||
proc readTxLegacy(rlp: var Rlp, tx: var Transaction)=
|
||||
tx.txType = TxLegacy
|
||||
rlp.tryEnterList()
|
||||
rlp.read(tx.nonce)
|
||||
rlp.read(tx.gasPrice)
|
||||
rlp.read(tx.gasLimit)
|
||||
rlp.read(tx.to)
|
||||
rlp.read(tx.value)
|
||||
rlp.read(tx.payload)
|
||||
rlp.read(tx.V)
|
||||
rlp.read(tx.R)
|
||||
rlp.read(tx.S)
|
||||
|
||||
proc readTxEip2930(rlp: var Rlp, tx: var Transaction)=
|
||||
tx.txType = TxEip2930
|
||||
rlp.tryEnterList()
|
||||
tx.chainId = rlp.read(uint64).ChainId
|
||||
rlp.read(tx.nonce)
|
||||
rlp.read(tx.gasPrice)
|
||||
rlp.read(tx.gasLimit)
|
||||
rlp.read(tx.to)
|
||||
rlp.read(tx.value)
|
||||
rlp.read(tx.payload)
|
||||
rlp.read(tx.accessList)
|
||||
rlp.read(tx.V)
|
||||
rlp.read(tx.R)
|
||||
rlp.read(tx.S)
|
||||
|
||||
proc readTxEip1559(rlp: var Rlp, tx: var Transaction)=
|
||||
tx.txType = TxEip1559
|
||||
rlp.tryEnterList()
|
||||
tx.chainId = rlp.read(uint64).ChainId
|
||||
rlp.read(tx.nonce)
|
||||
rlp.read(tx.maxPriorityFee)
|
||||
rlp.read(tx.maxFee)
|
||||
rlp.read(tx.gasLimit)
|
||||
rlp.read(tx.to)
|
||||
rlp.read(tx.value)
|
||||
rlp.read(tx.payload)
|
||||
rlp.read(tx.accessList)
|
||||
rlp.read(tx.V)
|
||||
rlp.read(tx.R)
|
||||
rlp.read(tx.S)
|
||||
|
||||
proc readTxTyped(rlp: var Rlp, tx: var Transaction) {.inline.} =
|
||||
# EIP-2718: We MUST decode the first byte as a byte, not `rlp.read(int)`.
|
||||
# If decoded with `rlp.read(int)`, bad transaction data (from the network)
|
||||
# or even just incorrectly framed data for other reasons fails with
|
||||
# any of these misleading error messages:
|
||||
# - "Message too large to fit in memory"
|
||||
# - "Number encoded with a leading zero"
|
||||
# - "Read past the end of the RLP stream"
|
||||
# - "Small number encoded in a non-canonical way"
|
||||
# - "Attempt to read an Int value past the RLP end"
|
||||
# - "The RLP contains a larger than expected Int value"
|
||||
if not rlp.isSingleByte:
|
||||
if not rlp.hasData:
|
||||
raise newException(MalformedRlpError,
|
||||
"Transaction expected but source RLP is empty")
|
||||
raise newException(MalformedRlpError,
|
||||
"TypedTransaction type byte is out of range, must be 0x00 to 0x7f")
|
||||
let txType = rlp.getByteValue
|
||||
rlp.position += 1
|
||||
|
||||
var txVal: TxType
|
||||
if checkedEnumAssign(txVal, txType):
|
||||
case txVal:
|
||||
of TxEip2930:
|
||||
rlp.readTxEip2930(tx)
|
||||
return
|
||||
of TxEip1559:
|
||||
rlp.readTxEip1559(tx)
|
||||
return
|
||||
else:
|
||||
discard
|
||||
|
||||
raise newException(UnsupportedRlpError,
|
||||
"TypedTransaction type must be 1 or 2 in this version, got " & $txType)
|
||||
|
||||
|
||||
proc read*(rlp: var Rlp, T: type Transaction): T =
|
||||
# Individual transactions are encoded and stored as either `RLP([fields..])`
|
||||
# for legacy transactions, or `Type || RLP([fields..])`. Both of these
|
||||
# encodings are byte sequences. The part after `Type` doesn't have to be
|
||||
# RLP in theory, but all types so far use RLP. EIP-2718 covers this.
|
||||
if rlp.isList:
|
||||
rlp.readTxLegacy(result)
|
||||
else:
|
||||
rlp.readTxTyped(result)
|
||||
|
||||
proc read*(rlp: var Rlp,
|
||||
T: (type seq[Transaction]) | (type openArray[Transaction])): seq[Transaction] =
|
||||
# In arrays (sequences), transactions are encoded as either `RLP([fields..])`
|
||||
# for legacy transactions, or `RLP(Type || RLP([fields..]))` for all typed
|
||||
# transactions to date. Spot the extra `RLP(..)` blob encoding, to make it
|
||||
# valid RLP inside a larger RLP. EIP-2976 covers this, "Typed Transactions
|
||||
# over Gossip", although it's not very clear about the blob encoding.
|
||||
#
|
||||
# In practice the extra `RLP(..)` applies to all arrays/sequences of
|
||||
# transactions. In principle, all aggregates (objects etc.), but
|
||||
# arrays/sequences are enough. In `eth/65` protocol this is essential for
|
||||
# the correct encoding/decoding of `Transactions`, `NewBlock`, and
|
||||
# `PooledTransactions` network calls. We need a type match on both
|
||||
# `openArray[Transaction]` and `seq[Transaction]` to catch all cases.
|
||||
if not rlp.isList:
|
||||
raise newException(RlpTypeMismatch,
|
||||
"Transaction list expected, but source RLP is not a list")
|
||||
for item in rlp:
|
||||
var tx: Transaction
|
||||
if item.isList:
|
||||
item.readTxLegacy(tx)
|
||||
else:
|
||||
var rr = rlpFromBytes(rlp.read(Blob))
|
||||
rr.readTxTyped(tx)
|
||||
result.add tx
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter,
|
||||
txs: seq[Transaction] | openArray[Transaction]) {.inline.} =
|
||||
# See above about encoding arrays/sequences of transactions.
|
||||
rlpWriter.startList(txs.len)
|
||||
for tx in txs:
|
||||
if tx.txType == TxLegacy:
|
||||
rlpWriter.append(tx)
|
||||
else:
|
||||
rlpWriter.append(rlp.encode(tx))
|
||||
|
||||
proc append*(w: var RlpWriter, rec: Receipt) =
|
||||
if rec.receiptType in {Eip2930Receipt, Eip1559Receipt}:
|
||||
w.append(rec.receiptType.int)
|
||||
|
||||
w.startList(4)
|
||||
if rec.isHash:
|
||||
w.append(rec.hash)
|
||||
else:
|
||||
w.append(rec.status.uint8)
|
||||
|
||||
w.append(rec.cumulativeGasUsed)
|
||||
w.append(rec.bloom)
|
||||
w.append(rec.logs)
|
||||
|
||||
proc read*(rlp: var Rlp, T: type Receipt): T =
|
||||
if rlp.isList:
|
||||
result.receiptType = LegacyReceipt
|
||||
else:
|
||||
# EIP 2718
|
||||
let recType = rlp.read(int)
|
||||
if recType notin {1, 2}:
|
||||
raise newException(UnsupportedRlpError,
|
||||
"TxType expect 1 or 2 got " & $recType)
|
||||
result.receiptType = ReceiptType(recType)
|
||||
|
||||
rlp.tryEnterList()
|
||||
if rlp.isBlob and rlp.blobLen in {0, 1}:
|
||||
result.isHash = false
|
||||
result.status = rlp.read(uint8) == 1
|
||||
elif rlp.isBlob and rlp.blobLen == 32:
|
||||
result.isHash = true
|
||||
result.hash = rlp.read(Hash256)
|
||||
else:
|
||||
raise newException(RlpTypeMismatch,
|
||||
"HashOrStatus expected, but the source RLP is not a blob of right size.")
|
||||
|
||||
rlp.read(result.cumulativeGasUsed)
|
||||
rlp.read(result.bloom)
|
||||
rlp.read(result.logs)
|
||||
|
||||
proc read*(rlp: var Rlp, T: type Time): T {.inline.} =
|
||||
result = fromUnix(rlp.read(int64))
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, value: HashOrNum) =
|
||||
case value.isHash
|
||||
of true:
|
||||
rlpWriter.append(value.hash)
|
||||
else:
|
||||
rlpWriter.append(value.number)
|
||||
|
||||
proc read*(rlp: var Rlp, T: type HashOrNum): T =
|
||||
if rlp.blobLen == 32:
|
||||
result = HashOrNum(isHash: true, hash: rlp.read(Hash256))
|
||||
else:
|
||||
result = HashOrNum(isHash: false, number: rlp.read(BlockNumber))
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, t: Time) {.inline.} =
|
||||
rlpWriter.append(t.toUnix())
|
||||
|
||||
proc append*(w: var RlpWriter, h: BlockHeader) =
|
||||
w.startList(if h.fee.isSome: 16 else: 15)
|
||||
for k, v in fieldPairs(h):
|
||||
when k != "fee":
|
||||
w.append(v)
|
||||
if h.fee.isSome:
|
||||
w.append(h.fee.get())
|
||||
|
||||
proc read*(rlp: var Rlp, T: type BlockHeader): T =
|
||||
let len = rlp.listLen
|
||||
|
||||
if len notin {15, 16}:
|
||||
raise newException(UnsupportedRlpError,
|
||||
"BlockHeader elems should be 15 or 16 got " & $len)
|
||||
|
||||
rlp.tryEnterList()
|
||||
for k, v in fieldPairs(result):
|
||||
when k != "fee":
|
||||
v = rlp.read(type v)
|
||||
|
||||
if len == 16:
|
||||
# EIP-1559
|
||||
result.baseFee = rlp.read(UInt256)
|
||||
|
||||
proc rlpHash*[T](v: T): Hash256 =
|
||||
keccakHash(rlp.encode(v))
|
||||
|
||||
func blockHash*(h: BlockHeader): KeccakHash {.inline.} = rlpHash(h)
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
import
|
||||
../trie/[trie_defs, db, hexary], ../rlp,
|
||||
./eth_types
|
||||
../trie/[trie_defs, db, hexary],
|
||||
../rlp,
|
||||
./chaindb
|
||||
|
||||
export chaindb
|
||||
|
||||
proc getAccount*(db: TrieDatabaseRef,
|
||||
rootHash: KeccakHash,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import
|
||||
nimcrypto/keccak,
|
||||
".."/[common, rlp, keys]
|
||||
../common/eth_types_rlp
|
||||
|
||||
export eth_types_rlp
|
||||
|
||||
const
|
||||
EIP155_CHAIN_ID_OFFSET* = 35'i64
|
||||
|
@ -74,4 +75,4 @@ func rlpEncode*(tx: Transaction): auto =
|
|||
|
||||
func txHashNoSignature*(tx: Transaction): Hash256 =
|
||||
# Hash transaction without signature
|
||||
keccak256.digest(rlpEncode(tx))
|
||||
keccakHash(rlpEncode(tx))
|
||||
|
|
20
eth/keys.nim
20
eth/keys.nim
|
@ -18,7 +18,7 @@ import
|
|||
std/strformat,
|
||||
secp256k1, bearssl/hash as bhash, bearssl/rand,
|
||||
stew/[byteutils, objects, results],
|
||||
nimcrypto/[hash, keccak]
|
||||
./common/eth_hash
|
||||
|
||||
from nimcrypto/utils import burnMem
|
||||
|
||||
|
@ -124,16 +124,16 @@ func toRaw*(sig: SignatureNR): array[RawSignatureNRSize, byte] {.borrow.}
|
|||
|
||||
func toAddress*(pubkey: PublicKey, with0x = true): string =
|
||||
## Convert public key to hexadecimal string address.
|
||||
var hash = keccak256.digest(pubkey.toRaw())
|
||||
var hash = keccakHash(pubkey.toRaw())
|
||||
result = if with0x: "0x" else: ""
|
||||
result.add(toHex(toOpenArray(hash.data, 12, len(hash.data) - 1)))
|
||||
|
||||
func toChecksumAddress*(pubkey: PublicKey, with0x = true): string =
|
||||
## Convert public key to checksumable mixed-case address (EIP-55).
|
||||
result = if with0x: "0x" else: ""
|
||||
var hash1 = keccak256.digest(pubkey.toRaw())
|
||||
var hash1 = keccakHash(pubkey.toRaw())
|
||||
var hhash1 = toHex(toOpenArray(hash1.data, 12, len(hash1.data) - 1))
|
||||
var hash2 = keccak256.digest(hhash1)
|
||||
var hash2 = keccakHash(hhash1)
|
||||
var hhash2 = toHex(hash2.data)
|
||||
for i in 0..<len(hhash1):
|
||||
if hhash2[i] >= '0' and hhash2[i] <= '7':
|
||||
|
@ -161,7 +161,7 @@ func validateChecksumAddress*(a: string): bool =
|
|||
address &= chr(ord(ch) - ord('A') + ord('a'))
|
||||
else:
|
||||
return false
|
||||
var hash = keccak256.digest(address)
|
||||
var hash = keccakHash(address)
|
||||
var hexhash = toHex(hash.data)
|
||||
for i in 0..<len(address):
|
||||
if hexhash[i] >= '0' and hexhash[i] <= '7':
|
||||
|
@ -176,7 +176,7 @@ func validateChecksumAddress*(a: string): bool =
|
|||
|
||||
func toCanonicalAddress*(pubkey: PublicKey): array[20, byte] =
|
||||
## Convert public key to canonical address.
|
||||
var hash = keccak256.digest(pubkey.toRaw())
|
||||
var hash = keccakHash(pubkey.toRaw())
|
||||
copyMem(addr result[0], addr hash.data[12], 20)
|
||||
|
||||
func `$`*(pubkey: PublicKey): string =
|
||||
|
@ -206,28 +206,28 @@ func sign*(seckey: PrivateKey, msg: SkMessage): Signature =
|
|||
Signature(signRecoverable(SkSecretKey(seckey), msg))
|
||||
|
||||
func sign*(seckey: PrivateKey, msg: openArray[byte]): Signature =
|
||||
let hash = keccak256.digest(msg)
|
||||
let hash = keccakHash(msg)
|
||||
sign(seckey, SkMessage(hash.data))
|
||||
|
||||
func signNR*(seckey: PrivateKey, msg: SkMessage): SignatureNR =
|
||||
SignatureNR(sign(SkSecretKey(seckey), msg))
|
||||
|
||||
func signNR*(seckey: PrivateKey, msg: openArray[byte]): SignatureNR =
|
||||
let hash = keccak256.digest(msg)
|
||||
let hash = keccakHash(msg)
|
||||
signNR(seckey, SkMessage(hash.data))
|
||||
|
||||
func recover*(sig: Signature, msg: SkMessage): SkResult[PublicKey] =
|
||||
recover(SkRecoverableSignature(sig), msg).mapConvert(PublicKey)
|
||||
|
||||
func recover*(sig: Signature, msg: openArray[byte]): SkResult[PublicKey] =
|
||||
let hash = keccak256.digest(msg)
|
||||
let hash = keccakHash(msg)
|
||||
recover(sig, SkMessage(hash.data))
|
||||
|
||||
func verify*(sig: SignatureNR, msg: SkMessage, key: PublicKey): bool =
|
||||
verify(SkSignature(sig), msg, SkPublicKey(key))
|
||||
|
||||
func verify*(sig: SignatureNR, msg: openArray[byte], key: PublicKey): bool =
|
||||
let hash = keccak256.digest(msg)
|
||||
let hash = keccakHash(msg)
|
||||
verify(sig, SkMessage(hash.data), key)
|
||||
|
||||
func ecdhRaw*(seckey: PrivateKey, pubkey: PublicKey): SharedSecret =
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
import
|
||||
std/[tables, algorithm, random],
|
||||
chronos, chronos/timer, chronicles,
|
||||
./keys, ./common/eth_types, ./p2p/private/p2p_types,
|
||||
./keys, ./common/chaindb, ./p2p/private/p2p_types,
|
||||
./p2p/[kademlia, discovery, enode, peer_pool, rlpx]
|
||||
|
||||
export
|
||||
|
|
|
@ -10,9 +10,12 @@ import
|
|||
std/[deques, tables],
|
||||
chronos,
|
||||
stew/results,
|
||||
".."/../[rlp, keys], ".."/../common/eth_types,
|
||||
../../common/chaindb,
|
||||
".."/../[rlp, keys],
|
||||
".."/[enode, kademlia, discovery, rlpxcrypt]
|
||||
|
||||
export chaindb
|
||||
|
||||
const
|
||||
useSnappy* = defined(useSnappy)
|
||||
|
||||
|
|
|
@ -342,7 +342,7 @@ proc queueMessage(node: EthereumNode, msg: Message): bool =
|
|||
|
||||
proc postMessage*(node: EthereumNode, pubKey = none[PublicKey](),
|
||||
symKey = none[SymKey](), src = none[PrivateKey](),
|
||||
ttl: uint32, topic: Topic, payload: seq[byte],
|
||||
ttl: uint32, topic: whisper_types.Topic, payload: seq[byte],
|
||||
padding = none[seq[byte]](), powTime = 1'f,
|
||||
powTarget = defaultMinPow,
|
||||
targetPeer = none[NodeId]()): bool =
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
nimcrypto, stew/results
|
||||
nimcrypto/[bcmode, keccak, rijndael, utils], stew/results
|
||||
from auth import ConnectionSecret
|
||||
|
||||
export results
|
||||
|
|
|
@ -27,7 +27,7 @@ proc run(s: FullNodeSyncer) {.async.} =
|
|||
|
||||
# Ensure we have the state for our current head.
|
||||
head = await self.wait(self.chaindb.coro_get_canonical_head())
|
||||
if head.state_root != BLANK_ROOT_HASH and head.state_root not in self.base_db:
|
||||
if head.state_root != EMPTY_ROOT_HASH and head.state_root not in self.base_db:
|
||||
self.logger.info(
|
||||
"Missing state for current head (#%d), downloading it", head.block_number)
|
||||
downloader = StateDownloader(
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import
|
||||
std/[tables, hashes, sets],
|
||||
nimcrypto/[hash, keccak],
|
||||
"."/[trie_defs, db_tracing]
|
||||
|
||||
type
|
||||
|
@ -54,9 +53,6 @@ proc get*(db: TrieDatabaseRef, key: openArray[byte]): seq[byte] {.gcsafe.}
|
|||
proc del*(db: TrieDatabaseRef, key: openArray[byte]) {.gcsafe.}
|
||||
proc beginTransaction*(db: TrieDatabaseRef): DbTransaction {.gcsafe.}
|
||||
|
||||
proc keccak*(r: openArray[byte]): KeccakHash =
|
||||
keccak256.digest r
|
||||
|
||||
proc get*(db: MemoryLayer, key: openArray[byte]): seq[byte] =
|
||||
result = db.records.getOrDefault(@key).value
|
||||
traceGet key, result
|
||||
|
|
|
@ -21,9 +21,6 @@ type
|
|||
template len(key: TrieNodeKey): int =
|
||||
key.usedBytes.int
|
||||
|
||||
proc keccak*(r: openArray[byte]): KeccakHash =
|
||||
keccak256.digest r
|
||||
|
||||
template asDbKey(k: TrieNodeKey): untyped =
|
||||
doAssert k.usedBytes == 32
|
||||
k.hash.data
|
||||
|
@ -350,11 +347,11 @@ proc getBranch*(self: HexaryTrie; key: openArray[byte]): seq[seq[byte]] =
|
|||
getBranchAux(self.db, node, initNibbleRange(key), result)
|
||||
|
||||
proc dbDel(t: var HexaryTrie, data: openArray[byte]) =
|
||||
if data.len >= 32: t.prune(data.keccak.data)
|
||||
if data.len >= 32: t.prune(data.keccakHash.data)
|
||||
|
||||
proc dbPut(db: DB, data: openArray[byte]): TrieNodeKey
|
||||
{.raises: [Defect].} =
|
||||
result.hash = data.keccak
|
||||
result.hash = data.keccakHash
|
||||
result.usedBytes = 32
|
||||
put(db, result.asDbKey, data)
|
||||
|
||||
|
@ -549,7 +546,7 @@ proc mergeAt(self: var HexaryTrie, orig: Rlp, origHash: KeccakHash,
|
|||
proc mergeAt(self: var HexaryTrie, rlp: Rlp,
|
||||
key: NibblesSeq, value: openArray[byte],
|
||||
isInline = false): seq[byte] =
|
||||
self.mergeAt(rlp, rlp.rawData.keccak, key, value, isInline)
|
||||
self.mergeAt(rlp, rlp.rawData.keccakHash, key, value, isInline)
|
||||
|
||||
proc mergeAtAux(self: var HexaryTrie, output: var RlpWriter, orig: Rlp,
|
||||
key: NibblesSeq, value: openArray[byte]) =
|
||||
|
@ -662,13 +659,13 @@ proc put*(self: var HexaryTrie; key, value: openArray[byte]) =
|
|||
self.root = self.db.dbPut(newRootBytes)
|
||||
|
||||
proc put*(self: var SecureHexaryTrie; key, value: openArray[byte]) =
|
||||
put(HexaryTrie(self), key.keccak.data, value)
|
||||
put(HexaryTrie(self), key.keccakHash.data, value)
|
||||
|
||||
proc get*(self: SecureHexaryTrie; key: openArray[byte]): seq[byte] =
|
||||
return get(HexaryTrie(self), key.keccak.data)
|
||||
return get(HexaryTrie(self), key.keccakHash.data)
|
||||
|
||||
proc del*(self: var SecureHexaryTrie; key: openArray[byte]) =
|
||||
del(HexaryTrie(self), key.keccak.data)
|
||||
del(HexaryTrie(self), key.keccakHash.data)
|
||||
|
||||
proc rootHash*(self: SecureHexaryTrie): KeccakHash {.borrow.}
|
||||
proc rootHashHex*(self: SecureHexaryTrie): string {.borrow.}
|
||||
|
@ -686,7 +683,7 @@ proc isValidBranch*(branch: seq[seq[byte]], rootHash: KeccakHash, key, value: se
|
|||
var db = newMemoryDB()
|
||||
for node in branch:
|
||||
doAssert(node.len != 0)
|
||||
let nodeHash = hexary.keccak(node)
|
||||
let nodeHash = keccakHash(node)
|
||||
db.put(nodeHash.data, node)
|
||||
|
||||
var trie = initHexaryTrie(db, rootHash)
|
||||
|
|
|
@ -29,9 +29,9 @@ proc verifyProofAux*(proof: seq[seq[byte]], root, key, value: openArray[byte]):
|
|||
if node.len != 32: return false
|
||||
if path[i]: # right
|
||||
# reuse curHash without more alloc
|
||||
curHash.data.keccakHash(node, curHash.data)
|
||||
curHash = keccakHash(node, curHash.data)
|
||||
else:
|
||||
curHash.data.keccakHash(curHash.data, node)
|
||||
curHash = keccakHash(curHash.data, node)
|
||||
|
||||
result = curHash.data == root
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import
|
||||
nimcrypto/hash,
|
||||
../rlp
|
||||
../common/eth_hash_rlp
|
||||
|
||||
export eth_hash_rlp
|
||||
|
||||
type
|
||||
KeccakHash* = MDigest[256]
|
||||
|
||||
TrieError* = object of CatchableError
|
||||
# A common base type of all Trie errors.
|
||||
|
||||
|
@ -21,9 +20,3 @@ const
|
|||
blankStringHash* = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest
|
||||
emptyRlp* = @[128.byte]
|
||||
emptyRlpHash* = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest
|
||||
|
||||
proc read*(rlp: var Rlp, T: typedesc[MDigest]): T {.inline.} =
|
||||
result.data = rlp.read(type(result.data))
|
||||
|
||||
proc append*(rlpWriter: var RlpWriter, a: MDigest) {.inline.} =
|
||||
rlpWriter.append(a.data)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import
|
||||
stew/byteutils,
|
||||
nimcrypto/[hash, keccak],
|
||||
./trie_defs
|
||||
|
||||
export trie_defs
|
||||
|
||||
template checkValidHashZ*(x: untyped) =
|
||||
when x.type isnot KeccakHash:
|
||||
doAssert(x.len == 32 or x.len == 0)
|
||||
|
@ -14,21 +15,3 @@ proc hashFromHex*(bits: static[int], input: string): MDigest[bits] =
|
|||
MDigest(data: hexToByteArray[bits div 8](input))
|
||||
|
||||
template hashFromHex*(s: static[string]): untyped = hashFromHex(s.len * 4, s)
|
||||
|
||||
proc keccakHash*(input: openArray[byte]): KeccakHash =
|
||||
keccak256.digest(input)
|
||||
|
||||
proc keccakHash*(dest: var openArray[byte], a, b: openArray[byte]) =
|
||||
var ctx: keccak256
|
||||
ctx.init()
|
||||
if a.len != 0:
|
||||
ctx.update(a[0].unsafeAddr, uint(a.len))
|
||||
if b.len != 0:
|
||||
ctx.update(b[0].unsafeAddr, uint(b.len))
|
||||
ctx.finish dest
|
||||
ctx.clear()
|
||||
|
||||
proc keccakHash*(a, b: openArray[byte]): KeccakHash =
|
||||
var s: array[32, byte]
|
||||
keccakHash(s, a, b)
|
||||
KeccakHash(data: s)
|
||||
|
|
Loading…
Reference in New Issue