| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.tuweni.blockprocessor |
| |
| import org.apache.tuweni.eth.Address |
| import org.apache.tuweni.eth.BlockHeader |
| import org.apache.tuweni.eth.Hash |
| import org.apache.tuweni.eth.LogsBloomFilter |
| import org.apache.tuweni.eth.Transaction |
| import org.apache.tuweni.eth.TransactionReceipt |
| import org.apache.tuweni.eth.precompiles.PrecompileContract |
| import org.apache.tuweni.eth.repository.BlockchainRepository |
| import org.apache.tuweni.eth.repository.TransientStateRepository |
| import org.apache.tuweni.evm.EthereumVirtualMachine |
| import org.apache.tuweni.evm.HardFork |
| import org.apache.tuweni.evm.impl.EvmVmImpl |
| import org.apache.tuweni.evm.impl.StepListener |
| import org.apache.tuweni.rlp.RLP |
| import org.apache.tuweni.trie.MerklePatriciaTrie |
| import org.apache.tuweni.units.bigints.UInt256 |
| import org.apache.tuweni.units.ethereum.Gas |
| import org.slf4j.LoggerFactory |
| |
| /** |
| * A block processor executing blocks, executing in sequence transactions |
| * and committing data to a blockchain repository. |
| * |
| * @param chainId the chain identifier |
| */ |
| class BlockProcessor(val chainId: UInt256) { |
| |
| companion object { |
| val logger = LoggerFactory.getLogger(BlockProcessor::class.java) |
| } |
| |
| /** |
| * Executes a state transition. |
| * |
| * @param parentBlock the parent block header |
| * @param coinbase the coinbase of the block |
| * @param gasLimit the gas limit of the block |
| * @param gasUsed the gas used already |
| * @param transactions the list of transactions to execute |
| * @param repository the blockchain repository to execute against |
| * @param stepListener an optional listener that can follow the steps of the execution |
| * @param precompiles the map of precompiles |
| * @param hardFork the current hard fork |
| */ |
| suspend fun execute( |
| parentBlock: BlockHeader, |
| coinbase: Address, |
| gasLimit: Gas, |
| gasUsed: Gas, |
| timestamp: UInt256, |
| transactions: List<Transaction>, |
| repository: BlockchainRepository, |
| precompiles: Map<Address, PrecompileContract>, |
| hardFork: HardFork, |
| stepListener: StepListener? = null, |
| ): BlockProcessorResult { |
| val stateChanges = TransientStateRepository(repository) |
| val vm = EthereumVirtualMachine(stateChanges, repository, precompiles, { EvmVmImpl.create(stepListener) }) |
| vm.start() |
| |
| val bloomFilter = LogsBloomFilter() |
| |
| val transactionsTrie = MerklePatriciaTrie.storingBytes() |
| val receiptsTrie = MerklePatriciaTrie.storingBytes() |
| val allReceipts = mutableListOf<TransactionReceipt>() |
| |
| var counter = 0L |
| var allGasUsed = Gas.ZERO |
| var success = true |
| for (tx in transactions) { |
| val txProcessor = TransactionProcessor(vm, hardFork, repository, stateChanges) |
| val indexKey = RLP.encodeValue(UInt256.valueOf(counter).trimLeadingZeros()) |
| transactionsTrie.put(indexKey, tx.toBytes()) |
| val txResult = txProcessor.execute(tx, timestamp, chainId, parentBlock, gasLimit, gasUsed, allGasUsed, coinbase, bloomFilter) |
| if (txResult.success) { |
| val receipt = txResult.receipt!! |
| receiptsTrie.put(indexKey, receipt.toBytes()) |
| allReceipts.add(receipt) |
| allGasUsed = allGasUsed.add(txResult.gasUsed) |
| } else { |
| success = false |
| } |
| counter++ |
| } |
| |
| val block = ProtoBlock( |
| SealableHeader( |
| parentBlock.hash, |
| Hash.fromBytes(stateChanges.stateRootHash()), |
| Hash.fromBytes(transactionsTrie.rootHash()), |
| Hash.fromBytes(receiptsTrie.rootHash()), |
| bloomFilter.toBytes(), |
| parentBlock.number.add(1), |
| parentBlock.gasLimit, |
| allGasUsed, |
| ), |
| ProtoBlockBody(transactions), |
| allReceipts, |
| stateChanges |
| ) |
| return BlockProcessorResult(block, success) |
| } |
| } |
| |
| data class BlockProcessorResult(val block: ProtoBlock, val success: Boolean) |