|
|
|
@ -14,7 +14,7 @@ |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
package org.hyperledger.besu.ethereum.trie.bonsai.worldview; |
|
|
|
|
package org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator; |
|
|
|
|
|
|
|
|
|
import org.hyperledger.besu.datatypes.AccountValue; |
|
|
|
|
import org.hyperledger.besu.datatypes.Address; |
|
|
|
@ -23,9 +23,15 @@ import org.hyperledger.besu.datatypes.StorageSlotKey; |
|
|
|
|
import org.hyperledger.besu.datatypes.Wei; |
|
|
|
|
import org.hyperledger.besu.ethereum.rlp.RLP; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.MerkleTrieException; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.bonsai.BonsaiAccount; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.bonsai.BonsaiValue; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.bonsai.storage.BonsaiWorldStateKeyValueStorage; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiAccount; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedAccount; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedValue; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldView; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.preload.AccountConsumingMap; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.preload.Consumer; |
|
|
|
|
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.preload.StorageConsumingMap; |
|
|
|
|
import org.hyperledger.besu.evm.account.Account; |
|
|
|
|
import org.hyperledger.besu.evm.account.MutableAccount; |
|
|
|
|
import org.hyperledger.besu.evm.internal.EvmConfiguration; |
|
|
|
@ -44,41 +50,39 @@ import java.util.Optional; |
|
|
|
|
import java.util.Set; |
|
|
|
|
import java.util.TreeSet; |
|
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
|
import java.util.concurrent.ConcurrentMap; |
|
|
|
|
import java.util.function.Function; |
|
|
|
|
|
|
|
|
|
import com.google.common.collect.ForwardingMap; |
|
|
|
|
import org.apache.tuweni.bytes.Bytes; |
|
|
|
|
import org.apache.tuweni.bytes.Bytes32; |
|
|
|
|
import org.apache.tuweni.units.bigints.UInt256; |
|
|
|
|
import org.jetbrains.annotations.NotNull; |
|
|
|
|
import org.slf4j.Logger; |
|
|
|
|
import org.slf4j.LoggerFactory; |
|
|
|
|
|
|
|
|
|
public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
extends AbstractWorldUpdater<BonsaiWorldView, BonsaiAccount> |
|
|
|
|
implements BonsaiWorldView, TrieLogAccumulator { |
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffBasedAccount> |
|
|
|
|
extends AbstractWorldUpdater<DiffBasedWorldView, ACCOUNT> |
|
|
|
|
implements DiffBasedWorldView, TrieLogAccumulator { |
|
|
|
|
private static final Logger LOG = |
|
|
|
|
LoggerFactory.getLogger(BonsaiWorldStateUpdateAccumulator.class); |
|
|
|
|
private final Consumer<BonsaiValue<BonsaiAccount>> accountPreloader; |
|
|
|
|
LoggerFactory.getLogger(DiffBasedWorldStateUpdateAccumulator.class); |
|
|
|
|
private final Consumer<DiffBasedValue<ACCOUNT>> accountPreloader; |
|
|
|
|
private final Consumer<StorageSlotKey> storagePreloader; |
|
|
|
|
|
|
|
|
|
private final AccountConsumingMap<BonsaiValue<BonsaiAccount>> accountsToUpdate; |
|
|
|
|
private final Map<Address, BonsaiValue<Bytes>> codeToUpdate = new ConcurrentHashMap<>(); |
|
|
|
|
private final AccountConsumingMap<DiffBasedValue<ACCOUNT>> accountsToUpdate; |
|
|
|
|
private final Map<Address, DiffBasedValue<Bytes>> codeToUpdate = new ConcurrentHashMap<>(); |
|
|
|
|
private final Set<Address> storageToClear = Collections.synchronizedSet(new HashSet<>()); |
|
|
|
|
private final EvmConfiguration evmConfiguration; |
|
|
|
|
|
|
|
|
|
// storage sub mapped by _hashed_ key. This is because in self_destruct calls we need to
|
|
|
|
|
// enumerate the old storage and delete it. Those are trie stored by hashed key by spec and the
|
|
|
|
|
// alternative was to keep a giant pre-image cache of the entire trie.
|
|
|
|
|
private final Map<Address, StorageConsumingMap<StorageSlotKey, BonsaiValue<UInt256>>> |
|
|
|
|
private final Map<Address, StorageConsumingMap<StorageSlotKey, DiffBasedValue<UInt256>>> |
|
|
|
|
storageToUpdate = new ConcurrentHashMap<>(); |
|
|
|
|
|
|
|
|
|
private boolean isAccumulatorStateChanged; |
|
|
|
|
|
|
|
|
|
public BonsaiWorldStateUpdateAccumulator( |
|
|
|
|
final BonsaiWorldView world, |
|
|
|
|
final Consumer<BonsaiValue<BonsaiAccount>> accountPreloader, |
|
|
|
|
public DiffBasedWorldStateUpdateAccumulator( |
|
|
|
|
final DiffBasedWorldView world, |
|
|
|
|
final Consumer<DiffBasedValue<ACCOUNT>> accountPreloader, |
|
|
|
|
final Consumer<StorageSlotKey> storagePreloader, |
|
|
|
|
final EvmConfiguration evmConfiguration) { |
|
|
|
|
super(world, evmConfiguration); |
|
|
|
@ -89,15 +93,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
this.evmConfiguration = evmConfiguration; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public BonsaiWorldStateUpdateAccumulator copy() { |
|
|
|
|
final BonsaiWorldStateUpdateAccumulator copy = |
|
|
|
|
new BonsaiWorldStateUpdateAccumulator( |
|
|
|
|
wrappedWorldView(), accountPreloader, storagePreloader, evmConfiguration); |
|
|
|
|
copy.cloneFromUpdater(this); |
|
|
|
|
return copy; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void cloneFromUpdater(final BonsaiWorldStateUpdateAccumulator source) { |
|
|
|
|
protected void cloneFromUpdater(final DiffBasedWorldStateUpdateAccumulator<ACCOUNT> source) { |
|
|
|
|
accountsToUpdate.putAll(source.getAccountsToUpdate()); |
|
|
|
|
codeToUpdate.putAll(source.codeToUpdate); |
|
|
|
|
storageToClear.addAll(source.storageToClear); |
|
|
|
@ -107,14 +103,25 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
this.isAccumulatorStateChanged = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected Consumer<DiffBasedValue<ACCOUNT>> getAccountPreloader() { |
|
|
|
|
return accountPreloader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected Consumer<StorageSlotKey> getStoragePreloader() { |
|
|
|
|
return storagePreloader; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected EvmConfiguration getEvmConfiguration() { |
|
|
|
|
return evmConfiguration; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Account get(final Address address) { |
|
|
|
|
return super.get(address); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected UpdateTrackingAccount<BonsaiAccount> track( |
|
|
|
|
final UpdateTrackingAccount<BonsaiAccount> account) { |
|
|
|
|
protected UpdateTrackingAccount<ACCOUNT> track(final UpdateTrackingAccount<ACCOUNT> account) { |
|
|
|
|
return super.track(account); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -125,10 +132,10 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { |
|
|
|
|
BonsaiValue<BonsaiAccount> bonsaiValue = accountsToUpdate.get(address); |
|
|
|
|
DiffBasedValue<ACCOUNT> bonsaiValue = accountsToUpdate.get(address); |
|
|
|
|
|
|
|
|
|
if (bonsaiValue == null) { |
|
|
|
|
bonsaiValue = new BonsaiValue<>(null, null); |
|
|
|
|
bonsaiValue = new DiffBasedValue<>(null, null); |
|
|
|
|
accountsToUpdate.put(address, bonsaiValue); |
|
|
|
|
} else if (bonsaiValue.getUpdated() != null) { |
|
|
|
|
if (bonsaiValue.getUpdated().isEmpty()) { |
|
|
|
@ -138,8 +145,8 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final BonsaiAccount newAccount = |
|
|
|
|
new BonsaiAccount( |
|
|
|
|
final ACCOUNT newAccount = |
|
|
|
|
createAccount( |
|
|
|
|
this, |
|
|
|
|
address, |
|
|
|
|
hashAndSavePreImage(address), |
|
|
|
@ -153,12 +160,12 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Map<Address, BonsaiValue<BonsaiAccount>> getAccountsToUpdate() { |
|
|
|
|
public Map<Address, DiffBasedValue<ACCOUNT>> getAccountsToUpdate() { |
|
|
|
|
return accountsToUpdate; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Map<Address, BonsaiValue<Bytes>> getCodeToUpdate() { |
|
|
|
|
public Map<Address, DiffBasedValue<Bytes>> getCodeToUpdate() { |
|
|
|
|
return codeToUpdate; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -167,40 +174,41 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Map<Address, StorageConsumingMap<StorageSlotKey, BonsaiValue<UInt256>>> |
|
|
|
|
public Map<Address, StorageConsumingMap<StorageSlotKey, DiffBasedValue<UInt256>>> |
|
|
|
|
getStorageToUpdate() { |
|
|
|
|
return storageToUpdate; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected BonsaiAccount getForMutation(final Address address) { |
|
|
|
|
return loadAccount(address, BonsaiValue::getUpdated); |
|
|
|
|
protected ACCOUNT getForMutation(final Address address) { |
|
|
|
|
return loadAccount(address, DiffBasedValue::getUpdated); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected BonsaiAccount loadAccount( |
|
|
|
|
final Address address, |
|
|
|
|
final Function<BonsaiValue<BonsaiAccount>, BonsaiAccount> bonsaiAccountFunction) { |
|
|
|
|
protected ACCOUNT loadAccount( |
|
|
|
|
final Address address, final Function<DiffBasedValue<ACCOUNT>, ACCOUNT> accountFunction) { |
|
|
|
|
try { |
|
|
|
|
final BonsaiValue<BonsaiAccount> bonsaiValue = accountsToUpdate.get(address); |
|
|
|
|
final DiffBasedValue<ACCOUNT> bonsaiValue = accountsToUpdate.get(address); |
|
|
|
|
if (bonsaiValue == null) { |
|
|
|
|
final Account account; |
|
|
|
|
if (wrappedWorldView() |
|
|
|
|
instanceof BonsaiWorldStateUpdateAccumulator bonsaiWorldStateUpdateAccumulator) { |
|
|
|
|
account = bonsaiWorldStateUpdateAccumulator.loadAccount(address, bonsaiAccountFunction); |
|
|
|
|
if (wrappedWorldView() instanceof DiffBasedWorldStateUpdateAccumulator) { |
|
|
|
|
final DiffBasedWorldStateUpdateAccumulator<ACCOUNT> worldStateUpdateAccumulator = |
|
|
|
|
(DiffBasedWorldStateUpdateAccumulator<ACCOUNT>) wrappedWorldView(); |
|
|
|
|
account = worldStateUpdateAccumulator.loadAccount(address, accountFunction); |
|
|
|
|
} else { |
|
|
|
|
account = wrappedWorldView().get(address); |
|
|
|
|
} |
|
|
|
|
if (account instanceof BonsaiAccount bonsaiAccount) { |
|
|
|
|
BonsaiAccount mutableAccount = new BonsaiAccount(bonsaiAccount, this, true); |
|
|
|
|
accountsToUpdate.put(address, new BonsaiValue<>(bonsaiAccount, mutableAccount)); |
|
|
|
|
if (account instanceof DiffBasedAccount diffBasedAccount) { |
|
|
|
|
ACCOUNT mutableAccount = copyAccount((ACCOUNT) diffBasedAccount, this, true); |
|
|
|
|
accountsToUpdate.put( |
|
|
|
|
address, new DiffBasedValue<>((ACCOUNT) diffBasedAccount, mutableAccount)); |
|
|
|
|
return mutableAccount; |
|
|
|
|
} else { |
|
|
|
|
// add the empty read in accountsToUpdate
|
|
|
|
|
accountsToUpdate.put(address, new BonsaiValue<>(null, null)); |
|
|
|
|
accountsToUpdate.put(address, new DiffBasedValue<>(null, null)); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
return bonsaiAccountFunction.apply(bonsaiValue); |
|
|
|
|
return accountFunction.apply(bonsaiValue); |
|
|
|
|
} |
|
|
|
|
} catch (MerkleTrieException e) { |
|
|
|
|
// need to throw to trigger the heal
|
|
|
|
@ -228,12 +236,12 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
public void commit() { |
|
|
|
|
this.isAccumulatorStateChanged = true; |
|
|
|
|
for (final Address deletedAddress : getDeletedAccounts()) { |
|
|
|
|
final BonsaiValue<BonsaiAccount> accountValue = |
|
|
|
|
final DiffBasedValue<ACCOUNT> accountValue = |
|
|
|
|
accountsToUpdate.computeIfAbsent( |
|
|
|
|
deletedAddress, |
|
|
|
|
__ -> loadAccountFromParent(deletedAddress, new BonsaiValue<>(null, null, true))); |
|
|
|
|
__ -> loadAccountFromParent(deletedAddress, new DiffBasedValue<>(null, null, true))); |
|
|
|
|
storageToClear.add(deletedAddress); |
|
|
|
|
final BonsaiValue<Bytes> codeValue = codeToUpdate.get(deletedAddress); |
|
|
|
|
final DiffBasedValue<Bytes> codeValue = codeToUpdate.get(deletedAddress); |
|
|
|
|
if (codeValue != null) { |
|
|
|
|
codeValue.setUpdated(null).setCleared(); |
|
|
|
|
} else { |
|
|
|
@ -241,26 +249,27 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
.getCode( |
|
|
|
|
deletedAddress, |
|
|
|
|
Optional.ofNullable(accountValue) |
|
|
|
|
.map(BonsaiValue::getPrior) |
|
|
|
|
.map(BonsaiAccount::getCodeHash) |
|
|
|
|
.map(DiffBasedValue::getPrior) |
|
|
|
|
.map(DiffBasedAccount::getCodeHash) |
|
|
|
|
.orElse(Hash.EMPTY)) |
|
|
|
|
.ifPresent( |
|
|
|
|
deletedCode -> |
|
|
|
|
codeToUpdate.put(deletedAddress, new BonsaiValue<>(deletedCode, null, true))); |
|
|
|
|
codeToUpdate.put( |
|
|
|
|
deletedAddress, new DiffBasedValue<>(deletedCode, null, true))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// mark all updated storage as to be cleared
|
|
|
|
|
final Map<StorageSlotKey, BonsaiValue<UInt256>> deletedStorageUpdates = |
|
|
|
|
final Map<StorageSlotKey, DiffBasedValue<UInt256>> deletedStorageUpdates = |
|
|
|
|
storageToUpdate.computeIfAbsent( |
|
|
|
|
deletedAddress, |
|
|
|
|
k -> |
|
|
|
|
new StorageConsumingMap<>( |
|
|
|
|
deletedAddress, new ConcurrentHashMap<>(), storagePreloader)); |
|
|
|
|
final Iterator<Map.Entry<StorageSlotKey, BonsaiValue<UInt256>>> iter = |
|
|
|
|
final Iterator<Map.Entry<StorageSlotKey, DiffBasedValue<UInt256>>> iter = |
|
|
|
|
deletedStorageUpdates.entrySet().iterator(); |
|
|
|
|
while (iter.hasNext()) { |
|
|
|
|
final Map.Entry<StorageSlotKey, BonsaiValue<UInt256>> updateEntry = iter.next(); |
|
|
|
|
final BonsaiValue<UInt256> updatedSlot = updateEntry.getValue(); |
|
|
|
|
final Map.Entry<StorageSlotKey, DiffBasedValue<UInt256>> updateEntry = iter.next(); |
|
|
|
|
final DiffBasedValue<UInt256> updatedSlot = updateEntry.getValue(); |
|
|
|
|
if (updatedSlot.getPrior() == null || updatedSlot.getPrior().isZero()) { |
|
|
|
|
iter.remove(); |
|
|
|
|
} else { |
|
|
|
@ -268,7 +277,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final BonsaiAccount originalValue = accountValue.getPrior(); |
|
|
|
|
final ACCOUNT originalValue = accountValue.getPrior(); |
|
|
|
|
if (originalValue != null) { |
|
|
|
|
// Enumerate and delete addresses not updated
|
|
|
|
|
wrappedWorldView() |
|
|
|
@ -279,7 +288,8 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
new StorageSlotKey(Hash.wrap(keyHash), Optional.empty()); |
|
|
|
|
if (!deletedStorageUpdates.containsKey(storageSlotKey)) { |
|
|
|
|
final UInt256 value = UInt256.fromBytes(RLP.decodeOne(entryValue)); |
|
|
|
|
deletedStorageUpdates.put(storageSlotKey, new BonsaiValue<>(value, null, true)); |
|
|
|
|
deletedStorageUpdates.put( |
|
|
|
|
storageSlotKey, new DiffBasedValue<>(value, null, true)); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
@ -293,11 +303,11 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
.forEach( |
|
|
|
|
tracked -> { |
|
|
|
|
final Address updatedAddress = tracked.getAddress(); |
|
|
|
|
final BonsaiAccount updatedAccount; |
|
|
|
|
final BonsaiValue<BonsaiAccount> updatedAccountValue = |
|
|
|
|
final ACCOUNT updatedAccount; |
|
|
|
|
final DiffBasedValue<ACCOUNT> updatedAccountValue = |
|
|
|
|
accountsToUpdate.get(updatedAddress); |
|
|
|
|
|
|
|
|
|
final Map<StorageSlotKey, BonsaiValue<UInt256>> pendingStorageUpdates = |
|
|
|
|
final Map<StorageSlotKey, DiffBasedValue<UInt256>> pendingStorageUpdates = |
|
|
|
|
storageToUpdate.computeIfAbsent( |
|
|
|
|
updatedAddress, |
|
|
|
|
k -> |
|
|
|
@ -310,12 +320,12 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tracked.getWrappedAccount() == null) { |
|
|
|
|
updatedAccount = new BonsaiAccount(this, tracked); |
|
|
|
|
updatedAccount = createAccount(this, tracked); |
|
|
|
|
tracked.setWrappedAccount(updatedAccount); |
|
|
|
|
if (updatedAccountValue == null) { |
|
|
|
|
accountsToUpdate.put(updatedAddress, new BonsaiValue<>(null, updatedAccount)); |
|
|
|
|
accountsToUpdate.put(updatedAddress, new DiffBasedValue<>(null, updatedAccount)); |
|
|
|
|
codeToUpdate.put( |
|
|
|
|
updatedAddress, new BonsaiValue<>(null, updatedAccount.getCode())); |
|
|
|
|
updatedAddress, new DiffBasedValue<>(null, updatedAccount.getCode())); |
|
|
|
|
} else { |
|
|
|
|
updatedAccountValue.setUpdated(updatedAccount); |
|
|
|
|
} |
|
|
|
@ -333,17 +343,17 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tracked.codeWasUpdated()) { |
|
|
|
|
final BonsaiValue<Bytes> pendingCode = |
|
|
|
|
final DiffBasedValue<Bytes> pendingCode = |
|
|
|
|
codeToUpdate.computeIfAbsent( |
|
|
|
|
updatedAddress, |
|
|
|
|
addr -> |
|
|
|
|
new BonsaiValue<>( |
|
|
|
|
new DiffBasedValue<>( |
|
|
|
|
wrappedWorldView() |
|
|
|
|
.getCode( |
|
|
|
|
addr, |
|
|
|
|
Optional.ofNullable(updatedAccountValue) |
|
|
|
|
.map(BonsaiValue::getPrior) |
|
|
|
|
.map(BonsaiAccount::getCodeHash) |
|
|
|
|
.map(DiffBasedValue::getPrior) |
|
|
|
|
.map(DiffBasedAccount::getCodeHash) |
|
|
|
|
.orElse(Hash.EMPTY)) |
|
|
|
|
.orElse(null), |
|
|
|
|
null)); |
|
|
|
@ -368,11 +378,11 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
final StorageSlotKey slotKey = |
|
|
|
|
new StorageSlotKey(slotHash, Optional.of(keyUInt)); |
|
|
|
|
final UInt256 value = storageUpdate.getValue(); |
|
|
|
|
final BonsaiValue<UInt256> pendingValue = pendingStorageUpdates.get(slotKey); |
|
|
|
|
final DiffBasedValue<UInt256> pendingValue = pendingStorageUpdates.get(slotKey); |
|
|
|
|
if (pendingValue == null) { |
|
|
|
|
pendingStorageUpdates.put( |
|
|
|
|
slotKey, |
|
|
|
|
new BonsaiValue<>( |
|
|
|
|
new DiffBasedValue<>( |
|
|
|
|
updatedAccount.getOriginalStorageValue(keyUInt), value)); |
|
|
|
|
} else { |
|
|
|
|
pendingValue.setUpdated(value); |
|
|
|
@ -393,7 +403,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public Optional<Bytes> getCode(final Address address, final Hash codeHash) { |
|
|
|
|
final BonsaiValue<Bytes> localCode = codeToUpdate.get(address); |
|
|
|
|
final DiffBasedValue<Bytes> localCode = codeToUpdate.get(address); |
|
|
|
|
if (localCode == null) { |
|
|
|
|
final Optional<Bytes> code = wrappedWorldView().getCode(address, codeHash); |
|
|
|
|
if (code.isEmpty() && !codeHash.equals(Hash.EMPTY)) { |
|
|
|
@ -416,10 +426,10 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
@Override |
|
|
|
|
public Optional<UInt256> getStorageValueByStorageSlotKey( |
|
|
|
|
final Address address, final StorageSlotKey storageSlotKey) { |
|
|
|
|
final Map<StorageSlotKey, BonsaiValue<UInt256>> localAccountStorage = |
|
|
|
|
final Map<StorageSlotKey, DiffBasedValue<UInt256>> localAccountStorage = |
|
|
|
|
storageToUpdate.get(address); |
|
|
|
|
if (localAccountStorage != null) { |
|
|
|
|
final BonsaiValue<UInt256> value = localAccountStorage.get(storageSlotKey); |
|
|
|
|
final DiffBasedValue<UInt256> value = localAccountStorage.get(storageSlotKey); |
|
|
|
|
if (value != null) { |
|
|
|
|
return Optional.ofNullable(value.getUpdated()); |
|
|
|
|
} |
|
|
|
@ -429,8 +439,8 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
(wrappedWorldView() instanceof BonsaiWorldState bonsaiWorldState) |
|
|
|
|
? bonsaiWorldState.getStorageValueByStorageSlotKey( |
|
|
|
|
() -> |
|
|
|
|
Optional.ofNullable(loadAccount(address, BonsaiValue::getPrior)) |
|
|
|
|
.map(BonsaiAccount::getStorageRoot), |
|
|
|
|
Optional.ofNullable(loadAccount(address, DiffBasedValue::getPrior)) |
|
|
|
|
.map(DiffBasedAccount::getStorageRoot), |
|
|
|
|
address, |
|
|
|
|
storageSlotKey) |
|
|
|
|
: wrappedWorldView().getStorageValueByStorageSlotKey(address, storageSlotKey); |
|
|
|
@ -439,7 +449,8 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
address, |
|
|
|
|
key -> |
|
|
|
|
new StorageConsumingMap<>(address, new ConcurrentHashMap<>(), storagePreloader)) |
|
|
|
|
.put(storageSlotKey, new BonsaiValue<>(valueUInt.orElse(null), valueUInt.orElse(null))); |
|
|
|
|
.put( |
|
|
|
|
storageSlotKey, new DiffBasedValue<>(valueUInt.orElse(null), valueUInt.orElse(null))); |
|
|
|
|
|
|
|
|
|
return valueUInt; |
|
|
|
|
} catch (MerkleTrieException e) { |
|
|
|
@ -454,10 +465,10 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
// TODO maybe log the read into the trie layer?
|
|
|
|
|
StorageSlotKey storageSlotKey = |
|
|
|
|
new StorageSlotKey(hashAndSavePreImage(storageKey), Optional.of(storageKey)); |
|
|
|
|
final Map<StorageSlotKey, BonsaiValue<UInt256>> localAccountStorage = |
|
|
|
|
final Map<StorageSlotKey, DiffBasedValue<UInt256>> localAccountStorage = |
|
|
|
|
storageToUpdate.get(address); |
|
|
|
|
if (localAccountStorage != null) { |
|
|
|
|
final BonsaiValue<UInt256> value = localAccountStorage.get(storageSlotKey); |
|
|
|
|
final DiffBasedValue<UInt256> value = localAccountStorage.get(storageSlotKey); |
|
|
|
|
if (value != null) { |
|
|
|
|
if (value.isCleared()) { |
|
|
|
|
return UInt256.ZERO; |
|
|
|
@ -481,7 +492,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
@Override |
|
|
|
|
public Map<Bytes32, Bytes> getAllAccountStorage(final Address address, final Hash rootHash) { |
|
|
|
|
final Map<Bytes32, Bytes> results = wrappedWorldView().getAllAccountStorage(address, rootHash); |
|
|
|
|
final StorageConsumingMap<StorageSlotKey, BonsaiValue<UInt256>> bonsaiValueStorage = |
|
|
|
|
final StorageConsumingMap<StorageSlotKey, DiffBasedValue<UInt256>> bonsaiValueStorage = |
|
|
|
|
storageToUpdate.get(address); |
|
|
|
|
if (bonsaiValueStorage != null) { |
|
|
|
|
// hash the key to match the implied storage interface of hashed slotKey
|
|
|
|
@ -497,7 +508,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public BonsaiWorldStateKeyValueStorage getWorldStateStorage() { |
|
|
|
|
public DiffBasedWorldStateKeyValueStorage getWorldStateStorage() { |
|
|
|
|
return wrappedWorldView().getWorldStateStorage(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -549,7 +560,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
// non-change, a cached read.
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
BonsaiValue<BonsaiAccount> accountValue = accountsToUpdate.get(address); |
|
|
|
|
DiffBasedValue<ACCOUNT> accountValue = accountsToUpdate.get(address); |
|
|
|
|
if (accountValue == null) { |
|
|
|
|
accountValue = loadAccountFromParent(address, accountValue); |
|
|
|
|
} |
|
|
|
@ -557,7 +568,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
if (expectedValue == null && replacementValue != null) { |
|
|
|
|
accountsToUpdate.put( |
|
|
|
|
address, |
|
|
|
|
new BonsaiValue<>(null, new BonsaiAccount(this, address, replacementValue, true))); |
|
|
|
|
new DiffBasedValue<>(null, createAccount(this, address, replacementValue, true))); |
|
|
|
|
} else { |
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
String.format( |
|
|
|
@ -571,10 +582,9 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
"Expected to create account, but the account exists. Address=%s", address)); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
BonsaiAccount.assertCloseEnoughForDiffing( |
|
|
|
|
accountValue.getUpdated(), |
|
|
|
|
expectedValue, |
|
|
|
|
"Address=" + address + " Prior Value in Rolling Change"); |
|
|
|
|
assertCloseEnoughForDiffing(accountValue.getUpdated(), |
|
|
|
|
expectedValue, |
|
|
|
|
"Address=" + address + " Prior Value in Rolling Change"); |
|
|
|
|
} |
|
|
|
|
if (replacementValue == null) { |
|
|
|
|
if (accountValue.getPrior() == null) { |
|
|
|
@ -585,19 +595,18 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
accountValue.setUpdated(null); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
accountValue.setUpdated( |
|
|
|
|
new BonsaiAccount(wrappedWorldView(), address, replacementValue, true)); |
|
|
|
|
accountValue.setUpdated(createAccount(wrappedWorldView(), address, replacementValue, true)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private BonsaiValue<BonsaiAccount> loadAccountFromParent( |
|
|
|
|
final Address address, final BonsaiValue<BonsaiAccount> defaultValue) { |
|
|
|
|
private DiffBasedValue<ACCOUNT> loadAccountFromParent( |
|
|
|
|
final Address address, final DiffBasedValue<ACCOUNT> defaultValue) { |
|
|
|
|
try { |
|
|
|
|
final Account parentAccount = wrappedWorldView().get(address); |
|
|
|
|
if (parentAccount instanceof BonsaiAccount account) { |
|
|
|
|
final BonsaiValue<BonsaiAccount> loadedAccountValue = |
|
|
|
|
new BonsaiValue<>(new BonsaiAccount(account), account); |
|
|
|
|
if (parentAccount instanceof DiffBasedAccount account) { |
|
|
|
|
final DiffBasedValue<ACCOUNT> loadedAccountValue = |
|
|
|
|
new DiffBasedValue<>(copyAccount((ACCOUNT) account), ((ACCOUNT) account)); |
|
|
|
|
accountsToUpdate.put(address, loadedAccountValue); |
|
|
|
|
return loadedAccountValue; |
|
|
|
|
} else { |
|
|
|
@ -616,7 +625,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
// non-change, a cached read.
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
BonsaiValue<Bytes> codeValue = codeToUpdate.get(address); |
|
|
|
|
DiffBasedValue<Bytes> codeValue = codeToUpdate.get(address); |
|
|
|
|
if (codeValue == null) { |
|
|
|
|
final Bytes storedCode = |
|
|
|
|
wrappedWorldView() |
|
|
|
@ -624,14 +633,14 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
address, Optional.ofNullable(expectedCode).map(Hash::hash).orElse(Hash.EMPTY)) |
|
|
|
|
.orElse(Bytes.EMPTY); |
|
|
|
|
if (!storedCode.isEmpty()) { |
|
|
|
|
codeValue = new BonsaiValue<>(storedCode, storedCode); |
|
|
|
|
codeValue = new DiffBasedValue<>(storedCode, storedCode); |
|
|
|
|
codeToUpdate.put(address, codeValue); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (codeValue == null) { |
|
|
|
|
if ((expectedCode == null || expectedCode.isEmpty()) && replacementCode != null) { |
|
|
|
|
codeToUpdate.put(address, new BonsaiValue<>(null, replacementCode)); |
|
|
|
|
codeToUpdate.put(address, new DiffBasedValue<>(null, replacementCode)); |
|
|
|
|
} else { |
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
String.format( |
|
|
|
@ -659,10 +668,10 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Map<StorageSlotKey, BonsaiValue<UInt256>> maybeCreateStorageMap( |
|
|
|
|
final Map<StorageSlotKey, BonsaiValue<UInt256>> storageMap, final Address address) { |
|
|
|
|
private Map<StorageSlotKey, DiffBasedValue<UInt256>> maybeCreateStorageMap( |
|
|
|
|
final Map<StorageSlotKey, DiffBasedValue<UInt256>> storageMap, final Address address) { |
|
|
|
|
if (storageMap == null) { |
|
|
|
|
final StorageConsumingMap<StorageSlotKey, BonsaiValue<UInt256>> newMap = |
|
|
|
|
final StorageConsumingMap<StorageSlotKey, DiffBasedValue<UInt256>> newMap = |
|
|
|
|
new StorageConsumingMap<>(address, new ConcurrentHashMap<>(), storagePreloader); |
|
|
|
|
storageToUpdate.put(address, newMap); |
|
|
|
|
return newMap; |
|
|
|
@ -684,13 +693,13 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
// corner case on deletes, non-change
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
final Map<StorageSlotKey, BonsaiValue<UInt256>> storageMap = storageToUpdate.get(address); |
|
|
|
|
BonsaiValue<UInt256> slotValue = storageMap == null ? null : storageMap.get(storageSlotKey); |
|
|
|
|
final Map<StorageSlotKey, DiffBasedValue<UInt256>> storageMap = storageToUpdate.get(address); |
|
|
|
|
DiffBasedValue<UInt256> slotValue = storageMap == null ? null : storageMap.get(storageSlotKey); |
|
|
|
|
if (slotValue == null) { |
|
|
|
|
final Optional<UInt256> storageValue = |
|
|
|
|
wrappedWorldView().getStorageValueByStorageSlotKey(address, storageSlotKey); |
|
|
|
|
if (storageValue.isPresent()) { |
|
|
|
|
slotValue = new BonsaiValue<>(storageValue.get(), storageValue.get()); |
|
|
|
|
slotValue = new DiffBasedValue<>(storageValue.get(), storageValue.get()); |
|
|
|
|
storageToUpdate |
|
|
|
|
.computeIfAbsent( |
|
|
|
|
address, |
|
|
|
@ -702,7 +711,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
if (slotValue == null) { |
|
|
|
|
if ((expectedValue == null || expectedValue.isZero()) && replacementValue != null) { |
|
|
|
|
maybeCreateStorageMap(storageMap, address) |
|
|
|
|
.put(storageSlotKey, new BonsaiValue<>(null, replacementValue)); |
|
|
|
|
.put(storageSlotKey, new DiffBasedValue<>(null, replacementValue)); |
|
|
|
|
} else { |
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
String.format( |
|
|
|
@ -729,7 +738,7 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
existingSlotValue == null ? "null" : existingSlotValue.toShortHexString())); |
|
|
|
|
} |
|
|
|
|
if (replacementValue == null && slotValue.getPrior() == null) { |
|
|
|
|
final Map<StorageSlotKey, BonsaiValue<UInt256>> thisStorageUpdate = |
|
|
|
|
final Map<StorageSlotKey, DiffBasedValue<UInt256>> thisStorageUpdate = |
|
|
|
|
maybeCreateStorageMap(storageMap, address); |
|
|
|
|
thisStorageUpdate.remove(storageSlotKey); |
|
|
|
|
if (thisStorageUpdate.isEmpty()) { |
|
|
|
@ -767,69 +776,37 @@ public class BonsaiWorldStateUpdateAccumulator |
|
|
|
|
deletedAccounts.clear(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static class AccountConsumingMap<T> extends ForwardingMap<Address, T> { |
|
|
|
|
|
|
|
|
|
private final ConcurrentMap<Address, T> accounts; |
|
|
|
|
private final Consumer<T> consumer; |
|
|
|
|
|
|
|
|
|
public AccountConsumingMap( |
|
|
|
|
final ConcurrentMap<Address, T> accounts, final Consumer<T> consumer) { |
|
|
|
|
this.accounts = accounts; |
|
|
|
|
this.consumer = consumer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public T put(@NotNull final Address address, @NotNull final T value) { |
|
|
|
|
consumer.process(address, value); |
|
|
|
|
return accounts.put(address, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Consumer<T> getConsumer() { |
|
|
|
|
return consumer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected Map<Address, T> delegate() { |
|
|
|
|
return accounts; |
|
|
|
|
} |
|
|
|
|
protected Hash hashAndSavePreImage(final Bytes bytes) { |
|
|
|
|
// by default do not save hash preImages
|
|
|
|
|
return Hash.hash(bytes); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static class StorageConsumingMap<K, T> extends ForwardingMap<K, T> { |
|
|
|
|
public abstract DiffBasedWorldStateUpdateAccumulator<ACCOUNT> copy(); |
|
|
|
|
|
|
|
|
|
private final Address address; |
|
|
|
|
protected abstract ACCOUNT copyAccount(final ACCOUNT account); |
|
|
|
|
|
|
|
|
|
private final ConcurrentMap<K, T> storages; |
|
|
|
|
private final Consumer<K> consumer; |
|
|
|
|
protected abstract ACCOUNT copyAccount( |
|
|
|
|
final ACCOUNT toCopy, final DiffBasedWorldView context, final boolean mutable); |
|
|
|
|
|
|
|
|
|
public StorageConsumingMap( |
|
|
|
|
final Address address, final ConcurrentMap<K, T> storages, final Consumer<K> consumer) { |
|
|
|
|
this.address = address; |
|
|
|
|
this.storages = storages; |
|
|
|
|
this.consumer = consumer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public T put(@NotNull final K slotKey, @NotNull final T value) { |
|
|
|
|
consumer.process(address, slotKey); |
|
|
|
|
return storages.put(slotKey, value); |
|
|
|
|
} |
|
|
|
|
protected abstract ACCOUNT createAccount( |
|
|
|
|
final DiffBasedWorldView context, |
|
|
|
|
final Address address, |
|
|
|
|
final AccountValue stateTrieAccount, |
|
|
|
|
final boolean mutable); |
|
|
|
|
|
|
|
|
|
public Consumer<K> getConsumer() { |
|
|
|
|
return consumer; |
|
|
|
|
} |
|
|
|
|
protected abstract ACCOUNT createAccount( |
|
|
|
|
final DiffBasedWorldView context, |
|
|
|
|
final Address address, |
|
|
|
|
final Hash addressHash, |
|
|
|
|
final long nonce, |
|
|
|
|
final Wei balance, |
|
|
|
|
final Hash storageRoot, |
|
|
|
|
final Hash codeHash, |
|
|
|
|
final boolean mutable); |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected Map<K, T> delegate() { |
|
|
|
|
return storages; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
protected abstract ACCOUNT createAccount( |
|
|
|
|
final DiffBasedWorldView context, final UpdateTrackingAccount<ACCOUNT> tracked); |
|
|
|
|
|
|
|
|
|
public interface Consumer<T> { |
|
|
|
|
void process(final Address address, T value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected Hash hashAndSavePreImage(final Bytes bytes) { |
|
|
|
|
// by default do not save hash preImages
|
|
|
|
|
return Hash.hash(bytes); |
|
|
|
|
} |
|
|
|
|
protected abstract void assertCloseEnoughForDiffing(final ACCOUNT source, final AccountValue account, final String context); |
|
|
|
|
} |