Skip to content

Commit 90570b6

Browse files
committed
Add the possibility for a plugin to validate a tx according to txpool rules
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
1 parent 90db487 commit 90570b6

6 files changed

Lines changed: 121 additions & 3 deletions

File tree

besu/src/main/java/org/hyperledger/besu/services/TransactionPoolServiceImpl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
package org.hyperledger.besu.services;
1616

1717
import org.hyperledger.besu.datatypes.PendingTransaction;
18+
import org.hyperledger.besu.datatypes.Transaction;
1819
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
20+
import org.hyperledger.besu.plugin.data.ValidationResult;
1921
import org.hyperledger.besu.plugin.services.transactionpool.TransactionPoolService;
2022

2123
import java.util.Collection;
@@ -48,4 +50,17 @@ public void enableTransactionPool() {
4850
public Collection<? extends PendingTransaction> getPendingTransactions() {
4951
return transactionPool.getPendingTransactions();
5052
}
53+
54+
@Override
55+
public ValidationResult validateTransaction(
56+
final Transaction transaction, final boolean isLocal, final boolean hasPriority) {
57+
final var tx =
58+
transaction instanceof org.hyperledger.besu.ethereum.core.Transaction coreTx
59+
? coreTx
60+
: new org.hyperledger.besu.ethereum.core.Transaction.Builder()
61+
.copiedFrom(transaction)
62+
.build();
63+
64+
return transactionPool.validateTransaction(tx, isLocal, hasPriority);
65+
}
5166
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@
5252
import java.util.List;
5353
import java.util.Objects;
5454
import java.util.Optional;
55+
import java.util.function.Supplier;
5556

57+
import com.google.common.base.Suppliers;
5658
import com.google.common.cache.Cache;
5759
import com.google.common.cache.CacheBuilder;
5860
import com.google.common.primitives.Longs;
@@ -65,6 +67,9 @@
6567
public class Transaction
6668
implements org.hyperledger.besu.datatypes.Transaction,
6769
org.hyperledger.besu.plugin.data.UnsignedPrivateMarkerTransaction {
70+
// Supplier for the signature algorithm
71+
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
72+
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
6873

6974
// Used for transactions that are not tied to a specific chain
7075
// (e.g. does not have a chain id associated with it).
@@ -1226,6 +1231,51 @@ public Builder copiedFrom(final Transaction toCopy) {
12261231
return this;
12271232
}
12281233

1234+
public Builder copiedFrom(final org.hyperledger.besu.datatypes.Transaction toCopy) {
1235+
this.transactionType = toCopy.getType();
1236+
this.nonce = toCopy.getNonce();
1237+
this.gasPrice = toCopy.getGasPrice().map(Wei::fromQuantity).orElse(null);
1238+
this.maxPriorityFeePerGas =
1239+
toCopy.getMaxPriorityFeePerGas().map(Wei::fromQuantity).orElse(null);
1240+
this.maxFeePerGas = toCopy.getMaxFeePerGas().map(Wei::fromQuantity).orElse(null);
1241+
this.maxFeePerBlobGas = toCopy.getMaxFeePerBlobGas().map(Wei::fromQuantity).orElse(null);
1242+
this.gasLimit = toCopy.getGasLimit();
1243+
this.to = Optional.ofNullable(toCopy.getTo().orElse(null));
1244+
this.value = Wei.fromQuantity(toCopy.getValue());
1245+
this.payload = toCopy.getPayload();
1246+
this.accessList = toCopy.getAccessList();
1247+
this.sender = toCopy.getSender();
1248+
this.chainId = toCopy.getChainId();
1249+
this.versionedHashes = toCopy.getVersionedHashes().orElse(null);
1250+
this.blobsWithCommitments = toCopy.getBlobsWithCommitments().orElse(null);
1251+
this.codeDelegationAuthorizations = toCopy.getCodeDelegationList();
1252+
1253+
final byte recId = getRecId(toCopy);
1254+
this.signature =
1255+
SIGNATURE_ALGORITHM.get().createSignature(toCopy.getR(), toCopy.getS(), recId);
1256+
1257+
return this;
1258+
}
1259+
1260+
private byte getRecId(final org.hyperledger.besu.datatypes.Transaction toCopy) {
1261+
final byte recId;
1262+
if (this.transactionType.equals(TransactionType.FRONTIER)) {
1263+
final BigInteger v = toCopy.getV();
1264+
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
1265+
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
1266+
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
1267+
recId =
1268+
v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
1269+
} else {
1270+
throw new RuntimeException(
1271+
String.format("An unsupported encoded `v` value of %s was found", v));
1272+
}
1273+
} else {
1274+
recId = toCopy.getYParity().byteValueExact();
1275+
}
1276+
return recId;
1277+
}
1278+
12291279
public Builder type(final TransactionType transactionType) {
12301280
this.transactionType = transactionType;
12311281
return this;

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
import com.google.common.base.MoreObjects;
2424

25-
public final class ValidationResult<T> {
25+
public final class ValidationResult<T>
26+
implements org.hyperledger.besu.plugin.data.ValidationResult {
2627

2728
private final Optional<T> invalidReason;
2829
private final Optional<String> errorMessage;
@@ -32,6 +33,7 @@ private ValidationResult(final Optional<T> invalidReason, final Optional<String>
3233
this.errorMessage = errorMessage;
3334
}
3435

36+
@Override
3537
public boolean isValid() {
3638
return invalidReason.isEmpty();
3739
}
@@ -40,6 +42,7 @@ public T getInvalidReason() throws NoSuchElementException {
4042
return invalidReason.get();
4143
}
4244

45+
@Override
4346
public String getErrorMessage() {
4447
return errorMessage.orElse(getInvalidReason().toString());
4548
}

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ private ValidationResult<TransactionInvalidReason> addTransaction(
259259
}
260260

261261
final ValidationResultAndAccount validationResult =
262-
validateTransaction(transaction, isLocal, hasPriority);
262+
internalValidateTransaction(transaction, isLocal, hasPriority);
263263

264264
if (validationResult.result.isValid()) {
265265
final TransactionAddedResult status =
@@ -409,7 +409,12 @@ private TransactionValidator getTransactionValidator() {
409409
.get();
410410
}
411411

412-
private ValidationResultAndAccount validateTransaction(
412+
public ValidationResult<TransactionInvalidReason> validateTransaction(
413+
final Transaction transaction, final boolean isLocal, final boolean hasPriority) {
414+
return internalValidateTransaction(transaction, isLocal, hasPriority).result;
415+
}
416+
417+
private ValidationResultAndAccount internalValidateTransaction(
413418
final Transaction transaction, final boolean isLocal, final boolean hasPriority) {
414419

415420
final BlockHeader chainHeadBlockHeader = getChainHeadBlockHeader().orElse(null);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright contributors to Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.plugin.data;
16+
17+
/** Represent a result of a transaction validation */
18+
public interface ValidationResult {
19+
/**
20+
* Return if transaction is valid or not
21+
*
22+
* @return true if transaction is valid
23+
*/
24+
boolean isValid();
25+
26+
/**
27+
* If the transaction is not valid returns the related error message, null otherwise
28+
*
29+
* @return the reason why the transaction is not valid or null if the transaction is valid
30+
*/
31+
String getErrorMessage();
32+
}

plugin-api/src/main/java/org/hyperledger/besu/plugin/services/transactionpool/TransactionPoolService.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
package org.hyperledger.besu.plugin.services.transactionpool;
1616

1717
import org.hyperledger.besu.datatypes.PendingTransaction;
18+
import org.hyperledger.besu.datatypes.Transaction;
19+
import org.hyperledger.besu.plugin.data.ValidationResult;
1820
import org.hyperledger.besu.plugin.services.BesuService;
1921

2022
import java.util.Collection;
@@ -33,4 +35,15 @@ public interface TransactionPoolService extends BesuService {
3335
* @return a collection of pending transactions
3436
*/
3537
Collection<? extends PendingTransaction> getPendingTransactions();
38+
39+
/**
40+
* Validate a transaction for txpool addition
41+
*
42+
* @param transaction the transaction
43+
* @param isLocal if the transaction is from a local source
44+
* @param hasPriority if the transaction has priority
45+
* @return the result of the validation
46+
*/
47+
ValidationResult validateTransaction(
48+
Transaction transaction, boolean isLocal, boolean hasPriority);
3649
}

0 commit comments

Comments
 (0)