diff --git a/nimbus/p2p/chain.nim b/nimbus/p2p/chain.nim index ff835ed5e..33cba076b 100644 --- a/nimbus/p2p/chain.nim +++ b/nimbus/p2p/chain.nim @@ -71,80 +71,23 @@ method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarr debug "Number of headers not matching number of bodies" return ValidationResult.Error - let blockReward = 5.u256 * pow(10.u256, 18) # 5 ETH - let transaction = c.db.db.beginTransaction() defer: transaction.dispose() trace "Persisting blocks", fromBlock = headers[0].blockNumber, toBlock = headers[^1].blockNumber for i in 0 ..< headers.len: let head = c.db.getCanonicalHead() - var stateDb = newAccountStateDB(c.db.db, head.stateRoot, c.db.pruneTrie) - var receipts = newSeq[Receipt](bodies[i].transactions.len) + let vmState = if headers[i].txRoot != BLANK_ROOT_HASH: newBaseVMState(head, c.db) + else: nil + let success = processBlock(c.db, head, headers[i], bodies[i], vmState) - if bodies[i].transactions.calcTxRoot != headers[i].txRoot: - debug "Mismatched txRoot", i - return ValidationResult.Error + if not success: + let ttrace = traceTransaction(c.db, headers[i], bodies[i], bodies[i].transactions.len - 1, {}) + trace "NIMBUS TRACE", transactionTrace=ttrace.pretty() + let dump = dumpBlockState(headers[i], bodies[i]) + trace "NIMBUS STATE DUMP", dump=dump.pretty() - if headers[i].txRoot != BLANK_ROOT_HASH: - let vmState = newBaseVMState(head, c.db) - if bodies[i].transactions.len == 0: - debug "No transactions in body", i - return ValidationResult.Error - else: - trace "Has transactions", blockNumber = headers[i].blockNumber, blockHash = headers[i].blockHash - - var cumulativeGasUsed = GasInt(0) - for txIndex, tx in bodies[i].transactions: - var sender: EthAddress - if tx.getSender(sender): - let txFee = processTransaction(stateDb, tx, sender, vmState) - - # perhaps this can be altered somehow - # or processTransaction return only gasUsed - # a `div` here is ugly and possibly div by zero - let gasUsed = (txFee div tx.gasPrice.u256).truncate(GasInt) - cumulativeGasUsed += gasUsed - - # miner fee - stateDb.addBalance(headers[i].coinbase, txFee) - else: - debug "Could not get sender", i, tx - return ValidationResult.Error - receipts[txIndex] = makeReceipt(vmState, stateDb.rootHash, cumulativeGasUsed) - - var mainReward = blockReward - if headers[i].ommersHash != EMPTY_UNCLE_HASH: - let h = c.db.persistUncles(bodies[i].uncles) - if h != headers[i].ommersHash: - debug "Uncle hash mismatch" - return ValidationResult.Error - for u in 0 ..< bodies[i].uncles.len: - var uncleReward = bodies[i].uncles[u].blockNumber + 8.u256 - uncleReward -= headers[i].blockNumber - uncleReward = uncleReward * blockReward - uncleReward = uncleReward div 8.u256 - stateDb.addBalance(bodies[i].uncles[u].coinbase, uncleReward) - mainReward += blockReward div 32.u256 - - # Reward beneficiary - stateDb.addBalance(headers[i].coinbase, mainReward) - - if headers[i].stateRoot != stateDb.rootHash: - error "Wrong state root in block", blockNumber = headers[i].blockNumber, expected = headers[i].stateRoot, actual = stateDb.rootHash, arrivedFrom = c.db.getCanonicalHead().stateRoot - # this one is a show stopper until we are confident in our VM's - # compatibility with the main chain - raise(newException(Exception, "Wrong state root in block")) - - 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 receiptRoot in block", blockNumber = headers[i].blockNumber, actual=receiptRoot, expected=headers[i].receiptRoot - assert(headers[i].receiptRoot == receiptRoot) + assert(success) discard c.db.persistHeaderToDb(headers[i]) if c.db.getCanonicalHead().blockHash != headers[i].blockHash: diff --git a/nimbus/p2p/executor.nim b/nimbus/p2p/executor.nim index eefffd848..82b602393 100644 --- a/nimbus/p2p/executor.nim +++ b/nimbus/p2p/executor.nim @@ -1,7 +1,8 @@ import ../db/[db_chain, state_db], ../transaction, eth_common, ../vm_state, ../vm_types, ../vm_state_transactions, ranges, chronicles, ../vm/[computation, interpreter_dispatch, message], - ../rpc/hexstrings, byteutils, nimcrypto + ../rpc/hexstrings, byteutils, nimcrypto, + ../utils, ../constants proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddress, vmState: BaseVMState): UInt256 = ## Process the transaction, write the results to db. @@ -66,3 +67,74 @@ proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddr db.addBalance(sender, refund) return gasUsed.u256 * t.gasPrice.u256 + + +proc processBlock*(chainDB: BaseChainDB, head, header: BlockHeader, body: BlockBody, vmState: BaseVMState): bool = + let blockReward = 5.u256 * pow(10.u256, 18) # 5 ETH + + var stateDb = newAccountStateDB(c.db.db, head.stateRoot, c.db.pruneTrie) + var receipts = newSeq[Receipt](bodies[i].transactions.len) + + if bodies[i].transactions.calcTxRoot != headers[i].txRoot: + debug "Mismatched txRoot", i + return ValidationResult.Error + + if headers[i].txRoot != BLANK_ROOT_HASH: + let vmState = newBaseVMState(head, c.db) + if bodies[i].transactions.len == 0: + debug "No transactions in body", i + return ValidationResult.Error + else: + trace "Has transactions", blockNumber = headers[i].blockNumber, blockHash = headers[i].blockHash + + var cumulativeGasUsed = GasInt(0) + for txIndex, tx in bodies[i].transactions: + var sender: EthAddress + if tx.getSender(sender): + let txFee = processTransaction(stateDb, tx, sender, vmState) + + # perhaps this can be altered somehow + # or processTransaction return only gasUsed + # a `div` here is ugly and possibly div by zero + let gasUsed = (txFee div tx.gasPrice.u256).truncate(GasInt) + cumulativeGasUsed += gasUsed + + # miner fee + stateDb.addBalance(headers[i].coinbase, txFee) + else: + debug "Could not get sender", i, tx + return ValidationResult.Error + receipts[txIndex] = makeReceipt(vmState, stateDb.rootHash, cumulativeGasUsed) + + var mainReward = blockReward + if headers[i].ommersHash != EMPTY_UNCLE_HASH: + let h = c.db.persistUncles(bodies[i].uncles) + if h != headers[i].ommersHash: + debug "Uncle hash mismatch" + return ValidationResult.Error + for u in 0 ..< bodies[i].uncles.len: + var uncleReward = bodies[i].uncles[u].blockNumber + 8.u256 + uncleReward -= headers[i].blockNumber + uncleReward = uncleReward * blockReward + uncleReward = uncleReward div 8.u256 + stateDb.addBalance(bodies[i].uncles[u].coinbase, uncleReward) + mainReward += blockReward div 32.u256 + + # Reward beneficiary + stateDb.addBalance(headers[i].coinbase, mainReward) + + if headers[i].stateRoot != stateDb.rootHash: + error "Wrong state root in block", blockNumber = headers[i].blockNumber, expected = headers[i].stateRoot, actual = stateDb.rootHash, arrivedFrom = c.db.getCanonicalHead().stateRoot + # this one is a show stopper until we are confident in our VM's + # compatibility with the main chain + raise(newException(Exception, "Wrong state root in block")) + + 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 receiptRoot in block", blockNumber = headers[i].blockNumber, actual=receiptRoot, expected=headers[i].receiptRoot + assert(headers[i].receiptRoot == receiptRoot) \ No newline at end of file diff --git a/nimbus/rpc/debug.nim b/nimbus/rpc/debug.nim index a2c533272..5f3054ace 100644 --- a/nimbus/rpc/debug.nim +++ b/nimbus/rpc/debug.nim @@ -69,8 +69,10 @@ proc setupDebugRpc*(chainDB: BaseChainDB, rpcsrv: RpcServer) = ## "latest" or "pending", as in the default block parameter. let header = chainDB.headerFromTag(quantityTag) + blockHash = chainDB.getBlockHash(header.blockNumber) + body = getBlockBody(blockHash) - dumpBlockState(header) + dumpBlockState(header, body) rpcsrv.rpc("debug_dumpBlockStateByHash") do(data: EthHashStr) -> JsonNode: ## Retrieves the state that corresponds to the block number and returns @@ -80,5 +82,7 @@ proc setupDebugRpc*(chainDB: BaseChainDB, rpcsrv: RpcServer) = let h = data.toHash header = chainDB.getBlockHeader(h) + blockHash = chainDB.getBlockHash(header.blockNumber) + body = getBlockBody(blockHash) - dumpBlockState(header) + dumpBlockState(header, body) diff --git a/nimbus/tracer.nim b/nimbus/tracer.nim index 5f53abe2c..fc9192db9 100644 --- a/nimbus/tracer.nim +++ b/nimbus/tracer.nim @@ -9,7 +9,7 @@ proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader = proc prefixHex(x: openArray[byte]): string = "0x" & toHex(x, true) -proc toJson(db: AccountStateDB, address: EthAddress, name: string): JsonNode = +proc accountToJson(db: AccountStateDB, address: EthAddress, name: string): JsonNode = result = newJObject() result["name"] = %name result["address"] = %($address) @@ -28,14 +28,14 @@ proc toJson(db: AccountStateDB, address: EthAddress, name: string): JsonNode = result["storage"] = storage proc captureStateAccount(n: JsonNode, db: AccountStateDB, sender: EthAddress, header: BlockHeader, tx: Transaction) = - n.add toJson(db, sender, "sender") - n.add toJson(db, header.coinbase, "miner") + n.add accountToJson(db, sender, "sender") + n.add accountToJson(db, header.coinbase, "miner") if tx.isContractCreation: let contractAddress = generateAddress(sender, tx.accountNonce) - n.add toJson(db, contractAddress, "contract") + n.add accountToJson(db, contractAddress, "contract") else: - n.add toJson(db, tx.to, "recipient") + n.add accountToJson(db, tx.to, "recipient") proc traceTransaction*(db: BaseChainDB, header: BlockHeader, body: BlockBody, txIndex: int, tracerFlags: set[TracerFlags] = {}): JsonNode = @@ -88,5 +88,5 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader, n[k.prefixHex] = %v.prefixHex result["state"] = n -proc dumpBlockState*(header: BlockHeader): JsonNode = +proc dumpBlockState*(header: BlockHeader, body: BlockBody): JsonNode = discard \ No newline at end of file