Introduce transaction validator interface (phase 2) (#5682)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/5715/head
Fabio Di Fabio 1 year ago committed by GitHub
parent 1a7635bc3e
commit 6603ebb716
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  2. 15
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  3. 6
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java
  4. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java
  5. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolSchedule.java
  6. 22
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  7. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  8. 59
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  9. 75
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PermissionTransactionValidator.java
  10. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacySupportingProtocolSchedule.java
  11. 16
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpec.java
  12. 33
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java
  13. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java
  14. 109
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidatorFactory.java
  15. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java
  16. 20
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java
  17. 165
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java
  18. 161
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PermissionTransactionValidatorTest.java
  19. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  20. 47
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  21. 45
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionsLayeredPendingTransactionsTest.java
  22. 2
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/core/TransactionTest.java
  23. 7
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java

@ -1136,7 +1136,7 @@ public class RunnerBuilder {
permissioningController ->
besuController
.getProtocolSchedule()
.setTransactionFilter(permissioningController::isPermitted));
.setPermissionTransactionFilter(permissioningController::isPermitted));
return accountPermissioningController;
} else {

@ -51,7 +51,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidatorFactory;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
@ -79,6 +79,7 @@ import com.google.common.base.Suppliers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@ -99,7 +100,10 @@ public class BesuEventsImplTest {
@Mock private EthContext mockEthContext;
@Mock private EthMessages mockEthMessages;
@Mock private EthScheduler mockEthScheduler;
@Mock private TransactionValidator mockTransactionValidator;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private TransactionValidatorFactory mockTransactionValidatorFactory;
@Mock private ProtocolSpec mockProtocolSpec;
@Mock private WorldStateArchive mockWorldStateArchive;
@Mock private MutableWorldState mockWorldState;
@ -128,11 +132,12 @@ public class BesuEventsImplTest {
when(mockProtocolContext.getBlockchain()).thenReturn(blockchain);
when(mockProtocolContext.getWorldStateArchive()).thenReturn(mockWorldStateArchive);
when(mockProtocolSchedule.getByBlockHeader(any())).thenReturn(mockProtocolSpec);
when(mockProtocolSpec.getTransactionValidator()).thenReturn(mockTransactionValidator);
when(mockProtocolSpec.getTransactionValidatorFactory())
.thenReturn(mockTransactionValidatorFactory);
when(mockProtocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L));
when(mockTransactionValidator.validate(any(), any(Optional.class), any()))
when(mockTransactionValidatorFactory.get().validate(any(), any(Optional.class), any()))
.thenReturn(ValidationResult.valid());
when(mockTransactionValidator.validateForSender(any(), any(), any()))
when(mockTransactionValidatorFactory.get().validateForSender(any(), any(), any()))
.thenReturn(ValidationResult.valid());
when(mockWorldStateArchive.getMutable(any(), anyBoolean()))
.thenReturn(Optional.of(mockWorldState));

@ -219,9 +219,11 @@ public class TransitionProtocolSchedule implements ProtocolSchedule {
* @param permissionTransactionFilter the transaction filter
*/
@Override
public void setTransactionFilter(final PermissionTransactionFilter permissionTransactionFilter) {
public void setPermissionTransactionFilter(
final PermissionTransactionFilter permissionTransactionFilter) {
transitionUtils.dispatchConsumerAccordingToMergeState(
protocolSchedule -> protocolSchedule.setTransactionFilter(permissionTransactionFilter));
protocolSchedule ->
protocolSchedule.setPermissionTransactionFilter(permissionTransactionFilter));
}
/**

@ -76,7 +76,7 @@ public class ClassicProtocolSpecs {
.gasCalculator(TangerineWhistleGasCalculator::new)
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(gasCalculator, gasLimitCalculator, true, chainId))
new TransactionValidatorFactory(gasCalculator, gasLimitCalculator, true, chainId))
.name("ClassicTangerineWhistle");
}
@ -130,7 +130,7 @@ public class ClassicProtocolSpecs {
.difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_REMOVED)
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(gasCalculator, gasLimitCalculator, true, chainId))
new TransactionValidatorFactory(gasCalculator, gasLimitCalculator, true, chainId))
.name("DefuseDifficultyBomb");
}
@ -293,7 +293,7 @@ public class ClassicProtocolSpecs {
.gasCalculator(BerlinGasCalculator::new)
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
new TransactionValidatorFactory(
gasCalculator,
gasLimitCalculator,
true,

@ -111,11 +111,12 @@ public class DefaultProtocolSchedule implements ProtocolSchedule {
}
@Override
public void setTransactionFilter(final PermissionTransactionFilter permissionTransactionFilter) {
public void setPermissionTransactionFilter(
final PermissionTransactionFilter permissionTransactionFilter) {
protocolSpecs.forEach(
spec ->
spec.spec()
.getTransactionValidator()
.getTransactionValidatorFactory()
.setPermissionTransactionFilter(permissionTransactionFilter));
}

@ -124,16 +124,16 @@ public abstract class MainnetProtocolSpecs {
0))
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
new TransactionValidatorFactory(
gasCalculator, gasLimitCalculator, false, Optional.empty()))
.transactionProcessorBuilder(
(gasCalculator,
transactionValidator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
false,
@ -142,12 +142,12 @@ public abstract class MainnetProtocolSpecs {
FeeMarket.legacy(),
CoinbaseFeePriceCalculator.frontier()))
.privateTransactionProcessorBuilder(
(transactionValidator,
(transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
privateTransactionValidator) ->
new PrivateTransactionProcessor(
transactionValidator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
false,
@ -199,7 +199,7 @@ public abstract class MainnetProtocolSpecs {
0))
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
new TransactionValidatorFactory(
gasCalculator, gasLimitCalculator, true, Optional.empty()))
.difficultyCalculator(MainnetDifficultyCalculators.HOMESTEAD)
.name("Homestead");
@ -277,7 +277,7 @@ public abstract class MainnetProtocolSpecs {
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES))
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(gasCalculator, gasLimitCalculator, true, chainId))
new TransactionValidatorFactory(gasCalculator, gasLimitCalculator, true, chainId))
.transactionProcessorBuilder(
(gasCalculator,
transactionValidator,
@ -412,7 +412,7 @@ public abstract class MainnetProtocolSpecs {
.gasCalculator(BerlinGasCalculator::new)
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
new TransactionValidatorFactory(
gasCalculator,
gasLimitCalculator,
true,
@ -452,7 +452,7 @@ public abstract class MainnetProtocolSpecs {
new LondonTargetingGasLimitCalculator(londonForkBlockNumber, londonFeeMarket))
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
new TransactionValidatorFactory(
gasCalculator,
gasLimitCalculator,
londonFeeMarket,
@ -613,7 +613,7 @@ public abstract class MainnetProtocolSpecs {
// Contract creation rules for EIP-3860 Limit and meter intitcode
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
new TransactionValidatorFactory(
gasCalculator,
gasLimitCalculator,
londonFeeMarket,
@ -696,7 +696,7 @@ public abstract class MainnetProtocolSpecs {
// change to check for max data gas per block for EIP-4844
.transactionValidatorBuilder(
(gasCalculator, gasLimitCalculator) ->
new MainnetTransactionValidator(
new TransactionValidatorFactory(
gasCalculator,
gasLimitCalculator,
cancunFeeMarket,

@ -64,7 +64,7 @@ public class MainnetTransactionProcessor {
protected final GasCalculator gasCalculator;
protected final TransactionValidator transactionValidator;
protected final TransactionValidatorFactory transactionValidatorFactory;
private final AbstractMessageProcessor contractCreationProcessor;
@ -81,7 +81,7 @@ public class MainnetTransactionProcessor {
public MainnetTransactionProcessor(
final GasCalculator gasCalculator,
final TransactionValidator transactionValidator,
final TransactionValidatorFactory transactionValidatorFactory,
final AbstractMessageProcessor contractCreationProcessor,
final AbstractMessageProcessor messageCallProcessor,
final boolean clearEmptyAccounts,
@ -90,7 +90,7 @@ public class MainnetTransactionProcessor {
final FeeMarket feeMarket,
final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator) {
this.gasCalculator = gasCalculator;
this.transactionValidator = transactionValidator;
this.transactionValidatorFactory = transactionValidatorFactory;
this.contractCreationProcessor = contractCreationProcessor;
this.messageCallProcessor = messageCallProcessor;
this.clearEmptyAccounts = clearEmptyAccounts;
@ -271,6 +271,7 @@ public class MainnetTransactionProcessor {
final PrivateMetadataUpdater privateMetadataUpdater,
final Wei dataGasPrice) {
try {
final var transactionValidator = transactionValidatorFactory.get();
LOG.trace("Starting execution of {}", transaction);
ValidationResult<TransactionInvalidReason> validationResult =
transactionValidator.validate(
@ -498,10 +499,6 @@ public class MainnetTransactionProcessor {
}
}
public TransactionValidator getTransactionValidator() {
return transactionValidator;
}
protected void process(final MessageFrame frame, final OperationTracer operationTracer) {
final AbstractMessageProcessor executor = getMessageProcessor(frame.getType());

@ -22,7 +22,6 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.PermissionTransactionFilter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
@ -49,40 +48,10 @@ public class MainnetTransactionValidator implements TransactionValidator {
private final Optional<BigInteger> chainId;
private Optional<PermissionTransactionFilter> permissionTransactionFilter = Optional.empty();
private final Set<TransactionType> acceptedTransactionTypes;
private final int maxInitcodeSize;
public MainnetTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId) {
this(
gasCalculator,
gasLimitCalculator,
checkSignatureMalleability,
chainId,
Set.of(TransactionType.FRONTIER));
}
public MainnetTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
final Set<TransactionType> acceptedTransactionTypes) {
this(
gasCalculator,
gasLimitCalculator,
FeeMarket.legacy(),
checkSignatureMalleability,
chainId,
acceptedTransactionTypes,
Integer.MAX_VALUE);
}
public MainnetTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
@ -239,12 +208,6 @@ public class MainnetTransactionValidator implements TransactionValidator {
transaction.getSender()));
}
if (!isSenderAllowed(transaction, validationParams)) {
return ValidationResult.invalid(
TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED,
String.format("Sender %s is not on the Account Allowlist", transaction.getSender()));
}
return ValidationResult.valid();
}
@ -286,26 +249,4 @@ public class MainnetTransactionValidator implements TransactionValidator {
}
return ValidationResult.valid();
}
private boolean isSenderAllowed(
final Transaction transaction, final TransactionValidationParams validationParams) {
if (validationParams.checkLocalPermissions() || validationParams.checkOnchainPermissions()) {
return permissionTransactionFilter
.map(
c ->
c.permitted(
transaction,
validationParams.checkLocalPermissions(),
validationParams.checkOnchainPermissions()))
.orElse(true);
} else {
return true;
}
}
@Override
public void setPermissionTransactionFilter(
final PermissionTransactionFilter permissionTransactionFilter) {
this.permissionTransactionFilter = Optional.of(permissionTransactionFilter);
}
}

@ -0,0 +1,75 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.PermissionTransactionFilter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account;
import java.util.Optional;
/**
* Validates a transaction based on Frontier protocol runtime requirements.
*
* <p>The {@link PermissionTransactionValidator} performs the intrinsic gas cost check on the given
* {@link Transaction}.
*/
public class PermissionTransactionValidator implements TransactionValidator {
private final TransactionValidator delegate;
private final PermissionTransactionFilter permissionTransactionFilter;
public PermissionTransactionValidator(
final TransactionValidator delegate,
final PermissionTransactionFilter permissionTransactionFilter) {
this.delegate = delegate;
this.permissionTransactionFilter = permissionTransactionFilter;
}
@Override
public ValidationResult<TransactionInvalidReason> validate(
final Transaction transaction,
final Optional<Wei> baseFee,
final TransactionValidationParams transactionValidationParams) {
return delegate.validate(transaction, baseFee, transactionValidationParams);
}
@Override
public ValidationResult<TransactionInvalidReason> validateForSender(
final Transaction transaction,
final Account sender,
final TransactionValidationParams validationParams) {
if (!isSenderAllowed(transaction, validationParams)) {
return ValidationResult.invalid(
TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED,
String.format("Sender %s is not on the Account Allowlist", transaction.getSender()));
}
return delegate.validateForSender(transaction, sender, validationParams);
}
private boolean isSenderAllowed(
final Transaction transaction, final TransactionValidationParams validationParams) {
if (validationParams.checkLocalPermissions() || validationParams.checkOnchainPermissions()) {
return permissionTransactionFilter.permitted(
transaction,
validationParams.checkLocalPermissions(),
validationParams.checkOnchainPermissions());
}
return true;
}
}

@ -22,7 +22,8 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
public interface PrivacySupportingProtocolSchedule {
void setTransactionFilter(final PermissionTransactionFilter permissionTransactionFilter);
void setPermissionTransactionFilter(
final PermissionTransactionFilter permissionTransactionFilter);
void setPublicWorldStateArchiveForPrivacyBlockProcessor(
final WorldStateArchive publicWorldStateArchive);

@ -38,7 +38,7 @@ public class ProtocolSpec {
private final GasLimitCalculator gasLimitCalculator;
private final TransactionValidator transactionValidator;
private final TransactionValidatorFactory transactionValidatorFactory;
private final MainnetTransactionProcessor transactionProcessor;
@ -87,7 +87,7 @@ public class ProtocolSpec {
*
* @param name the protocol specification name
* @param evm the EVM supporting the appropriate operations for this specification
* @param transactionValidator the transaction validator to use
* @param transactionValidatorFactory the transaction validator factory to use
* @param transactionProcessor the transaction processor to use
* @param privateTransactionProcessor the private transaction processor to use
* @param blockHeaderValidator the block header validator to use
@ -118,7 +118,7 @@ public class ProtocolSpec {
public ProtocolSpec(
final String name,
final EVM evm,
final TransactionValidator transactionValidator,
final TransactionValidatorFactory transactionValidatorFactory,
final MainnetTransactionProcessor transactionProcessor,
final PrivateTransactionProcessor privateTransactionProcessor,
final BlockHeaderValidator blockHeaderValidator,
@ -146,7 +146,7 @@ public class ProtocolSpec {
final boolean isReplayProtectionSupported) {
this.name = name;
this.evm = evm;
this.transactionValidator = transactionValidator;
this.transactionValidatorFactory = transactionValidatorFactory;
this.transactionProcessor = transactionProcessor;
this.privateTransactionProcessor = privateTransactionProcessor;
this.blockHeaderValidator = blockHeaderValidator;
@ -184,12 +184,12 @@ public class ProtocolSpec {
}
/**
* Returns the transaction validator used in this specification.
* Returns the transaction validator factory used in this specification.
*
* @return the transaction validator
* @return the transaction validator factory
*/
public TransactionValidator getTransactionValidator() {
return transactionValidator;
public TransactionValidatorFactory getTransactionValidatorFactory() {
return transactionValidatorFactory;
}
public boolean isReplayProtectionSupported() {

@ -53,8 +53,8 @@ public class ProtocolSpecBuilder {
private DifficultyCalculator difficultyCalculator;
private EvmConfiguration evmConfiguration;
private BiFunction<GasCalculator, EvmConfiguration, EVM> evmBuilder;
private BiFunction<GasCalculator, GasLimitCalculator, TransactionValidator>
transactionValidatorBuilder;
private BiFunction<GasCalculator, GasLimitCalculator, TransactionValidatorFactory>
transactionValidatorFactoryBuilder;
private Function<FeeMarket, BlockHeaderValidator.Builder> blockHeaderValidatorBuilder;
private Function<FeeMarket, BlockHeaderValidator.Builder> ommerHeaderValidatorBuilder;
private Function<ProtocolSchedule, BlockBodyValidator> blockBodyValidatorBuilder;
@ -126,9 +126,9 @@ public class ProtocolSpecBuilder {
}
public ProtocolSpecBuilder transactionValidatorBuilder(
final BiFunction<GasCalculator, GasLimitCalculator, TransactionValidator>
transactionValidatorBuilder) {
this.transactionValidatorBuilder = transactionValidatorBuilder;
final BiFunction<GasCalculator, GasLimitCalculator, TransactionValidatorFactory>
transactionValidatorFactoryBuilder) {
this.transactionValidatorFactoryBuilder = transactionValidatorFactoryBuilder;
return this;
}
@ -282,7 +282,7 @@ public class ProtocolSpecBuilder {
checkNotNull(gasLimitCalculator, "Missing gasLimitCalculator");
checkNotNull(evmBuilder, "Missing operation registry");
checkNotNull(evmConfiguration, "Missing evm configuration");
checkNotNull(transactionValidatorBuilder, "Missing transaction validator");
checkNotNull(transactionValidatorFactoryBuilder, "Missing transaction validator");
checkNotNull(privateTransactionValidatorBuilder, "Missing private transaction validator");
checkNotNull(contractCreationProcessorBuilder, "Missing contract creation processor");
checkNotNull(precompileContractRegistryBuilder, "Missing precompile contract registry");
@ -309,8 +309,8 @@ public class ProtocolSpecBuilder {
final EVM evm = evmBuilder.apply(gasCalculator, evmConfiguration);
final PrecompiledContractConfiguration precompiledContractConfiguration =
new PrecompiledContractConfiguration(gasCalculator, privacyParameters);
final TransactionValidator transactionValidator =
transactionValidatorBuilder.apply(gasCalculator, gasLimitCalculator);
final TransactionValidatorFactory transactionValidatorFactory =
transactionValidatorFactoryBuilder.apply(gasCalculator, gasLimitCalculator);
final AbstractMessageProcessor contractCreationProcessor =
contractCreationProcessorBuilder.apply(gasCalculator, evm);
final PrecompileContractRegistry precompileContractRegistry =
@ -319,7 +319,10 @@ public class ProtocolSpecBuilder {
messageCallProcessorBuilder.apply(evm, precompileContractRegistry);
final MainnetTransactionProcessor transactionProcessor =
transactionProcessorBuilder.apply(
gasCalculator, transactionValidator, contractCreationProcessor, messageCallProcessor);
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor);
final BlockHeaderValidator blockHeaderValidator =
createBlockHeaderValidator(blockHeaderValidatorBuilder);
@ -333,7 +336,7 @@ public class ProtocolSpecBuilder {
// Set private Tx Processor
PrivateTransactionProcessor privateTransactionProcessor =
createPrivateTransactionProcessor(
transactionValidator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
precompileContractRegistry);
@ -357,7 +360,7 @@ public class ProtocolSpecBuilder {
return new ProtocolSpec(
name,
evm,
transactionValidator,
transactionValidatorFactory,
transactionProcessor,
privateTransactionProcessor,
blockHeaderValidator,
@ -386,7 +389,7 @@ public class ProtocolSpecBuilder {
}
private PrivateTransactionProcessor createPrivateTransactionProcessor(
final TransactionValidator transactionValidator,
final TransactionValidatorFactory transactionValidatorFactory,
final AbstractMessageProcessor contractCreationProcessor,
final AbstractMessageProcessor messageCallProcessor,
final PrecompileContractRegistry precompileContractRegistry) {
@ -396,7 +399,7 @@ public class ProtocolSpecBuilder {
privateTransactionValidatorBuilder.apply();
privateTransactionProcessor =
privateTransactionProcessorBuilder.apply(
transactionValidator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
privateTransactionValidator);
@ -443,14 +446,14 @@ public class ProtocolSpecBuilder {
public interface TransactionProcessorBuilder {
MainnetTransactionProcessor apply(
GasCalculator gasCalculator,
TransactionValidator transactionValidator,
TransactionValidatorFactory transactionValidatorFactory,
AbstractMessageProcessor contractCreationProcessor,
AbstractMessageProcessor messageCallProcessor);
}
public interface PrivateTransactionProcessorBuilder {
PrivateTransactionProcessor apply(
TransactionValidator transactionValidator,
TransactionValidatorFactory transactionValidatorFactory,
AbstractMessageProcessor contractCreationProcessor,
AbstractMessageProcessor messageCallProcessor,
PrivateTransactionValidator privateTransactionValidator);

@ -15,7 +15,6 @@
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.PermissionTransactionFilter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account;
@ -53,13 +52,4 @@ public interface TransactionValidator {
*/
ValidationResult<TransactionInvalidReason> validateForSender(
Transaction transaction, Account sender, TransactionValidationParams validationParams);
/**
* Set the permission transaction filter. This way of setting the filter is deprecated and will be
* removed.
*
* @param permissionTransactionFilter the permission transaction filter
*/
@Deprecated
void setPermissionTransactionFilter(PermissionTransactionFilter permissionTransactionFilter);
}

@ -0,0 +1,109 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.datatypes.TransactionType;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.PermissionTransactionFilter;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.math.BigInteger;
import java.util.Optional;
import java.util.Set;
import com.google.common.base.Suppliers;
public class TransactionValidatorFactory {
private final GasCalculator gasCalculator;
private final GasLimitCalculator gasLimitCalculator;
private final FeeMarket feeMarket;
private final boolean disallowSignatureMalleability;
private final Optional<BigInteger> chainId;
private final Set<TransactionType> acceptedTransactionTypes;
private final int maxInitcodeSize;
private Optional<PermissionTransactionFilter> permissionTransactionFilter = Optional.empty();
public TransactionValidatorFactory(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId) {
this(
gasCalculator,
gasLimitCalculator,
checkSignatureMalleability,
chainId,
Set.of(TransactionType.FRONTIER));
}
public TransactionValidatorFactory(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
final Set<TransactionType> acceptedTransactionTypes) {
this(
gasCalculator,
gasLimitCalculator,
FeeMarket.legacy(),
checkSignatureMalleability,
chainId,
acceptedTransactionTypes,
Integer.MAX_VALUE);
}
public TransactionValidatorFactory(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final FeeMarket feeMarket,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
final Set<TransactionType> acceptedTransactionTypes,
final int maxInitcodeSize) {
this.gasCalculator = gasCalculator;
this.gasLimitCalculator = gasLimitCalculator;
this.feeMarket = feeMarket;
this.disallowSignatureMalleability = checkSignatureMalleability;
this.chainId = chainId;
this.acceptedTransactionTypes = acceptedTransactionTypes;
this.maxInitcodeSize = maxInitcodeSize;
}
public void setPermissionTransactionFilter(
final PermissionTransactionFilter permissionTransactionFilter) {
this.permissionTransactionFilter = Optional.of(permissionTransactionFilter);
}
public TransactionValidator get() {
return Suppliers.memoize(this::createTransactionValidator).get();
}
private TransactionValidator createTransactionValidator() {
final TransactionValidator baseValidator =
new MainnetTransactionValidator(
gasCalculator,
gasLimitCalculator,
feeMarket,
disallowSignatureMalleability,
chainId,
acceptedTransactionTypes,
maxInitcodeSize);
if (permissionTransactionFilter.isPresent()) {
return new PermissionTransactionValidator(baseValidator, permissionTransactionFilter.get());
}
return baseValidator;
}
}

@ -20,7 +20,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidatorFactory;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
@ -49,7 +49,7 @@ public class PrivateTransactionProcessor {
private static final Logger LOG = LoggerFactory.getLogger(PrivateTransactionProcessor.class);
@SuppressWarnings("unused")
private final TransactionValidator transactionValidator;
private final TransactionValidatorFactory transactionValidatorFactory;
private final PrivateTransactionValidator privateTransactionValidator;
@ -63,13 +63,13 @@ public class PrivateTransactionProcessor {
private final boolean clearEmptyAccounts;
public PrivateTransactionProcessor(
final TransactionValidator transactionValidator,
final TransactionValidatorFactory transactionValidatorFactory,
final AbstractMessageProcessor contractCreationProcessor,
final AbstractMessageProcessor messageCallProcessor,
final boolean clearEmptyAccounts,
final int maxStackSize,
final PrivateTransactionValidator privateTransactionValidator) {
this.transactionValidator = transactionValidator;
this.transactionValidatorFactory = transactionValidatorFactory;
this.contractCreationProcessor = contractCreationProcessor;
this.messageCallProcessor = messageCallProcessor;
this.clearEmptyAccounts = clearEmptyAccounts;

@ -43,6 +43,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@ -53,7 +54,10 @@ public class MainnetTransactionProcessorTest {
private static final int MAX_STACK_SIZE = 1024;
private final GasCalculator gasCalculator = new LondonGasCalculator();
@Mock private TransactionValidator transactionValidator;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private TransactionValidatorFactory transactionValidatorFactory;
@Mock private AbstractMessageProcessor contractCreationProcessor;
@Mock private AbstractMessageProcessor messageCallProcessor;
@ -69,7 +73,7 @@ public class MainnetTransactionProcessorTest {
MainnetTransactionProcessor createTransactionProcessor(final boolean warmCoinbase) {
return new MainnetTransactionProcessor(
gasCalculator,
transactionValidator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
false,
@ -92,8 +96,9 @@ public class MainnetTransactionProcessorTest {
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
when(transaction.getSender()).thenReturn(senderAddress);
when(transaction.getValue()).thenReturn(Wei.ZERO);
when(transactionValidator.validate(any(), any(), any())).thenReturn(ValidationResult.valid());
when(transactionValidator.validateForSender(any(), any(), any()))
when(transactionValidatorFactory.get().validate(any(), any(), any()))
.thenReturn(ValidationResult.valid());
when(transactionValidatorFactory.get().validateForSender(any(), any(), any()))
.thenReturn(ValidationResult.valid());
when(worldState.getOrCreate(any())).thenReturn(senderAccount);
when(worldState.getOrCreateSenderAccount(any())).thenReturn(senderAccount);
@ -168,9 +173,12 @@ public class MainnetTransactionProcessorTest {
private ArgumentCaptor<TransactionValidationParams> transactionValidationParamCaptor() {
final ArgumentCaptor<TransactionValidationParams> txValidationParamCaptor =
ArgumentCaptor.forClass(TransactionValidationParams.class);
when(transactionValidator.validate(any(), any(), any())).thenReturn(ValidationResult.valid());
when(transactionValidatorFactory.get().validate(any(), any(), any()))
.thenReturn(ValidationResult.valid());
// returning invalid transaction to halt method execution
when(transactionValidator.validateForSender(any(), any(), txValidationParamCaptor.capture()))
when(transactionValidatorFactory
.get()
.validateForSender(any(), any(), txValidationParamCaptor.capture()))
.thenReturn(ValidationResult.invalid(TransactionInvalidReason.NONCE_TOO_HIGH));
return txValidationParamCaptor;
}

@ -24,7 +24,6 @@ import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
@ -36,7 +35,6 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.PermissionTransactionFilter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
@ -53,7 +51,6 @@ import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@ -74,10 +71,43 @@ public class MainnetTransactionValidatorTest {
.chainId(Optional.of(BigInteger.ONE))
.createTransaction(senderKeys);
protected TransactionValidator createTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final FeeMarket feeMarket,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId,
final Set<TransactionType> acceptedTransactionTypes,
final int maxInitcodeSize) {
return new MainnetTransactionValidator(
gasCalculator,
gasLimitCalculator,
feeMarket,
checkSignatureMalleability,
chainId,
acceptedTransactionTypes,
maxInitcodeSize);
}
protected TransactionValidator createTransactionValidator(
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final boolean checkSignatureMalleability,
final Optional<BigInteger> chainId) {
return createTransactionValidator(
gasCalculator,
gasLimitCalculator,
FeeMarket.legacy(),
checkSignatureMalleability,
chainId,
Set.of(TransactionType.FRONTIER),
Integer.MAX_VALUE);
}
@Test
public void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
final Transaction transaction =
new TransactionTestFixture()
@ -94,7 +124,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionWhenTransactionHasChainIdAndValidatorDoesNot() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
assertThat(validator.validate(basicTransaction, Optional.empty(), transactionValidationParams))
.isEqualTo(
@ -105,7 +135,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionWhenTransactionHasIncorrectChainId() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
false,
@ -117,7 +147,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionWhenSenderAccountDoesNotExist() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE));
assertThat(validator.validateForSender(basicTransaction, null, processingBlockParams))
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE));
@ -126,7 +156,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionWhenTransactionNonceBelowAccountNonce() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE));
final Account account = accountWithNonce(basicTransaction.getNonce() + 1);
@ -138,7 +168,7 @@ public class MainnetTransactionValidatorTest {
public void
shouldRejectTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsNotAllowed() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE));
final Account account = accountWithNonce(basicTransaction.getNonce() - 1);
@ -150,7 +180,7 @@ public class MainnetTransactionValidatorTest {
public void
shouldAcceptTransactionWhenTransactionNonceAboveAccountNonceAndFutureNonceIsAllowed() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE));
final Account account = accountWithNonce(basicTransaction.getNonce() - 1);
@ -161,7 +191,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionWhenNonceExceedsMaximumAllowedNonce() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE));
final Transaction transaction =
@ -175,7 +205,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void transactionWithNullSenderCanBeValidIfGasPriceAndValueIsZero() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.of(BigInteger.ONE));
final TransactionTestFixture builder = new TransactionTestFixture();
@ -192,9 +222,8 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionIfAccountIsNotEOA() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
validator.setPermissionTransactionFilter(transactionFilter(false));
Account invalidEOA =
when(account(basicTransaction.getUpfrontCost(0L), basicTransaction.getNonce())
@ -202,42 +231,15 @@ public class MainnetTransactionValidatorTest {
.thenReturn(Hash.fromHexStringLenient("0xdeadbeef"))
.getMock();
assertThat(validator.validateForSender(basicTransaction, invalidEOA, transactionPoolParams))
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED));
}
@Test
public void shouldRejectTransactionIfAccountIsNotPermitted() {
final TransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
validator.setPermissionTransactionFilter(transactionFilter(false));
assertThat(
validator.validateForSender(
basicTransaction, accountWithNonce(0), transactionPoolParams))
assertThat(validator.validateForSender(basicTransaction, invalidEOA, processingBlockParams))
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED));
}
@Test
public void shouldAcceptValidTransactionIfAccountIsPermitted() {
final TransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
validator.setPermissionTransactionFilter(transactionFilter(true));
assertThat(
validator.validateForSender(
basicTransaction, accountWithNonce(0), transactionPoolParams))
.isEqualTo(ValidationResult.valid());
}
@Test
public void shouldRejectTransactionWithMaxFeeTimesGasLimitGreaterThanBalance() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
validator.setPermissionTransactionFilter(transactionFilter(true));
assertThat(
validator.validateForSender(
@ -260,7 +262,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionWithMaxPriorityFeeGreaterThanMaxFee() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
@ -271,7 +273,6 @@ public class MainnetTransactionValidatorTest {
TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559
}),
Integer.MAX_VALUE);
validator.setPermissionTransactionFilter(transactionFilter(true));
final Transaction transaction =
Transaction.builder()
@ -294,62 +295,10 @@ public class MainnetTransactionValidatorTest {
.isEqualTo("max priority fee per gas cannot be greater than max fee per gas");
}
@Test
public void shouldPropagateCorrectStateChangeParamToTransactionFilter() {
final ArgumentCaptor<Boolean> stateChangeLocalParamCaptor =
ArgumentCaptor.forClass(Boolean.class);
final ArgumentCaptor<Boolean> stateChangeOnchainParamCaptor =
ArgumentCaptor.forClass(Boolean.class);
final PermissionTransactionFilter permissionTransactionFilter =
mock(PermissionTransactionFilter.class);
when(permissionTransactionFilter.permitted(
any(Transaction.class),
stateChangeLocalParamCaptor.capture(),
stateChangeOnchainParamCaptor.capture()))
.thenReturn(true);
final TransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
validator.setPermissionTransactionFilter(permissionTransactionFilter);
final TransactionValidationParams validationParams =
ImmutableTransactionValidationParams.builder().checkOnchainPermissions(true).build();
validator.validateForSender(basicTransaction, accountWithNonce(0), validationParams);
assertThat(stateChangeLocalParamCaptor.getValue()).isTrue();
assertThat(stateChangeOnchainParamCaptor.getValue()).isTrue();
}
@Test
public void shouldNotCheckAccountPermissionIfBothValidationParamsCheckPermissionsAreFalse() {
final PermissionTransactionFilter permissionTransactionFilter =
mock(PermissionTransactionFilter.class);
final TransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
validator.setPermissionTransactionFilter(permissionTransactionFilter);
final TransactionValidationParams validationParams =
ImmutableTransactionValidationParams.builder()
.checkOnchainPermissions(false)
.checkLocalPermissions(false)
.build();
validator.validateForSender(basicTransaction, accountWithNonce(0), validationParams);
assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), validationParams))
.isEqualTo(ValidationResult.valid());
verifyNoInteractions(permissionTransactionFilter);
}
@Test
public void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() {
final TransactionValidator frontierValidator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.legacy(),
@ -392,7 +341,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTransactionIfEIP1559TransactionGasPriceLessBaseFee() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
@ -416,7 +365,7 @@ public class MainnetTransactionValidatorTest {
public void shouldAcceptZeroGasPriceTransactionIfBaseFeeIsZero() {
final Optional<Wei> zeroBaseFee = Optional.of(Wei.ZERO);
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L, zeroBaseFee),
@ -439,7 +388,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldAcceptValidEIP1559() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
@ -464,7 +413,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransactionPool() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
@ -490,7 +439,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldRejectTooLargeInitcode() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
@ -517,7 +466,7 @@ public class MainnetTransactionValidatorTest {
@Test
public void shouldAcceptTransactionWithAtLeastOneBlob() {
final TransactionValidator validator =
new MainnetTransactionValidator(
createTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
@ -547,12 +496,4 @@ public class MainnetTransactionValidatorTest {
when(account.getNonce()).thenReturn(nonce);
return account;
}
private PermissionTransactionFilter transactionFilter(final boolean permitted) {
final PermissionTransactionFilter permissionTransactionFilter =
mock(PermissionTransactionFilter.class);
when(permissionTransactionFilter.permitted(any(Transaction.class), anyBoolean(), anyBoolean()))
.thenReturn(permitted);
return permissionTransactionFilter;
}
}

@ -0,0 +1,161 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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 static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams.transactionPoolParams;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.PermissionTransactionFilter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.math.BigInteger;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PermissionTransactionValidatorTest extends MainnetTransactionValidatorTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair();
@Mock private GasCalculator gasCalculator;
private final Transaction basicTransaction =
new TransactionTestFixture()
.chainId(Optional.of(BigInteger.ONE))
.createTransaction(senderKeys);
@Test
public void shouldRejectTransactionIfAccountIsNotPermitted() {
final TransactionValidator baseValidator =
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
final TransactionValidator validator =
new PermissionTransactionValidator(baseValidator, transactionFilter(false));
assertThat(
validator.validateForSender(
basicTransaction, accountWithNonce(0), transactionPoolParams))
.isEqualTo(ValidationResult.invalid(TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED));
}
@Test
public void shouldAcceptValidTransactionIfAccountIsPermitted() {
final TransactionValidator baseValidator =
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
final TransactionValidator validator =
new PermissionTransactionValidator(baseValidator, transactionFilter(true));
assertThat(
validator.validateForSender(
basicTransaction, accountWithNonce(0), transactionPoolParams))
.isEqualTo(ValidationResult.valid());
}
@Test
public void shouldPropagateCorrectStateChangeParamToTransactionFilter() {
final ArgumentCaptor<Boolean> stateChangeLocalParamCaptor =
ArgumentCaptor.forClass(Boolean.class);
final ArgumentCaptor<Boolean> stateChangeOnchainParamCaptor =
ArgumentCaptor.forClass(Boolean.class);
final PermissionTransactionFilter permissionTransactionFilter =
mock(PermissionTransactionFilter.class);
when(permissionTransactionFilter.permitted(
any(Transaction.class),
stateChangeLocalParamCaptor.capture(),
stateChangeOnchainParamCaptor.capture()))
.thenReturn(true);
final TransactionValidator baseValidator =
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
final TransactionValidator validator =
new PermissionTransactionValidator(baseValidator, permissionTransactionFilter);
final TransactionValidationParams validationParams =
ImmutableTransactionValidationParams.builder().checkOnchainPermissions(true).build();
validator.validateForSender(basicTransaction, accountWithNonce(0), validationParams);
assertThat(stateChangeLocalParamCaptor.getValue()).isTrue();
assertThat(stateChangeOnchainParamCaptor.getValue()).isTrue();
}
@Test
public void shouldNotCheckAccountPermissionIfBothValidationParamsCheckPermissionsAreFalse() {
final PermissionTransactionFilter permissionTransactionFilter =
mock(PermissionTransactionFilter.class);
final TransactionValidator baseValidator =
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
final TransactionValidator validator =
new PermissionTransactionValidator(baseValidator, permissionTransactionFilter);
final TransactionValidationParams validationParams =
ImmutableTransactionValidationParams.builder()
.checkOnchainPermissions(false)
.checkLocalPermissions(false)
.build();
validator.validateForSender(basicTransaction, accountWithNonce(0), validationParams);
assertThat(validator.validateForSender(basicTransaction, accountWithNonce(0), validationParams))
.isEqualTo(ValidationResult.valid());
verifyNoInteractions(permissionTransactionFilter);
}
private Account accountWithNonce(final long nonce) {
return account(basicTransaction.getUpfrontCost(0L), nonce);
}
private Account account(final Wei balance, final long nonce) {
final Account account = mock(Account.class);
when(account.getBalance()).thenReturn(balance);
when(account.getNonce()).thenReturn(nonce);
return account;
}
private PermissionTransactionFilter transactionFilter(final boolean permitted) {
final PermissionTransactionFilter permissionTransactionFilter =
mock(PermissionTransactionFilter.class);
when(permissionTransactionFilter.permitted(any(Transaction.class), anyBoolean(), anyBoolean()))
.thenReturn(permitted);
return permissionTransactionFilter;
}
}

@ -364,7 +364,8 @@ public class TransactionPool implements BlockAddedObserver {
private TransactionValidator getTransactionValidator() {
return protocolSchedule
.getByBlockHeader(protocolContext.getBlockchain().getChainHeadHeader())
.getTransactionValidator();
.getTransactionValidatorFactory()
.get();
}
private ValidationResultAndAccount validateLocalTransaction(final Transaction transaction) {

@ -61,7 +61,7 @@ import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidatorFactory;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
@ -81,6 +81,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ -97,7 +98,10 @@ public abstract class AbstractTransactionPoolTest {
private static final KeyPair KEY_PAIR2 =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
@Mock protected TransactionValidator transactionValidator;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected TransactionValidatorFactory transactionValidatorFactory;
@Mock protected PendingTransactionAddedListener listener;
@Mock protected MiningParameters miningParameters;
@Mock protected TransactionsMessageSender transactionsMessageSender;
@ -136,7 +140,7 @@ public abstract class AbstractTransactionPoolTest {
protocolContext = executionContext.getProtocolContext();
blockchain = executionContext.getBlockchain();
transactions = spy(createPendingTransactionsSorter());
when(protocolSpec.getTransactionValidator()).thenReturn(transactionValidator);
when(protocolSpec.getTransactionValidatorFactory()).thenReturn(transactionValidatorFactory);
when(protocolSpec.getFeeMarket()).thenReturn(getFeeMarket());
protocolSchedule = spy(executionContext.getProtocolSchedule());
doReturn(protocolSpec).when(protocolSchedule).getByBlockHeader(any());
@ -364,13 +368,13 @@ public abstract class AbstractTransactionPoolTest {
transactionPool.addRemoteTransactions(singletonList(transaction));
assertTransactionNotPending(transaction);
verifyNoMoreInteractions(transactionValidator);
verifyNoMoreInteractions(transactionValidatorFactory);
}
@Test
public void shouldNotAddRemoteTransactionsWhenThereIsAnLowestInvalidNonceForTheSender() {
givenTransactionIsValid(transaction2);
when(transactionValidator.validate(eq(transaction1), any(Optional.class), any()))
when(transactionValidatorFactory.get().validate(eq(transaction1), any(Optional.class), any()))
.thenReturn(ValidationResult.invalid(NONCE_TOO_LOW));
transactionPool.addRemoteTransactions(asList(transaction1, transaction2));
@ -384,20 +388,23 @@ public abstract class AbstractTransactionPoolTest {
public void shouldNotAddRemoteTransactionsThatAreInvalidAccordingToStateDependentChecks() {
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
when(transactionValidator.validateForSender(
eq(transaction2), eq(null), any(TransactionValidationParams.class)))
when(transactionValidatorFactory
.get()
.validateForSender(eq(transaction2), eq(null), any(TransactionValidationParams.class)))
.thenReturn(ValidationResult.invalid(NONCE_TOO_LOW));
transactionPool.addRemoteTransactions(asList(transaction1, transaction2));
assertTransactionPending(transaction1);
assertTransactionNotPending(transaction2);
verify(transactionBroadcaster).onTransactionsAdded(singletonList(transaction1));
verify(transactionValidator).validate(eq(transaction1), any(Optional.class), any());
verify(transactionValidator)
verify(transactionValidatorFactory.get())
.validate(eq(transaction1), any(Optional.class), any());
verify(transactionValidatorFactory.get())
.validateForSender(eq(transaction1), eq(null), any(TransactionValidationParams.class));
verify(transactionValidator).validate(eq(transaction2), any(Optional.class), any());
verify(transactionValidator).validateForSender(eq(transaction2), any(), any());
verifyNoMoreInteractions(transactionValidator);
verify(transactionValidatorFactory.get())
.validate(eq(transaction2), any(Optional.class), any());
verify(transactionValidatorFactory.get()).validateForSender(eq(transaction2), any(), any());
verifyNoMoreInteractions(transactionValidatorFactory.get());
}
@ParameterizedTest
@ -440,7 +447,7 @@ public abstract class AbstractTransactionPoolTest {
transactionPool.addRemoteTransactions(singletonList(transaction1));
verify(transactions).containsTransaction(transaction1);
verifyNoInteractions(transactionValidator);
verifyNoInteractions(transactionValidatorFactory);
}
@Test
@ -584,9 +591,11 @@ public abstract class AbstractTransactionPoolTest {
final ArgumentCaptor<TransactionValidationParams> txValidationParamCaptor =
ArgumentCaptor.forClass(TransactionValidationParams.class);
when(transactionValidator.validate(eq(transaction1), any(Optional.class), any()))
when(transactionValidatorFactory.get().validate(eq(transaction1), any(Optional.class), any()))
.thenReturn(valid());
when(transactionValidator.validateForSender(any(), any(), txValidationParamCaptor.capture()))
when(transactionValidatorFactory
.get()
.validateForSender(any(), any(), txValidationParamCaptor.capture()))
.thenReturn(valid());
final TransactionValidationParams expectedValidationParams =
@ -685,10 +694,12 @@ public abstract class AbstractTransactionPoolTest {
}
protected void givenTransactionIsValid(final Transaction transaction) {
when(transactionValidator.validate(eq(transaction), any(Optional.class), any()))
when(transactionValidatorFactory.get().validate(eq(transaction), any(Optional.class), any()))
.thenReturn(valid());
when(transactionValidator.validateForSender(
eq(transaction), nullable(Account.class), any(TransactionValidationParams.class)))
when(transactionValidatorFactory
.get()
.validateForSender(
eq(transaction), nullable(Account.class), any(TransactionValidationParams.class)))
.thenReturn(valid());
}

@ -61,7 +61,7 @@ import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidatorFactory;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
@ -80,6 +80,7 @@ import java.util.function.Consumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@ -93,7 +94,10 @@ public abstract class AbstractTransactionsLayeredPendingTransactionsTest {
private static final KeyPair KEY_PAIR2 =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
@Mock protected TransactionValidator transactionValidator;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected TransactionValidatorFactory transactionValidatorFactory;
@Mock protected PendingTransactionAddedListener listener;
@Mock protected MiningParameters miningParameters;
@Mock protected TransactionsMessageSender transactionsMessageSender;
@ -135,7 +139,7 @@ public abstract class AbstractTransactionsLayeredPendingTransactionsTest {
protocolContext = executionContext.getProtocolContext();
blockchain = executionContext.getBlockchain();
when(protocolSpec.getTransactionValidator()).thenReturn(transactionValidator);
when(protocolSpec.getTransactionValidatorFactory()).thenReturn(transactionValidatorFactory);
when(protocolSpec.getFeeMarket()).thenReturn(getFeeMarket());
protocolSchedule = spy(executionContext.getProtocolSchedule());
doReturn(protocolSpec).when(protocolSchedule).getByBlockHeader(any());
@ -362,27 +366,30 @@ public abstract class AbstractTransactionsLayeredPendingTransactionsTest {
transactionPool.addRemoteTransactions(singletonList(transaction));
assertTransactionNotPending(transaction);
verifyNoMoreInteractions(transactionValidator);
verifyNoMoreInteractions(transactionValidatorFactory);
}
@Test
public void shouldNotAddRemoteTransactionsThatAreInvalidAccordingToStateDependentChecks() {
givenTransactionIsValid(transaction0);
givenTransactionIsValid(transaction1);
when(transactionValidator.validateForSender(
eq(transaction1), eq(null), any(TransactionValidationParams.class)))
when(transactionValidatorFactory
.get()
.validateForSender(eq(transaction1), eq(null), any(TransactionValidationParams.class)))
.thenReturn(ValidationResult.invalid(NONCE_TOO_LOW));
transactionPool.addRemoteTransactions(asList(transaction0, transaction1));
assertTransactionPending(transaction0);
assertTransactionNotPending(transaction1);
verify(transactionBroadcaster).onTransactionsAdded(singletonList(transaction0));
verify(transactionValidator).validate(eq(transaction0), any(Optional.class), any());
verify(transactionValidator)
verify(transactionValidatorFactory.get())
.validate(eq(transaction0), any(Optional.class), any());
verify(transactionValidatorFactory.get())
.validateForSender(eq(transaction0), eq(null), any(TransactionValidationParams.class));
verify(transactionValidator).validate(eq(transaction1), any(Optional.class), any());
verify(transactionValidator).validateForSender(eq(transaction1), any(), any());
verifyNoMoreInteractions(transactionValidator);
verify(transactionValidatorFactory.get())
.validate(eq(transaction1), any(Optional.class), any());
verify(transactionValidatorFactory.get()).validateForSender(eq(transaction1), any(), any());
verifyNoMoreInteractions(transactionValidatorFactory.get());
}
@Test
@ -423,7 +430,7 @@ public abstract class AbstractTransactionsLayeredPendingTransactionsTest {
transactionPool.addRemoteTransactions(singletonList(transaction0));
verify(transactions).containsTransaction(transaction0);
verifyNoInteractions(transactionValidator);
verifyNoInteractions(transactionValidatorFactory);
}
@Test
@ -551,9 +558,11 @@ public abstract class AbstractTransactionsLayeredPendingTransactionsTest {
final ArgumentCaptor<TransactionValidationParams> txValidationParamCaptor =
ArgumentCaptor.forClass(TransactionValidationParams.class);
when(transactionValidator.validate(eq(transaction0), any(Optional.class), any()))
when(transactionValidatorFactory.get().validate(eq(transaction0), any(Optional.class), any()))
.thenReturn(valid());
when(transactionValidator.validateForSender(any(), any(), txValidationParamCaptor.capture()))
when(transactionValidatorFactory
.get()
.validateForSender(any(), any(), txValidationParamCaptor.capture()))
.thenReturn(valid());
final TransactionValidationParams expectedValidationParams =
@ -647,10 +656,12 @@ public abstract class AbstractTransactionsLayeredPendingTransactionsTest {
}
protected void givenTransactionIsValid(final Transaction transaction) {
when(transactionValidator.validate(eq(transaction), any(Optional.class), any()))
when(transactionValidatorFactory.get().validate(eq(transaction), any(Optional.class), any()))
.thenReturn(valid());
when(transactionValidator.validateForSender(
eq(transaction), nullable(Account.class), any(TransactionValidationParams.class)))
when(transactionValidatorFactory
.get()
.validateForSender(
eq(transaction), nullable(Account.class), any(TransactionValidationParams.class)))
.thenReturn(valid());
}

@ -54,7 +54,7 @@ public class TransactionTest {
return REFERENCE_TEST_PROTOCOL_SCHEDULES
.getByName(name)
.getByBlockHeader(BlockHeaderBuilder.createDefault().buildBlockHeader())
.getTransactionValidator();
.getTransactionValidatorFactory().get();
}
private static final String TEST_CONFIG_FILE_DIR_PATH = "TransactionTests/";

@ -62,7 +62,7 @@ public class NoRewardProtocolScheduleWrapper implements ProtocolSchedule {
return new ProtocolSpec(
original.getName(),
original.getEvm(),
original.getTransactionValidator(),
original.getTransactionValidatorFactory(),
original.getTransactionProcessor(),
original.getPrivateTransactionProcessor(),
original.getBlockHeaderValidator(),
@ -121,8 +121,9 @@ public class NoRewardProtocolScheduleWrapper implements ProtocolSchedule {
}
@Override
public void setTransactionFilter(final PermissionTransactionFilter permissionTransactionFilter) {
delegate.setTransactionFilter(permissionTransactionFilter);
public void setPermissionTransactionFilter(
final PermissionTransactionFilter permissionTransactionFilter) {
delegate.setPermissionTransactionFilter(permissionTransactionFilter);
}
@Override

Loading…
Cancel
Save