Skip to content
/ besu Public
forked from hyperledger/besu

Commit

Permalink
Reduce receipt size (hyperledger#6602)
Browse files Browse the repository at this point in the history
Signed-off-by: Jason Frame <[email protected]>
Signed-off-by: amsmota <[email protected]>
  • Loading branch information
jframe authored and amsmota committed Apr 16, 2024
1 parent 51176e1 commit 5ff0c3a
Show file tree
Hide file tree
Showing 48 changed files with 601 additions and 99 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- BFT networks won't start with SNAP or CHECKPOINT sync (previously Besu would start with this config but quietly fail to sync, so it's now more obvious that it won't work) [#6625](https://github.com/hyperledger/besu/pull/6625), [#6667](https://github.com/hyperledger/besu/pull/6667)

### Upcoming Breaking Changes
- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version.

### Deprecations

Expand All @@ -25,6 +26,7 @@
- `eth_call` for blob tx allows for empty `maxFeePerBlobGas` [#6731](https://github.com/hyperledger/besu/pull/6731)
- Extend error handling of plugin RPC methods [#6759](https://github.com/hyperledger/besu/pull/6759)
- Added engine_newPayloadV4 and engine_getPayloadV4 methods [#6783](https://github.com/hyperledger/besu/pull/6783)
- Reduce storage size of receipts [#6602](https://github.com/hyperledger/besu/pull/6602)

### Bug fixes
- Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.hyperledger.besu.cli.options.stable;

import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_RECEIPT_COMPACTION_ENABLED;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_CODE_USING_CODE_HASH_ENABLED;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE;
Expand Down Expand Up @@ -61,6 +62,12 @@ public class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
arity = "1")
private Long bonsaiMaxLayersToLoad = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD;

@Option(
names = "--receipt-compaction-enabled",
description = "Enables compact storing of receipts (default: ${DEFAULT-VALUE}).",
arity = "1")
private Boolean receiptCompactionEnabled = DEFAULT_RECEIPT_COMPACTION_ENABLED;

@CommandLine.ArgGroup(validate = false)
private final DataStorageOptions.Unstable unstableOptions = new Unstable();

Expand Down Expand Up @@ -149,6 +156,7 @@ public static DataStorageOptions fromConfig(final DataStorageConfiguration domai
final DataStorageOptions dataStorageOptions = DataStorageOptions.create();
dataStorageOptions.dataStorageFormat = domainObject.getDataStorageFormat();
dataStorageOptions.bonsaiMaxLayersToLoad = domainObject.getBonsaiMaxLayersToLoad();
dataStorageOptions.receiptCompactionEnabled = domainObject.getReceiptCompactionEnabled();
dataStorageOptions.unstableOptions.bonsaiLimitTrieLogsEnabled =
domainObject.getUnstable().getBonsaiLimitTrieLogsEnabled();
dataStorageOptions.unstableOptions.bonsaiTrieLogPruningWindowSize =
Expand All @@ -164,6 +172,7 @@ public DataStorageConfiguration toDomainObject() {
return ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(dataStorageFormat)
.bonsaiMaxLayersToLoad(bonsaiMaxLayersToLoad)
.receiptCompactionEnabled(receiptCompactionEnabled)
.unstable(
ImmutableDataStorageConfiguration.Unstable.builder()
.bonsaiLimitTrieLogsEnabled(unstableOptions.bonsaiLimitTrieLogsEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,8 @@ public BesuController build() {
storageProvider.createWorldStateStorageCoordinator(dataStorageConfiguration);

final BlockchainStorage blockchainStorage =
storageProvider.createBlockchainStorage(protocolSchedule, variablesStorage);
storageProvider.createBlockchainStorage(
protocolSchedule, variablesStorage, dataStorageConfiguration);

final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,38 @@ public DataStorageFormat getDatabaseFormat() {
public Wei getMinGasPrice() {
return miningParameters.getMinTransactionGasPrice();
}

@Override
public org.hyperledger.besu.plugin.services.storage.DataStorageConfiguration
getDataStorageConfiguration() {
return new DataStoreConfigurationImpl(dataStorageConfiguration);
}

/**
* A concrete implementation of DataStorageConfiguration which is used in Besu plugin framework.
*/
public static class DataStoreConfigurationImpl
implements org.hyperledger.besu.plugin.services.storage.DataStorageConfiguration {

private final DataStorageConfiguration dataStorageConfiguration;

/**
* Instantiate the concrete implementation of the plugin DataStorageConfiguration.
*
* @param dataStorageConfiguration The Ethereum core module data storage configuration
*/
public DataStoreConfigurationImpl(final DataStorageConfiguration dataStorageConfiguration) {
this.dataStorageConfiguration = dataStorageConfiguration;
}

@Override
public DataStorageFormat getDatabaseFormat() {
return dataStorageConfiguration.getDataStorageFormat();
}

@Override
public boolean getReceiptCompactionEnabled() {
return dataStorageConfiguration.getReceiptCompactionEnabled();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,24 @@ public void bonsaiCodeUsingCodeHashEnabledCanBeDisabled() {
"false");
}

@Test
public void receiptCompactionCanBeEnabled() {
internalTestSuccess(
dataStorageConfiguration ->
assertThat(dataStorageConfiguration.getReceiptCompactionEnabled()).isEqualTo(true),
"--receipt-compaction-enabled",
"true");
}

@Test
public void receiptCompactionCanBeDisabled() {
internalTestSuccess(
dataStorageConfiguration ->
assertThat(dataStorageConfiguration.getReceiptCompactionEnabled()).isEqualTo(false),
"--receipt-compaction-enabled",
"false");
}

@Override
protected DataStorageConfiguration createDefaultDomainObject() {
return DataStorageConfiguration.DEFAULT_CONFIG;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ public void setup() throws JsonProcessingException {
lenient().when(genesisConfigFile.getConfigOptions()).thenReturn(genesisConfigOptions);
lenient().when(genesisConfigOptions.getCheckpointOptions()).thenReturn(checkpointConfigOptions);
lenient()
.when(storageProvider.createBlockchainStorage(any(), any()))
.when(storageProvider.createBlockchainStorage(any(), any(), any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()));
new MainnetBlockHeaderFunctions(),
false));
lenient()
.when(
storageProvider.createWorldStateStorageCoordinator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,13 @@ public void setup() {
when(ethashConfigOptions.getFixedDifficulty()).thenReturn(OptionalLong.empty());
when(storageProvider.getStorageBySegmentIdentifier(any()))
.thenReturn(new InMemoryKeyValueStorage());
when(storageProvider.createBlockchainStorage(any(), any()))
when(storageProvider.createBlockchainStorage(any(), any(), any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()));
new MainnetBlockHeaderFunctions(),
false));
when(synchronizerConfiguration.getDownloaderParallelism()).thenReturn(1);
when(synchronizerConfiguration.getTransactionsParallelism()).thenReturn(1);
when(synchronizerConfiguration.getComputationParallelism()).thenReturn(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,13 @@ public void setup() throws JsonProcessingException {
lenient().when(genesisConfigFile.getConfigOptions()).thenReturn(genesisConfigOptions);
lenient().when(genesisConfigOptions.getCheckpointOptions()).thenReturn(checkpointConfigOptions);
lenient()
.when(storageProvider.createBlockchainStorage(any(), any()))
.when(storageProvider.createBlockchainStorage(any(), any(), any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()));
new MainnetBlockHeaderFunctions(),
false));
lenient()
.when(
storageProvider.createWorldStateStorageCoordinator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,13 @@ public void setup() {
when(genesisConfigOptions.getTerminalBlockHash()).thenReturn(Optional.of(Hash.ZERO));
lenient().when(genesisConfigOptions.getTerminalBlockNumber()).thenReturn(OptionalLong.of(1L));
lenient()
.when(storageProvider.createBlockchainStorage(any(), any()))
.when(storageProvider.createBlockchainStorage(any(), any(), any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()));
new MainnetBlockHeaderFunctions(),
false));
lenient()
.when(storageProvider.getStorageBySegmentIdentifier(any()))
.thenReturn(new InMemoryKeyValueStorage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ public void setUp() {
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()),
new MainnetBlockHeaderFunctions(),
false),
new NoOpMetricsSystem(),
0);

Expand Down
1 change: 1 addition & 0 deletions besu/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ ethstats-cacert-file="./root.cert"
# Data storage
data-storage-format="BONSAI"
bonsai-historical-block-limit=512
receipt-compaction-enabled=true

# feature flags
Xsecp256k1-native-enabled=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public Optional<Bytes> getRawReceipt(final DataFetchingEnvironment environment)
.map(
receipt -> {
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
receipt.getReceipt().writeTo(rlpOutput);
receipt.getReceipt().writeToForNetwork(rlpOutput);
return rlpOutput.encoded();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected Object resultByBlockHash(final JsonRpcRequestContext request, final Ha

private String[] toRLP(final List<TransactionReceipt> receipts) {
return receipts.stream()
.map(receipt -> RLP.encode(receipt::writeTo).toHexString())
.map(receipt -> RLP.encode(receipt::writeToForNetwork).toHexString())
.toArray(String[]::new);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ private void backupChainData() throws IOException {
bodyWriter.writeBytes(bodyOutput.encoded().toArrayUnsafe());

final BytesValueRLPOutput receiptsOutput = new BytesValueRLPOutput();
receiptsOutput.writeList(receipts.get(), TransactionReceipt::writeToWithRevertReason);
receiptsOutput.writeList(receipts.get(), (r, rlpOut) -> r.writeToForStorage(rlpOut, false));
receiptsWriter.writeBytes(receiptsOutput.encoded().toArrayUnsafe());

backupStatus.storedBlock = blockNumber;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ public class NewBlockHeadersSubscriptionServiceTest {
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions());
new MainnetBlockHeaderFunctions(),
false);
private final Block genesisBlock = gen.genesisBlock();
private final MutableBlockchain blockchain =
DefaultBlockchain.createMutable(genesisBlock, blockchainStorage, new NoOpMetricsSystem(), 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ public void setup() {
new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(),
new VariablesKeyValueStorage(new InMemoryKeyValueStorage()),
new MainnetBlockHeaderFunctions()),
new MainnetBlockHeaderFunctions(),
false),
new NoOpMetricsSystem(),
0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import org.apache.tuweni.bytes.Bytes;

Expand Down Expand Up @@ -169,23 +170,26 @@ private TransactionReceipt(
*
* @param out The RLP output to write to
*/
public void writeTo(final RLPOutput out) {
writeTo(out, false);
public void writeToForNetwork(final RLPOutput out) {
writeTo(out, false, false);
}

public void writeToWithRevertReason(final RLPOutput out) {
writeTo(out, true);
public void writeToForStorage(final RLPOutput out, final boolean compacted) {
writeTo(out, true, compacted);
}

private void writeTo(final RLPOutput rlpOutput, final boolean withRevertReason) {
@VisibleForTesting
void writeTo(final RLPOutput rlpOutput, final boolean withRevertReason, final boolean compacted) {
if (transactionType.equals(TransactionType.FRONTIER)) {
writeToForReceiptTrie(rlpOutput, withRevertReason);
writeToForReceiptTrie(rlpOutput, withRevertReason, compacted);
} else {
rlpOutput.writeBytes(RLP.encode(out -> writeToForReceiptTrie(out, withRevertReason)));
rlpOutput.writeBytes(
RLP.encode(out -> writeToForReceiptTrie(out, withRevertReason, compacted)));
}
}

public void writeToForReceiptTrie(final RLPOutput rlpOutput, final boolean withRevertReason) {
public void writeToForReceiptTrie(
final RLPOutput rlpOutput, final boolean withRevertReason, final boolean compacted) {
if (!transactionType.equals(TransactionType.FRONTIER)) {
rlpOutput.writeIntScalar(transactionType.getSerializedType());
}
Expand All @@ -200,8 +204,10 @@ public void writeToForReceiptTrie(final RLPOutput rlpOutput, final boolean withR
rlpOutput.writeLongScalar(status);
}
rlpOutput.writeLongScalar(cumulativeGasUsed);
rlpOutput.writeBytes(bloomFilter);
rlpOutput.writeList(logs, Log::writeTo);
if (!compacted) {
rlpOutput.writeBytes(bloomFilter);
}
rlpOutput.writeList(logs, (log, logOutput) -> log.writeTo(logOutput, compacted));
if (withRevertReason && revertReason.isPresent()) {
rlpOutput.writeBytes(revertReason.get());
}
Expand Down Expand Up @@ -240,10 +246,21 @@ public static TransactionReceipt readFrom(
// correct transaction receipt encoding to use.
final RLPInput firstElement = input.readAsRlp();
final long cumulativeGas = input.readLongScalar();
// The logs below will populate the bloom filter upon construction.

LogsBloomFilter bloomFilter = null;

final boolean hasLogs = !input.nextIsList() && input.nextSize() == LogsBloomFilter.BYTE_SIZE;
if (hasLogs) {
// The logs below will populate the bloom filter upon construction.
bloomFilter = LogsBloomFilter.readFrom(input);
}
// TODO consider validating that the logs and bloom filter match.
final LogsBloomFilter bloomFilter = LogsBloomFilter.readFrom(input);
final List<Log> logs = input.readList(Log::readFrom);
final boolean compacted = !hasLogs;
final List<Log> logs = input.readList(logInput -> Log.readFrom(logInput, compacted));
if (compacted) {
bloomFilter = LogsBloomFilter.builder().insertLogs(logs).build();
}

final Optional<Bytes> revertReason;
if (input.isEndOfCurrentList()) {
revertReason = Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ public static Hash receiptsRoot(final List<TransactionReceipt> receipts) {
trie.put(
indexKey(i),
RLP.encode(
rlpOutput -> receipts.get(i).writeToForReceiptTrie(rlpOutput, false))));
rlpOutput ->
receipts.get(i).writeToForReceiptTrie(rlpOutput, false, false))));

return Hash.wrap(trie.getRootHash());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public interface StorageProvider extends Closeable {
VariablesStorage createVariablesStorage();

BlockchainStorage createBlockchainStorage(
ProtocolSchedule protocolSchedule, VariablesStorage variablesStorage);
ProtocolSchedule protocolSchedule,
VariablesStorage variablesStorage,
DataStorageConfiguration storageConfiguration);

WorldStateKeyValueStorage createWorldStateStorage(
DataStorageConfiguration dataStorageConfiguration);
Expand Down
Loading

0 comments on commit 5ff0c3a

Please sign in to comment.