Skip to content

Commit 65ed623

Browse files
shemnonmacfarla
authored andcommitted
Warm Coinbase (EIP-3651) (hyperledger#4620)
Add a flag to MainnetTransactionProcessor to add the miningBeneficiary to the list of pre-warmed addresses. Have shandong fork set this flag to true by default. Signed-off-by: Danno Ferrin <[email protected]> Signed-off-by: Sally MacFarlane <[email protected]>
1 parent ee34ee2 commit 65ed623

File tree

4 files changed

+145
-39
lines changed

4 files changed

+145
-39
lines changed

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@
5050
public class ClassicProtocolSpecs {
5151
private static final Wei MAX_BLOCK_REWARD = Wei.fromEth(5);
5252

53+
private ClassicProtocolSpecs() {
54+
// utility class
55+
}
56+
5357
public static ProtocolSpecBuilder classicRecoveryInitDefinition(
5458
final OptionalInt contractSizeLimit,
5559
final OptionalInt configStackSizeLimit,
@@ -80,7 +84,7 @@ public static ProtocolSpecBuilder tangerineWhistleDefinition(
8084

8185
public static ProtocolSpecBuilder dieHardDefinition(
8286
final Optional<BigInteger> chainId,
83-
final OptionalInt configContractSizeLimit,
87+
final OptionalInt ignoredConfigContractSizeLimit,
8488
final OptionalInt configStackSizeLimit,
8589
final boolean quorumCompatibilityMode,
8690
final EvmConfiguration evmConfiguration) {
@@ -197,6 +201,7 @@ public static ProtocolSpecBuilder atlantisDefinition(
197201
contractCreationProcessor,
198202
messageCallProcessor,
199203
true,
204+
false,
200205
stackSizeLimit,
201206
FeeMarket.legacy(),
202207
CoinbaseFeePriceCalculator.frontier()))

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java

+24
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public static ProtocolSpecBuilder frontierDefinition(
134134
contractCreationProcessor,
135135
messageCallProcessor,
136136
false,
137+
false,
137138
stackSizeLimit,
138139
FeeMarket.legacy(),
139140
CoinbaseFeePriceCalculator.frontier()))
@@ -324,6 +325,7 @@ public static ProtocolSpecBuilder spuriousDragonDefinition(
324325
contractCreationProcessor,
325326
messageCallProcessor,
326327
true,
328+
false,
327329
stackSizeLimit,
328330
FeeMarket.legacy(),
329331
CoinbaseFeePriceCalculator.frontier()))
@@ -540,6 +542,7 @@ static ProtocolSpecBuilder londonDefinition(
540542
contractCreationProcessor,
541543
messageCallProcessor,
542544
true,
545+
false,
543546
stackSizeLimit,
544547
londonFeeMarket,
545548
CoinbaseFeePriceCalculator.eip1559()))
@@ -638,6 +641,12 @@ static ProtocolSpecBuilder shandongDefinition(
638641
final GenesisConfigOptions genesisConfigOptions,
639642
final boolean quorumCompatibilityMode,
640643
final EvmConfiguration evmConfiguration) {
644+
final int stackSizeLimit = configStackSizeLimit.orElse(MessageFrame.DEFAULT_MAX_STACK_SIZE);
645+
final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L);
646+
final BaseFeeMarket londonFeeMarket =
647+
genesisConfigOptions.isZeroBaseFee()
648+
? FeeMarket.zeroBaseFee(londonForkBlockNumber)
649+
: FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas());
641650

642651
return parisDefinition(
643652
chainId,
@@ -651,6 +660,21 @@ static ProtocolSpecBuilder shandongDefinition(
651660
(gasCalculator, jdCacheConfig) ->
652661
MainnetEVMs.shandong(
653662
gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration))
663+
.transactionProcessorBuilder(
664+
(gasCalculator,
665+
transactionValidator,
666+
contractCreationProcessor,
667+
messageCallProcessor) ->
668+
new MainnetTransactionProcessor(
669+
gasCalculator,
670+
transactionValidator,
671+
contractCreationProcessor,
672+
messageCallProcessor,
673+
true,
674+
true,
675+
stackSizeLimit,
676+
londonFeeMarket,
677+
CoinbaseFeePriceCalculator.eip1559()))
654678
.name("Shandong");
655679
}
656680

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java

+29-22
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,32 @@ public class MainnetTransactionProcessor {
7575

7676
protected final boolean clearEmptyAccounts;
7777

78+
protected final boolean warmCoinbase;
79+
7880
protected final FeeMarket feeMarket;
7981
protected final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator;
8082

83+
public MainnetTransactionProcessor(
84+
final GasCalculator gasCalculator,
85+
final MainnetTransactionValidator transactionValidator,
86+
final AbstractMessageProcessor contractCreationProcessor,
87+
final AbstractMessageProcessor messageCallProcessor,
88+
final boolean clearEmptyAccounts,
89+
final boolean warmCoinbase,
90+
final int maxStackSize,
91+
final FeeMarket feeMarket,
92+
final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator) {
93+
this.gasCalculator = gasCalculator;
94+
this.transactionValidator = transactionValidator;
95+
this.contractCreationProcessor = contractCreationProcessor;
96+
this.messageCallProcessor = messageCallProcessor;
97+
this.clearEmptyAccounts = clearEmptyAccounts;
98+
this.warmCoinbase = warmCoinbase;
99+
this.maxStackSize = maxStackSize;
100+
this.feeMarket = feeMarket;
101+
this.coinbaseFeePriceCalculator = coinbaseFeePriceCalculator;
102+
}
103+
81104
/**
82105
* Applies a transaction to the current system state.
83106
*
@@ -228,27 +251,8 @@ public TransactionProcessingResult processTransaction(
228251
null);
229252
}
230253

231-
public MainnetTransactionProcessor(
232-
final GasCalculator gasCalculator,
233-
final MainnetTransactionValidator transactionValidator,
234-
final AbstractMessageProcessor contractCreationProcessor,
235-
final AbstractMessageProcessor messageCallProcessor,
236-
final boolean clearEmptyAccounts,
237-
final int maxStackSize,
238-
final FeeMarket feeMarket,
239-
final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator) {
240-
this.gasCalculator = gasCalculator;
241-
this.transactionValidator = transactionValidator;
242-
this.contractCreationProcessor = contractCreationProcessor;
243-
this.messageCallProcessor = messageCallProcessor;
244-
this.clearEmptyAccounts = clearEmptyAccounts;
245-
this.maxStackSize = maxStackSize;
246-
this.feeMarket = feeMarket;
247-
this.coinbaseFeePriceCalculator = coinbaseFeePriceCalculator;
248-
}
249-
250254
public TransactionProcessingResult processTransaction(
251-
final Blockchain blockchain,
255+
final Blockchain ignoredBlockchain,
252256
final WorldUpdater worldState,
253257
final ProcessableBlockHeader blockHeader,
254258
final Transaction transaction,
@@ -314,6 +318,9 @@ public TransactionProcessingResult processTransaction(
314318
storageList.putAll(address, storageKeys);
315319
accessListStorageCount += storageKeys.size();
316320
}
321+
if (warmCoinbase) {
322+
addressList.add(miningBeneficiary);
323+
}
317324

318325
final long intrinsicGas =
319326
gasCalculator.transactionIntrinsicGasCost(
@@ -448,7 +455,7 @@ public TransactionProcessingResult processTransaction(
448455
initialFrame.getSelfDestructs().forEach(worldState::deleteAccount);
449456

450457
if (clearEmptyAccounts) {
451-
clearEmptyAccounts(worldState);
458+
clearAccountsThatAreEmpty(worldState);
452459
}
453460

454461
if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
@@ -474,7 +481,7 @@ public MainnetTransactionValidator getTransactionValidator() {
474481
return transactionValidator;
475482
}
476483

477-
protected static void clearEmptyAccounts(final WorldUpdater worldState) {
484+
protected static void clearAccountsThatAreEmpty(final WorldUpdater worldState) {
478485
new ArrayList<>(worldState.getTouchedAccounts())
479486
.stream().filter(Account::isEmpty).forEach(a -> worldState.deleteAccount(a.getAddress()));
480487
}

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java

+86-16
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,31 @@
1616

1717
import static org.assertj.core.api.Assertions.assertThat;
1818
import static org.mockito.ArgumentMatchers.any;
19+
import static org.mockito.Mockito.doAnswer;
1920
import static org.mockito.Mockito.when;
2021

2122
import org.hyperledger.besu.datatypes.Address;
23+
import org.hyperledger.besu.datatypes.Hash;
24+
import org.hyperledger.besu.datatypes.Wei;
2225
import org.hyperledger.besu.ethereum.chain.Blockchain;
2326
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
2427
import org.hyperledger.besu.ethereum.core.Transaction;
2528
import org.hyperledger.besu.ethereum.core.feemarket.CoinbaseFeePriceCalculator;
2629
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
2730
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
2831
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
32+
import org.hyperledger.besu.evm.account.EvmAccount;
33+
import org.hyperledger.besu.evm.account.MutableAccount;
34+
import org.hyperledger.besu.evm.frame.MessageFrame;
2935
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
36+
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
3037
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
3138
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
3239

33-
import org.junit.Before;
40+
import java.util.Optional;
41+
import java.util.concurrent.atomic.AtomicBoolean;
42+
43+
import org.apache.tuweni.bytes.Bytes;
3444
import org.junit.Test;
3545
import org.junit.runner.RunWith;
3646
import org.mockito.ArgumentCaptor;
@@ -42,9 +52,7 @@ public class MainnetTransactionProcessorTest {
4252

4353
private static final int MAX_STACK_SIZE = 1024;
4454

45-
private MainnetTransactionProcessor transactionProcessor;
46-
47-
@Mock private GasCalculator gasCalculator;
55+
private final GasCalculator gasCalculator = new LondonGasCalculator();
4856
@Mock private MainnetTransactionValidator transactionValidator;
4957
@Mock private AbstractMessageProcessor contractCreationProcessor;
5058
@Mock private AbstractMessageProcessor messageCallProcessor;
@@ -55,18 +63,78 @@ public class MainnetTransactionProcessorTest {
5563
@Mock private Transaction transaction;
5664
@Mock private BlockHashLookup blockHashLookup;
5765

58-
@Before
59-
public void before() {
60-
transactionProcessor =
61-
new MainnetTransactionProcessor(
62-
gasCalculator,
63-
transactionValidator,
64-
contractCreationProcessor,
65-
messageCallProcessor,
66-
false,
67-
MAX_STACK_SIZE,
68-
FeeMarket.legacy(),
69-
CoinbaseFeePriceCalculator.frontier());
66+
@Mock private EvmAccount senderAccount;
67+
@Mock private MutableAccount mutableSenderAccount;
68+
69+
MainnetTransactionProcessor createTransactionProcessor(final boolean warmCoinbase) {
70+
return new MainnetTransactionProcessor(
71+
gasCalculator,
72+
transactionValidator,
73+
contractCreationProcessor,
74+
messageCallProcessor,
75+
false,
76+
warmCoinbase,
77+
MAX_STACK_SIZE,
78+
FeeMarket.legacy(),
79+
CoinbaseFeePriceCalculator.frontier());
80+
}
81+
82+
@Test
83+
public void shouldWarmCoinbaseIfRequested() {
84+
Optional<Address> toAddresss =
85+
Optional.of(Address.fromHexString("0x2222222222222222222222222222222222222222"));
86+
when(transaction.getTo()).thenReturn(toAddresss);
87+
Address senderAddress = Address.fromHexString("0x5555555555555555555555555555555555555555");
88+
Address coinbaseAddress = Address.fromHexString("0x4242424242424242424242424242424242424242");
89+
90+
when(senderAccount.getMutable()).thenReturn(mutableSenderAccount);
91+
when(transaction.getHash()).thenReturn(Hash.EMPTY);
92+
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
93+
when(transaction.getSender()).thenReturn(senderAddress);
94+
when(transaction.getValue()).thenReturn(Wei.ZERO);
95+
when(transactionValidator.validate(any(), any(), any())).thenReturn(ValidationResult.valid());
96+
when(transactionValidator.validateForSender(any(), any(), any()))
97+
.thenReturn(ValidationResult.valid());
98+
when(worldState.getOrCreate(any())).thenReturn(senderAccount);
99+
when(worldState.getOrCreateSenderAccount(any())).thenReturn(senderAccount);
100+
when(worldState.updater()).thenReturn(worldState);
101+
102+
AtomicBoolean coinbaseWarmed = new AtomicBoolean(false);
103+
doAnswer(
104+
invocation -> {
105+
MessageFrame messageFrame = invocation.getArgument(0);
106+
coinbaseWarmed.set(messageFrame.warmUpAddress(coinbaseAddress));
107+
messageFrame.getMessageFrameStack().pop();
108+
return null;
109+
})
110+
.when(messageCallProcessor)
111+
.process(any(), any());
112+
113+
var transactionProcessor = createTransactionProcessor(true);
114+
transactionProcessor.processTransaction(
115+
blockchain,
116+
worldState,
117+
blockHeader,
118+
transaction,
119+
coinbaseAddress,
120+
blockHashLookup,
121+
false,
122+
ImmutableTransactionValidationParams.builder().build());
123+
124+
assertThat(coinbaseWarmed).isTrue();
125+
126+
transactionProcessor = createTransactionProcessor(false);
127+
transactionProcessor.processTransaction(
128+
blockchain,
129+
worldState,
130+
blockHeader,
131+
transaction,
132+
coinbaseAddress,
133+
blockHashLookup,
134+
false,
135+
ImmutableTransactionValidationParams.builder().build());
136+
137+
assertThat(coinbaseWarmed).isFalse();
70138
}
71139

72140
@Test
@@ -77,6 +145,8 @@ public void shouldCallTransactionValidatorWithExpectedTransactionValidationParam
77145
final TransactionValidationParams expectedValidationParams =
78146
ImmutableTransactionValidationParams.builder().build();
79147

148+
var transactionProcessor = createTransactionProcessor(false);
149+
80150
transactionProcessor.processTransaction(
81151
blockchain,
82152
worldState,

0 commit comments

Comments
 (0)