Fix transaction pool issue (#4964)

* fix transaction pool issue
* add block replay
* support in-memory snapshots

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
pull/4971/head
matkt 2 years ago committed by GitHub
parent 42d67de5b7
commit e36b1f6dbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  2. 13
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java
  3. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java
  4. 63
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldStateKeyValueStorage.java
  5. 12
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  6. 3
      plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBSnapshotTransaction.java
  7. 20
      services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/InMemoryKeyValueStorage.java

@ -132,6 +132,7 @@ public class BesuEventsImplTest {
.thenReturn(ValidationResult.valid()); .thenReturn(ValidationResult.valid());
when(mockTransactionValidator.validateForSender(any(), any(), any())) when(mockTransactionValidator.validateForSender(any(), any(), any()))
.thenReturn(ValidationResult.valid()); .thenReturn(ValidationResult.valid());
when(mockWorldState.copy()).thenReturn(mockWorldState);
when(mockWorldStateArchive.getMutable(any(), any(), anyBoolean())) when(mockWorldStateArchive.getMutable(any(), any(), anyBoolean()))
.thenReturn(Optional.of(mockWorldState)); .thenReturn(Optional.of(mockWorldState));

@ -145,15 +145,22 @@ public class BlockReplay {
if (previous == null) { if (previous == null) {
return Optional.empty(); return Optional.empty();
} }
try (final MutableWorldState mutableWorldState = try (final var worldState =
worldStateArchive worldStateArchive
.getMutable(previous.getStateRoot(), previous.getHash(), false) .getMutable(previous.getStateRoot(), previous.getBlockHash(), false)
.map(
ws -> {
if (!ws.isPersistable()) {
return ws.copy();
}
return ws;
})
.orElseThrow( .orElseThrow(
() -> () ->
new IllegalArgumentException( new IllegalArgumentException(
"Missing worldstate for stateroot " "Missing worldstate for stateroot "
+ previous.getStateRoot().toShortHexString()))) { + previous.getStateRoot().toShortHexString()))) {
return action.perform(body, header, blockchain, mutableWorldState, transactionProcessor); return action.perform(body, header, blockchain, worldState, transactionProcessor);
} catch (Exception ex) { } catch (Exception ex) {
return Optional.empty(); return Optional.empty();
} }

@ -118,6 +118,7 @@ public class TransactionTracerTest {
when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase); when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase);
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
when(protocolSpec.getBadBlocksManager()).thenReturn(new BadBlockManager()); when(protocolSpec.getBadBlocksManager()).thenReturn(new BadBlockManager());
when(mutableWorldState.copy()).thenReturn(mutableWorldState);
} }
@Test @Test

@ -180,10 +180,10 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey
public static class SnapshotUpdater implements BonsaiWorldStateKeyValueStorage.BonsaiUpdater { public static class SnapshotUpdater implements BonsaiWorldStateKeyValueStorage.BonsaiUpdater {
private final SnappedKeyValueStorage accountStorage; private final KeyValueStorageTransaction accountStorageTransaction;
private final SnappedKeyValueStorage codeStorage; private final KeyValueStorageTransaction codeStorageTransaction;
private final SnappedKeyValueStorage storageStorage; private final KeyValueStorageTransaction storageStorageTransaction;
private final SnappedKeyValueStorage trieBranchStorage; private final KeyValueStorageTransaction trieBranchStorageTransaction;
private final KeyValueStorageTransaction trieLogStorageTransaction; private final KeyValueStorageTransaction trieLogStorageTransaction;
public SnapshotUpdater( public SnapshotUpdater(
@ -192,16 +192,16 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey
final SnappedKeyValueStorage storageStorage, final SnappedKeyValueStorage storageStorage,
final SnappedKeyValueStorage trieBranchStorage, final SnappedKeyValueStorage trieBranchStorage,
final KeyValueStorage trieLogStorage) { final KeyValueStorage trieLogStorage) {
this.accountStorage = accountStorage; this.accountStorageTransaction = accountStorage.getSnapshotTransaction();
this.codeStorage = codeStorage; this.codeStorageTransaction = codeStorage.getSnapshotTransaction();
this.storageStorage = storageStorage; this.storageStorageTransaction = storageStorage.getSnapshotTransaction();
this.trieBranchStorage = trieBranchStorage; this.trieBranchStorageTransaction = trieBranchStorage.getSnapshotTransaction();
this.trieLogStorageTransaction = trieLogStorage.startTransaction(); this.trieLogStorageTransaction = trieLogStorage.startTransaction();
} }
@Override @Override
public BonsaiUpdater removeCode(final Hash accountHash) { public BonsaiUpdater removeCode(final Hash accountHash) {
codeStorage.getSnapshotTransaction().remove(accountHash.toArrayUnsafe()); codeStorageTransaction.remove(accountHash.toArrayUnsafe());
return this; return this;
} }
@ -212,13 +212,13 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey
// Don't save empty values // Don't save empty values
return this; return this;
} }
codeStorage.getSnapshotTransaction().put(accountHash.toArrayUnsafe(), code.toArrayUnsafe()); codeStorageTransaction.put(accountHash.toArrayUnsafe(), code.toArrayUnsafe());
return this; return this;
} }
@Override @Override
public BonsaiUpdater removeAccountInfoState(final Hash accountHash) { public BonsaiUpdater removeAccountInfoState(final Hash accountHash) {
accountStorage.getSnapshotTransaction().remove(accountHash.toArrayUnsafe()); accountStorageTransaction.remove(accountHash.toArrayUnsafe());
return this; return this;
} }
@ -228,31 +228,26 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey
// Don't save empty values // Don't save empty values
return this; return this;
} }
accountStorage accountStorageTransaction.put(accountHash.toArrayUnsafe(), accountValue.toArrayUnsafe());
.getSnapshotTransaction()
.put(accountHash.toArrayUnsafe(), accountValue.toArrayUnsafe());
return this; return this;
} }
@Override @Override
public BonsaiUpdater putStorageValueBySlotHash( public BonsaiUpdater putStorageValueBySlotHash(
final Hash accountHash, final Hash slotHash, final Bytes storage) { final Hash accountHash, final Hash slotHash, final Bytes storage) {
storageStorage storageStorageTransaction.put(
.getSnapshotTransaction() Bytes.concatenate(accountHash, slotHash).toArrayUnsafe(), storage.toArrayUnsafe());
.put(Bytes.concatenate(accountHash, slotHash).toArrayUnsafe(), storage.toArrayUnsafe());
return this; return this;
} }
@Override @Override
public void removeStorageValueBySlotHash(final Hash accountHash, final Hash slotHash) { public void removeStorageValueBySlotHash(final Hash accountHash, final Hash slotHash) {
storageStorage storageStorageTransaction.remove(Bytes.concatenate(accountHash, slotHash).toArrayUnsafe());
.getSnapshotTransaction()
.remove(Bytes.concatenate(accountHash, slotHash).toArrayUnsafe());
} }
@Override @Override
public KeyValueStorageTransaction getTrieBranchStorageTransaction() { public KeyValueStorageTransaction getTrieBranchStorageTransaction() {
return trieBranchStorage.getSnapshotTransaction(); return trieBranchStorageTransaction;
} }
@Override @Override
@ -263,13 +258,9 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey
@Override @Override
public WorldStateStorage.Updater saveWorldState( public WorldStateStorage.Updater saveWorldState(
final Bytes blockHash, final Bytes32 nodeHash, final Bytes node) { final Bytes blockHash, final Bytes32 nodeHash, final Bytes node) {
trieBranchStorage trieBranchStorageTransaction.put(Bytes.EMPTY.toArrayUnsafe(), node.toArrayUnsafe());
.getSnapshotTransaction() trieBranchStorageTransaction.put(WORLD_ROOT_HASH_KEY, nodeHash.toArrayUnsafe());
.put(Bytes.EMPTY.toArrayUnsafe(), node.toArrayUnsafe()); trieBranchStorageTransaction.put(WORLD_BLOCK_HASH_KEY, blockHash.toArrayUnsafe());
trieBranchStorage.getSnapshotTransaction().put(WORLD_ROOT_HASH_KEY, nodeHash.toArrayUnsafe());
trieBranchStorage
.getSnapshotTransaction()
.put(WORLD_BLOCK_HASH_KEY, blockHash.toArrayUnsafe());
return this; return this;
} }
@ -280,16 +271,14 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey
// Don't save empty nodes // Don't save empty nodes
return this; return this;
} }
trieBranchStorage trieBranchStorageTransaction.put(location.toArrayUnsafe(), node.toArrayUnsafe());
.getSnapshotTransaction()
.put(location.toArrayUnsafe(), node.toArrayUnsafe());
return this; return this;
} }
@Override @Override
public WorldStateStorage.Updater removeAccountStateTrieNode( public WorldStateStorage.Updater removeAccountStateTrieNode(
final Bytes location, final Bytes32 nodeHash) { final Bytes location, final Bytes32 nodeHash) {
trieBranchStorage.getSnapshotTransaction().remove(location.toArrayUnsafe()); trieBranchStorageTransaction.remove(location.toArrayUnsafe());
return this; return this;
} }
@ -300,15 +289,17 @@ public class BonsaiSnapshotWorldStateKeyValueStorage extends BonsaiWorldStateKey
// Don't save empty nodes // Don't save empty nodes
return this; return this;
} }
trieBranchStorage trieBranchStorageTransaction.put(
.getSnapshotTransaction() Bytes.concatenate(accountHash, location).toArrayUnsafe(), node.toArrayUnsafe());
.put(Bytes.concatenate(accountHash, location).toArrayUnsafe(), node.toArrayUnsafe());
return this; return this;
} }
@Override @Override
public void commit() { public void commit() {
// only commit the trielog layer transaction, leave the snapshot transactions open: accountStorageTransaction.commit();
codeStorageTransaction.commit();
storageStorageTransaction.commit();
trieBranchStorageTransaction.commit();
trieLogStorageTransaction.commit(); trieLogStorageTransaction.commit();
} }

@ -323,10 +323,18 @@ public class TransactionPool implements BlockAddedObserver {
"EIP-1559 transaction are not allowed yet"); "EIP-1559 transaction are not allowed yet");
} }
try (var worldState = try (final var worldState =
protocolContext protocolContext
.getWorldStateArchive() .getWorldStateArchive()
.getMutable(chainHeadBlockHeader.getStateRoot(), chainHeadBlockHeader.getHash(), false) .getMutable(
chainHeadBlockHeader.getStateRoot(), chainHeadBlockHeader.getBlockHash(), false)
.map(
ws -> {
if (!ws.isPersistable()) {
return ws.copy();
}
return ws;
})
.orElseThrow()) { .orElseThrow()) {
final Account senderAccount = worldState.get(transaction.getSender()); final Account senderAccount = worldState.get(transaction.getSender());
return new ValidationResultAndAccount( return new ValidationResultAndAccount(

@ -164,8 +164,7 @@ public class RocksDBSnapshotTransaction implements KeyValueStorageTransaction, A
@Override @Override
public void commit() throws StorageException { public void commit() throws StorageException {
// no-op or throw? // no-op
throw new UnsupportedOperationException("RocksDBSnapshotTransaction does not support commit");
} }
@Override @Override

@ -19,6 +19,8 @@ import static java.util.stream.Collectors.toUnmodifiableSet;
import org.hyperledger.besu.plugin.services.exception.StorageException; import org.hyperledger.besu.plugin.services.exception.StorageException;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
import org.hyperledger.besu.plugin.services.storage.SnappableKeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.SnappedKeyValueStorage;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.HashMap; import java.util.HashMap;
@ -37,7 +39,8 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
/** The In memory key value storage. */ /** The In memory key value storage. */
public class InMemoryKeyValueStorage implements KeyValueStorage { public class InMemoryKeyValueStorage
implements SnappedKeyValueStorage, SnappableKeyValueStorage, KeyValueStorage {
private final Map<Bytes, byte[]> hashValueStore; private final Map<Bytes, byte[]> hashValueStore;
private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
@ -154,6 +157,21 @@ public class InMemoryKeyValueStorage implements KeyValueStorage {
return Set.copyOf(hashValueStore.keySet()); return Set.copyOf(hashValueStore.keySet());
} }
@Override
public SnappedKeyValueStorage takeSnapshot() {
return new InMemoryKeyValueStorage(new HashMap<>(hashValueStore));
}
@Override
public KeyValueStorageTransaction getSnapshotTransaction() {
return startTransaction();
}
@Override
public SnappedKeyValueStorage cloneFromSnapshot() {
return takeSnapshot();
}
private class InMemoryTransaction implements KeyValueStorageTransaction { private class InMemoryTransaction implements KeyValueStorageTransaction {
private Map<Bytes, byte[]> updatedValues = new HashMap<>(); private Map<Bytes, byte[]> updatedValues = new HashMap<>();

Loading…
Cancel
Save