diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java index 7833d267e66..e68f1ba31a5 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoMap.java @@ -75,6 +75,11 @@ public long lastUpdate() { return undoLog.get(undoLog.size() - 1).level; } + + /** + * Has the map been changed + * @return true if there are any undo entries in the log + */ public boolean updated() { return !undoLog.isEmpty(); } diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java index 2edf58cb01d..f9c54c42412 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoNavigableMap.java @@ -41,6 +41,13 @@ public UndoNavigableMap(final NavigableMap delegate) { super(delegate); } + /** + * Create an undo navigable map backed by a specific map. + * @param map The map storing the current state + * @return an undoable map + * @param the key type + * @param the value type + */ public static UndoNavigableMap of(final NavigableMap map) { return new UndoNavigableMap<>(map); } diff --git a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java index bfa79a9ec89..0e3356c379b 100644 --- a/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java +++ b/evm/src/main/java/org/hyperledger/besu/collections/undo/UndoScalar.java @@ -19,6 +19,11 @@ import java.util.List; import java.util.Objects; +/** + * An undoable value that tracks the value across time. + * + * @param The type of the scaler. + */ public class UndoScalar implements Undoable { record UndoEntry(T value, long level) { UndoEntry(final T value) { @@ -29,10 +34,22 @@ record UndoEntry(T value, long level) { T value; final List> undoLog; + /** + * Create an undoable scalar with an initial value + * + * @param value the initial value + * @return the undoable scalar + * @param the type of the scalar + */ public static UndoScalar of(final T value) { return new UndoScalar<>(value); } + /** + * Create an undo scalar with an initial value + * + * @param value the initial value + */ public UndoScalar(final T value) { undoLog = new ArrayList<>(); this.value = value; @@ -43,14 +60,29 @@ public long lastUpdate() { return undoLog.isEmpty() ? 0L : undoLog.get(undoLog.size() - 1).level; } + /** + * Has this scalar had any change since the inital value + * + * @return true if there are any changes to undo + */ public boolean updated() { return !undoLog.isEmpty(); } + /** + * Get the current value of the scalar. + * + * @return the current value + */ public T get() { return value; } + /** + * Set a new value in the scalar. + * + * @param value new value + */ public void set(final T value) { if (!Objects.equals(this.value, value)) { undoLog.add(new UndoEntry<>(this.value)); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java index 403c72d4b2c..755523e6b49 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java @@ -188,4 +188,20 @@ public Map getUpdatedStorage() { public void becomeImmutable() { immutable = true; } + + /** + * Commit this simple account entry to the parent. + * + * @return true if there was a parent account that was committed to + */ + public boolean commit() { + if (parent instanceof SimpleAccount simpleAccount) { + simpleAccount.balance = balance; + simpleAccount.nonce = nonce; + simpleAccount.storage.putAll(storage); + return true; + } else { + return false; + } + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java index 6bc19ca0dae..404edf17025 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java @@ -66,6 +66,9 @@ public Account get(final Address address) { @Override public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { + if (getAccount(address) != null) { + throw new IllegalStateException("Cannot create an account when one already exists"); + } SimpleAccount account = new SimpleAccount(address, nonce, balance); accounts.put(address, account); return account; @@ -75,11 +78,23 @@ public MutableAccount createAccount(final Address address, final long nonce, fin public MutableAccount getAccount(final Address address) { if (accounts.containsKey(address)) { return accounts.get(address); - } else if (parent != null) { - return parent.getAccount(address); - } else { + } + if (parent == null) { return null; } + Account parentAccount = parent.getAccount(address); + if (parentAccount == null) { + return null; + } + SimpleAccount account = + new SimpleAccount( + parentAccount, + parentAccount.getAddress(), + parentAccount.getNonce(), + parentAccount.getBalance(), + parentAccount.getCode()); + accounts.put(address, account); + return account; } @Override @@ -107,11 +122,16 @@ public void revert() { @Override public void commit() { - parent.accounts.putAll(accounts); + accounts.forEach( + (address, account) -> { + if (!account.commit()) { + accounts.put(address, account); + } + }); } @Override public Optional parentUpdater() { - return Optional.empty(); + return Optional.ofNullable(parent); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java index 9f33b45b2ff..42e3279c727 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/BigIntegerModularExponentiationPrecompiledContract.java @@ -126,6 +126,10 @@ public PrecompileContractResult computePrecompile( * Compute default precompile contract. * * @param input the input + * @param base base of the exponent + * @param exp the exponent + * @param mod the modulus + * @param modulusLength the length of the modulus, in bytes * @return the precompile contract result */ @Nonnull diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java index ff02e0d421e..9d2bd060d41 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledAccount.java @@ -226,6 +226,10 @@ public boolean hasCode() { return !code.get().isEmpty(); } + /** + * Mark the account as deleted/not deleted + * @param accountDeleted delete or don't delete this account. + */ public void setDeleted(final boolean accountDeleted) { if (immutable) { throw new ModificationNotAllowedException(); @@ -233,6 +237,10 @@ public void setDeleted(final boolean accountDeleted) { deleted.set(accountDeleted); } + /** + * Is the account marked as deleted? + * @return is the account deleted? + */ public Boolean getDeleted() { return deleted.get(); } @@ -368,6 +376,7 @@ public void undo(final long mark) { updatedStorage.undo(mark); } + /** Commit this journaled account entry to the parent, if it is not a journaled account. */ public void commit() { if (!(account instanceof JournaledAccount)) { if (nonce.updated()) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java index 59f81faa1d1..85acf5ed204 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java @@ -64,6 +64,12 @@ public JournaledUpdater(final WorldUpdater world) { undoMark = accounts.mark(); } + /** + * Get an account suitable for mutation. Defer to parent if not tracked locally. + * + * @param address the account at the address, for mutaton. + * @return the mutable account + */ protected MutableAccount getForMutation(final Address address) { final JournaledAccount wrappedTracker = accounts.get(address); if (wrappedTracker != null) { @@ -83,6 +89,9 @@ public Collection
getDeletedAccountAddresses() { return new ArrayList<>(deleted); } + /** + * Remove all changes done by this layer. Rollback to the state prior to the updater's changes. + */ protected void reset() { accounts.values().forEach(a -> a.undo(undoMark)); accounts.undo(undoMark);