From ad1202f98a0c98f271001bfa346075fae5d2bfad Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 10 Dec 2018 19:04:34 +0700 Subject: [PATCH] initial make_receipt --- nimbus.nimble | 1 + nimbus/db/db_chain.nim | 9 ++- nimbus/p2p/chain.nim | 59 +++++++++++++++++-- nimbus/p2p/executor.nim | 4 +- nimbus/vm/computation.nim | 12 +--- .../utils/macros_procs_opcodes.nim | 16 +++-- nimbus/vm/stack.nim | 4 ++ nimbus/vm_state.nim | 12 ++++ nimbus/vm_state_transactions.nim | 1 + nimbus/vm_types.nim | 3 +- 10 files changed, 91 insertions(+), 30 deletions(-) diff --git a/nimbus.nimble b/nimbus.nimble index e4ee07a15..0f267d275 100644 --- a/nimbus.nimble +++ b/nimbus.nimble @@ -20,6 +20,7 @@ requires "nim >= 0.18.1", "eth_p2p", "eth_keyfile", "eth_keys", + "eth_bloom", "https://github.com/status-im/nim-bncurve >= 1.0.1" proc buildBinary(name: string, srcDir = ".", lang = "c") = diff --git a/nimbus/db/db_chain.nim b/nimbus/db/db_chain.nim index c4f640d72..f3ac0e30f 100644 --- a/nimbus/db/db_chain.nim +++ b/nimbus/db/db_chain.nim @@ -186,14 +186,19 @@ proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool = ## Returns True if the header with the given block hash is in our DB. self.db.contains(genericHashKey(blockHash).toOpenArray) -iterator getReceipts*(self: BaseChainDB; header: BlockHeader; receiptClass: typedesc): Receipt = +proc persistReceipts*(self: BaseChainDB, receipts: openArray[Receipt]) = + var trie = initHexaryTrie(self.db) + for idx, rec in receipts: + trie.put(rlp.encode(idx).toRange, rlp.encode(rec).toRange) + +iterator getReceipts*(self: BaseChainDB; header: BlockHeader): Receipt = var receiptDb = initHexaryTrie(self.db, header.receiptRoot) var receiptIdx = 0 while true: let receiptKey = rlp.encode(receiptIdx).toRange if receiptKey in receiptDb: let receiptData = receiptDb.get(receiptKey) - yield rlp.decode(receiptData, receiptClass) + yield rlp.decode(receiptData, Receipt) else: break inc receiptIdx diff --git a/nimbus/p2p/chain.nim b/nimbus/p2p/chain.nim index 8fd656f86..1b7d102e6 100644 --- a/nimbus/p2p/chain.nim +++ b/nimbus/p2p/chain.nim @@ -1,6 +1,35 @@ import ../db/[db_chain, state_db], eth_common, chronicles, ../vm_state, ../vm_types, ../transaction, ranges, ../vm/[computation, interpreter_dispatch, message], ../constants, stint, nimcrypto, - ../vm_state_transactions, sugar, ../utils, eth_trie/db, ../tracer, ./executor, json + ../vm_state_transactions, sugar, ../utils, eth_trie/db, ../tracer, ./executor, json, + eth_bloom + +type + # these types need to eradicated + # once eth_bloom and eth_common sync'ed + Bloom = eth_common.BloomFilter + LogsBloom = eth_bloom.BloomFilter + +func logsBloom(logs: openArray[Log]): LogsBloom = + for log in logs: + result.incl log.address + for topic in log.topics: + result.incl topic + +func createBloom*(receipts: openArray[Receipt]): Bloom = + var bloom: LogsBloom + for receipt in receipts: + bloom.value = bloom.value or logsBloom(receipt.logs).value + result = bloom.value.toByteArrayBE + +proc makeReceipt(vmState: BaseVMState, stateRoot: Hash256, cumulativeGasUsed: GasInt): Receipt = + # TODO: post byzantium fork use status instead of rootHash + # currently, vmState.rootHash vs stateDb.rootHash can be different + # need to wait #188 solved + #result.stateRootOrStatus = hashOrStatus(vmState.blockHeader.stateRoot) + result.stateRootOrStatus = hashOrStatus(stateRoot) + result.cumulativeGasUsed = cumulativeGasUsed + result.logs = vmState.getAndClearLogEntries() + result.bloom = logsBloom(result.logs).value.toByteArrayBE type Chain* = ref object of AbstractChainDB @@ -30,7 +59,7 @@ method getSuccessorHeader*(c: Chain, h: BlockHeader, output: var BlockHeader): b method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef = result = nil -method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]) = +method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarray[BlockBody]): bool = # Run the VM here assert(headers.len == bodies.len) @@ -45,6 +74,7 @@ method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarr assert(head.blockNumber == headers[i].blockNumber - 1) var stateDb = newAccountStateDB(c.db.db, head.stateRoot, c.db.pruneTrie) var gasReward = 0.u256 + var receipts = newSeq[Receipt](bodies[i].transactions.len) assert(bodies[i].transactions.calcTxRoot == headers[i].txRoot) @@ -52,16 +82,21 @@ method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarr # assert(head.blockNumber == headers[i].blockNumber - 1) let vmState = newBaseVMState(head, c.db) assert(bodies[i].transactions.len != 0) + var cumulativeGasUsed = GasInt(0) if bodies[i].transactions.len != 0: trace "Has transactions", blockNumber = headers[i].blockNumber, blockHash = headers[i].blockHash - for t in bodies[i].transactions: + for txIndex, tx in bodies[i].transactions: var sender: EthAddress - if t.getSender(sender): - gasReward += processTransaction(stateDb, t, sender, vmState) + if tx.getSender(sender): + let txFee = processTransaction(stateDb, tx, sender, vmState) + gasReward += txFee + let gasUsed = (txFee div tx.gasPrice.u256).truncate(GasInt) + cumulativeGasUsed += gasUsed else: assert(false, "Could not get sender") + receipts[txIndex] = makeReceipt(vmState, stateDb.rootHash, cumulativeGasUsed) var mainReward = blockReward + gasReward #echo "mainReward = ", mainReward , " with blockReward = ", blockReward, " and gasReward = ", gasReward @@ -87,10 +122,24 @@ method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarr assert(headers[i].stateRoot == stateDb.rootHash) + let bloom = createBloom(receipts) + if headers[i].bloom != bloom: + debug "wrong bloom in block", blockNumber = headers[i].blockNumber + assert(headers[i].bloom == bloom) + + let receiptRoot = calcReceiptRoot(receipts) + if headers[i].receiptRoot != receiptRoot: + debug "wrong receipt in block", blockNumber = headers[i].blockNumber, receiptRoot, valid=headers[i].receiptRoot + echo "CA:", receipts + return false + assert(headers[i].receiptRoot == receiptRoot) + discard c.db.persistHeaderToDb(headers[i]) assert(c.db.getCanonicalHead().blockHash == headers[i].blockHash) c.db.persistTransactions(headers[i].blockNumber, bodies[i].transactions) + c.db.persistReceipts(receipts) transaction.commit() + result = true diff --git a/nimbus/p2p/executor.nim b/nimbus/p2p/executor.nim index d54f5df41..eefffd848 100644 --- a/nimbus/p2p/executor.nim +++ b/nimbus/p2p/executor.nim @@ -1,6 +1,7 @@ import ../db/[db_chain, state_db], ../transaction, eth_common, ../vm_state, ../vm_types, ../vm_state_transactions, ranges, - chronicles, ../vm/[computation, interpreter_dispatch, message] + chronicles, ../vm/[computation, interpreter_dispatch, message], + ../rpc/hexstrings, byteutils, nimcrypto proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddress, vmState: BaseVMState): UInt256 = ## Process the transaction, write the results to db. @@ -64,5 +65,4 @@ proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddr refund += t.value db.addBalance(sender, refund) - return gasUsed.u256 * t.gasPrice.u256 diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index a8069f9ed..db4824313 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -26,7 +26,6 @@ proc newBaseComputation*(vmState: BaseVMState, blockNumber: UInt256, message: Me result.gasMeter.init(message.gas) result.children = @[] result.accountsToDelete = initTable[EthAddress, EthAddress]() - result.logEntries = @[] result.code = newCodeStream(message.code) # result.rawOutput = "0x" result.gasCosts = @@ -261,8 +260,8 @@ proc registerAccountForDeletion*(c: var BaseComputation, beneficiary: EthAddress "registered for deletion multiple times") c.accountsToDelete[c.msg.storageAddress] = beneficiary -proc addLogEntry*(c: var BaseComputation, account: EthAddress, topics: seq[UInt256], data: seq[byte]) = - c.logEntries.add((account, topics, data)) +proc addLogEntry*(c: var BaseComputation, log: Log) {.inline.} = + c.vmState.addLogEntry(log) # many methods are basically TODO, but they still return valid values # in order to test some existing code @@ -275,13 +274,6 @@ func getAccountsForDeletion*(c: BaseComputation): seq[EthAddress] = for account in c.accountsToDelete.keys: result.add(account) -proc getLogEntries*(c: BaseComputation): seq[(string, seq[UInt256], string)] = - # TODO - if c.isError: - result = @[] - else: - result = @[] - proc getGasRefund*(c: BaseComputation): GasInt = if c.isError: result = 0 diff --git a/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim b/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim index 9f576d83f..d247d1e16 100644 --- a/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim +++ b/nimbus/vm/interpreter/utils/macros_procs_opcodes.nim @@ -9,7 +9,7 @@ # Macros to facilitate opcode procs creation import - macros, strformat, stint, + macros, strformat, stint, eth_common, ../../computation, ../../stack, ../../code_stream, ../../../constants, ../../../vm_types, ../../memory, ../../../errors, ../../message, ../../interpreter/[gas_meter, opcode_values], @@ -108,20 +108,18 @@ proc logImpl(c: var BaseComputation, opcode: Op, topicCount: int) = if memPos < 0 or len < 0: raise newException(OutOfBoundsRead, "Out of bounds memory access") - var topics = newSeqOfCap[UInt256](topicCount) + var log: Log + log.topics = newSeqOfCap[Topic](topicCount) for i in 0 ..< topicCount: - topics.add(c.stack.popInt()) + log.topics.add(c.stack.popTopic()) c.gasMeter.consumeGas( c.gasCosts[opcode].m_handler(c.memory.len, memPos, len), reason="Memory expansion, Log topic and data gas cost") c.memory.extend(memPos, len) - let logData = c.memory.read(memPos, len) - addLogEntry( - c, - account = c.msg.storageAddress, - topics = topics, - data = logData) + log.data = c.memory.read(memPos, len) + log.address = c.msg.storageAddress + c.addLogEntry(log) template genLog*() = proc log0*(c: var BaseComputation) {.inline.} = logImpl(c, Log0, 0) diff --git a/nimbus/vm/stack.nim b/nimbus/vm/stack.nim index 442c3a836..17d281845 100644 --- a/nimbus/vm/stack.nim +++ b/nimbus/vm/stack.nim @@ -33,6 +33,7 @@ proc toStackElement(v: MDigest, elem: var StackElement) {.inline.} = elem.initFr proc fromStackElement(elem: StackElement, v: var UInt256) {.inline.} = v = elem proc fromStackElement(elem: StackElement, v: var EthAddress) {.inline.} = v[0 .. ^1] = elem.toByteArrayBE().toOpenArray(12, 31) proc fromStackElement(elem: StackElement, v: var Hash256) {.inline.} = v.data = elem.toByteArrayBE() +proc fromStackElement(elem: StackElement, v: var Topic) {.inline.} = v = elem.toByteArrayBE() proc toStackElement(v: openarray[byte], elem: var StackElement) {.inline.} = # TODO: This needs to go @@ -87,6 +88,9 @@ proc popInt*(stack: var Stack, numItems: static[int]): auto {.inline.} = proc popAddress*(stack: var Stack): EthAddress {.inline.} = popAux(stack, result) +proc popTopic*(stack: var Stack): Topic {.inline.} = + popAux(stack, result) + proc newStack*(): Stack = new(result) result.values = @[] diff --git a/nimbus/vm_state.nim b/nimbus/vm_state.nim index 2a808a9b9..1bbde02ae 100644 --- a/nimbus/vm_state.nim +++ b/nimbus/vm_state.nim @@ -33,6 +33,7 @@ proc newBaseVMState*(header: BlockHeader, chainDB: BaseChainDB, tracerFlags: set result.chaindb = chainDB result.tracer.initTracer(tracerFlags) result.tracingEnabled = TracerFlags.EnableTracing in tracerFlags + result.logEntries = @[] method blockhash*(vmState: BaseVMState): Hash256 = vmState.blockHeader.hash @@ -116,3 +117,14 @@ proc beginTransaction*(vmState: BaseVMState): DbTransaction = proc getTracingResult*(vmState: BaseVMState): JsonNode = assert(vmState.tracingEnabled) vmState.tracer.trace + +proc addLogEntry*(vmState: BaseVMState, log: Log) = + vmState.logEntries.add(log) + +proc getAndClearLogEntries*(vmState: BaseVMState): seq[Log] = + shallowCopy(result, vmState.logEntries) + vmState.logEntries = @[] + +proc clearLogs*(vmState: BaseVMState) = + # call this when computation error + vmState.logEntries.setLen(0) diff --git a/nimbus/vm_state_transactions.nim b/nimbus/vm_state_transactions.nim index c5944804d..48bcc158d 100644 --- a/nimbus/vm_state_transactions.nim +++ b/nimbus/vm_state_transactions.nim @@ -115,6 +115,7 @@ proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: Ba debug "execComputation() error", isError = c.isError if c.tracingEnabled: c.traceError() + vmState.clearLogs() return t.gasLimit.u256 * t.gasPrice.u256 method executeTransaction(vmState: BaseVMState, transaction: Transaction): (BaseComputation, BlockHeader) {.base.}= diff --git a/nimbus/vm_types.nim b/nimbus/vm_types.nim index 40f4ac279..c279ff1bb 100644 --- a/nimbus/vm_types.nim +++ b/nimbus/vm_types.nim @@ -15,13 +15,13 @@ import type BaseVMState* = ref object of RootObj prevHeaders* : seq[BlockHeader] - # receipts*: chaindb* : BaseChainDB accessLogs* : AccessLogs blockHeader* : BlockHeader name* : string tracingEnabled*: bool tracer* : TransactionTracer + logEntries* : seq[Log] AccessLogs* = ref object reads*: Table[string, string] @@ -53,7 +53,6 @@ type rawOutput*: seq[byte] returnData*: seq[byte] error*: Error - logEntries*: seq[(EthAddress, seq[UInt256], seq[byte])] shouldEraseReturnData*: bool accountsToDelete*: Table[EthAddress, EthAddress] opcodes*: Table[Op, proc(computation: var BaseComputation){.nimcall.}]