blob: 590d7d3e1b2b82d890e49ab8bfe26e76d66f7387 [file] [log] [blame]
/*
* 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)