PrivateMigrationBlockProcessor.java
/*
* Copyright ConsenSys AG.
*
* Licensed 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.privacy.storage.migration;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PrivateMigrationBlockProcessor {
private static final Logger LOG = LoggerFactory.getLogger(PrivateMigrationBlockProcessor.class);
static final int MAX_GENERATION = 6;
private final MainnetTransactionProcessor transactionProcessor;
private final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
final Wei blockReward;
private final boolean skipZeroBlockRewards;
private final MiningBeneficiaryCalculator miningBeneficiaryCalculator;
public PrivateMigrationBlockProcessor(
final MainnetTransactionProcessor transactionProcessor,
final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory,
final Wei blockReward,
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
final boolean skipZeroBlockRewards) {
this.transactionProcessor = transactionProcessor;
this.transactionReceiptFactory = transactionReceiptFactory;
this.blockReward = blockReward;
this.miningBeneficiaryCalculator = miningBeneficiaryCalculator;
this.skipZeroBlockRewards = skipZeroBlockRewards;
}
public PrivateMigrationBlockProcessor(final ProtocolSpec protocolSpec) {
this(
protocolSpec.getTransactionProcessor(),
protocolSpec.getTransactionReceiptFactory(),
protocolSpec.getBlockReward(),
protocolSpec.getMiningBeneficiaryCalculator(),
protocolSpec.isSkipZeroBlockRewards());
}
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final BlockHeader blockHeader,
final List<Transaction> transactions,
final List<BlockHeader> ommers) {
long gasUsed = 0;
final List<TransactionReceipt> receipts = new ArrayList<>();
for (final Transaction transaction : transactions) {
final long remainingGasBudget = blockHeader.getGasLimit() - gasUsed;
if (Long.compareUnsigned(transaction.getGasLimit(), remainingGasBudget) > 0) {
LOG.warn(
"Transaction processing error: transaction gas limit {} exceeds available block budget"
+ " remaining {}",
transaction.getGasLimit(),
remainingGasBudget);
return BlockProcessingResult.FAILED;
}
final WorldUpdater worldStateUpdater = worldState.updater();
final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain);
final Address miningBeneficiary =
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);
final TransactionProcessingResult result =
transactionProcessor.processTransaction(
blockchain,
worldStateUpdater,
blockHeader,
transaction,
miningBeneficiary,
blockHashLookup,
true,
TransactionValidationParams.processingBlock(),
Wei.ZERO);
if (result.isInvalid()) {
return BlockProcessingResult.FAILED;
}
worldStateUpdater.commit();
gasUsed = transaction.getGasLimit() - result.getGasRemaining() + gasUsed;
final TransactionReceipt transactionReceipt =
transactionReceiptFactory.create(transaction.getType(), result, worldState, gasUsed);
receipts.add(transactionReceipt);
}
if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) {
return BlockProcessingResult.FAILED;
}
BlockProcessingOutputs blockProcessingOutput = new BlockProcessingOutputs(worldState, receipts);
return new BlockProcessingResult(Optional.of(blockProcessingOutput));
}
private boolean rewardCoinbase(
final MutableWorldState worldState,
final ProcessableBlockHeader header,
final List<BlockHeader> ommers,
final boolean skipZeroBlockRewards) {
if (skipZeroBlockRewards && blockReward.isZero()) {
return true;
}
final Wei coinbaseReward = blockReward.add(blockReward.multiply(ommers.size()).divide(32));
final WorldUpdater updater = worldState.updater();
final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase());
coinbase.incrementBalance(coinbaseReward);
for (final BlockHeader ommerHeader : ommers) {
if (ommerHeader.getNumber() - header.getNumber() > MAX_GENERATION) {
LOG.warn(
"Block processing error: ommer block number {} more than {} generations current block"
+ " number {}",
ommerHeader.getNumber(),
MAX_GENERATION,
header.getNumber());
return false;
}
final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase());
final long distance = header.getNumber() - ommerHeader.getNumber();
final Wei ommerReward = blockReward.subtract(blockReward.multiply(distance).divide(8));
ommerCoinbase.incrementBalance(ommerReward);
}
return true;
}
}