diff --git a/eth/bloom.nim b/eth/bloom.nim index a8735a6..0976deb 100644 --- a/eth/bloom.nim +++ b/eth/bloom.nim @@ -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)) diff --git a/eth/common.nim b/eth/common.nim index abe3686..3cba37d 100644 --- a/eth/common.nim +++ b/eth/common.nim @@ -1,2 +1,2 @@ -import ./common/[eth_types, utils] -export eth_types, utils +import ./common/[eth_types_rlp, utils] +export eth_types_rlp, utils diff --git a/eth/common/chaindb.nim b/eth/common/chaindb.nim new file mode 100644 index 0000000..697da62 --- /dev/null +++ b/eth/common/chaindb.nim @@ -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") diff --git a/eth/common/eth_hash.nim b/eth/common/eth_hash.nim new file mode 100644 index 0000000..f4fbb3f --- /dev/null +++ b/eth/common/eth_hash.nim @@ -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 diff --git a/eth/common/eth_hash_rlp.nim b/eth/common/eth_hash_rlp.nim new file mode 100644 index 0000000..4fcfc04 --- /dev/null +++ b/eth/common/eth_hash_rlp.nim @@ -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) diff --git a/eth/common/eth_types.nim b/eth/common/eth_types.nim index 77b0ee8..d7c5b21 100644 --- a/eth/common/eth_types.nim +++ b/eth/common/eth_types.nim @@ -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") diff --git a/eth/common/eth_types_rlp.nim b/eth/common/eth_types_rlp.nim new file mode 100644 index 0000000..cf0b119 --- /dev/null +++ b/eth/common/eth_types_rlp.nim @@ -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) + diff --git a/eth/common/state_accessors.nim b/eth/common/state_accessors.nim index d4d6370..f3f8ad0 100644 --- a/eth/common/state_accessors.nim +++ b/eth/common/state_accessors.nim @@ -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, diff --git a/eth/common/transaction.nim b/eth/common/transaction.nim index dba9010..e9c939a 100644 --- a/eth/common/transaction.nim +++ b/eth/common/transaction.nim @@ -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)) diff --git a/eth/keys.nim b/eth/keys.nim index cd43a47..7699466 100644 --- a/eth/keys.nim +++ b/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..= '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..= '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 = diff --git a/eth/p2p.nim b/eth/p2p.nim index ec14ae1..f5982e7 100644 --- a/eth/p2p.nim +++ b/eth/p2p.nim @@ -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 diff --git a/eth/p2p/private/p2p_types.nim b/eth/p2p/private/p2p_types.nim index 24d81d0..d0ea286 100644 --- a/eth/p2p/private/p2p_types.nim +++ b/eth/p2p/private/p2p_types.nim @@ -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) diff --git a/eth/p2p/rlpx_protocols/whisper_protocol.nim b/eth/p2p/rlpx_protocols/whisper_protocol.nim index 019a4f1..7f69096 100644 --- a/eth/p2p/rlpx_protocols/whisper_protocol.nim +++ b/eth/p2p/rlpx_protocols/whisper_protocol.nim @@ -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 = diff --git a/eth/p2p/rlpxcrypt.nim b/eth/p2p/rlpxcrypt.nim index 193bb41..8545f3b 100644 --- a/eth/p2p/rlpxcrypt.nim +++ b/eth/p2p/rlpxcrypt.nim @@ -13,7 +13,7 @@ {.push raises: [Defect].} import - nimcrypto, stew/results + nimcrypto/[bcmode, keccak, rijndael, utils], stew/results from auth import ConnectionSecret export results diff --git a/eth/p2p/sync.nim b/eth/p2p/sync.nim index 0529213..6e3e053 100644 --- a/eth/p2p/sync.nim +++ b/eth/p2p/sync.nim @@ -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( diff --git a/eth/trie/db.nim b/eth/trie/db.nim index 0e1212f..71e7a4d 100644 --- a/eth/trie/db.nim +++ b/eth/trie/db.nim @@ -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 diff --git a/eth/trie/hexary.nim b/eth/trie/hexary.nim index a3d7fe9..f8c6289 100644 --- a/eth/trie/hexary.nim +++ b/eth/trie/hexary.nim @@ -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) diff --git a/eth/trie/sparse_proofs.nim b/eth/trie/sparse_proofs.nim index c72a689..14c3eda 100644 --- a/eth/trie/sparse_proofs.nim +++ b/eth/trie/sparse_proofs.nim @@ -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 diff --git a/eth/trie/trie_defs.nim b/eth/trie/trie_defs.nim index 9a317e1..df0c137 100644 --- a/eth/trie/trie_defs.nim +++ b/eth/trie/trie_defs.nim @@ -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) diff --git a/eth/trie/trie_utils.nim b/eth/trie/trie_utils.nim index 7bb8e2c..e87a85a 100644 --- a/eth/trie/trie_utils.nim +++ b/eth/trie/trie_utils.nim @@ -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)