Cached blob txs (#6147)

* basic test coverage
* blob caching on tx removal in legacy pool
* blob caching on tx removal in layered pool
* blob restoral in both legacy and layered implementations and test coverage of tx copy in builder
* refactors into reusable BlobCache, and rekeys on versioned hash

---------

Signed-off-by: Justin Florentine <justin+github@florentine.us>
Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/6172/head
Justin Florentine 1 year ago committed by GitHub
parent 0ccb4d4400
commit 488755a728
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      besu/src/main/java/org/hyperledger/besu/components/BesuComponent.java
  2. 4
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  3. 4
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  4. 15
      datatypes/src/main/java/org/hyperledger/besu/datatypes/Blob.java
  5. 63
      datatypes/src/main/java/org/hyperledger/besu/datatypes/BlobsWithCommitments.java
  6. 15
      datatypes/src/main/java/org/hyperledger/besu/datatypes/KZGCommitment.java
  7. 15
      datatypes/src/main/java/org/hyperledger/besu/datatypes/KZGProof.java
  8. 25
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  9. 12
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java
  10. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java
  11. 41
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java
  12. 3
      ethereum/eth/build.gradle
  13. 91
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/BlobCache.java
  14. 31
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/BlobCacheModule.java
  15. 5
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/DisabledPendingTransactions.java
  16. 2
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java
  17. 2
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactions.java
  18. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  19. 36
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  20. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractPrioritizedTransactions.java
  21. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractSequentialTransactionsLayer.java
  22. 17
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractTransactionsLayer.java
  23. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactions.java
  24. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/GasPricePrioritizedTransactions.java
  25. 5
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java
  26. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/ReadyTransactions.java
  27. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/SparseTransactions.java
  28. 18
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java
  29. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java
  30. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java
  31. 24
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  32. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionEstimatedMemorySizeTest.java
  33. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java
  34. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java
  35. 10
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractLayeredTransactionPoolTest.java
  36. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactionsTest.java
  37. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/GasPricePrioritizedTransactionsTest.java
  38. 16
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactionsTest.java
  39. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredTransactionPoolBaseFeeTest.java
  40. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredTransactionPoolGasPriceTest.java
  41. 16
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayersTest.java
  42. 10
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/ReplayTest.java
  43. 4
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  44. 56
      gradle/verification-metadata.xml
  45. 2
      gradle/versions.gradle

@ -19,6 +19,8 @@ package org.hyperledger.besu.components;
import org.hyperledger.besu.cli.BesuCommand;
import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader;
import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoaderModule;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCacheModule;
import org.hyperledger.besu.metrics.MetricsSystemModule;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.services.BesuPluginContextImpl;
@ -36,7 +38,8 @@ import org.slf4j.Logger;
BesuCommandModule.class,
MetricsSystemModule.class,
CachedMerkleTrieLoaderModule.class,
BesuPluginContextModule.class
BesuPluginContextModule.class,
BlobCacheModule.class
})
public interface BesuComponent {
@ -72,8 +75,15 @@ public interface BesuComponent {
/**
* Besu plugin context for doing plugin service discovery.
*
* @return BesuComponent
* @return BesuPluginContextImpl
*/
@Named("besuPluginContext")
BesuPluginContextImpl getBesuPluginContext();
/**
* Cache to store blobs in for re-use after reorgs.
*
* @return BlobCache
*/
BlobCache getBlobCache();
}

@ -73,6 +73,7 @@ import org.hyperledger.besu.ethereum.eth.sync.fastsync.checkpoint.Checkpoint;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.checkpoint.ImmutableCheckpoint;
import org.hyperledger.besu.ethereum.eth.sync.fullsync.SyncTerminationCondition;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
@ -722,7 +723,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
syncState,
miningParameters,
transactionPoolConfiguration,
pluginTransactionValidatorFactory);
pluginTransactionValidatorFactory,
besuComponent.map(BesuComponent::getBlobCache).orElse(new BlobCache()));
final List<PeerValidator> peerValidators = createPeerValidators(protocolSchedule);

@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.BlockBroadcaster;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
@ -161,7 +162,8 @@ public class BesuEventsImplTest {
MutableInitValues.builder().minTransactionGasPrice(Wei.ZERO).build())
.build(),
txPoolConfig,
null);
null,
new BlobCache());
serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);
}

@ -17,6 +17,8 @@ package org.hyperledger.besu.datatypes;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.util.Objects;
import org.apache.tuweni.bytes.Bytes;
/** Arbitrary data for use in the KZG scheme. */
@ -61,4 +63,17 @@ public class Blob {
public Bytes getData() {
return data;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Blob blob = (Blob) o;
return Objects.equals(getData(), blob.getData());
}
@Override
public int hashCode() {
return Objects.hash(getData());
}
}

@ -15,15 +15,18 @@
package org.hyperledger.besu.datatypes;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/** A class to hold the blobs, commitments, proofs and versioned hashes for a set of blobs. */
public class BlobsWithCommitments {
private final List<KZGCommitment> kzgCommitments;
private final List<Blob> blobs;
private final List<KZGProof> kzgProofs;
private final List<VersionedHash> versionedHashes;
/** A record to hold the blob, commitment, proof and versioned hash for a blob. */
public record BlobQuad(
Blob blob, KZGCommitment kzgCommitment, KZGProof kzgProof, VersionedHash versionedHash) {}
private final List<BlobQuad> blobQuads;
/**
* A class to hold the blobs, commitments and proofs for a set of blobs.
@ -38,7 +41,7 @@ public class BlobsWithCommitments {
final List<Blob> blobs,
final List<KZGProof> kzgProofs,
final List<VersionedHash> versionedHashes) {
if (blobs.size() == 0) {
if (blobs.isEmpty()) {
throw new InvalidParameterException(
"There needs to be a minimum of one blob in a blob transaction with commitments");
}
@ -48,10 +51,22 @@ public class BlobsWithCommitments {
throw new InvalidParameterException(
"There must be an equal number of blobs, commitments, proofs, and versioned hashes");
}
this.kzgCommitments = kzgCommitments;
this.blobs = blobs;
this.kzgProofs = kzgProofs;
this.versionedHashes = versionedHashes;
List<BlobQuad> toBuild = new ArrayList<>(blobs.size());
for (int i = 0; i < blobs.size(); i++) {
toBuild.add(
new BlobQuad(
blobs.get(i), kzgCommitments.get(i), kzgProofs.get(i), versionedHashes.get(i)));
}
this.blobQuads = toBuild;
}
/**
* Construct the class from a list of BlobQuads.
*
* @param quads the list of blob quads to be attached to the transaction
*/
public BlobsWithCommitments(final List<BlobQuad> quads) {
this.blobQuads = quads;
}
/**
@ -60,7 +75,7 @@ public class BlobsWithCommitments {
* @return the blobs
*/
public List<Blob> getBlobs() {
return blobs;
return blobQuads.stream().map(BlobQuad::blob).toList();
}
/**
@ -69,7 +84,7 @@ public class BlobsWithCommitments {
* @return the commitments
*/
public List<KZGCommitment> getKzgCommitments() {
return kzgCommitments;
return blobQuads.stream().map(BlobQuad::kzgCommitment).toList();
}
/**
@ -78,7 +93,7 @@ public class BlobsWithCommitments {
* @return the proofs
*/
public List<KZGProof> getKzgProofs() {
return kzgProofs;
return blobQuads.stream().map(BlobQuad::kzgProof).toList();
}
/**
@ -87,6 +102,28 @@ public class BlobsWithCommitments {
* @return the hashes
*/
public List<VersionedHash> getVersionedHashes() {
return versionedHashes;
return blobQuads.stream().map(BlobQuad::versionedHash).toList();
}
/**
* Get the list of BlobQuads.
*
* @return blob quads
*/
public List<BlobQuad> getBlobQuads() {
return blobQuads;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BlobsWithCommitments that = (BlobsWithCommitments) o;
return Objects.equals(getBlobQuads(), that.getBlobQuads());
}
@Override
public int hashCode() {
return Objects.hash(getBlobQuads());
}
}

@ -17,6 +17,8 @@ package org.hyperledger.besu.datatypes;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.util.Objects;
import org.apache.tuweni.bytes.Bytes48;
/** This class contains the data for a KZG commitment. */
@ -60,4 +62,17 @@ public class KZGCommitment {
public Bytes48 getData() {
return data;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KZGCommitment that = (KZGCommitment) o;
return Objects.equals(getData(), that.getData());
}
@Override
public int hashCode() {
return Objects.hash(getData());
}
}

@ -17,6 +17,8 @@ package org.hyperledger.besu.datatypes;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.util.Objects;
import org.apache.tuweni.bytes.Bytes48;
/** This class contains the data for a KZG proof for a KZG commitment. */
@ -60,4 +62,17 @@ public class KZGProof {
public Bytes48 getData() {
return data;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KZGProof kzgProof = (KZGProof) o;
return Objects.equals(getData(), kzgProof.getData());
}
@Override
public int hashCode() {
return Objects.hash(getData());
}
}

@ -1096,6 +1096,26 @@ public class Transaction
protected List<VersionedHash> versionedHashes = null;
private BlobsWithCommitments blobsWithCommitments;
public Builder copiedFrom(final Transaction toCopy) {
this.transactionType = toCopy.transactionType;
this.nonce = toCopy.nonce;
this.gasPrice = toCopy.gasPrice.orElse(null);
this.maxPriorityFeePerGas = toCopy.maxPriorityFeePerGas.orElse(null);
this.maxFeePerGas = toCopy.maxFeePerGas.orElse(null);
this.maxFeePerBlobGas = toCopy.maxFeePerBlobGas.orElse(null);
this.gasLimit = toCopy.gasLimit;
this.to = toCopy.to;
this.value = toCopy.value;
this.signature = toCopy.signature;
this.payload = toCopy.payload;
this.accessList = toCopy.maybeAccessList;
this.sender = toCopy.sender;
this.chainId = toCopy.chainId;
this.versionedHashes = toCopy.versionedHashes.orElse(null);
this.blobsWithCommitments = toCopy.blobsWithCommitments.orElse(null);
return this;
}
public Builder type(final TransactionType transactionType) {
this.transactionType = transactionType;
return this;
@ -1260,5 +1280,10 @@ public class Transaction
new BlobsWithCommitments(kzgCommitments, blobs, kzgProofs, versionedHashes);
return this;
}
public Builder blobsWithCommitments(final BlobsWithCommitments blobsWithCommitments) {
this.blobsWithCommitments = blobsWithCommitments;
return this;
}
}
}

@ -85,12 +85,12 @@ public class TransactionTestFixture {
builder.maxFeePerGas(maxFeePerGas.orElse(Wei.of(5000)));
builder.accessList(accessListEntries.orElse(List.of()));
builder.maxFeePerBlobGas(maxFeePerBlobGas.orElse(Wei.ONE));
builder.versionedHashes(
versionedHashes.orElse(List.of(VersionedHash.DEFAULT_VERSIONED_HASH)));
blobs.ifPresent(
bwc -> {
builder.kzgBlobs(bwc.getKzgCommitments(), bwc.getBlobs(), bwc.getKzgProofs());
});
if (blobs.isPresent()) {
builder.kzgBlobs(
blobs.get().getKzgCommitments(), blobs.get().getBlobs(), blobs.get().getKzgProofs());
} else if (versionedHashes.isPresent()) {
builder.versionedHashes(versionedHashes.get());
}
break;
}

@ -50,6 +50,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
@ -127,7 +128,8 @@ public abstract class AbstractIsolationTests {
poolConfiguration,
new EndLayer(txPoolMetrics),
txPoolMetrics,
transactionReplacementTester));
transactionReplacementTester,
new BlobCache()));
protected final List<GenesisAllocation> accounts =
GenesisConfigFile.development()

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.core;
import static java.util.stream.Collectors.toUnmodifiableSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import org.hyperledger.besu.crypto.KeyPair;
@ -25,6 +26,7 @@ import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.math.BigInteger;
import java.util.List;
@ -36,13 +38,13 @@ import java.util.stream.Stream;
import com.google.common.base.Suppliers;
import org.junit.jupiter.api.Test;
public class TransactionBuilderTest {
class TransactionBuilderTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair();
@Test
public void guessTypeCanGuessAllTypes() {
void guessTypeCanGuessAllTypes() {
final BlockDataGenerator gen = new BlockDataGenerator();
final Transaction.Builder frontierBuilder = Transaction.builder();
final Transaction.Builder eip1559Builder = Transaction.builder().maxFeePerGas(Wei.of(5));
@ -61,19 +63,36 @@ public class TransactionBuilderTest {
}
@Test
public void zeroBlobTransactionIsInvalid() {
void zeroBlobTransactionIsInvalid() {
TransactionTestFixture ttf =
new TransactionTestFixture()
.type(TransactionType.BLOB)
.chainId(Optional.of(BigInteger.ONE))
.versionedHashes(Optional.of(List.of()))
.maxFeePerGas(Optional.of(Wei.of(5)))
.maxPriorityFeePerGas(Optional.of(Wei.of(5)))
.maxFeePerBlobGas(Optional.of(Wei.of(5)));
try {
new TransactionTestFixture()
.type(TransactionType.BLOB)
.chainId(Optional.of(BigInteger.ONE))
.versionedHashes(Optional.of(List.of()))
.maxFeePerGas(Optional.of(Wei.of(5)))
.maxPriorityFeePerGas(Optional.of(Wei.of(5)))
.maxFeePerBlobGas(Optional.of(Wei.of(5)))
.createTransaction(senderKeys);
ttf.createTransaction(senderKeys);
fail();
} catch (IllegalArgumentException iea) {
assertThat(iea).hasMessage("Blob transaction must have at least one versioned hash");
}
}
@Test
@SuppressWarnings("ReferenceEquality")
void copyFromIsIdentical() {
final TransactionTestFixture fixture = new TransactionTestFixture();
final Transaction transaction = fixture.createTransaction(senderKeys);
final Transaction.Builder builder = Transaction.builder();
final Transaction copy = builder.copiedFrom(transaction).build();
assertThat(copy).isEqualTo(transaction).isNotSameAs(transaction);
assertThat(copy.getHash()).isEqualTo(transaction.getHash());
BytesValueRLPOutput sourceRLP = new BytesValueRLPOutput();
transaction.writeTo(sourceRLP);
BytesValueRLPOutput copyRLP = new BytesValueRLPOutput();
copy.writeTo(copyRLP);
assertEquals(sourceRLP.encoded(), copyRLP.encoded());
}
}

@ -59,6 +59,9 @@ dependencies {
implementation 'io.tmio:tuweni-units'
implementation 'io.tmio:tuweni-rlp'
implementation 'org.rocksdb:rocksdbjni'
implementation 'com.github.ben-manes.caffeine:caffeine'
implementation 'com.google.dagger:dagger'
annotationProcessor 'com.google.dagger:dagger-compiler'
annotationProcessor "org.immutables:value"
implementation "org.immutables:value-annotations"

@ -0,0 +1,91 @@
/*
* 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.eth.transactions;
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.ethereum.core.Transaction;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BlobCache {
private final Cache<VersionedHash, BlobsWithCommitments.BlobQuad> cache;
private static final Logger LOG = LoggerFactory.getLogger(BlobCache.class);
public BlobCache() {
this.cache =
Caffeine.newBuilder()
.maximumSize(6 * 32 * 3L) // 6 blobs max per 32 slots per 3 epochs
.expireAfterWrite(
3 * 32 * 12L, TimeUnit.SECONDS) // 3 epochs of 32 slots which take 12 seconds each.
.build();
}
public void cacheBlobs(final Transaction t) {
if (t.getType().supportsBlob()) {
var bwc = t.getBlobsWithCommitments();
if (bwc.isPresent()) {
bwc.get().getBlobQuads().stream()
.forEach(blobQuad -> this.cache.put(blobQuad.versionedHash(), blobQuad));
} else {
LOG.debug("transaction is missing blobs, cannot cache");
}
}
}
public Optional<Transaction> restoreBlob(final Transaction transaction) {
if (transaction.getType().supportsBlob()) {
Optional<List<VersionedHash>> maybeHashes = transaction.getVersionedHashes();
if (maybeHashes.isPresent()) {
if (!maybeHashes.get().isEmpty()) {
Transaction.Builder txBuilder = Transaction.builder();
txBuilder.copiedFrom(transaction);
List<BlobsWithCommitments.BlobQuad> blobQuads =
maybeHashes.get().stream().map(cache::getIfPresent).toList();
final BlobsWithCommitments bwc = new BlobsWithCommitments(blobQuads);
if (blobQuads.stream()
.map(BlobsWithCommitments.BlobQuad::versionedHash)
.toList()
.containsAll(maybeHashes.get())) {
txBuilder.blobsWithCommitments(bwc);
return Optional.of(txBuilder.build());
} else {
LOG.debug("did not find all versioned hashes to restore from cache");
return Optional.empty();
}
} else {
LOG.warn("can't restore blobs for transaction with empty list of versioned hashes");
return Optional.empty();
}
} else {
LOG.warn("can't restore blobs for transaction without list of versioned hashes");
return Optional.empty();
}
} else {
LOG.debug(
"can't restore blobs for non-blob transaction of type {}", transaction.getType().name());
return Optional.empty();
}
}
}

@ -0,0 +1,31 @@
/*
* 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.eth.transactions;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class BlobCacheModule {
@Provides
@Singleton
public BlobCache provideBlobCache() {
return new BlobCache();
}
}

@ -116,4 +116,9 @@ public class DisabledPendingTransactions implements PendingTransactions {
public String logStats() {
return "Disabled";
}
@Override
public Optional<Transaction> restoreBlob(final Transaction transaction) {
return Optional.empty();
}
}

@ -44,7 +44,7 @@ public abstract class PendingTransaction
static final int BASE_OPTIONAL_SIZE = 16;
static final int KZG_COMMITMENT_OR_PROOF_SIZE = 112;
static final int BLOB_SIZE = 131136;
static final int BLOBS_WITH_COMMITMENTS_SIZE = 32;
static final int BLOBS_WITH_COMMITMENTS_SIZE = 40;
static final int PENDING_TRANSACTION_MEMORY_SIZE = 40;
private static final AtomicLong TRANSACTIONS_ADDED = new AtomicLong();
private final Transaction transaction;

@ -72,6 +72,8 @@ public interface PendingTransactions {
String logStats();
Optional<Transaction> restoreBlob(Transaction transaction);
@FunctionalInterface
interface TransactionSelector {
TransactionSelectionResult evaluateTransaction(PendingTransaction pendingTransaction);

@ -373,8 +373,11 @@ public class TransactionPool implements BlockAddedObserver {
private void reAddTransactions(final List<Transaction> reAddTransactions) {
if (!reAddTransactions.isEmpty()) {
// if adding a blob tx, and it is missing its blob, is a re-org and we should restore the blob
// from cache.
var txsByOrigin =
reAddTransactions.stream()
.map(t -> pendingTransactions.restoreBlob(t).orElse(t))
.collect(Collectors.partitioningBy(tx -> isLocalSender(tx.getSender())));
var reAddLocalTxs = txsByOrigin.get(true);
var reAddRemoteTxs = txsByOrigin.get(false);

@ -56,7 +56,8 @@ public class TransactionPoolFactory {
final SyncState syncState,
final MiningParameters miningParameters,
final TransactionPoolConfiguration transactionPoolConfiguration,
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory,
final BlobCache blobCache) {
final TransactionPoolMetrics metrics = new TransactionPoolMetrics(metricsSystem);
@ -79,7 +80,8 @@ public class TransactionPoolFactory {
transactionTracker,
transactionsMessageSender,
newPooledTransactionHashesMessageSender,
pluginTransactionValidatorFactory);
pluginTransactionValidatorFactory,
blobCache);
}
static TransactionPool createTransactionPool(
@ -94,7 +96,8 @@ public class TransactionPoolFactory {
final PeerTransactionTracker transactionTracker,
final TransactionsMessageSender transactionsMessageSender,
final NewPooledTransactionHashesMessageSender newPooledTransactionHashesMessageSender,
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory,
final BlobCache blobCache) {
final TransactionPool transactionPool =
new TransactionPool(
@ -104,7 +107,8 @@ public class TransactionPoolFactory {
protocolContext,
clock,
metrics,
transactionPoolConfiguration),
transactionPoolConfiguration,
blobCache),
protocolSchedule,
protocolContext,
new TransactionBroadcaster(
@ -195,7 +199,8 @@ public class TransactionPoolFactory {
final ProtocolContext protocolContext,
final Clock clock,
final TransactionPoolMetrics metrics,
final TransactionPoolConfiguration transactionPoolConfiguration) {
final TransactionPoolConfiguration transactionPoolConfiguration,
final BlobCache blobCache) {
boolean isFeeMarketImplementBaseFee =
protocolSchedule.anyMatch(
@ -207,7 +212,8 @@ public class TransactionPoolFactory {
protocolContext,
metrics,
transactionPoolConfiguration,
isFeeMarketImplementBaseFee);
isFeeMarketImplementBaseFee,
blobCache);
} else {
return createPendingTransactionSorter(
protocolContext,
@ -244,7 +250,8 @@ public class TransactionPoolFactory {
final ProtocolContext protocolContext,
final TransactionPoolMetrics metrics,
final TransactionPoolConfiguration transactionPoolConfiguration,
final boolean isFeeMarketImplementBaseFee) {
final boolean isFeeMarketImplementBaseFee,
final BlobCache blobCache) {
final TransactionPoolReplacementHandler transactionReplacementHandler =
new TransactionPoolReplacementHandler(transactionPoolConfiguration.getPriceBump());
@ -258,14 +265,19 @@ public class TransactionPoolFactory {
final SparseTransactions sparseTransactions =
new SparseTransactions(
transactionPoolConfiguration, endLayer, metrics, transactionReplacementTester);
transactionPoolConfiguration,
endLayer,
metrics,
transactionReplacementTester,
blobCache);
final ReadyTransactions readyTransactions =
new ReadyTransactions(
transactionPoolConfiguration,
sparseTransactions,
metrics,
transactionReplacementTester);
transactionReplacementTester,
blobCache);
final AbstractPrioritizedTransactions pendingTransactionsSorter;
if (isFeeMarketImplementBaseFee) {
@ -281,14 +293,16 @@ public class TransactionPoolFactory {
readyTransactions,
metrics,
transactionReplacementTester,
feeMarket);
feeMarket,
blobCache);
} else {
pendingTransactionsSorter =
new GasPricePrioritizedTransactions(
transactionPoolConfiguration,
readyTransactions,
metrics,
transactionReplacementTester);
transactionReplacementTester,
blobCache);
}
return new LayeredPendingTransactions(transactionPoolConfiguration, pendingTransactionsSorter);

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.eth.transactions.layered;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
@ -41,8 +42,9 @@ public abstract class AbstractPrioritizedTransactions extends AbstractSequential
final TransactionsLayer prioritizedTransactions,
final TransactionPoolMetrics metrics,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester) {
super(poolConfig, prioritizedTransactions, transactionReplacementTester, metrics);
transactionReplacementTester,
final BlobCache blobCache) {
super(poolConfig, prioritizedTransactions, transactionReplacementTester, metrics, blobCache);
this.orderByFee = new TreeSet<>(this::compareByFee);
}

@ -18,6 +18,7 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction
import static org.hyperledger.besu.ethereum.eth.transactions.layered.TransactionsLayer.RemovalReason.FOLLOW_INVALIDATED;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
@ -36,8 +37,9 @@ public abstract class AbstractSequentialTransactionsLayer extends AbstractTransa
final TransactionsLayer nextLayer,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester,
final TransactionPoolMetrics metrics) {
super(poolConfig, nextLayer, transactionReplacementTester, metrics);
final TransactionPoolMetrics metrics,
final BlobCache blobCache) {
super(poolConfig, nextLayer, transactionReplacementTester, metrics, blobCache);
}
@Override

@ -28,6 +28,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactionAddedListener;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactionDroppedListener;
@ -74,12 +75,15 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer {
private OptionalLong nextLayerOnDroppedListenerId = OptionalLong.empty();
protected long spaceUsed = 0;
public AbstractTransactionsLayer(
private final BlobCache blobCache;
protected AbstractTransactionsLayer(
final TransactionPoolConfiguration poolConfig,
final TransactionsLayer nextLayer,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester,
final TransactionPoolMetrics metrics) {
final TransactionPoolMetrics metrics,
final BlobCache blobCache) {
this.poolConfig = poolConfig;
this.nextLayer = nextLayer;
this.transactionReplacementTester = transactionReplacementTester;
@ -87,6 +91,7 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer {
metrics.initSpaceUsed(this::getLayerSpaceUsed, name());
metrics.initTransactionCount(pendingTransactions::size, name());
metrics.initUniqueSenderCount(txsBySender::size, name());
this.blobCache = blobCache;
}
protected abstract boolean gapsAllowed();
@ -361,6 +366,7 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer {
final Transaction transaction,
final RemovalReason removalReason) {
final PendingTransaction removedTx = pendingTransactions.remove(transaction.getHash());
if (removedTx != null) {
decreaseSpaceUsed(removedTx);
metrics.incrementRemoved(removedTx, removalReason.label(), name());
@ -428,6 +434,9 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer {
while (itConfirmedTxs.hasNext()) {
final var confirmedTx = itConfirmedTxs.next();
itConfirmedTxs.remove();
if (confirmedTx.getTransaction().getBlobsWithCommitments().isPresent()) {
this.blobCache.cacheBlobs(confirmedTx.getTransaction());
}
processRemove(senderTxs, confirmedTx.getTransaction(), CONFIRMED);
metrics.incrementRemoved(confirmedTx, "confirmed", name());
@ -598,4 +607,8 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer {
protected abstract void internalConsistencyCheck(
final Map<Address, TreeMap<Long, PendingTransaction>> prevLayerTxsBySender);
public BlobCache getBlobCache() {
return blobCache;
}
}

@ -19,6 +19,7 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
@ -45,8 +46,9 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti
final TransactionPoolMetrics metrics,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester,
final FeeMarket feeMarket) {
super(poolConfig, nextLayer, metrics, transactionReplacementTester);
final FeeMarket feeMarket,
final BlobCache blobCache) {
super(poolConfig, nextLayer, metrics, transactionReplacementTester, blobCache);
this.nextBlockBaseFee =
Optional.of(calculateNextBlockBaseFee(feeMarket, chainHeadHeaderSupplier.get()));
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.transactions.layered;
import static java.util.Comparator.comparing;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
@ -37,8 +38,9 @@ public class GasPricePrioritizedTransactions extends AbstractPrioritizedTransact
final TransactionsLayer nextLayer,
final TransactionPoolMetrics metrics,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester) {
super(poolConfig, nextLayer, metrics, transactionReplacementTester);
transactionReplacementTester,
final BlobCache blobCache) {
super(poolConfig, nextLayer, metrics, transactionReplacementTester, blobCache);
}
@Override

@ -535,4 +535,9 @@ public class LayeredPendingTransactions implements PendingTransactions {
public synchronized String logStats() {
return prioritizedTransactions.logStats();
}
@Override
public Optional<Transaction> restoreBlob(final Transaction transaction) {
return prioritizedTransactions.getBlobCache().restoreBlob(transaction);
}
}

@ -19,6 +19,7 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
@ -52,8 +53,9 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer {
final TransactionsLayer nextLayer,
final TransactionPoolMetrics metrics,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester) {
super(poolConfig, nextLayer, transactionReplacementTester, metrics);
transactionReplacementTester,
final BlobCache blobCache) {
super(poolConfig, nextLayer, transactionReplacementTester, metrics, blobCache);
}
@Override

@ -20,6 +20,7 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
@ -60,8 +61,9 @@ public class SparseTransactions extends AbstractTransactionsLayer {
final TransactionsLayer nextLayer,
final TransactionPoolMetrics metrics,
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester) {
super(poolConfig, nextLayer, transactionReplacementTester, metrics);
transactionReplacementTester,
final BlobCache blobCache) {
super(poolConfig, nextLayer, transactionReplacementTester, metrics, blobCache);
orderByGap = new ArrayList<>(poolConfig.getMaxFutureBySender());
IntStream.range(0, poolConfig.getMaxFutureBySender())
.forEach(i -> orderByGap.add(new SendersByPriority()));

@ -23,6 +23,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactionAddedListener;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactionDroppedListener;
@ -93,6 +94,8 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
protected final TransactionPoolReplacementHandler transactionReplacementHandler;
protected final Supplier<BlockHeader> chainHeadHeaderSupplier;
private final BlobCache blobCache;
public AbstractPendingTransactionsSorter(
final TransactionPoolConfiguration poolConfig,
final Clock clock,
@ -126,6 +129,8 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
"transactions",
"Current size of the transaction pool",
pendingTransactions::size);
this.blobCache = new BlobCache();
}
@Override
@ -393,6 +398,10 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
removePendingTransactionBySenderAndNonce(removedPendingTx);
incrementTransactionRemovedCounter(
removedPendingTx.isReceivedFromLocalSource(), addedToBlock);
if (removedPendingTx.getTransaction().getBlobsWithCommitments().isPresent()
&& addedToBlock) {
this.blobCache.cacheBlobs(removedPendingTx.getTransaction());
}
}
}
}
@ -485,4 +494,13 @@ public abstract class AbstractPendingTransactionsSorter implements PendingTransa
return sb.toString();
}
}
/**
* @param transaction to restore blobs onto
* @return an optional copy of the supplied transaction, but with the BlobsWithCommitments
* restored. If none could be restored, empty.
*/
@Override
public Optional<Transaction> restoreBlob(final Transaction transaction) {
return blobCache.restoreBlob(transaction);
}
}

@ -60,6 +60,7 @@ import org.hyperledger.besu.ethereum.eth.messages.TransactionsMessage;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
@ -1118,7 +1119,8 @@ public final class EthProtocolManagerTest {
new SyncState(blockchain, ethManager.ethContext().getEthPeers()),
MiningParameters.newDefault(),
TransactionPoolConfiguration.DEFAULT,
null)
null,
new BlobCache())
.setEnabled();
// Send just a transaction message.

@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
@ -137,7 +138,8 @@ public abstract class AbstractMessageTaskTest<T, R> {
syncState,
MiningParameters.newDefault(),
TransactionPoolConfiguration.DEFAULT,
null);
null,
new BlobCache());
transactionPool.setEnabled();
ethProtocolManager =

@ -122,7 +122,6 @@ import org.mockito.junit.jupiter.MockitoSettings;
@MockitoSettings(strictness = LENIENT)
public abstract class AbstractTransactionPoolTest {
protected static final int MAX_TRANSACTIONS = 5;
protected static final KeyPair KEY_PAIR1 =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
private static final KeyPair KEY_PAIR2 =
@ -150,7 +149,7 @@ public abstract class AbstractTransactionPoolTest {
protected PendingTransactions transactions;
protected final Transaction transaction0 = createTransaction(0);
protected final Transaction transaction1 = createTransaction(1);
protected final Transaction transactionBlob = createBlobTransaction(0);
protected final Transaction transactionBlob = createBlobTransaction(2);
protected final Transaction transactionOtherSender = createTransaction(1, KEY_PAIR2);
private ExecutionContextTestFixture executionContext;
@ -326,7 +325,7 @@ public abstract class AbstractTransactionPoolTest {
assertThat(transactions.size()).isEqualTo(3);
assertThat(transactions.getLocalTransactions()).contains(localTransaction2);
assertThat(transactions.getPriorityTransactions().size()).isEqualTo(noLocalPriority ? 0 : 1);
assertThat(transactions.getPriorityTransactions()).hasSize(noLocalPriority ? 0 : 1);
}
@Test
@ -462,14 +461,14 @@ public abstract class AbstractTransactionPoolTest {
}
@Test
public void shouldNotReAddBlobTxsWhenReorgHappens() {
public void shouldReAddBlobTxsWhenReorgHappens() {
givenTransactionIsValid(transaction0);
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transactionBlob);
addAndAssertRemoteTransactionsValid(transaction0);
addAndAssertRemoteTransactionsValid(transaction1);
addAndAssertRemoteTransactionInvalid(transactionBlob);
addAndAssertRemoteTransactionsValid(transactionBlob);
final BlockHeader commonParent = getHeaderForCurrentChainHead();
final Block originalFork1 = appendBlock(Difficulty.of(1000), commonParent, transaction0);
@ -490,9 +489,16 @@ public abstract class AbstractTransactionPoolTest {
final Block reorgFork3 = appendBlock(Difficulty.of(3000), reorgFork2.getHeader());
verifyChainHeadIs(reorgFork3);
assertTransactionNotPending(transactionBlob);
assertTransactionPending(transaction0);
assertTransactionPending(transaction1);
assertTransactionPending(transactionBlob);
Optional<Transaction> maybeBlob = transactions.getTransactionByHash(transactionBlob.getHash());
assertThat(maybeBlob).isPresent();
Transaction restoredBlob = maybeBlob.get();
assertThat(restoredBlob).isEqualTo(transactionBlob);
assertThat(restoredBlob.getBlobsWithCommitments().get().getBlobQuads())
.isEqualTo(transactionBlob.getBlobsWithCommitments().get().getBlobQuads());
}
@ParameterizedTest
@ -1217,7 +1223,7 @@ public abstract class AbstractTransactionPoolTest {
assertThat(
add1559TxAndGetPendingTxsCount(
genesisBaseFee, minGasPrice, lastBlockBaseFee, txMaxFeePerGas, false, hasPriority))
.isEqualTo(0);
.isZero();
}
@ParameterizedTest
@ -1245,7 +1251,7 @@ public abstract class AbstractTransactionPoolTest {
assertThat(
add1559TxAndGetPendingTxsCount(
genesisBaseFee, minGasPrice, lastBlockBaseFee, txMaxFeePerGas, true, true))
.isEqualTo(0);
.isZero();
}
@Test
@ -1460,7 +1466,7 @@ public abstract class AbstractTransactionPoolTest {
.maxFeePerGas(Optional.of(Wei.of(5000L)))
.maxPriorityFeePerGas(Optional.of(Wei.of(1000L)))
.type(TransactionType.BLOB)
.blobsWithCommitments(Optional.of(new BlobTestFixture().createBlobsWithCommitments(1)))
.blobsWithCommitments(Optional.of(new BlobTestFixture().createBlobsWithCommitments(6)))
.createTransaction(KEY_PAIR1);
}

@ -256,8 +256,12 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
final ClassLayout cl = ClassLayout.parseInstance(bwc);
System.out.println(cl.toPrintable());
System.out.println("BlobsWithCommitments size: " + cl.instanceSize());
final ClassLayout rl = ClassLayout.parseInstance(bwc.getBlobs());
System.out.println(rl.toPrintable());
System.out.println("BlobQuad size:" + rl.instanceSize());
assertThat(cl.instanceSize()).isEqualTo(PendingTransaction.BLOBS_WITH_COMMITMENTS_SIZE);
assertThat(cl.instanceSize() + rl.instanceSize())
.isEqualTo(PendingTransaction.BLOBS_WITH_COMMITMENTS_SIZE);
}
@Test

@ -164,7 +164,8 @@ public class TestNode implements Closeable {
syncState,
MiningParameters.newDefault(),
TransactionPoolConfiguration.DEFAULT,
null);
null,
new BlobCache());
final EthProtocolManager ethProtocolManager =
new EthProtocolManager(

@ -252,7 +252,8 @@ public class TransactionPoolFactoryTest {
peerTransactionTracker,
transactionsMessageSender,
newPooledTransactionHashesMessageSender,
null);
null,
new BlobCache());
ethProtocolManager =
new EthProtocolManager(
@ -359,7 +360,8 @@ public class TransactionPoolFactoryTest {
.txMessageKeepAliveSeconds(1)
.build())
.build(),
null);
null,
new BlobCache());
txPool.setEnabled();
return txPool;

@ -19,6 +19,7 @@ import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.AbstractTransactionPoolTest;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
@ -40,9 +41,14 @@ public abstract class AbstractLayeredTransactionPoolTest extends AbstractTransac
final var txPoolMetrics = new TransactionPoolMetrics(metricsSystem);
final TransactionsLayer sparseLayer =
new SparseTransactions(
poolConfig, new EndLayer(txPoolMetrics), txPoolMetrics, transactionReplacementTester);
poolConfig,
new EndLayer(txPoolMetrics),
txPoolMetrics,
transactionReplacementTester,
new BlobCache());
final TransactionsLayer readyLayer =
new ReadyTransactions(poolConfig, sparseLayer, txPoolMetrics, transactionReplacementTester);
new ReadyTransactions(
poolConfig, sparseLayer, txPoolMetrics, transactionReplacementTester, new BlobCache());
return new LayeredPendingTransactions(
poolConfig,
createPrioritizedTransactions(

@ -25,6 +25,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
@ -58,7 +59,8 @@ public class BaseFeePrioritizedTransactionsTest extends AbstractPrioritizedTrans
nextLayer,
txPoolMetrics,
transactionReplacementTester,
FeeMarket.london(0L));
FeeMarket.london(0L),
new BlobCache());
}
@Override

@ -22,6 +22,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
@ -44,7 +45,7 @@ public class GasPricePrioritizedTransactionsTest extends AbstractPrioritizedTran
transactionReplacementTester) {
return new GasPricePrioritizedTransactions(
poolConfig, nextLayer, txPoolMetrics, transactionReplacementTester);
poolConfig, nextLayer, txPoolMetrics, transactionReplacementTester, new BlobCache());
}
@Override

@ -40,6 +40,7 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactionAddedListener;
@ -113,11 +114,19 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
final SparseTransactions sparseTransactions =
new SparseTransactions(
poolConfig, evictCollector, txPoolMetrics, transactionReplacementTester);
poolConfig,
evictCollector,
txPoolMetrics,
transactionReplacementTester,
new BlobCache());
final ReadyTransactions readyTransactions =
new ReadyTransactions(
poolConfig, sparseTransactions, txPoolMetrics, transactionReplacementTester);
poolConfig,
sparseTransactions,
txPoolMetrics,
transactionReplacementTester,
new BlobCache());
final BaseFeePrioritizedTransactions prioritizedTransactions =
new BaseFeePrioritizedTransactions(
@ -126,7 +135,8 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
readyTransactions,
txPoolMetrics,
transactionReplacementTester,
FeeMarket.london(0L));
FeeMarket.london(0L),
new BlobCache());
return new CreatedLayers(
prioritizedTransactions, readyTransactions, sparseTransactions, evictCollector);
}

@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
@ -44,7 +45,8 @@ public class LayeredTransactionPoolBaseFeeTest extends AbstractLayeredTransactio
nextLayer,
txPoolMetrics,
transactionReplacementTester,
FeeMarket.london(0L));
FeeMarket.london(0L),
new BlobCache());
}
@Override

@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
@ -38,7 +39,7 @@ public class LayeredTransactionPoolGasPriceTest extends AbstractLayeredTransacti
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
transactionReplacementTester) {
return new GasPricePrioritizedTransactions(
poolConfig, nextLayer, txPoolMetrics, transactionReplacementTester);
poolConfig, nextLayer, txPoolMetrics, transactionReplacementTester, new BlobCache());
}
@Override

@ -30,6 +30,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
@ -71,11 +72,19 @@ public class LayersTest extends BaseTransactionPoolTest {
private final EvictCollectorLayer evictCollector = new EvictCollectorLayer(txPoolMetrics);
private final SparseTransactions sparseTransactions =
new SparseTransactions(
poolConfig, evictCollector, txPoolMetrics, this::transactionReplacementTester);
poolConfig,
evictCollector,
txPoolMetrics,
this::transactionReplacementTester,
new BlobCache());
private final ReadyTransactions readyTransactions =
new ReadyTransactions(
poolConfig, sparseTransactions, txPoolMetrics, this::transactionReplacementTester);
poolConfig,
sparseTransactions,
txPoolMetrics,
this::transactionReplacementTester,
new BlobCache());
private final BaseFeePrioritizedTransactions prioritizedTransactions =
new BaseFeePrioritizedTransactions(
@ -84,7 +93,8 @@ public class LayersTest extends BaseTransactionPoolTest {
readyTransactions,
txPoolMetrics,
this::transactionReplacementTester,
FeeMarket.london(0L));
FeeMarket.london(0L),
new BlobCache());
private final LayeredPendingTransactions pendingTransactions =
new LayeredPendingTransactions(poolConfig, prioritizedTransactions);

@ -25,6 +25,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult;
@ -187,10 +188,12 @@ public class ReplayTest {
final BiFunction<PendingTransaction, PendingTransaction, Boolean> txReplacementTester =
(tx1, tx2) -> transactionReplacementTester(poolConfig, tx1, tx2);
final SparseTransactions sparseTransactions =
new SparseTransactions(poolConfig, evictCollector, txPoolMetrics, txReplacementTester);
new SparseTransactions(
poolConfig, evictCollector, txPoolMetrics, txReplacementTester, new BlobCache());
final ReadyTransactions readyTransactions =
new ReadyTransactions(poolConfig, sparseTransactions, txPoolMetrics, txReplacementTester);
new ReadyTransactions(
poolConfig, sparseTransactions, txPoolMetrics, txReplacementTester, new BlobCache());
return new BaseFeePrioritizedTransactions(
poolConfig,
@ -198,7 +201,8 @@ public class ReplayTest {
readyTransactions,
txPoolMetrics,
txReplacementTester,
baseFeeMarket);
baseFeeMarket,
new BlobCache());
}
// ToDo: commented since not always working, needs fix

@ -42,6 +42,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
@ -252,7 +253,8 @@ public class RetestethContext {
syncState,
miningParameters,
transactionPoolConfiguration,
null);
null,
new BlobCache());
if (LOG.isTraceEnabled()) {
LOG.trace("Genesis Block {} ", genesisState.getBlock());

@ -350,15 +350,15 @@
<sha256 value="cd194cc51c48ba7218ebc3b1f41fce51dde1f8ee6666817ab31fa01e7e51b255" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.ben-manes.caffeine" name="caffeine" version="3.1.5">
<artifact name="caffeine-3.1.5.jar">
<sha256 value="02c34fd2ba78b1956ef6e5de9ac915aee9bfa498589966fa87255fbadb653499" origin="Generated by Gradle"/>
<component group="com.github.ben-manes.caffeine" name="caffeine" version="3.1.8">
<artifact name="caffeine-3.1.8.jar">
<sha256 value="7dd15f9df1be238ffaa367ce6f556737a88031de4294dad18eef57c474ddf1d3" origin="Generated by Gradle"/>
</artifact>
<artifact name="caffeine-3.1.5.module">
<sha256 value="873f7f03027b1ac94d55f84894790451a7ed55574219301bb795aab496c1ce1a" origin="Generated by Gradle"/>
<artifact name="caffeine-3.1.8.module">
<sha256 value="49acd4cf81be1e4e868054f0d34fcf5c775608459507307ce878cdba4fdd4d84" origin="Generated by Gradle"/>
</artifact>
<artifact name="caffeine-3.1.5.pom">
<sha256 value="a2983b2212090f5256b1e944cdad09f8f4e8f94cef17545ecab9063ea593c34b" origin="Generated by Gradle"/>
<artifact name="caffeine-3.1.8.pom">
<sha256 value="463f4f89638aae82c654b3f2842e8d03f7a0d48194c426046dd9c95ce06326ee" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.ben-manes.versions" name="com.github.ben-manes.versions.gradle.plugin" version="0.42.0">
@ -803,6 +803,14 @@
<sha256 value="94f50653af659be1d1a0d785f97cba768571f794d2e655fc3d22e4eee6512b39" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_annotations" version="2.21.1">
<artifact name="error_prone_annotations-2.21.1.jar">
<sha256 value="d1f3c66aa91ac52549e00ae3b208ba4b9af7d72d68f230643553beb38e6118ac" origin="Generated by Gradle"/>
</artifact>
<artifact name="error_prone_annotations-2.21.1.pom">
<sha256 value="f598880feefaea9d674dc41db13ab37004bf03776b5bb21c04dede8e920c1f12" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_annotations" version="2.3.4">
<artifact name="error_prone_annotations-2.3.4.jar">
<sha256 value="baf7d6ea97ce606c53e11b6854ba5f2ce7ef5c24dddf0afa18d1260bd25b002c" origin="Generated by Gradle"/>
@ -842,6 +850,11 @@
<sha256 value="e0b8bc77fc794d22d50aa4851896d8202a616610161b13cbff2a148ff1e102c2" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_parent" version="2.21.1">
<artifact name="error_prone_parent-2.21.1.pom">
<sha256 value="32bb0b5ff241fd6ba1feea448aebb9cedef1699be73cb6f319365387b82bf92c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.errorprone" name="error_prone_parent" version="2.3.4">
<artifact name="error_prone_parent-2.3.4.pom">
<sha256 value="40495b437a60d2398f0fdfc054b89d9c394a82347a274a0721c2e950a4302186" origin="Generated by Gradle"/>
@ -3579,15 +3592,15 @@
<sha256 value="19c5c98bf51711bcdbbd6f34d58fe552bfc60f5ca7888baa74eca1b1f7c4c6dc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.checkerframework" name="checker-qual" version="3.32.0">
<artifact name="checker-qual-3.32.0.jar">
<sha256 value="b66e025da0a6bf85f0b7f5fdadca832a32771f85a12cac1401a3c0cd8fd73ccd" origin="Generated by Gradle"/>
<component group="org.checkerframework" name="checker-qual" version="3.37.0">
<artifact name="checker-qual-3.37.0.jar">
<sha256 value="e4ce1376cc2735e1dde220b62ad0913f51297704daad155a33f386bc5db0d9f7" origin="Generated by Gradle"/>
</artifact>
<artifact name="checker-qual-3.32.0.module">
<sha256 value="ccdecdaa1aaeeb783951f6a7c18dc19395c2e2a5e7f77450d707b8d62e87de4f" origin="Generated by Gradle"/>
<artifact name="checker-qual-3.37.0.module">
<sha256 value="7258a769dcaa26b98154d229d85cc72e5b3666b0bcb637d2daf16ec498956638" origin="Generated by Gradle"/>
</artifact>
<artifact name="checker-qual-3.32.0.pom">
<sha256 value="795a88652a6edefa1c30fdbab280129e4183b1acf994ac74be43f3eb8aa77dcf" origin="Generated by Gradle"/>
<artifact name="checker-qual-3.37.0.pom">
<sha256 value="02392fbd4ce21901f945615472b1d4d4d3591b3aabdf013105f5c92ec32cb4f0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.checkerframework" name="checker-qual" version="3.5.0">
@ -4735,6 +4748,9 @@
<artifact name="junit-platform-commons-1.9.2.module">
<sha256 value="6ba4c84613e5b89e668ee6a89971f0d90d0e1b81721b8b5fe0c80058f0ceb9ee" origin="Generated by Gradle"/>
</artifact>
<artifact name="junit-platform-commons-1.9.2.pom">
<sha256 value="24023f20c235a69609fb2f95a60738557fcf9413f3ae92a92e1329547d6145c9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.junit.platform" name="junit-platform-console" version="1.8.1">
<artifact name="junit-platform-console-1.8.1.module">
@ -4756,6 +4772,9 @@
<artifact name="junit-platform-console-1.9.2.module">
<sha256 value="c0c9280709d247697e3aa7ca51b5e274f9e59f731fe3845a12f413601a55a3f5" origin="Generated by Gradle"/>
</artifact>
<artifact name="junit-platform-console-1.9.2.pom">
<sha256 value="3739feafb6c2dc183b49ef9698fb7650e5c693e016af85f254b15a832d88c732" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.junit.platform" name="junit-platform-engine" version="1.5.2">
<artifact name="junit-platform-engine-1.5.2.jar">
@ -4783,6 +4802,9 @@
<artifact name="junit-platform-engine-1.9.2.module">
<sha256 value="1de85141af9f58153e24575069a1375fbbedd7af5d844cbe3287965349cba1f7" origin="Generated by Gradle"/>
</artifact>
<artifact name="junit-platform-engine-1.9.2.pom">
<sha256 value="2df9420a6b24e5f4f314235e01cd9c3b8f646285db2f6e82d86ee06ef7af4e24" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.junit.platform" name="junit-platform-launcher" version="1.5.2">
<artifact name="junit-platform-launcher-1.5.2.jar">
@ -4812,6 +4834,9 @@
<artifact name="junit-platform-launcher-1.9.2.module">
<sha256 value="b77d18a92fc5f0454292799b6aa5948225207f2549b3ce7f1151ccf5e6b68686" origin="Generated by Gradle"/>
</artifact>
<artifact name="junit-platform-launcher-1.9.2.pom">
<sha256 value="ce9148998a8ef1de67e5a8f44079c03e1752eab52a23a1f76e07a8147f2c198e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.junit.platform" name="junit-platform-reporting" version="1.8.1">
<artifact name="junit-platform-reporting-1.8.1.module">
@ -4833,6 +4858,9 @@
<artifact name="junit-platform-reporting-1.9.2.module">
<sha256 value="8cf90e7ae96f087e2e315c626253c18377c654bccce73c99c1dc43805a5c88ec" origin="Generated by Gradle"/>
</artifact>
<artifact name="junit-platform-reporting-1.9.2.pom">
<sha256 value="e77c33e7ffbda399fcfc19b6c2f20d7c0df9f549261ca2bb25c86323b94cd7ca" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.junit.vintage" name="junit-vintage-engine" version="5.5.2">
<artifact name="junit-vintage-engine-5.5.2.jar">

@ -27,7 +27,7 @@ dependencyManagement {
entry 'jackson-datatype-jdk8'
}
dependency 'com.github.ben-manes.caffeine:caffeine:3.1.5'
dependency 'com.github.ben-manes.caffeine:caffeine:3.1.8'
dependencySet(group: 'com.github.tomakehurst', version: '2.35.0') {
entry'wiremock-jre8-standalone'

Loading…
Cancel
Save