mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-08 23:17:54 -05:00
Make QBFT validator smart contract mode work with london fork (#5277)
Override the transactionSimulator's default TransactionValidationParams with one that allows for exceeding the account balance (which effectively zeros the baseFee). This mimics the way that eth_estimateGas and eth_call are implemented. Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
### Bug Fixes
|
||||
- Fix QBFT and IBFT unable to propose blocks on London when zeroBaseFee is used [#5276](https://github.com/hyperledger/besu/pull/5276)
|
||||
|
||||
- Make QBFT validator smart contract mode work with london fork [#5249](https://github.com/hyperledger/besu/issues/5249)
|
||||
|
||||
### Download Links
|
||||
|
||||
## 23.1.2
|
||||
|
||||
@@ -75,6 +75,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
|
||||
private BftConfigOptions bftConfigOptions = JsonBftConfigOptions.DEFAULT;
|
||||
private TransitionsConfigOptions transitions = TransitionsConfigOptions.DEFAULT;
|
||||
private static final DiscoveryOptions DISCOVERY_OPTIONS = DiscoveryOptions.DEFAULT;
|
||||
private boolean zeroBaseFee = false;
|
||||
|
||||
@Override
|
||||
public StubGenesisConfigOptions clone() {
|
||||
@@ -439,7 +440,7 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
|
||||
|
||||
@Override
|
||||
public boolean isZeroBaseFee() {
|
||||
return false;
|
||||
return zeroBaseFee;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -695,6 +696,17 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero base fee per gas stub genesis config options.
|
||||
*
|
||||
* @param zeroBaseFee the zero base fee override
|
||||
* @return the stub genesis config options
|
||||
*/
|
||||
public StubGenesisConfigOptions zeroBaseFee(final boolean zeroBaseFee) {
|
||||
this.zeroBaseFee = zeroBaseFee;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classic fork block stub genesis config options.
|
||||
*
|
||||
|
||||
@@ -125,6 +125,8 @@ public class TestContextBuilder {
|
||||
|
||||
private static final MetricsSystem metricsSystem = new NoOpMetricsSystem();
|
||||
private boolean useValidatorContract;
|
||||
private boolean useLondonMilestone = false;
|
||||
private boolean useZeroBaseFee = false;
|
||||
|
||||
private static class ControllerAndState {
|
||||
|
||||
@@ -237,6 +239,16 @@ public class TestContextBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestContextBuilder useLondonMilestone(final boolean useLondonMilestone) {
|
||||
this.useLondonMilestone = useLondonMilestone;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestContextBuilder useZeroBaseFee(final boolean useZeroBaseFee) {
|
||||
this.useZeroBaseFee = useZeroBaseFee;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestContextBuilder qbftForks(final List<QbftFork> qbftForks) {
|
||||
this.qbftForks = qbftForks;
|
||||
return this;
|
||||
@@ -301,6 +313,8 @@ public class TestContextBuilder {
|
||||
gossiper,
|
||||
synchronizerUpdater,
|
||||
useValidatorContract,
|
||||
useLondonMilestone,
|
||||
useZeroBaseFee,
|
||||
qbftForks);
|
||||
|
||||
// Add each networkNode to the Multicaster (such that each can receive msgs from local node).
|
||||
@@ -379,6 +393,8 @@ public class TestContextBuilder {
|
||||
final Gossiper gossiper,
|
||||
final SynchronizerUpdater synchronizerUpdater,
|
||||
final boolean useValidatorContract,
|
||||
final boolean useLondonMilestone,
|
||||
final boolean useZeroBaseFee,
|
||||
final List<QbftFork> qbftForks) {
|
||||
|
||||
final MiningParameters miningParams =
|
||||
@@ -398,7 +414,14 @@ public class TestContextBuilder {
|
||||
: Collections.emptyMap();
|
||||
final QbftConfigOptions qbftConfigOptions = createGenesisConfig(useValidatorContract);
|
||||
|
||||
genesisConfigOptions.byzantiumBlock(0);
|
||||
if (useLondonMilestone) {
|
||||
genesisConfigOptions.londonBlock(0);
|
||||
} else {
|
||||
genesisConfigOptions.berlinBlock(0);
|
||||
}
|
||||
if (useZeroBaseFee) {
|
||||
genesisConfigOptions.zeroBaseFee(true);
|
||||
}
|
||||
genesisConfigOptions.qbftConfigOptions(
|
||||
new JsonQbftConfigOptions(JsonUtil.objectNodeFromMap(qbftConfigValues)));
|
||||
genesisConfigOptions.transitions(TestTransitions.createQbftTestTransitions(qbftForks));
|
||||
|
||||
@@ -94,6 +94,52 @@ public class ValidatorContractTest {
|
||||
assertThat(validatorProvider.getValidatorsForBlock(block1)).containsExactly(NODE_ADDRESS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrievesValidatorsFromValidatorContract_LondonFork() {
|
||||
final TestContext context =
|
||||
new TestContextBuilder()
|
||||
.indexOfFirstLocallyProposedBlock(0)
|
||||
.nodeParams(
|
||||
List.of(new NodeParams(NODE_ADDRESS, NodeKeyUtils.createFrom(NODE_PRIVATE_KEY))))
|
||||
.clock(TestClock.fixed())
|
||||
.genesisFile(Resources.getResource("genesis_validator_contract_london.json").getFile())
|
||||
.useValidatorContract(true)
|
||||
.useLondonMilestone(true)
|
||||
.buildAndStart();
|
||||
|
||||
createNewBlockAsProposer(context, 1);
|
||||
|
||||
final ValidatorProvider validatorProvider = context.getValidatorProvider();
|
||||
final BlockHeader genesisBlock = context.getBlockchain().getBlockHeader(0).get();
|
||||
final BlockHeader block1 = context.getBlockchain().getBlockHeader(1).get();
|
||||
assertThat(validatorProvider.getValidatorsForBlock(genesisBlock)).containsExactly(NODE_ADDRESS);
|
||||
assertThat(validatorProvider.getValidatorsForBlock(block1)).containsExactly(NODE_ADDRESS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrievesValidatorsFromValidatorContract_LondonFork_ZeroBaseFee() {
|
||||
// Using London on a free gas network
|
||||
final TestContext context =
|
||||
new TestContextBuilder()
|
||||
.indexOfFirstLocallyProposedBlock(0)
|
||||
.nodeParams(
|
||||
List.of(new NodeParams(NODE_ADDRESS, NodeKeyUtils.createFrom(NODE_PRIVATE_KEY))))
|
||||
.clock(TestClock.fixed())
|
||||
.genesisFile(Resources.getResource("genesis_validator_contract_london.json").getFile())
|
||||
.useValidatorContract(true)
|
||||
.useLondonMilestone(true)
|
||||
.useZeroBaseFee(true)
|
||||
.buildAndStart();
|
||||
|
||||
createNewBlockAsProposer(context, 1);
|
||||
|
||||
final ValidatorProvider validatorProvider = context.getValidatorProvider();
|
||||
final BlockHeader genesisBlock = context.getBlockchain().getBlockHeader(0).get();
|
||||
final BlockHeader block1 = context.getBlockchain().getBlockHeader(1).get();
|
||||
assertThat(validatorProvider.getValidatorsForBlock(genesisBlock)).containsExactly(NODE_ADDRESS);
|
||||
assertThat(validatorProvider.getValidatorsForBlock(block1)).containsExactly(NODE_ADDRESS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transitionsFromBlockHeaderModeToValidatorContractMode() {
|
||||
final List<QbftFork> qbftForks =
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x0",
|
||||
"extraData": "0xe5a00000000000000000000000000000000000000000000000000000000000000000c0c080c0",
|
||||
"gasLimit": "0x29b92700",
|
||||
"difficulty": "0x1",
|
||||
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"64d9be4177f418bcf4e56adad85f33e3a64efe22": {
|
||||
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
|
||||
},
|
||||
"9f66f8a0f0a6537e4a36aa1799673ea7ae97a166": {
|
||||
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
|
||||
},
|
||||
"a7f25969fb6f3d5ac09a88862c90b5ff664557a7": {
|
||||
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
|
||||
},
|
||||
"f4bbfd32c11c9d63e9b4c77bb225810f840342df": {
|
||||
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
|
||||
},
|
||||
"0x0000000000000000000000000000000000008888": {
|
||||
"comment": "validator smart contract. This is compiled from validator_contract.sol using solc --evm-version byzantium --bin-runtime validator_contract.sol",
|
||||
"balance": "0",
|
||||
"code": "608060405234801561001057600080fd5b5060043610610048576000357c010000000000000000000000000000000000000000000000000000000090048063b7ab4db51461004d575b600080fd5b61005561006b565b604051610062919061017e565b60405180910390f35b606060008054806020026020016040519081016040528092919081815260200182805480156100ef57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116100a5575b5050505050905090565b60006101058383610111565b60208301905092915050565b61011a816101d9565b82525050565b600061012b826101b0565b61013581856101c8565b9350610140836101a0565b8060005b8381101561017157815161015888826100f9565b9750610163836101bb565b925050600181019050610144565b5085935050505092915050565b600060208201905081810360008301526101988184610120565b905092915050565b6000819050602082019050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b60006101e4826101eb565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff8216905091905056fea26469706673582212206d880cf012c1677c691bf6f2f0a0e4eadf57866ffe5cd2d9833d3cfdf27b15f664736f6c63430008060033",
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": "000000000000000000000000eac51e3fe1afc9894f0dfeab8ceb471899b932df"
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000009999": {
|
||||
"comment": "validator smart contract. This is compiled from validator_contract.sol using solc --evm-version byzantium --bin-runtime validator_contract.sol",
|
||||
"balance": "0",
|
||||
"code": "608060405234801561001057600080fd5b5060043610610048576000357c010000000000000000000000000000000000000000000000000000000090048063b7ab4db51461004d575b600080fd5b61005561006b565b604051610062919061017e565b60405180910390f35b606060008054806020026020016040519081016040528092919081815260200182805480156100ef57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116100a5575b5050505050905090565b60006101058383610111565b60208301905092915050565b61011a816101d9565b82525050565b600061012b826101b0565b61013581856101c8565b9350610140836101a0565b8060005b8381101561017157815161015888826100f9565b9750610163836101bb565b925050600181019050610144565b5085935050505092915050565b600060208201905081810360008301526101988184610120565b905092915050565b6000819050602082019050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b60006101e4826101eb565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff8216905091905056fea26469706673582212206d880cf012c1677c691bf6f2f0a0e4eadf57866ffe5cd2d9833d3cfdf27b15f664736f6c63430008060033",
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000002",
|
||||
"290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": "000000000000000000000000e98d92560fac3069ccff53ef348ded26a51d4b68",
|
||||
"290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564": "000000000000000000000000eac51e3fe1afc9894f0dfeab8ceb471899b932df"
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"baseFeePerGas": "0x7"
|
||||
}
|
||||
@@ -15,9 +15,12 @@
|
||||
package org.hyperledger.besu.consensus.qbft.validator;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
|
||||
import org.hyperledger.besu.evm.tracing.OperationTracer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -89,7 +92,13 @@ public class ValidatorContractController {
|
||||
final Bytes payload = Bytes.fromHexString(FunctionEncoder.encode(function));
|
||||
final CallParameter callParams =
|
||||
new CallParameter(null, contractAddress, -1, null, null, payload);
|
||||
return transactionSimulator.process(callParams, blockNumber);
|
||||
final TransactionValidationParams transactionValidationParams =
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(true)
|
||||
.build();
|
||||
return transactionSimulator.process(
|
||||
callParams, transactionValidationParams, OperationTracer.NO_TRACING, blockNumber);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@@ -107,7 +116,8 @@ public class ValidatorContractController {
|
||||
|
||||
return decodedList;
|
||||
} else {
|
||||
throw new IllegalStateException("Failed validator smart contract call");
|
||||
throw new IllegalStateException(
|
||||
"Failed validator smart contract call: " + result.getValidationResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,15 @@ import org.hyperledger.besu.config.JsonQbftConfigOptions;
|
||||
import org.hyperledger.besu.consensus.qbft.MutableQbftConfigOptions;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
|
||||
import org.hyperledger.besu.evm.tracing.OperationTracer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -49,9 +52,15 @@ public class ValidatorContractControllerTest {
|
||||
private static final Address VALIDATOR_ADDRESS =
|
||||
Address.fromHexString("0xeac51e3fe1afc9894f0dfeab8ceb471899b932df");
|
||||
private static final Address CONTRACT_ADDRESS = Address.fromHexString("1");
|
||||
private static final TransactionValidationParams ALLOW_EXCEEDING_BALANCE_VALIDATION_PARAMS =
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(true)
|
||||
.build();
|
||||
|
||||
private final TransactionSimulator transactionSimulator =
|
||||
Mockito.mock(TransactionSimulator.class);
|
||||
|
||||
private final Transaction transaction = Mockito.mock(Transaction.class);
|
||||
private CallParameter callParameter;
|
||||
|
||||
@@ -81,7 +90,12 @@ public class ValidatorContractControllerTest {
|
||||
Bytes.fromHexString(GET_VALIDATORS_FUNCTION_RESULT),
|
||||
ValidationResult.valid()));
|
||||
|
||||
when(transactionSimulator.process(callParameter, 1)).thenReturn(Optional.of(result));
|
||||
when(transactionSimulator.process(
|
||||
callParameter,
|
||||
ALLOW_EXCEEDING_BALANCE_VALIDATION_PARAMS,
|
||||
OperationTracer.NO_TRACING,
|
||||
1))
|
||||
.thenReturn(Optional.of(result));
|
||||
|
||||
final ValidatorContractController validatorContractController =
|
||||
new ValidatorContractController(transactionSimulator);
|
||||
@@ -98,13 +112,18 @@ public class ValidatorContractControllerTest {
|
||||
TransactionProcessingResult.invalid(
|
||||
ValidationResult.invalid(TransactionInvalidReason.INTERNAL_ERROR)));
|
||||
|
||||
when(transactionSimulator.process(callParameter, 1)).thenReturn(Optional.of(result));
|
||||
when(transactionSimulator.process(
|
||||
callParameter,
|
||||
ALLOW_EXCEEDING_BALANCE_VALIDATION_PARAMS,
|
||||
OperationTracer.NO_TRACING,
|
||||
1))
|
||||
.thenReturn(Optional.of(result));
|
||||
|
||||
final ValidatorContractController validatorContractController =
|
||||
new ValidatorContractController(transactionSimulator);
|
||||
Assertions.assertThatThrownBy(
|
||||
() -> validatorContractController.getValidators(1, CONTRACT_ADDRESS))
|
||||
.hasMessage("Failed validator smart contract call");
|
||||
.hasMessageContaining("Failed validator smart contract call");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -118,13 +137,18 @@ public class ValidatorContractControllerTest {
|
||||
ValidationResult.invalid(TransactionInvalidReason.INTERNAL_ERROR),
|
||||
Optional.empty()));
|
||||
|
||||
when(transactionSimulator.process(callParameter, 1)).thenReturn(Optional.of(result));
|
||||
when(transactionSimulator.process(
|
||||
callParameter,
|
||||
ALLOW_EXCEEDING_BALANCE_VALIDATION_PARAMS,
|
||||
OperationTracer.NO_TRACING,
|
||||
1))
|
||||
.thenReturn(Optional.of(result));
|
||||
|
||||
final ValidatorContractController validatorContractController =
|
||||
new ValidatorContractController(transactionSimulator);
|
||||
Assertions.assertThatThrownBy(
|
||||
() -> validatorContractController.getValidators(1, CONTRACT_ADDRESS))
|
||||
.hasMessage("Failed validator smart contract call");
|
||||
.hasMessageContaining("Failed validator smart contract call");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -135,7 +159,12 @@ public class ValidatorContractControllerTest {
|
||||
TransactionProcessingResult.successful(
|
||||
List.of(), 0, 0, Bytes.EMPTY, ValidationResult.valid()));
|
||||
|
||||
when(transactionSimulator.process(callParameter, 1)).thenReturn(Optional.of(result));
|
||||
when(transactionSimulator.process(
|
||||
callParameter,
|
||||
ALLOW_EXCEEDING_BALANCE_VALIDATION_PARAMS,
|
||||
OperationTracer.NO_TRACING,
|
||||
1))
|
||||
.thenReturn(Optional.of(result));
|
||||
|
||||
final ValidatorContractController validatorContractController =
|
||||
new ValidatorContractController(transactionSimulator);
|
||||
@@ -147,7 +176,12 @@ public class ValidatorContractControllerTest {
|
||||
|
||||
@Test
|
||||
public void throwErrorIfEmptySimulationResult() {
|
||||
when(transactionSimulator.process(callParameter, 1)).thenReturn(Optional.empty());
|
||||
when(transactionSimulator.process(
|
||||
callParameter,
|
||||
ALLOW_EXCEEDING_BALANCE_VALIDATION_PARAMS,
|
||||
OperationTracer.NO_TRACING,
|
||||
1))
|
||||
.thenReturn(Optional.empty());
|
||||
|
||||
final ValidatorContractController validatorContractController =
|
||||
new ValidatorContractController(transactionSimulator);
|
||||
|
||||
Reference in New Issue
Block a user