mirror of https://github.com/hyperledger/besu
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
parent
0ccb4d4400
commit
488755a728
@ -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(); |
||||
} |
||||
} |
Loading…
Reference in new issue