-
Notifications
You must be signed in to change notification settings - Fork 891
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR introduces support for the Gotham fork on the Ethereum Classic Network including: ECIP-1017: Monetary Policy and Final Modification to the Ethereum Classic Emission Schedule ECIP 1039: Monetary policy rounding specification ECIP 1041: Remove Difficulty Bomb Signed-off-by: edwardmack <ed@edwardmack.com>
- Loading branch information
1 parent
a6da172
commit e60aa3f
Showing
10 changed files
with
422 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
...reum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* 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.mainnet; | ||
|
||
import org.hyperledger.besu.ethereum.chain.Blockchain; | ||
import org.hyperledger.besu.ethereum.core.Address; | ||
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.core.Wei; | ||
import org.hyperledger.besu.ethereum.core.WorldState; | ||
import org.hyperledger.besu.ethereum.core.WorldUpdater; | ||
import org.hyperledger.besu.ethereum.vm.BlockHashLookup; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
public abstract class AbstractBlockProcessor implements BlockProcessor { | ||
@FunctionalInterface | ||
public interface TransactionReceiptFactory { | ||
|
||
TransactionReceipt create( | ||
TransactionProcessor.Result result, WorldState worldState, long gasUsed); | ||
} | ||
|
||
private static final Logger LOG = LogManager.getLogger(); | ||
|
||
static final int MAX_GENERATION = 6; | ||
|
||
public static class Result implements BlockProcessor.Result { | ||
|
||
private static final AbstractBlockProcessor.Result FAILED = | ||
new AbstractBlockProcessor.Result(false, null); | ||
|
||
private final boolean successful; | ||
|
||
private final List<TransactionReceipt> receipts; | ||
|
||
public static AbstractBlockProcessor.Result successful( | ||
final List<TransactionReceipt> receipts) { | ||
return new AbstractBlockProcessor.Result(true, ImmutableList.copyOf(receipts)); | ||
} | ||
|
||
public static AbstractBlockProcessor.Result failed() { | ||
return FAILED; | ||
} | ||
|
||
Result(final boolean successful, final List<TransactionReceipt> receipts) { | ||
this.successful = successful; | ||
this.receipts = receipts; | ||
} | ||
|
||
@Override | ||
public List<TransactionReceipt> getReceipts() { | ||
return receipts; | ||
} | ||
|
||
@Override | ||
public boolean isSuccessful() { | ||
return successful; | ||
} | ||
} | ||
|
||
private final TransactionProcessor transactionProcessor; | ||
|
||
private final MainnetBlockProcessor.TransactionReceiptFactory transactionReceiptFactory; | ||
|
||
final Wei blockReward; | ||
|
||
private final boolean skipZeroBlockRewards; | ||
|
||
private final MiningBeneficiaryCalculator miningBeneficiaryCalculator; | ||
|
||
public AbstractBlockProcessor( | ||
final TransactionProcessor transactionProcessor, | ||
final MainnetBlockProcessor.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; | ||
} | ||
|
||
@Override | ||
public AbstractBlockProcessor.Result 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 AbstractBlockProcessor.Result.failed(); | ||
} | ||
|
||
final WorldUpdater worldStateUpdater = worldState.updater(); | ||
final BlockHashLookup blockHashLookup = new BlockHashLookup(blockHeader, blockchain); | ||
final Address miningBeneficiary = | ||
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); | ||
|
||
final TransactionProcessor.Result result = | ||
transactionProcessor.processTransaction( | ||
blockchain, | ||
worldStateUpdater, | ||
blockHeader, | ||
transaction, | ||
miningBeneficiary, | ||
blockHashLookup, | ||
true, | ||
TransactionValidationParams.processingBlock()); | ||
if (result.isInvalid()) { | ||
return AbstractBlockProcessor.Result.failed(); | ||
} | ||
|
||
worldStateUpdater.commit(); | ||
gasUsed = transaction.getGasLimit() - result.getGasRemaining() + gasUsed; | ||
final TransactionReceipt transactionReceipt = | ||
transactionReceiptFactory.create(result, worldState, gasUsed); | ||
receipts.add(transactionReceipt); | ||
} | ||
|
||
if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) { | ||
return AbstractBlockProcessor.Result.failed(); | ||
} | ||
|
||
worldState.persist(); | ||
return AbstractBlockProcessor.Result.successful(receipts); | ||
} | ||
|
||
abstract boolean rewardCoinbase( | ||
final MutableWorldState worldState, | ||
final ProcessableBlockHeader header, | ||
final List<BlockHeader> ommers, | ||
final boolean skipZeroBlockRewards); | ||
} |
124 changes: 124 additions & 0 deletions
124
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicBlockProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* 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.mainnet; | ||
|
||
import org.hyperledger.besu.ethereum.core.BlockHeader; | ||
import org.hyperledger.besu.ethereum.core.MutableAccount; | ||
import org.hyperledger.besu.ethereum.core.MutableWorldState; | ||
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; | ||
import org.hyperledger.besu.ethereum.core.Wei; | ||
import org.hyperledger.besu.ethereum.core.WorldUpdater; | ||
|
||
import java.math.BigInteger; | ||
import java.util.List; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
public class ClassicBlockProcessor extends AbstractBlockProcessor { | ||
|
||
private static final Logger LOG = LogManager.getLogger(); | ||
|
||
private static final long ERA_LENGTH = 5_000_000L; | ||
|
||
public ClassicBlockProcessor( | ||
final TransactionProcessor transactionProcessor, | ||
final TransactionReceiptFactory transactionReceiptFactory, | ||
final Wei blockReward, | ||
final MiningBeneficiaryCalculator miningBeneficiaryCalculator, | ||
final boolean skipZeroBlockRewards) { | ||
super( | ||
transactionProcessor, | ||
transactionReceiptFactory, | ||
blockReward, | ||
miningBeneficiaryCalculator, | ||
skipZeroBlockRewards); | ||
} | ||
|
||
@Override | ||
boolean rewardCoinbase( | ||
final MutableWorldState worldState, | ||
final ProcessableBlockHeader header, | ||
final List<BlockHeader> ommers, | ||
final boolean skipZeroBlockRewards) { | ||
if (skipZeroBlockRewards && blockReward.isZero()) { | ||
return true; | ||
} | ||
final int blockEra = getBlockEra(header.getNumber(), ERA_LENGTH); | ||
final Wei winnerReward = getBlockWinnerRewardByEra(blockEra); | ||
final Wei uncleInclusionReward = winnerReward.dividedBy(32); | ||
final Wei coinbaseReward = winnerReward.plus(uncleInclusionReward.times(ommers.size())); | ||
final WorldUpdater updater = worldState.updater(); | ||
final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()).getMutable(); | ||
|
||
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()).getMutable(); | ||
ommerCoinbase.incrementBalance(uncleInclusionReward); | ||
} | ||
|
||
updater.commit(); | ||
return true; | ||
} | ||
|
||
// GetBlockEra gets which "Era" a given block is within, given an era length (ecip-1017 has | ||
// era=5,000,000 blocks) | ||
// Returns a zero-index era number, so "Era 1": 0, "Era 2": 1, "Era 3": 2 ... | ||
private int getBlockEra(final long blockNumber, final long eraLength) { | ||
// if genesis block or impossible nagative-numbered block, return zero | ||
if (blockNumber < 0) return 0; | ||
long remainder = (blockNumber - 1) % eraLength; | ||
long base = blockNumber - remainder; | ||
long d = base / eraLength; | ||
return Math.toIntExact(d); | ||
} | ||
|
||
// getRewardByEra gets a block reward at disinflation rate. | ||
// Constants MaxBlockReward, DisinflationRateQuotient, and DisinflationRateDivisor assumed. | ||
private Wei getBlockWinnerRewardByEra(final int era) { | ||
if (era == 0) { | ||
return this.blockReward; | ||
} | ||
|
||
// MaxBlockReward _r_ * (4/5)**era == MaxBlockReward * (4**era) / (5**era) | ||
// since (q/d)**n == q**n / d**n | ||
// qed | ||
|
||
BigInteger disinflationRateQuotient = BigInteger.valueOf(4); | ||
BigInteger q; | ||
q = disinflationRateQuotient.pow(era); | ||
|
||
BigInteger disinflationRateDivisor = BigInteger.valueOf(5); | ||
BigInteger d; | ||
d = disinflationRateDivisor.pow(era); | ||
|
||
BigInteger maximumBlockReward = BigInteger.valueOf(this.blockReward.toLong()); | ||
BigInteger r; | ||
r = maximumBlockReward.multiply(q); | ||
|
||
r = r.divide(d); | ||
return Wei.of(r); | ||
} | ||
} |
Oops, something went wrong.