From 182b7661e1d06b440f5261c7903a51a5cf6c8ef5 Mon Sep 17 00:00:00 2001 From: codyborn Date: Sun, 29 May 2022 13:54:36 -0700 Subject: [PATCH 01/21] Adding transient storage to StorageProvider --- .../IPartialStorageProvider.cs | 52 ++ .../Nethermind.State/IStorageProvider.cs | 10 +- .../PersistentStorageProvider.cs | 464 ++++++++++++++++++ src/Nethermind/Nethermind.State/Snapshot.cs | 11 +- .../Nethermind.State/StorageProvider.cs | 421 ++-------------- .../TransientStorageProvider.cs | 303 ++++++++++++ src/Nethermind/Nethermind.State/WorldState.cs | 5 +- 7 files changed, 868 insertions(+), 398 deletions(-) create mode 100644 src/Nethermind/Nethermind.State/IPartialStorageProvider.cs create mode 100644 src/Nethermind/Nethermind.State/PersistentStorageProvider.cs create mode 100644 src/Nethermind/Nethermind.State/TransientStorageProvider.cs diff --git a/src/Nethermind/Nethermind.State/IPartialStorageProvider.cs b/src/Nethermind/Nethermind.State/IPartialStorageProvider.cs new file mode 100644 index 00000000000..b5b9263a1a6 --- /dev/null +++ b/src/Nethermind/Nethermind.State/IPartialStorageProvider.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . + +using Nethermind.Core; + +namespace Nethermind.State +{ + public interface IPartialStorageProvider : IJournal + { + byte[] GetOriginal(StorageCell storageCell); + + byte[] Get(StorageCell storageCell); + + void Set(StorageCell storageCell, byte[] newValue); + + void Reset(); + + void CommitTrees(long blockNumber); + + void Commit(); + + void Commit(IStorageTracer stateTracer); + + /// + /// Creates a restartable snapshot. + /// + /// Indicates new transaction will start here. + /// Snapshot index + /// + /// If is true and there are already changes in then next call to + /// will use changes before this snapshot as original values for this new transaction. + /// + int TakeSnapshot(bool newTransactionStart = false); + + int IJournal.TakeSnapshot() => TakeSnapshot(); + + void ClearStorage(Address address); + } +} diff --git a/src/Nethermind/Nethermind.State/IStorageProvider.cs b/src/Nethermind/Nethermind.State/IStorageProvider.cs index 0c0d1ab281f..69b991edea8 100644 --- a/src/Nethermind/Nethermind.State/IStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/IStorageProvider.cs @@ -18,7 +18,7 @@ namespace Nethermind.State { - public interface IStorageProvider : IJournal + public interface IStorageProvider : IJournal { byte[] GetOriginal(StorageCell storageCell); @@ -26,6 +26,10 @@ public interface IStorageProvider : IJournal void Set(StorageCell storageCell, byte[] newValue); + byte[] GetTransientState(StorageCell storageCell); + + void SetTransientState(StorageCell storageCell, byte[] newValue); + void Reset(); void CommitTrees(long blockNumber); @@ -43,9 +47,9 @@ public interface IStorageProvider : IJournal /// If is true and there are already changes in then next call to /// will use changes before this snapshot as original values for this new transaction. /// - int TakeSnapshot(bool newTransactionStart = false); + Snapshot TakeSnapshot(bool newTransactionStart = false); - int IJournal.TakeSnapshot() => TakeSnapshot(); + Snapshot IJournal.TakeSnapshot() => TakeSnapshot(); void ClearStorage(Address address); } diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs new file mode 100644 index 00000000000..55516737110 --- /dev/null +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -0,0 +1,464 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . + +using System; +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Resettables; +using Nethermind.Logging; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State +{ + public class PersistentStorageProvider : IPartialStorageProvider + { + private readonly ResettableDictionary> _intraBlockCache = new(); + + /// + /// EIP-1283 + /// + private readonly ResettableDictionary _originalValues = new(); + + private readonly ResettableHashSet _committedThisRound = new(); + + private readonly ILogger _logger; + + private readonly ITrieStore _trieStore; + + private readonly IStateProvider _stateProvider; + private readonly ILogManager _logManager; + + private readonly ResettableDictionary _storages = new(); + + private const int StartCapacity = Resettable.StartCapacity; + private int _capacity = StartCapacity; + private Change?[] _changes = new Change[StartCapacity]; + private int _currentPosition = Resettable.EmptyPosition; + + // stack of snapshot indexes on changes for start of each transaction + // this is needed for OriginalValues for new transactions + private readonly Stack _transactionChangesSnapshots = new(); + + public PersistentStorageProvider(ITrieStore? trieStore, IStateProvider? stateProvider, ILogManager? logManager) + { + _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); + _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); + _trieStore = trieStore ?? throw new ArgumentNullException(nameof(trieStore)); + _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); + } + + public byte[] GetOriginal(StorageCell storageCell) + { + if (!_originalValues.ContainsKey(storageCell)) + { + throw new InvalidOperationException("Get original should only be called after get within the same caching round"); + } + + if (_transactionChangesSnapshots.TryPeek(out int snapshot)) + { + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) + { + if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) + { + return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; + } + } + } + + return _originalValues[storageCell]; + } + + public byte[] Get(StorageCell storageCell) + { + return GetCurrentValue(storageCell); + } + + public void Set(StorageCell storageCell, byte[] newValue) + { + PushUpdate(storageCell, newValue); + } + + private Keccak RecalculateRootHash(Address address) + { + StorageTree storageTree = GetOrCreateStorage(address); + storageTree.UpdateRootHash(); + return storageTree.RootHash; + } + + int IPartialStorageProvider.TakeSnapshot(bool newTransactionStart) + { + if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); + if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) + { + _transactionChangesSnapshots.Push(_currentPosition); + } + + return _currentPosition; + } + + public void Restore(int snapshot) + { + if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); + + if (snapshot > _currentPosition) + { + throw new InvalidOperationException($"{nameof(PersistentStorageProvider)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); + } + + if (snapshot == _currentPosition) + { + return; + } + + List keptInCache = new(); + + for (int i = 0; i < _currentPosition - snapshot; i++) + { + Change change = _changes[_currentPosition - i]; + if (_intraBlockCache[change!.StorageCell].Count == 1) + { + if (_changes[_intraBlockCache[change.StorageCell].Peek()]!.ChangeType == ChangeType.JustCache) + { + int actualPosition = _intraBlockCache[change.StorageCell].Pop(); + if (actualPosition != _currentPosition - i) + { + throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {_currentPosition} - {i}"); + } + + keptInCache.Add(change); + _changes[actualPosition] = null; + continue; + } + } + + int forAssertion = _intraBlockCache[change.StorageCell].Pop(); + if (forAssertion != _currentPosition - i) + { + throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); + } + + _changes[_currentPosition - i] = null; + + if (_intraBlockCache[change.StorageCell].Count == 0) + { + _intraBlockCache.Remove(change.StorageCell); + } + } + + _currentPosition = snapshot; + foreach (Change kept in keptInCache) + { + _currentPosition++; + _changes[_currentPosition] = kept; + _intraBlockCache[kept.StorageCell].Push(_currentPosition); + } + + while (_transactionChangesSnapshots.TryPeek(out int lastOriginalSnapshot) && lastOriginalSnapshot > snapshot) + { + _transactionChangesSnapshots.Pop(); + } + + } + + public void Commit() + { + Commit(NullStorageTracer.Instance); + } + + private static readonly byte[] _zeroValue = {0}; + + private readonly struct ChangeTrace + { + public ChangeTrace(byte[]? before, byte[]? after) + { + After = after ?? _zeroValue; + Before = before ?? _zeroValue; + } + + public ChangeTrace(byte[]? after) + { + After = after ?? _zeroValue; + Before = _zeroValue; + } + + public byte[] Before { get; } + public byte[] After { get; } + } + + public void Commit(IStorageTracer tracer) + { + if (_currentPosition == -1) + { + if (_logger.IsTrace) _logger.Trace("No storage changes to commit"); + return; + } + + if (_logger.IsTrace) _logger.Trace("Committing storage changes"); + + if (_changes[_currentPosition] is null) + { + throw new InvalidOperationException($"Change at current position {_currentPosition} was null when commiting {nameof(PersistentStorageProvider)}"); + } + + if (_changes[_currentPosition + 1] != null) + { + throw new InvalidOperationException($"Change after current position ({_currentPosition} + 1) was not null when commiting {nameof(PersistentStorageProvider)}"); + } + + HashSet
toUpdateRoots = new(); + + bool isTracing = tracer.IsTracingStorage; + Dictionary? trace = null; + if (isTracing) + { + trace = new Dictionary(); + } + + for (int i = 0; i <= _currentPosition; i++) + { + Change change = _changes[_currentPosition - i]; + if (!isTracing && change!.ChangeType == ChangeType.JustCache) + { + continue; + } + + if (_committedThisRound.Contains(change!.StorageCell)) + { + if (isTracing && change.ChangeType == ChangeType.JustCache) + { + trace![change.StorageCell] = new ChangeTrace(change.Value, trace[change.StorageCell].After); + } + + continue; + } + + if (isTracing && change.ChangeType == ChangeType.JustCache) + { + tracer!.ReportStorageRead(change.StorageCell); + } + + _committedThisRound.Add(change.StorageCell); + + if (change.ChangeType == ChangeType.Destroy) + { + continue; + } + + int forAssertion = _intraBlockCache[change.StorageCell].Pop(); + if (forAssertion != _currentPosition - i) + { + throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); + } + + switch (change.ChangeType) + { + case ChangeType.Destroy: + break; + case ChangeType.JustCache: + break; + case ChangeType.Update: + if (_logger.IsTrace) + { + _logger.Trace($" Update {change.StorageCell.Address}_{change.StorageCell.Index} V = {change.Value.ToHexString(true)}"); + } + + StorageTree tree = GetOrCreateStorage(change.StorageCell.Address); + Db.Metrics.StorageTreeWrites++; + toUpdateRoots.Add(change.StorageCell.Address); + tree.Set(change.StorageCell.Index, change.Value); + if (isTracing) + { + trace![change.StorageCell] = new ChangeTrace(change.Value); + } + + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + // TODO: it seems that we are unnecessarily recalculating root hashes all the time in storage? + foreach (Address address in toUpdateRoots) + { + // since the accounts could be empty accounts that are removing (EIP-158) + if (_stateProvider.AccountExists(address)) + { + Keccak root = RecalculateRootHash(address); + + // _logger.Warn($"Recalculating storage root {address}->{root} ({toUpdateRoots.Count})"); + _stateProvider.UpdateStorageRoot(address, root); + } + } + + Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition, StartCapacity); + _committedThisRound.Reset(); + _intraBlockCache.Reset(); + _originalValues.Reset(); + _transactionChangesSnapshots.Clear(); + + if (isTracing) + { + ReportChanges(tracer!, trace!); + } + } + + private static void ReportChanges(IStorageTracer tracer, Dictionary trace) + { + foreach ((StorageCell address, ChangeTrace change) in trace) + { + byte[] before = change.Before; + byte[] after = change.After; + + if (!Bytes.AreEqual(before, after)) + { + tracer.ReportStorageChange(address, before, after); + } + } + } + + public void Reset() + { + if (_logger.IsTrace) _logger.Trace("Resetting storage"); + + _intraBlockCache.Clear(); + _originalValues.Clear(); + _transactionChangesSnapshots.Clear(); + _currentPosition = -1; + _committedThisRound.Clear(); + Array.Clear(_changes, 0, _changes.Length); + _storages.Reset(); + } + + public void CommitTrees(long blockNumber) + { + // _logger.Warn($"Storage block commit {blockNumber}"); + foreach (KeyValuePair storage in _storages) + { + storage.Value.Commit(blockNumber); + } + + // TODO: maybe I could update storage roots only now? + + // only needed here as there is no control over cached storage size otherwise + _storages.Reset(); + } + + private StorageTree GetOrCreateStorage(Address address) + { + if (!_storages.ContainsKey(address)) + { + StorageTree storageTree = new(_trieStore, _stateProvider.GetStorageRoot(address), _logManager); + return _storages[address] = storageTree; + } + + return _storages[address]; + } + + private byte[] GetCurrentValue(StorageCell storageCell) + { + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) + { + int lastChangeIndex = stack.Peek(); + return _changes[lastChangeIndex]!.Value; + } + + return LoadFromTree(storageCell); + } + + private byte[] LoadFromTree(StorageCell storageCell) + { + StorageTree tree = GetOrCreateStorage(storageCell.Address); + + Db.Metrics.StorageTreeReads++; + byte[] value = tree.Get(storageCell.Index); + PushToRegistryOnly(storageCell, value); + return value; + } + + private void PushToRegistryOnly(StorageCell cell, byte[] value) + { + SetupRegistry(cell); + IncrementChangePosition(); + _intraBlockCache[cell].Push(_currentPosition); + _originalValues[cell] = value; + _changes[_currentPosition] = new Change(ChangeType.JustCache, cell, value); + } + + private void PushUpdate(StorageCell cell, byte[] value) + { + SetupRegistry(cell); + IncrementChangePosition(); + _intraBlockCache[cell].Push(_currentPosition); + _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); + } + + private void IncrementChangePosition() + { + Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); + } + + private void SetupRegistry(StorageCell cell) + { + if (!_intraBlockCache.ContainsKey(cell)) + { + _intraBlockCache[cell] = new StackList(); + } + } + + private class Change + { + public Change(ChangeType changeType, StorageCell storageCell, byte[] value) + { + StorageCell = storageCell; + Value = value; + ChangeType = changeType; + } + + public ChangeType ChangeType { get; } + public StorageCell StorageCell { get; } + public byte[] Value { get; } + } + + public void ClearStorage(Address address) + { + /* we are setting cached values to zero so we do not use previously set values + when the contract is revived with CREATE2 inside the same block */ + foreach (var cellByAddress in _intraBlockCache) + { + if (cellByAddress.Key.Address == address) + { + Set(cellByAddress.Key, _zeroValue); + } + } + + /* here it is important to make sure that we will not reuse the same tree when the contract is revived + by means of CREATE 2 - notice that the cached trie may carry information about items that were not + touched in this block, hence were not zeroed above */ + // TODO: how does it work with pruning? + _storages[address] = new StorageTree(_trieStore, Keccak.EmptyTreeHash, _logManager); + } + + private enum ChangeType + { + JustCache, + Update, + Destroy, + } + } +} diff --git a/src/Nethermind/Nethermind.State/Snapshot.cs b/src/Nethermind/Nethermind.State/Snapshot.cs index 11945148fa2..cd4cc040fd5 100644 --- a/src/Nethermind/Nethermind.State/Snapshot.cs +++ b/src/Nethermind/Nethermind.State/Snapshot.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -26,16 +26,17 @@ namespace Nethermind.State /// public readonly struct Snapshot { - public Snapshot(int stateSnapshot, int storageSnapshot) + public Snapshot(int stateSnapshot, int storageSnapshot, int transientStorageSnapshot) { StateSnapshot = stateSnapshot; - StorageSnapshot = storageSnapshot; + PersistentStorageSnapshot = storageSnapshot; + TransientStorageSnapshot = transientStorageSnapshot; } public int StateSnapshot { get; } - public int StorageSnapshot { get; } - + public int PersistentStorageSnapshot { get; } + public int TransientStorageSnapshot { get; } public const int EmptyPosition = -1; } } diff --git a/src/Nethermind/Nethermind.State/StorageProvider.cs b/src/Nethermind/Nethermind.State/StorageProvider.cs index 387ca0f6bed..5b431c8c3af 100644 --- a/src/Nethermind/Nethermind.State/StorageProvider.cs +++ b/src/Nethermind/Nethermind.State/StorageProvider.cs @@ -28,436 +28,81 @@ namespace Nethermind.State { public class StorageProvider : IStorageProvider { - private readonly ResettableDictionary> _intraBlockCache = new(); - - /// - /// EIP-1283 - /// - private readonly ResettableDictionary _originalValues = new(); - - private readonly ResettableHashSet _committedThisRound = new(); - - private readonly ILogger _logger; - - private readonly ITrieStore _trieStore; - - private readonly IStateProvider _stateProvider; - private readonly ILogManager _logManager; - - private readonly ResettableDictionary _storages = new(); - - private const int StartCapacity = Resettable.StartCapacity; - private int _capacity = StartCapacity; - private Change?[] _changes = new Change[StartCapacity]; - private int _currentPosition = Resettable.EmptyPosition; - - // stack of snapshot indexes on changes for start of each transaction - // this is needed for OriginalValues for new transactions - private readonly Stack _transactionChangesSnapshots = new(); + PersistentStorageProvider _persistentStorageProvider; + TransientStorageProvider _transientStorageProvider; public StorageProvider(ITrieStore? trieStore, IStateProvider? stateProvider, ILogManager? logManager) { - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - _trieStore = trieStore ?? throw new ArgumentNullException(nameof(trieStore)); - _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); - } - - public byte[] GetOriginal(StorageCell storageCell) - { - if (!_originalValues.ContainsKey(storageCell)) - { - throw new InvalidOperationException("Get original should only be called after get within the same caching round"); - } - - if (_transactionChangesSnapshots.TryPeek(out int snapshot)) - { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) - { - return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; - } - } - } - - return _originalValues[storageCell]; - } - - public byte[] Get(StorageCell storageCell) - { - return GetCurrentValue(storageCell); + _persistentStorageProvider = new PersistentStorageProvider(trieStore, stateProvider, logManager); + _transientStorageProvider = new TransientStorageProvider(logManager); } - public void Set(StorageCell storageCell, byte[] newValue) - { - PushUpdate(storageCell, newValue); - } - - private Keccak RecalculateRootHash(Address address) - { - StorageTree storageTree = GetOrCreateStorage(address); - storageTree.UpdateRootHash(); - return storageTree.RootHash; - } - - int IStorageProvider.TakeSnapshot(bool newTransactionStart) + public void ClearStorage(Address address) { - if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); - if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) - { - _transactionChangesSnapshots.Push(_currentPosition); - } - return _currentPosition; - } - - public void Restore(int snapshot) - { - if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); - - if (snapshot > _currentPosition) - { - throw new InvalidOperationException($"{nameof(StorageProvider)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); - } - - if (snapshot == _currentPosition) - { - return; - } - - List keptInCache = new(); - - for (int i = 0; i < _currentPosition - snapshot; i++) - { - Change change = _changes[_currentPosition - i]; - if (_intraBlockCache[change!.StorageCell].Count == 1) - { - if (_changes[_intraBlockCache[change.StorageCell].Peek()]!.ChangeType == ChangeType.JustCache) - { - int actualPosition = _intraBlockCache[change.StorageCell].Pop(); - if (actualPosition != _currentPosition - i) - { - throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {_currentPosition} - {i}"); - } - - keptInCache.Add(change); - _changes[actualPosition] = null; - continue; - } - } - - int forAssertion = _intraBlockCache[change.StorageCell].Pop(); - if (forAssertion != _currentPosition - i) - { - throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); - } - - _changes[_currentPosition - i] = null; - - if (_intraBlockCache[change.StorageCell].Count == 0) - { - _intraBlockCache.Remove(change.StorageCell); - } - } - - _currentPosition = snapshot; - foreach (Change kept in keptInCache) - { - _currentPosition++; - _changes[_currentPosition] = kept; - _intraBlockCache[kept.StorageCell].Push(_currentPosition); - } - - while (_transactionChangesSnapshots.TryPeek(out int lastOriginalSnapshot) && lastOriginalSnapshot > snapshot) - { - _transactionChangesSnapshots.Pop(); - } - + _persistentStorageProvider.ClearStorage(address); + _transientStorageProvider.ClearStorage(address); } public void Commit() { - Commit(NullStorageTracer.Instance); - } - - private static readonly byte[] _zeroValue = {0}; - - private readonly struct ChangeTrace - { - public ChangeTrace(byte[]? before, byte[]? after) - { - After = after ?? _zeroValue; - Before = before ?? _zeroValue; - } - - public ChangeTrace(byte[]? after) - { - After = after ?? _zeroValue; - Before = _zeroValue; - } - - public byte[] Before { get; } - public byte[] After { get; } - } - - public void Commit(IStorageTracer tracer) - { - if (_currentPosition == -1) - { - if (_logger.IsTrace) _logger.Trace("No storage changes to commit"); - return; - } - - if (_logger.IsTrace) _logger.Trace("Committing storage changes"); - - if (_changes[_currentPosition] is null) - { - throw new InvalidOperationException($"Change at current position {_currentPosition} was null when commiting {nameof(StorageProvider)}"); - } - - if (_changes[_currentPosition + 1] != null) - { - throw new InvalidOperationException($"Change after current position ({_currentPosition} + 1) was not null when commiting {nameof(StorageProvider)}"); - } - - HashSet
toUpdateRoots = new(); - - bool isTracing = tracer.IsTracingStorage; - Dictionary? trace = null; - if (isTracing) - { - trace = new Dictionary(); - } - - for (int i = 0; i <= _currentPosition; i++) - { - Change change = _changes[_currentPosition - i]; - if (!isTracing && change!.ChangeType == ChangeType.JustCache) - { - continue; - } - - if (_committedThisRound.Contains(change!.StorageCell)) - { - if (isTracing && change.ChangeType == ChangeType.JustCache) - { - trace![change.StorageCell] = new ChangeTrace(change.Value, trace[change.StorageCell].After); - } - - continue; - } - - if (isTracing && change.ChangeType == ChangeType.JustCache) - { - tracer!.ReportStorageRead(change.StorageCell); - } - - _committedThisRound.Add(change.StorageCell); - - if (change.ChangeType == ChangeType.Destroy) - { - continue; - } - - int forAssertion = _intraBlockCache[change.StorageCell].Pop(); - if (forAssertion != _currentPosition - i) - { - throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); - } - - switch (change.ChangeType) - { - case ChangeType.Destroy: - break; - case ChangeType.JustCache: - break; - case ChangeType.Update: - if (_logger.IsTrace) - { - _logger.Trace($" Update {change.StorageCell.Address}_{change.StorageCell.Index} V = {change.Value.ToHexString(true)}"); - } - - StorageTree tree = GetOrCreateStorage(change.StorageCell.Address); - Db.Metrics.StorageTreeWrites++; - toUpdateRoots.Add(change.StorageCell.Address); - tree.Set(change.StorageCell.Index, change.Value); - if (isTracing) - { - trace![change.StorageCell] = new ChangeTrace(change.Value); - } - - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - - // TODO: it seems that we are unnecessarily recalculating root hashes all the time in storage? - foreach (Address address in toUpdateRoots) - { - // since the accounts could be empty accounts that are removing (EIP-158) - if (_stateProvider.AccountExists(address)) - { - Keccak root = RecalculateRootHash(address); - - // _logger.Warn($"Recalculating storage root {address}->{root} ({toUpdateRoots.Count})"); - _stateProvider.UpdateStorageRoot(address, root); - } - } - - Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition, StartCapacity); - _committedThisRound.Reset(); - _intraBlockCache.Reset(); - _originalValues.Reset(); - _transactionChangesSnapshots.Clear(); - - if (isTracing) - { - ReportChanges(tracer!, trace!); - } + _persistentStorageProvider.Commit(); + _transientStorageProvider.Commit(); } - private static void ReportChanges(IStorageTracer tracer, Dictionary trace) + public void Commit(IStorageTracer stateTracer) { - foreach ((StorageCell address, ChangeTrace change) in trace) - { - byte[] before = change.Before; - byte[] after = change.After; - - if (!Bytes.AreEqual(before, after)) - { - tracer.ReportStorageChange(address, before, after); - } - } - } - - public void Reset() - { - if (_logger.IsTrace) _logger.Trace("Resetting storage"); - - _intraBlockCache.Clear(); - _originalValues.Clear(); - _transactionChangesSnapshots.Clear(); - _currentPosition = -1; - _committedThisRound.Clear(); - Array.Clear(_changes, 0, _changes.Length); - _storages.Reset(); + _persistentStorageProvider.Commit(stateTracer); + _transientStorageProvider.Commit(stateTracer); } public void CommitTrees(long blockNumber) { - // _logger.Warn($"Storage block commit {blockNumber}"); - foreach (KeyValuePair storage in _storages) - { - storage.Value.Commit(blockNumber); - } - - // TODO: maybe I could update storage roots only now? - - // only needed here as there is no control over cached storage size otherwise - _storages.Reset(); + _persistentStorageProvider.CommitTrees(blockNumber); } - private StorageTree GetOrCreateStorage(Address address) + public byte[] Get(StorageCell storageCell) { - if (!_storages.ContainsKey(address)) - { - StorageTree storageTree = new(_trieStore, _stateProvider.GetStorageRoot(address), _logManager); - return _storages[address] = storageTree; - } - - return _storages[address]; + return _persistentStorageProvider.Get(storageCell); } - private byte[] GetCurrentValue(StorageCell storageCell) - { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - int lastChangeIndex = stack.Peek(); - return _changes[lastChangeIndex]!.Value; - } - - return LoadFromTree(storageCell); - } - - private byte[] LoadFromTree(StorageCell storageCell) + public byte[] GetOriginal(StorageCell storageCell) { - StorageTree tree = GetOrCreateStorage(storageCell.Address); - - Db.Metrics.StorageTreeReads++; - byte[] value = tree.Get(storageCell.Index); - PushToRegistryOnly(storageCell, value); - return value; + return _persistentStorageProvider.GetOriginal(storageCell); } - private void PushToRegistryOnly(StorageCell cell, byte[] value) + public byte[] GetTransientState(StorageCell storageCell) { - SetupRegistry(cell); - IncrementChangePosition(); - _intraBlockCache[cell].Push(_currentPosition); - _originalValues[cell] = value; - _changes[_currentPosition] = new Change(ChangeType.JustCache, cell, value); + return _transientStorageProvider.Get(storageCell); } - private void PushUpdate(StorageCell cell, byte[] value) + public void Reset() { - SetupRegistry(cell); - IncrementChangePosition(); - _intraBlockCache[cell].Push(_currentPosition); - _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); + _persistentStorageProvider.Reset(); + _transientStorageProvider.Reset(); } - private void IncrementChangePosition() + public void Restore(Snapshot snapshot) { - Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); + ((IPartialStorageProvider)_persistentStorageProvider).Restore(snapshot.PersistentStorageSnapshot); + ((IPartialStorageProvider)_transientStorageProvider).Restore(snapshot.TransientStorageSnapshot); } - private void SetupRegistry(StorageCell cell) + public void Set(StorageCell storageCell, byte[] newValue) { - if (!_intraBlockCache.ContainsKey(cell)) - { - _intraBlockCache[cell] = new StackList(); - } + _persistentStorageProvider.Set(storageCell, newValue); } - private class Change + public void SetTransientState(StorageCell storageCell, byte[] newValue) { - public Change(ChangeType changeType, StorageCell storageCell, byte[] value) - { - StorageCell = storageCell; - Value = value; - ChangeType = changeType; - } - - public ChangeType ChangeType { get; } - public StorageCell StorageCell { get; } - public byte[] Value { get; } + _transientStorageProvider.Set(storageCell, newValue); } - public void ClearStorage(Address address) + Snapshot IStorageProvider.TakeSnapshot(bool newTransactionStart) { - /* we are setting cached values to zero so we do not use previously set values - when the contract is revived with CREATE2 inside the same block */ - foreach (var cellByAddress in _intraBlockCache) - { - if (cellByAddress.Key.Address == address) - { - Set(cellByAddress.Key, _zeroValue); - } - } + int persistentSnapshot = ((IPartialStorageProvider)_persistentStorageProvider).TakeSnapshot(newTransactionStart); + int transientSnapshot = ((IPartialStorageProvider)_transientStorageProvider).TakeSnapshot(newTransactionStart); - /* here it is important to make sure that we will not reuse the same tree when the contract is revived - by means of CREATE 2 - notice that the cached trie may carry information about items that were not - touched in this block, hence were not zeroed above */ - // TODO: how does it work with pruning? - _storages[address] = new StorageTree(_trieStore, Keccak.EmptyTreeHash, _logManager); - } - - private enum ChangeType - { - JustCache, - Update, - Destroy, + return new Snapshot(Snapshot.EmptyPosition, persistentSnapshot, transientSnapshot); } } } diff --git a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs new file mode 100644 index 00000000000..6b277be031f --- /dev/null +++ b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs @@ -0,0 +1,303 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . + +using System; +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Extensions; +using Nethermind.Core.Resettables; +using Nethermind.Logging; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State +{ + /// + /// EIP-1153 provides a transient store for contracts that doesn't persist + /// storage across calls. Reverts will rollback any transient state changes. + /// + public class TransientStorageProvider : IPartialStorageProvider + { + private readonly ResettableDictionary> _intraBlockCache = new(); + + /// + /// EIP-1283 + /// + private readonly ResettableDictionary _originalValues = new(); + + private readonly ResettableHashSet _committedThisRound = new(); + + private readonly ILogger _logger; + private readonly ILogManager _logManager; + + private readonly ResettableDictionary _storages = new(); + + private const int StartCapacity = Resettable.StartCapacity; + private int _capacity = StartCapacity; + private Change?[] _changes = new Change[StartCapacity]; + private int _currentPosition = Resettable.EmptyPosition; + + // stack of snapshot indexes on changes for start of each transaction + // this is needed for OriginalValues for new transactions + private readonly Stack _transactionChangesSnapshots = new(); + + public TransientStorageProvider(ILogManager? logManager) + { + _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); + _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); + } + + public byte[] GetOriginal(StorageCell storageCell) + { + if (!_originalValues.ContainsKey(storageCell)) + { + throw new InvalidOperationException("Get original should only be called after get within the same caching round"); + } + + if (_transactionChangesSnapshots.TryPeek(out int snapshot)) + { + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) + { + if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) + { + return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; + } + } + } + + return _originalValues[storageCell]; + } + + public byte[] Get(StorageCell storageCell) + { + return GetCurrentValue(storageCell); + } + + public void Set(StorageCell storageCell, byte[] newValue) + { + PushUpdate(storageCell, newValue); + } + + int IPartialStorageProvider.TakeSnapshot(bool newTransactionStart) + { + if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); + if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) + { + _transactionChangesSnapshots.Push(_currentPosition); + } + return _currentPosition; + } + + /// + /// Restore transient state to the snapshot index + /// + /// + /// + public void Restore(int snapshot) + { + if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); + + if (snapshot > _currentPosition) + { + throw new InvalidOperationException($"{nameof(TransientStorageProvider)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); + } + + if (snapshot == _currentPosition) + { + return; + } + + for (int i = 0; i < _currentPosition - snapshot; i++) + { + Change change = _changes[_currentPosition - i]; + + int forAssertion = _intraBlockCache[change.StorageCell].Pop(); + if (forAssertion != _currentPosition - i) + { + throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); + } + + _changes[_currentPosition - i] = null; + + if (_intraBlockCache[change.StorageCell].Count == 0) + { + _intraBlockCache.Remove(change.StorageCell); + } + } + + _currentPosition = snapshot; + + while (_transactionChangesSnapshots.TryPeek(out int lastOriginalSnapshot) && lastOriginalSnapshot > snapshot) + { + _transactionChangesSnapshots.Pop(); + } + } + + public void Commit() + { + Commit(NullStorageTracer.Instance); + } + + private static readonly byte[] _zeroValue = {0}; + + private readonly struct ChangeTrace + { + public ChangeTrace(byte[]? before, byte[]? after) + { + After = after ?? _zeroValue; + Before = before ?? _zeroValue; + } + + public ChangeTrace(byte[]? after) + { + After = after ?? _zeroValue; + Before = _zeroValue; + } + + public byte[] Before { get; } + public byte[] After { get; } + } + + /// + /// Nothing to commit to permanent storage + /// Reset the caches and return + /// + /// + /// + public void Commit(IStorageTracer tracer) + { + if (_currentPosition == -1) + { + if (_logger.IsTrace) _logger.Trace("No storage changes to commit"); + return; + } + + if (_logger.IsTrace) _logger.Trace("Committing transient storage changes"); + + Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition, StartCapacity); + _committedThisRound.Reset(); + _intraBlockCache.Reset(); + _originalValues.Reset(); + _transactionChangesSnapshots.Clear(); + } + + private static void ReportChanges(IStorageTracer tracer, Dictionary trace) + { + foreach ((StorageCell address, ChangeTrace change) in trace) + { + byte[] before = change.Before; + byte[] after = change.After; + + if (!Bytes.AreEqual(before, after)) + { + tracer.ReportStorageChange(address, before, after); + } + } + } + + public void Reset() + { + if (_logger.IsTrace) _logger.Trace("Resetting storage"); + + _intraBlockCache.Clear(); + _originalValues.Clear(); + _transactionChangesSnapshots.Clear(); + _currentPosition = -1; + _committedThisRound.Clear(); + Array.Clear(_changes, 0, _changes.Length); + _storages.Reset(); + } + + public void CommitTrees(long blockNumber) + { + // _logger.Warn($"Storage block commit {blockNumber}"); + foreach (KeyValuePair storage in _storages) + { + storage.Value.Commit(blockNumber); + } + + // TODO: maybe I could update storage roots only now? + + // only needed here as there is no control over cached storage size otherwise + _storages.Reset(); + } + + private byte[] GetCurrentValue(StorageCell storageCell) + { + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) + { + int lastChangeIndex = stack.Peek(); + return _changes[lastChangeIndex]!.Value; + } + + return _zeroValue; + } + + + private void PushUpdate(StorageCell cell, byte[] value) + { + SetupRegistry(cell); + IncrementChangePosition(); + _intraBlockCache[cell].Push(_currentPosition); + _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); + } + + private void IncrementChangePosition() + { + Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); + } + + private void SetupRegistry(StorageCell cell) + { + if (!_intraBlockCache.ContainsKey(cell)) + { + _intraBlockCache[cell] = new StackList(); + } + } + + private class Change + { + public Change(ChangeType changeType, StorageCell storageCell, byte[] value) + { + StorageCell = storageCell; + Value = value; + ChangeType = changeType; + } + + public ChangeType ChangeType { get; } + public StorageCell StorageCell { get; } + public byte[] Value { get; } + } + + public void ClearStorage(Address address) + { + /* we are setting cached values to zero so we do not use previously set values + when the contract is revived with CREATE2 inside the same block */ + foreach (var cellByAddress in _intraBlockCache) + { + if (cellByAddress.Key.Address == address) + { + Set(cellByAddress.Key, _zeroValue); + } + } + } + + private enum ChangeType + { + Update, + } + } +} diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 628a25c9f36..f04e1444bd5 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -25,13 +25,14 @@ public class WorldState : IWorldState public Snapshot TakeSnapshot(bool newTransactionStart = false) { - return new (StateProvider.TakeSnapshot(), StorageProvider.TakeSnapshot(newTransactionStart)); + Snapshot storageSnapshot = StorageProvider.TakeSnapshot(newTransactionStart); + return new (StateProvider.TakeSnapshot(), storageSnapshot.PersistentStorageSnapshot, storageSnapshot.TransientStorageSnapshot); } public void Restore(Snapshot snapshot) { StateProvider.Restore(snapshot.StateSnapshot); - StorageProvider.Restore(snapshot.StorageSnapshot); + StorageProvider.Restore(snapshot); } public IStorageProvider StorageProvider { get; } From b62f2c42f88e5349cecfa19d771c89342ea11a82 Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 30 May 2022 09:38:54 -0700 Subject: [PATCH 02/21] Base implementation complete --- .../Nethermind.Evm.Test/EvmStateTests.cs | 4 +- .../VirtualMachineTests.cs | 24 ++++++++- src/Nethermind/Nethermind.Evm/GasCostOf.cs | 2 + src/Nethermind/Nethermind.Evm/Instruction.cs | 4 +- src/Nethermind/Nethermind.Evm/Metrics.cs | 8 ++- .../Nethermind.Evm/ReleaseSpecExtensions.cs | 5 ++ .../Nethermind.Evm/VirtualMachine.cs | 52 +++++++++++++++++++ .../StorageProviderTests.cs | 4 +- .../Nethermind.State.Test/WorldStateTests.cs | 13 ++--- .../Nethermind.State/StorageProvider.cs | 14 ++--- 10 files changed, 111 insertions(+), 19 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs index 9f47b465921..a569587a7a8 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs @@ -250,13 +250,13 @@ parentEvmState is null new ExecutionEnvironment(), ExecutionType.Call, true, - new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition), + new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition, Snapshot.EmptyPosition), isContinuation) : new EvmState(10000, new ExecutionEnvironment(), ExecutionType.Call, false, - new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition), + new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition, Snapshot.EmptyPosition), 0, 0, false, diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs index f1998e937bf..0bf5c35a184 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -425,6 +425,28 @@ public void Sstore_twice_0_same_storage_should_refund_only_once() Assert.AreEqual(BigInteger.Zero.ToBigEndianByteArray(), Storage.Get(new StorageCell(Recipient, 0)), "storage"); } + [Test] + public void Tload() + { + TestAllTracerWithOutput receipt = Execute( + (byte)Instruction.PUSH1, + 0, // index + (byte)Instruction.TLOAD); + Assert.AreEqual(GasCostOf.Transaction + GasCostOf.VeryLow * 1 + GasCostOf.TLoad, receipt.GasSpent, "gas"); + } + + [Test] + public void Tstore() + { + TestAllTracerWithOutput receipt = Execute( + (byte)Instruction.PUSH1, + 96, // data + (byte)Instruction.PUSH1, + 64, // position + (byte)Instruction.TSTORE); + Assert.AreEqual(GasCostOf.Transaction + GasCostOf.VeryLow * 2 + GasCostOf.TStore, receipt.GasSpent, "gas"); + } + [Test] [Ignore("Not yet implemented")] public void Ropsten_attack_contract_test() diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index fa9576eb9e5..1e4792ee24d 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -70,5 +70,7 @@ public static class GasCostOf public const long AccessAccountListEntry = 2400; // eip-2930 public const long AccessStorageListEntry = 1900; // eip-2930 + public const long TLoad = 100; + public const long TStore = 100; } } diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 3fe7eadd270..609683ae98a 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -84,6 +84,8 @@ public enum Instruction : byte MSTORE8 = 0x53, SLOAD = 0x54, SSTORE = 0x55, + TLOAD = 0xb3, + TSTORE = 0xb4, JUMP = 0x56, JUMPI = 0x57, PC = 0x58, diff --git a/src/Nethermind/Nethermind.Evm/Metrics.cs b/src/Nethermind/Nethermind.Evm/Metrics.cs index b5514c7a54c..e60bdafe38e 100644 --- a/src/Nethermind/Nethermind.Evm/Metrics.cs +++ b/src/Nethermind/Nethermind.Evm/Metrics.cs @@ -34,7 +34,13 @@ public class Metrics [Description("Number of SSTORE opcodes executed.")] public static long SstoreOpcode { get; set; } - + + [Description("Number of TLOAD opcodes executed.")] + public static long TloadOpcode { get; set; } + + [Description("Number of TSTORE opcodes executed.")] + public static long TstoreOpcode { get; set; } + [Description("Number of MODEXP precompiles executed.")] public static long ModExpOpcode { get; set; } diff --git a/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs b/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs index 2031da65e3a..11b469a137d 100644 --- a/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs @@ -97,5 +97,10 @@ public static long GetExpByteCost(this IReleaseSpec spec) => spec.UseExpDDosProtection ? GasCostOf.ExpByteEip160 : GasCostOf.ExpByte; + + public static long GetTLoadCost(this IReleaseSpec spec) => + GasCostOf.TLoad; + public static long GetTStoreCost(this IReleaseSpec spec) => + GasCostOf.TStore; } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index a044d708aea..870d8e54fbc 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2009,6 +2009,58 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) break; } + case Instruction.TLOAD: + { + Metrics.TloadOpcode++; + var gasCost = spec.GetTLoadCost(); + + if (!UpdateGas(gasCost, ref gasAvailable)) + { + EndInstructionTraceError(EvmExceptionType.OutOfGas); + return CallResult.OutOfGasException; + } + + stack.PopUInt256(out UInt256 storageIndex); + StorageCell storageCell = new(env.ExecutingAccount, storageIndex); + + byte[] value = _storage.GetTransientState(storageCell); + stack.PushBytes(value); + + break; + } + case Instruction.TSTORE: + { + Metrics.TstoreOpcode++; + + if (vmState.IsStatic) + { + EndInstructionTraceError(EvmExceptionType.StaticCallViolation); + return CallResult.StaticCallViolationException; + } + + if (!UpdateGas(spec.GetTStoreCost(), ref gasAvailable)) + { + EndInstructionTraceError(EvmExceptionType.OutOfGas); + return CallResult.OutOfGasException; + } + + stack.PopUInt256(out UInt256 storageIndex); + Span newValue = stack.PopBytes(); + bool newIsZero = newValue.IsZero(); + if (!newIsZero) + { + newValue = newValue.WithoutLeadingZeros().ToArray(); + } + else + { + newValue = BytesZero; + } + + StorageCell storageCell = new(env.ExecutingAccount, storageIndex); + _storage.Set(storageCell, newValue.ToArray()); + + break; + } case Instruction.JUMP: { if (!UpdateGas(GasCostOf.Mid, ref gasAvailable)) diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index 6569adfafc5..99e56c4dae7 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -57,7 +57,7 @@ public void Empty_commit_restore() Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); provider.Commit(); - provider.Restore(-1); + provider.Restore(new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition, Snapshot.EmptyPosition)); } private StorageProvider BuildStorageProvider(Context ctx) diff --git a/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs b/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs index 0d44082d6eb..3e91774c88b 100644 --- a/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs +++ b/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -48,7 +48,7 @@ public void When_taking_a_snapshot_return_the_same_value_as_both() Snapshot snapshot = worldState.TakeSnapshot(); snapshot.StateSnapshot.Should().Be(0); - snapshot.StorageSnapshot.Should().Be(0); + snapshot.PersistentStorageSnapshot.Should().Be(0); } [Test] @@ -60,11 +60,12 @@ public void When_taking_a_snapshot_can_return_non_zero_snapshot_value() WorldState worldState = new(stateProvider, storageProvider); stateProvider.TakeSnapshot().Returns(1); - storageProvider.TakeSnapshot().Returns(2); + storageProvider.TakeSnapshot().Returns(new Snapshot(1, 2, 3)); Snapshot snapshot = worldState.TakeSnapshot(); snapshot.StateSnapshot.Should().Be(1); - snapshot.StorageSnapshot.Should().Be(2); + snapshot.PersistentStorageSnapshot.Should().Be(2); + snapshot.TransientStorageSnapshot.Should().Be(3); } [Test] @@ -85,9 +86,9 @@ public void Can_restore_snapshot() IStorageProvider storageProvider = Substitute.For(); WorldState worldState = new(stateProvider, storageProvider); - worldState.Restore(new Snapshot(1, 2)); + worldState.Restore(new Snapshot(1, 2, 1)); stateProvider.Received().Restore(1); - storageProvider.Received().Restore(2); + storageProvider.Received().Restore(new Snapshot(1, 2, 1)); } } } diff --git a/src/Nethermind/Nethermind.State/StorageProvider.cs b/src/Nethermind/Nethermind.State/StorageProvider.cs index 5b431c8c3af..615361c016b 100644 --- a/src/Nethermind/Nethermind.State/StorageProvider.cs +++ b/src/Nethermind/Nethermind.State/StorageProvider.cs @@ -14,13 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -using System; -using System.Collections.Generic; using Nethermind.Core; -using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; -using Nethermind.Core.Resettables; using Nethermind.Logging; using Nethermind.Trie.Pruning; @@ -81,6 +75,14 @@ public void Reset() _transientStorageProvider.Reset(); } + /// + /// Convenience for test cases + /// + /// + public void Restore(int snapshot) + { + Restore(new Snapshot(Snapshot.EmptyPosition, snapshot, Snapshot.EmptyPosition)); + } public void Restore(Snapshot snapshot) { ((IPartialStorageProvider)_persistentStorageProvider).Restore(snapshot.PersistentStorageSnapshot); From 6fccae8b99cb8c71121a5192fc09252ddc9aa9fa Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 30 May 2022 13:44:20 -0700 Subject: [PATCH 03/21] Add flag for enabling 1153 --- src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs | 7 +++++++ src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 10 ++++++++++ .../Nethermind.Specs.Test/OverridableReleaseSpec.cs | 3 ++- .../Nethermind.Specs/ChainSpecStyle/ChainParameters.cs | 4 +++- .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 3 ++- .../Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs | 3 ++- .../ChainSpecStyle/Json/ChainSpecParamsJson.cs | 4 +++- src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs | 1 + src/Nethermind/Nethermind.Specs/Forks/01_Frontier.cs | 1 + src/Nethermind/Nethermind.Specs/Forks/02_Homestead.cs | 1 + src/Nethermind/Nethermind.Specs/Forks/03_Dao.cs | 1 + .../Nethermind.Specs/Forks/04_TangerineWhistle.cs | 1 + .../Nethermind.Specs/Forks/05_SpuriousDragon.cs | 1 + src/Nethermind/Nethermind.Specs/Forks/06_Byzantium.cs | 1 + .../Nethermind.Specs/Forks/07_Constantinople.cs | 1 + .../Nethermind.Specs/Forks/08_ConstantinopleFix.cs | 1 + src/Nethermind/Nethermind.Specs/Forks/09_Istanbul.cs | 1 + .../Nethermind.Specs/Forks/10_MuirGlacier.cs | 1 + src/Nethermind/Nethermind.Specs/Forks/11_Berlin.cs | 1 + src/Nethermind/Nethermind.Specs/Forks/12_London.cs | 3 ++- .../Nethermind.Specs/Forks/13_ArrowGlacier.cs | 3 ++- src/Nethermind/Nethermind.Specs/ReleaseSpec.cs | 1 + .../Nethermind.Specs/SystemTransactionReleaseSpec.cs | 3 ++- 23 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 286376663da..cebf84f0bba 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -255,6 +255,11 @@ public interface IReleaseSpec /// bool IsEip3675Enabled { get; } + /// + /// Transient storage + /// + bool IsEip1153Enabled { get; } + /// /// Should transactions be validated against chainId. /// @@ -332,5 +337,7 @@ public interface IReleaseSpec public Address? Eip1559FeeCollector => null; public UInt256? Eip1559BaseFeeMinValue => null; + + public bool TransientStorageEnabled => IsEip1153Enabled; } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 870d8e54fbc..0848eb4632a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2011,6 +2011,11 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) } case Instruction.TLOAD: { + if (!spec.TransientStorageEnabled) + { + EndInstructionTraceError(EvmExceptionType.BadInstruction); + return CallResult.InvalidInstructionException; + } Metrics.TloadOpcode++; var gasCost = spec.GetTLoadCost(); @@ -2030,6 +2035,11 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) } case Instruction.TSTORE: { + if (!spec.TransientStorageEnabled) + { + EndInstructionTraceError(EvmExceptionType.BadInstruction); + return CallResult.InvalidInstructionException; + } Metrics.TstoreOpcode++; if (vmState.IsStatic) diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index b41965d7250..460f557c1f4 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -148,5 +148,6 @@ public long Eip1559TransitionBlock } public Address? Eip1559FeeCollector => _spec.Eip1559FeeCollector; + public bool IsEip1153Enabled => _spec.IsEip1153Enabled; } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs index 725607beb76..c64809e0a83 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs @@ -66,7 +66,8 @@ public class ChainParameters public long? Eip3541Transition { get; set; } public long? Eip3607Transition { get; set; } public long? Eip3675Transition { get; set; } - + + public UInt256 Eip1559BaseFeeInitialValue { get; set; } public UInt256 Eip1559BaseFeeMaxChangeDenominator { get; set; } @@ -114,5 +115,6 @@ public class ChainParameters /// Optional, minimal value of EIP1559 base fee /// public UInt256? Eip1559BaseFeeMinValue { get; set; } + public long? Eip1153Transition { get; set; } } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index c83ee74a521..dfbf65f9020 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -140,6 +140,7 @@ private void BuildTransitions() releaseSpec.IsEip3529Enabled = (_chainSpec.Parameters.Eip3529Transition ?? long.MaxValue) <= releaseStartBlock; releaseSpec.IsEip3607Enabled = (_chainSpec.Parameters.Eip3607Transition ?? long.MaxValue) <= releaseStartBlock; releaseSpec.IsEip3675Enabled = (_chainSpec.Parameters.Eip3675Transition ?? long.MaxValue) <= releaseStartBlock; + releaseSpec.IsEip1153Enabled = (_chainSpec.Parameters.Eip1153Transition ?? long.MaxValue) <= releaseStartBlock; releaseSpec.ValidateChainId = (_chainSpec.Parameters.ValidateChainIdTransition ?? 0) <= releaseStartBlock; releaseSpec.ValidateReceipts = ((_chainSpec.Parameters.ValidateReceiptsTransition > 0) ? Math.Max(_chainSpec.Parameters.ValidateReceiptsTransition ?? 0, _chainSpec.Parameters.Eip658Transition ?? 0) : 0) <= releaseStartBlock; releaseSpec.Eip1559FeeCollector = releaseSpec.IsEip1559Enabled && (_chainSpec.Parameters.Eip1559FeeCollectorTransition ?? long.MaxValue) <= releaseStartBlock ? _chainSpec.Parameters.Eip1559FeeCollector : null; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index acb6e4a8483..70f01690309 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -146,6 +146,7 @@ private void LoadParameters(ChainSpecJson chainSpecJson, ChainSpec chainSpec) Eip3541Transition = chainSpecJson.Params.Eip3541Transition, Eip3529Transition = chainSpecJson.Params.Eip3529Transition, Eip3607Transition = chainSpecJson.Params.Eip3607Transition, + Eip1153Transition = chainSpecJson.Params.Eip1153Transition, TransactionPermissionContract = chainSpecJson.Params.TransactionPermissionContract, TransactionPermissionContractTransition = chainSpecJson.Params.TransactionPermissionContractTransition, ValidateChainIdTransition = chainSpecJson.Params.ValidateChainIdTransition, diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs index b77d9b3cb43..dd30d1f6ba9 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -138,5 +138,7 @@ internal class ChainSpecParamsJson public long? Eip1559BaseFeeMinValueTransition { get; set; } public UInt256? Eip1559BaseFeeMinValue { get; set; } + + public long? Eip1153Transition { get; set; } } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs b/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs index 979ced5be17..b30b8e99cae 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs @@ -84,5 +84,6 @@ private Olympic() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => long.MaxValue; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/01_Frontier.cs b/src/Nethermind/Nethermind.Specs/Forks/01_Frontier.cs index 44aa135b4d8..0c0deba4e34 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/01_Frontier.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/01_Frontier.cs @@ -82,5 +82,6 @@ private Frontier() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public bool IsEip158IgnoredAccount(Address address) => false; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/02_Homestead.cs b/src/Nethermind/Nethermind.Specs/Forks/02_Homestead.cs index 606ce709f9e..e798d54f084 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/02_Homestead.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/02_Homestead.cs @@ -80,5 +80,6 @@ private Homestead() { } public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public bool IsEip158IgnoredAccount(Address address) => false; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/03_Dao.cs b/src/Nethermind/Nethermind.Specs/Forks/03_Dao.cs index 20267f22098..fa1fedfd3e4 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/03_Dao.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/03_Dao.cs @@ -82,5 +82,6 @@ private Dao() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public bool IsEip158IgnoredAccount(Address address) => false; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/04_TangerineWhistle.cs b/src/Nethermind/Nethermind.Specs/Forks/04_TangerineWhistle.cs index 4aebcb48e11..d601e20715d 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/04_TangerineWhistle.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/04_TangerineWhistle.cs @@ -82,5 +82,6 @@ private TangerineWhistle() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => long.MaxValue; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/05_SpuriousDragon.cs b/src/Nethermind/Nethermind.Specs/Forks/05_SpuriousDragon.cs index 9b328052c86..f4c1821fae3 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/05_SpuriousDragon.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/05_SpuriousDragon.cs @@ -82,5 +82,6 @@ private SpuriousDragon() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public bool IsEip158IgnoredAccount(Address address) => false; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/06_Byzantium.cs b/src/Nethermind/Nethermind.Specs/Forks/06_Byzantium.cs index d511b2e3e51..e08a22a4da4 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/06_Byzantium.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/06_Byzantium.cs @@ -82,5 +82,6 @@ private Byzantium() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public bool IsEip158IgnoredAccount(Address address) => false; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/07_Constantinople.cs b/src/Nethermind/Nethermind.Specs/Forks/07_Constantinople.cs index 1c6172213d3..9909e010423 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/07_Constantinople.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/07_Constantinople.cs @@ -82,5 +82,6 @@ private Constantinople() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => long.MaxValue; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/08_ConstantinopleFix.cs b/src/Nethermind/Nethermind.Specs/Forks/08_ConstantinopleFix.cs index 018309cb4ed..f1294a21c80 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/08_ConstantinopleFix.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/08_ConstantinopleFix.cs @@ -84,5 +84,6 @@ private ConstantinopleFix() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => long.MaxValue; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/09_Istanbul.cs b/src/Nethermind/Nethermind.Specs/Forks/09_Istanbul.cs index 0244b135f9e..a7472bf0b19 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/09_Istanbul.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/09_Istanbul.cs @@ -82,5 +82,6 @@ private Istanbul() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => long.MaxValue; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/10_MuirGlacier.cs b/src/Nethermind/Nethermind.Specs/Forks/10_MuirGlacier.cs index 2947f50866b..862da5d658f 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/10_MuirGlacier.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/10_MuirGlacier.cs @@ -80,5 +80,6 @@ private MuirGlacier() { } public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => long.MaxValue; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/11_Berlin.cs b/src/Nethermind/Nethermind.Specs/Forks/11_Berlin.cs index 0882acc081a..19525d9f8fc 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/11_Berlin.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/11_Berlin.cs @@ -82,5 +82,6 @@ private Berlin() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => long.MaxValue; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/12_London.cs b/src/Nethermind/Nethermind.Specs/Forks/12_London.cs index e6b9d268193..9c1bbd74b76 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/12_London.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/12_London.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -83,5 +83,6 @@ private London() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => 12965000; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs b/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs index 5d961a205ab..ce8c8568534 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -83,5 +83,6 @@ private ArrowGlacier() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => 12965000; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index f564f326b8d..e320ea515ae 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -77,5 +77,6 @@ public class ReleaseSpec : IReleaseSpec public long Eip1559TransitionBlock { get; set; } public Address Eip1559FeeCollector { get; set; } public UInt256? Eip1559BaseFeeMinValue { get; set; } + public bool IsEip1153Enabled { get; set; } } } diff --git a/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs index d013a1587a2..1db956d971b 100644 --- a/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -131,5 +131,6 @@ public bool IsEip158IgnoredAccount(Address address) public long Eip1559TransitionBlock => _spec.Eip1559TransitionBlock; public Address Eip1559FeeCollector => _spec.Eip1559FeeCollector; + public bool IsEip1153Enabled => _spec.IsEip1153Enabled; } } From c076c1af871af2758d49451567f527db78d9691f Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 30 May 2022 15:24:08 -0700 Subject: [PATCH 04/21] Added StorageProviderTests --- src/Nethermind/Nethermind.Evm/GasCostOf.cs | 4 +- .../StorageProviderTests.cs | 165 ++++++++++++++++++ 2 files changed, 167 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index 1e4792ee24d..562586e8931 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -70,7 +70,7 @@ public static class GasCostOf public const long AccessAccountListEntry = 2400; // eip-2930 public const long AccessStorageListEntry = 1900; // eip-2930 - public const long TLoad = 100; - public const long TStore = 100; + public const long TLoad = WarmStateRead; + public const long TStore = WarmStateRead; } } diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index 99e56c4dae7..3099b0cb2e3 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -236,6 +236,171 @@ public void Can_commit_when_exactly_at_capacity_regression() Assert.AreEqual(_values[(Resettable.StartCapacity + 1) % 2], valueAfter); } + [Test] + public void Can_tload_uninitialized_locations() + { + Context ctx = new(); + StorageProvider provider = BuildStorageProvider(ctx); + // Should be 0 if not set + Assert.True(provider.GetTransientState(new StorageCell(ctx.Address1, 1)).IsZero()); + + // Should be 0 if loading from the same contract but different index + provider.SetTransientState(new StorageCell(ctx.Address1, 2), _values[1]); + Assert.True(provider.GetTransientState(new StorageCell(ctx.Address1, 1)).IsZero()); + + // Should be 0 if loading from the same index but different contract + Assert.True(provider.GetTransientState(new StorageCell(ctx.Address2, 1)).IsZero()); + } + + [Test] + public void Can_tload_after_tstore() + { + Context ctx = new(); + StorageProvider provider = BuildStorageProvider(ctx); + + provider.SetTransientState(new StorageCell(ctx.Address1, 2), _values[1]); + Assert.AreEqual(_values[1], provider.GetTransientState(new StorageCell(ctx.Address1, 2))); + } + + [TestCase(-1)] + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + public void Tload_same_address_same_index_different_values_restore(int snapshot) + { + Context ctx = new(); + StorageProvider provider = BuildStorageProvider(ctx); + Snapshot[] snapshots = new Snapshot[4]; + snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); + provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[1]); + snapshots[1] = ((IStorageProvider)provider).TakeSnapshot(); + provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[2]); + snapshots[2] = ((IStorageProvider)provider).TakeSnapshot(); + provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[3]); + snapshots[3] = ((IStorageProvider)provider).TakeSnapshot(); + + Assert.AreEqual(snapshots[snapshot + 1].TransientStorageSnapshot, snapshot); + // Persistent storage is unimpacted by transient storage + Assert.AreEqual(snapshots[snapshot + 1].PersistentStorageSnapshot, -1); + provider.Restore(snapshots[snapshot + 1]); + + Assert.AreEqual(_values[snapshot + 1], provider.GetTransientState(new StorageCell(ctx.Address1, 1))); + } + + [Test] + public void Commit_resets_transient_state() + { + Context ctx = new(); + StorageProvider provider = BuildStorageProvider(ctx); + + provider.SetTransientState(new StorageCell(ctx.Address1, 2), _values[1]); + Assert.AreEqual(_values[1], provider.GetTransientState(new StorageCell(ctx.Address1, 2))); + + provider.Commit(); + Assert.True(provider.GetTransientState(new StorageCell(ctx.Address1, 2)).IsZero()); + } + + [Test] + public void Reset_resets_transient_state() + { + Context ctx = new(); + StorageProvider provider = BuildStorageProvider(ctx); + + provider.SetTransientState(new StorageCell(ctx.Address1, 2), _values[1]); + Assert.AreEqual(_values[1], provider.GetTransientState(new StorageCell(ctx.Address1, 2))); + + provider.Reset(); + Assert.True(provider.GetTransientState(new StorageCell(ctx.Address1, 2)).IsZero()); + } + + [TestCase(-1)] + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + public void Transient_state_restores_independed_of_permanent_state(int snapshot) + { + Context ctx = new(); + StorageProvider provider = BuildStorageProvider(ctx); + Snapshot[] snapshots = new Snapshot[4]; + + // No updates + snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[0].TransientStorageSnapshot, -1); + Assert.AreEqual(snapshots[0].PersistentStorageSnapshot, -1); + + // Only update transient + provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[1]); + snapshots[1] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[1].TransientStorageSnapshot, 0); + Assert.AreEqual(snapshots[1].PersistentStorageSnapshot, -1); + + // Update both + provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[2]); + provider.Set(new StorageCell(ctx.Address1, 1), _values[9]); + snapshots[2] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[2].TransientStorageSnapshot, 1); + Assert.AreEqual(snapshots[2].PersistentStorageSnapshot, 0); + + // Only update persistent + provider.Set(new StorageCell(ctx.Address1, 1), _values[8]); + snapshots[3] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[3].TransientStorageSnapshot, 1); + Assert.AreEqual(snapshots[3].PersistentStorageSnapshot, 1); + + provider.Restore(snapshots[snapshot + 1]); + + // Since we didn't update transient on the 3rd snapshot + if (snapshot == 2) + { + snapshot--; + } + Assert.AreEqual(_values[snapshot + 1], provider.GetTransientState(new StorageCell(ctx.Address1, 1))); + } + + [TestCase(-1)] + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + public void Permanent_state_restores_independed_of_transient_state(int snapshot) + { + Context ctx = new(); + StorageProvider provider = BuildStorageProvider(ctx); + Snapshot[] snapshots = new Snapshot[4]; + + // No updates + snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[0].TransientStorageSnapshot, -1); + Assert.AreEqual(snapshots[0].PersistentStorageSnapshot, -1); + + // Only update persistent + provider.Set(new StorageCell(ctx.Address1, 1), _values[1]); + snapshots[1] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[1].PersistentStorageSnapshot, 0); + Assert.AreEqual(snapshots[1].TransientStorageSnapshot, -1); + + // Update both + provider.Set(new StorageCell(ctx.Address1, 1), _values[2]); + provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[9]); + snapshots[2] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[2].PersistentStorageSnapshot, 1); + Assert.AreEqual(snapshots[2].TransientStorageSnapshot, 0); + + // Only update transient + provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[8]); + snapshots[3] = ((IStorageProvider)provider).TakeSnapshot(); + Assert.AreEqual(snapshots[3].PersistentStorageSnapshot, 1); + Assert.AreEqual(snapshots[3].TransientStorageSnapshot, 1); + + provider.Restore(snapshots[snapshot + 1]); + + // Since we didn't update transient on the 3rd snapshot + if (snapshot == 2) + { + snapshot--; + } + Assert.AreEqual(_values[snapshot + 1], provider.Get(new StorageCell(ctx.Address1, 1))); + } + private class Context { public IStateProvider StateProvider { get; } From f580065e81a5959bc598edf850a0b95a2c7bc4cd Mon Sep 17 00:00:00 2001 From: codyborn Date: Tue, 31 May 2022 20:32:11 -0700 Subject: [PATCH 05/21] Eip1153 specific tests --- .../Nethermind.Evm.Test/Eip1153Tests.cs | 84 ++++++++++++++++++ .../Nethermind.Evm.Test/InvalidOpcodeTests.cs | 22 ++++- .../VirtualMachineTests.cs | 24 ++--- .../VirtualMachineTestsBase.cs | 8 ++ .../Nethermind.Evm/VirtualMachine.cs | 7 +- .../ChainSpecStyle/ChainSpec.cs | 2 + .../ChainSpecStyle/ChainSpecLoader.cs | 1 + .../Nethermind.Specs/Forks/13_ArrowGlacier.cs | 10 +-- .../Nethermind.Specs/Forks/14_Shanghai.cs | 88 +++++++++++++++++++ .../Nethermind.Specs/MainNetSpecProvider.cs | 7 +- .../StorageProviderTests.cs | 2 +- 11 files changed, 233 insertions(+), 22 deletions(-) create mode 100644 src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs create mode 100644 src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs new file mode 100644 index 00000000000..ec667160b4f --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -0,0 +1,84 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . + +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; +using Nethermind.State; +using NUnit.Framework; + +namespace Nethermind.Evm.Test +{ + internal class Eip1153Tests : VirtualMachineTestsBase + { + protected override long BlockNumber => MainnetSpecProvider.ShanghaiBlockNumber; + protected override ISpecProvider SpecProvider => MainnetSpecProvider.Instance; + + [Test] + public void after_shanghai_can_call_tstore_tload() + { + byte[] code = Prepare.EvmCode + .PushData(96) // Value + .PushData(64) // Index + .Op(Instruction.TSTORE) + .PushData(64) // Index + .Op(Instruction.TLOAD) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(StatusCode.Success, result.StatusCode); + } + + [Test] + public void before_shanghai_can_not_call_tstore_tload() + { + byte[] code = Prepare.EvmCode + .PushData(96) // Value + .PushData(64) // Index + .Op(Instruction.TSTORE) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber - 1, 100000, code); + Assert.AreEqual(StatusCode.Failure, result.StatusCode); + } + + [Test] + public void tload_after_tstore() + { + byte[] code = Prepare.EvmCode + .PushData(96) // Value + .PushData(64) // Index + .Op(Instruction.TSTORE) + .PushData(64) // Index + .Op(Instruction.TLOAD) + .PushData(0) + .Op(Instruction.MSTORE) // Store the result in mem + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) // Return the result + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(StatusCode.Success, result.StatusCode); + + Assert.AreEqual(96, (int)result.ReturnValue.ToUInt256()); + } + } +} diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 878973fd674..a6a86ba7585 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -120,6 +120,22 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase ) ))))).ToArray(); + private static readonly Instruction[] ShanghaiInstructions = + FrontierInstructions.Union( + HomesteadInstructions.Union( + ByzantiumInstructions.Union( + ConstantinopleFixInstructions.Union( + IstanbulInstructions.Union( + BerlinInstructions.Union( + LondonInstructions.Union( + new Instruction[] + { + Instruction.TLOAD, + Instruction.TSTORE + } + ) + )))))).ToArray(); + private Dictionary _validOpcodes = new() { @@ -133,7 +149,8 @@ private Dictionary _validOpcodes {MainnetSpecProvider.MuirGlacierBlockNumber, IstanbulInstructions}, {MainnetSpecProvider.BerlinBlockNumber, BerlinInstructions}, {MainnetSpecProvider.LondonBlockNumber, LondonInstructions}, - {long.MaxValue, LondonInstructions} + {MainnetSpecProvider.ShanghaiBlockNumber, ShanghaiInstructions}, + {long.MaxValue, ShanghaiInstructions} }; private const string InvalidOpCodeErrorMessage = "BadInstruction"; @@ -157,6 +174,7 @@ protected override ILogManager GetLogManager() [TestCase(MainnetSpecProvider.BerlinBlockNumber)] [TestCase(MainnetSpecProvider.BerlinBlockNumber)] [TestCase(MainnetSpecProvider.LondonBlockNumber)] + [TestCase(MainnetSpecProvider.ShanghaiBlockNumber)] [TestCase(long.MaxValue)] public void Test(long blockNumber) { diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs index 0bf5c35a184..c30c218454d 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs @@ -22,6 +22,7 @@ using Nethermind.State; using Nethermind.Int256; using NUnit.Framework; +using Nethermind.Specs; namespace Nethermind.Evm.Test { @@ -428,22 +429,25 @@ public void Sstore_twice_0_same_storage_should_refund_only_once() [Test] public void Tload() { - TestAllTracerWithOutput receipt = Execute( - (byte)Instruction.PUSH1, - 0, // index - (byte)Instruction.TLOAD); + byte[] code = Prepare.EvmCode + .PushData(96) + .Op(Instruction.TLOAD) + .Done; + + TestAllTracerWithOutput receipt = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); Assert.AreEqual(GasCostOf.Transaction + GasCostOf.VeryLow * 1 + GasCostOf.TLoad, receipt.GasSpent, "gas"); } [Test] public void Tstore() { - TestAllTracerWithOutput receipt = Execute( - (byte)Instruction.PUSH1, - 96, // data - (byte)Instruction.PUSH1, - 64, // position - (byte)Instruction.TSTORE); + byte[] code = Prepare.EvmCode + .PushData(96) + .PushData(64) + .Op(Instruction.TSTORE) + .Done; + + TestAllTracerWithOutput receipt = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); Assert.AreEqual(GasCostOf.Transaction + GasCostOf.VeryLow * 2 + GasCostOf.TStore, receipt.GasSpent, "gas"); } diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 5b27f00a280..54fc4ba63f9 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -259,6 +259,14 @@ protected void AssertStorage(StorageCell storageCell, UInt256 expectedValue) } } + //protected void AssertTransientStorage(StorageCell storageCell, UInt256 expectedValue) + //{ + // _callIndex++; + + // byte[] actualValue = Storage.GetTransientState(storageCell); + // Assert.AreEqual(expectedValue.ToBigEndian().WithoutLeadingZeros().ToArray(), actualValue, $"storage {storageCell}, call {_callIndex}"); + //} + protected void AssertCodeHash(Address address, Keccak codeHash) { Assert.AreEqual(codeHash, TestState.GetCodeHash(address), "code hash"); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 0848eb4632a..2acb12ebba4 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2047,8 +2047,9 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) EndInstructionTraceError(EvmExceptionType.StaticCallViolation); return CallResult.StaticCallViolationException; } - - if (!UpdateGas(spec.GetTStoreCost(), ref gasAvailable)) + + var gasCost = spec.GetTStoreCost(); + if (!UpdateGas(gasCost, ref gasAvailable)) { EndInstructionTraceError(EvmExceptionType.OutOfGas); return CallResult.OutOfGasException; @@ -2067,7 +2068,7 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) } StorageCell storageCell = new(env.ExecutingAccount, storageIndex); - _storage.Set(storageCell, newValue.ToArray()); + _storage.SetTransientState(storageCell, newValue.ToArray()); break; } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs index 80569dc9734..83251df6330 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs @@ -79,5 +79,7 @@ public class ChainSpec public long? LondonBlockNumber { get; set; } public long? ArrowGlacierBlockNumber { get; set; } + + public long? ShanghaiBlockNumber { get; set; } } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index 70f01690309..f5ddbc3ee7a 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -217,6 +217,7 @@ private static void LoadTransitions(ChainSpecJson chainSpecJson, ChainSpec chain chainSpec.ArrowGlacierBlockNumber = chainSpec.Ethash?.DifficultyBombDelays.Count > 4 ? chainSpec.Ethash?.DifficultyBombDelays.Keys.ToArray()[4] : null; + chainSpec.ShanghaiBlockNumber = chainSpec.Parameters.Eip1153Transition ?? (long.MaxValue - 1); } private static void LoadEngine(ChainSpecJson chainSpecJson, ChainSpec chainSpec) diff --git a/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs b/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs index ce8c8568534..27982049251 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs @@ -22,17 +22,17 @@ namespace Nethermind.Specs.Forks { - public class ArrowGlacier : IReleaseSpec + public class Shanghai : IReleaseSpec { private static IReleaseSpec _instance; - private ArrowGlacier() + private Shanghai() { } - public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new ArrowGlacier()); + public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new Shanghai()); - public string Name => "ArrowGlacier"; + public string Name => "Shanghai"; public long MaximumExtraDataSize => 32; public long MaxCodeSize => 24576; public long MinGasLimit => 5000; @@ -83,6 +83,6 @@ private ArrowGlacier() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => 12965000; - public bool IsEip1153Enabled => false; + public bool IsEip1153Enabled => true; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs b/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs new file mode 100644 index 00000000000..ce8c8568534 --- /dev/null +++ b/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs @@ -0,0 +1,88 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System.Threading; +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Int256; + +namespace Nethermind.Specs.Forks +{ + public class ArrowGlacier : IReleaseSpec + { + private static IReleaseSpec _instance; + + private ArrowGlacier() + { + } + + public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new ArrowGlacier()); + + public string Name => "ArrowGlacier"; + public long MaximumExtraDataSize => 32; + public long MaxCodeSize => 24576; + public long MinGasLimit => 5000; + public long GasLimitBoundDivisor => 0x0400; + public UInt256 BlockReward { get; } = UInt256.Parse("2000000000000000000"); + public long DifficultyBombDelay => 10700000L; + public long DifficultyBoundDivisor => 0x0800; + public long? FixedDifficulty => null; + public int MaximumUncleCount => 2; + public bool IsTimeAdjustmentPostOlympic => true; + public bool IsEip2Enabled => true; + public bool IsEip7Enabled => true; + public bool IsEip100Enabled => true; + public bool IsEip140Enabled => true; + public bool IsEip150Enabled => true; + public bool IsEip155Enabled => true; + public bool IsEip158Enabled => true; + public bool IsEip160Enabled => true; + public bool IsEip170Enabled => true; + public bool IsEip196Enabled => true; + public bool IsEip197Enabled => true; + public bool IsEip198Enabled => true; + public bool IsEip211Enabled => true; + public bool IsEip214Enabled => true; + public bool IsEip649Enabled => true; + public bool IsEip658Enabled => true; + public bool IsEip145Enabled => true; + public bool IsEip1014Enabled => true; + public bool IsEip1052Enabled => true; + public bool IsEip1283Enabled => false; + public bool IsEip1234Enabled => true; + public bool IsEip1344Enabled => true; + public bool IsEip2028Enabled => true; + public bool IsEip152Enabled => true; + public bool IsEip1108Enabled => true; + public bool IsEip1884Enabled => true; + public bool IsEip2200Enabled => true; + public bool IsEip2315Enabled => false; + public bool IsEip2537Enabled => false; + public bool IsEip2565Enabled => true; + public bool IsEip2929Enabled => true; + public bool IsEip2930Enabled => true; + public bool IsEip158IgnoredAccount(Address address) => false; + public bool IsEip1559Enabled => true; + public bool IsEip3198Enabled => true; + public bool IsEip3529Enabled => true; + public bool IsEip3541Enabled => true; + public bool IsEip3607Enabled => true; + public bool IsEip3675Enabled => false; + public long Eip1559TransitionBlock => 12965000; + public bool IsEip1153Enabled => false; + } +} diff --git a/src/Nethermind/Nethermind.Specs/MainNetSpecProvider.cs b/src/Nethermind/Nethermind.Specs/MainNetSpecProvider.cs index f36845aff71..dbc1fb73566 100644 --- a/src/Nethermind/Nethermind.Specs/MainNetSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/MainNetSpecProvider.cs @@ -80,7 +80,12 @@ public IReleaseSpec GetSpec(long blockNumber) return London.Instance; } - return ArrowGlacier.Instance; + if (blockNumber < ShanghaiBlockNumber) + { + return ArrowGlacier.Instance; + } + + return Shanghai.Instance; } public const long HomesteadBlockNumber = 1_150_000; diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index 3099b0cb2e3..da5865860c1 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -393,7 +393,7 @@ public void Permanent_state_restores_independed_of_transient_state(int snapshot) provider.Restore(snapshots[snapshot + 1]); - // Since we didn't update transient on the 3rd snapshot + // Since we didn't update persistent on the 3rd snapshot if (snapshot == 2) { snapshot--; From a1f69444e064b346467b5503520eb48ea655257d Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 6 Jun 2022 19:34:19 +0200 Subject: [PATCH 06/21] Test Reentrancy --- .../Nethermind.Evm.Test/Eip1153Tests.cs | 487 +++++++++++++++++- .../Nethermind.Evm/ByteCodeBuilder.cs | 30 ++ 2 files changed, 498 insertions(+), 19 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index ec667160b4f..b1d6118105e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -14,14 +14,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Specs; using Nethermind.Core.Test.Builders; -using Nethermind.Int256; -using Nethermind.State; using NUnit.Framework; namespace Nethermind.Evm.Test @@ -35,11 +32,8 @@ internal class Eip1153Tests : VirtualMachineTestsBase public void after_shanghai_can_call_tstore_tload() { byte[] code = Prepare.EvmCode - .PushData(96) // Value - .PushData(64) // Index - .Op(Instruction.TSTORE) - .PushData(64) // Index - .Op(Instruction.TLOAD) + .StoreDataInTransientStorage(1, 8) + .LoadDataFromTransientStorage(1) .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -50,35 +44,490 @@ public void after_shanghai_can_call_tstore_tload() public void before_shanghai_can_not_call_tstore_tload() { byte[] code = Prepare.EvmCode - .PushData(96) // Value - .PushData(64) // Index - .Op(Instruction.TSTORE) + .StoreDataInTransientStorage(1, 8) + .LoadDataFromTransientStorage(1) .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber - 1, 100000, code); Assert.AreEqual(StatusCode.Failure, result.StatusCode); } + [Test] + public void tload_uninitialized_returns_zero() + { + byte[] code = Prepare.EvmCode + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .Return(32, 0) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(StatusCode.Success, result.StatusCode); + + // Should be 0 since it's not yet set + Assert.AreEqual(0, (int)result.ReturnValue.ToUInt256()); + } + [Test] public void tload_after_tstore() { byte[] code = Prepare.EvmCode - .PushData(96) // Value - .PushData(64) // Index - .Op(Instruction.TSTORE) - .PushData(64) // Index + .StoreDataInTransientStorage(1, 8) + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .Return(32, 0) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(StatusCode.Success, result.StatusCode); + + Assert.AreEqual(8, (int)result.ReturnValue.ToUInt256()); + } + + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + [TestCase(6)] + public void tload_after_tstore_from_different_locations(int loadLocation) + { + byte[] code = Prepare.EvmCode + .StoreDataInTransientStorage(1, 8) + .LoadDataFromTransientStorage(loadLocation) + .DataOnStackToMemory(0) + .Return(32, 0) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(StatusCode.Success, result.StatusCode); + + Assert.AreEqual(0, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Contracts have separate transient storage + /// + [Test] + public void tload_from_different_contract() + { + // TLOAD and RETURN the resulting value + byte[] contractCode = Prepare.EvmCode + .PushData(1) .Op(Instruction.TLOAD) + .DataOnStackToMemory(0) + .PushData(32) .PushData(0) - .Op(Instruction.MSTORE) // Store the result in mem + .Op(Instruction.RETURN) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + + // Store 8 at index 1 and call contract from above + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .StoreDataInTransientStorage(1, 8) + .Call(TestItem.AddressD, 50000) .PushData(32) .PushData(0) - .Op(Instruction.RETURN) // Return the result + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + // If transient state was not isolated, the return value would be 8 + Assert.AreEqual(0, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Reentrant calls access the same transient storage + /// + [Test] + public void tload_from_reentrant_call() + { + // If caller is self, TLOAD and return value (break recursion) + // Else, TSTORE and call self, return the response + byte[] contractCode = Prepare.EvmCode + // Check if caller is self + .Op(Instruction.CALLER) + .PushData(TestItem.AddressD) + .Op(Instruction.EQ) + .PushData(78) + .Op(Instruction.JUMPI) + + // Non-reentrant, call self after TSTORE + .StoreDataInTransientStorage(1, 8) + .Call(TestItem.AddressD, 50000) + // Return the response + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Reentrant, TLOAD and return value + .Op(Instruction.JUMPDEST) + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressD, 50000) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(8, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Reentrant calls can manipulate the same transient storage + /// + [Test] + public void tstore_from_reentrant_call() + { + // If caller is self, TLOAD and return value (break recursion) + // Else, TSTORE and call self, return the response + byte[] contractCode = Prepare.EvmCode + // Check if caller is self + .Op(Instruction.CALLER) + .PushData(TestItem.AddressD) + .Op(Instruction.EQ) + .PushData(78) + .Op(Instruction.JUMPI) + + // Non-reentrant, call self after TSTORE + .StoreDataInTransientStorage(1, 8) + .Call(TestItem.AddressD, 50000) + // Return the response + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Reentrant, TLOAD and return value + .Op(Instruction.JUMPDEST) // PC = 78 + .StoreDataInTransientStorage(1, 9) + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressD, 50000) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(9, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Successfully returned calls do not revert transient storage writes + /// + [Test] + public void tstore_from_reentrant_call_read_by_caller() + { + // If caller is self, TLOAD and return value (break recursion) + // Else, TSTORE and call self, return the response + byte[] contractCode = Prepare.EvmCode + // Check if caller is self + .Op(Instruction.CALLER) + .PushData(TestItem.AddressD) + .Op(Instruction.EQ) + .PushData(77) + .Op(Instruction.JUMPI) + + // Non-reentrant, call self after TSTORE + .StoreDataInTransientStorage(1, 8) + .Call(TestItem.AddressD, 50000) + // TLOAD and return value (should be 9) + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Reentrant, TSTORE 9 + .Op(Instruction.JUMPDEST) // PC = 77 + .StoreDataInTransientStorage(1, 9) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressD, 50000) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(9, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Revert undoes the transient storage write from the failed call + /// + [Test] + public void revert_resets_transient_state() + { + // If caller is self, TLOAD and return value (break recursion) + // Else, TSTORE and call self, return the response + byte[] contractCode = Prepare.EvmCode + // Check if caller is self + .Op(Instruction.CALLER) + .PushData(TestItem.AddressD) + .Op(Instruction.EQ) + .PushData(77) + .Op(Instruction.JUMPI) + + // Non-reentrant, call self after TSTORE + .StoreDataInTransientStorage(1, 8) + .Call(TestItem.AddressD, 50000) + // TLOAD and return value + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Reentrant, TSTORE 9 but REVERT + .Op(Instruction.JUMPDEST) // PC = 77 + .StoreDataInTransientStorage(1, 9) + .Op(Instruction.REVERT) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressD, 50000) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + // Should be original TSTORE value + Assert.AreEqual(8, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Revert undoes all the transient storage writes to the same key from the failed call + /// + [Test] + public void revert_resets_all_transient_state() + { + // If caller is self, TLOAD and return value (break recursion) + // Else, TSTORE and call self, return the response + byte[] contractCode = Prepare.EvmCode + // Check if caller is self + .Op(Instruction.CALLER) + .PushData(TestItem.AddressD) + .Op(Instruction.EQ) + .PushData(77) + .Op(Instruction.JUMPI) + + // Non-reentrant, call self after TSTORE + .StoreDataInTransientStorage(1, 8) + .Call(TestItem.AddressD, 50000) + // TLOAD and return value + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Reentrant, TSTORE 9 but REVERT + .Op(Instruction.JUMPDEST) // PC = 77 + .StoreDataInTransientStorage(1, 9) + .StoreDataInTransientStorage(1, 10) + .Op(Instruction.REVERT) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressD, 50000) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + // Should be original TSTORE value + Assert.AreEqual(8, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Revert undoes transient storage writes from inner calls that successfully returned + /// + [Test] + public void revert_resets_transient_state_from_succesful_calls() + { + // If caller is self, TLOAD and return value (break recursion) + // Else, TSTORE and call self, return the response + byte[] contractCode = Prepare.EvmCode + // Check call depth + .PushData(0) + .Op(Instruction.CALLDATALOAD) + // Store input in mem and reload it to stack + .DataOnStackToMemory(5) + .PushData(5) + .Op(Instruction.MLOAD) + + // See if we're at call depth 1 + .PushData(1) + .Op(Instruction.EQ) + .PushData(84) + .Op(Instruction.JUMPI) + + // See if we're at call depth 2 + .PushData(5) + .Op(Instruction.MLOAD) + .PushData(2) + .Op(Instruction.EQ) + .PushData(135) + .Op(Instruction.JUMPI) + + // Call depth = 0, call self after TSTORE 8 + .StoreDataInTransientStorage(1, 8) + + // Recursive call with input + // Depth++ + .PushData(5) + .Op(Instruction.MLOAD) + .PushData(1) + .Op(Instruction.ADD) + + .DataOnStackToMemory(0) + .PushData(0) + .PushData(0) + .PushData(32) + .PushData(0) + .PushData(0) + .PushData(TestItem.AddressD) + .PushData(50000) + .Op(Instruction.CALL) + + // TLOAD and return value + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Call depth 1, TSTORE 9 but REVERT after recursion + .Op(Instruction.JUMPDEST) // PC = 84 + .StoreDataInTransientStorage(1, 9) + + // Recursive call with input + // Depth++ + .PushData(5) + .Op(Instruction.MLOAD) + .PushData(1) + .Op(Instruction.ADD) + + .DataOnStackToMemory(0) + .PushData(0) + .PushData(0) + .PushData(32) + .PushData(0) + .PushData(0) + .PushData(TestItem.AddressD) + .PushData(50000) + .Op(Instruction.CALL) + + .Op(Instruction.REVERT) + + // Call depth 2, TSTORE 10 and complete + .Op(Instruction.JUMPDEST) // PC = 135 + .StoreDataInTransientStorage(1, 10) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .CallWithInput(TestItem.AddressD, 50000, new byte[32]) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); - Assert.AreEqual(StatusCode.Success, result.StatusCode); - Assert.AreEqual(96, (int)result.ReturnValue.ToUInt256()); + // Should be original TSTORE value + Assert.AreEqual(8, (int)result.ReturnValue.ToUInt256()); } } } diff --git a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs index 6db7c50e6af..eba90660932 100644 --- a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs +++ b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs @@ -271,5 +271,35 @@ private Prepare StoreDataInMemory(int position, byte[] data) return this; } + + public Prepare DataOnStackToMemory(int position) + { + PushData(position); + Op(Instruction.MSTORE); + return this; + } + + public Prepare StoreDataInTransientStorage(int key, int value) + { + PushData(value); + PushData(key); + Op(Instruction.TSTORE); + return this; + } + + public Prepare LoadDataFromTransientStorage(int key) + { + PushData(key); + Op(Instruction.TLOAD); + return this; + } + + public Prepare Return(int size, int position) + { + PushData(size); + PushData(position); + Op(Instruction.RETURN); + return this; + } } } From 8ff6cf25c7a18d9ed2c74742e7bc23d9cab8564a Mon Sep 17 00:00:00 2001 From: codyborn Date: Tue, 7 Jun 2022 07:52:11 +0200 Subject: [PATCH 07/21] Add DELEGATECALL+STATICALL tests --- .../Nethermind.Evm.Test/Eip1153Tests.cs | 305 ++++++++++++++++-- .../Nethermind.Evm/ByteCodeBuilder.cs | 42 ++- 2 files changed, 312 insertions(+), 35 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index b1d6118105e..feba9866e87 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -121,8 +121,8 @@ public void tload_from_different_contract() .Done; TestState.CreateAccount(TestItem.AddressD, 1.Ether()); - Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); - TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); // Store 8 at index 1 and call contract from above // Return the result received from the contract @@ -182,8 +182,8 @@ public void tload_from_reentrant_call() .Done; TestState.CreateAccount(TestItem.AddressD, 1.Ether()); - Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); - TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); // Return the result received from the contract byte[] code = Prepare.EvmCode @@ -241,8 +241,8 @@ public void tstore_from_reentrant_call() .Done; TestState.CreateAccount(TestItem.AddressD, 1.Ether()); - Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); - TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); // Return the result received from the contract byte[] code = Prepare.EvmCode @@ -293,8 +293,8 @@ public void tstore_from_reentrant_call_read_by_caller() .Done; TestState.CreateAccount(TestItem.AddressD, 1.Ether()); - Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); - TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); // Return the result received from the contract byte[] code = Prepare.EvmCode @@ -346,8 +346,8 @@ public void revert_resets_transient_state() .Done; TestState.CreateAccount(TestItem.AddressD, 1.Ether()); - Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); - TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); // Return the result received from the contract byte[] code = Prepare.EvmCode @@ -401,8 +401,8 @@ public void revert_resets_all_transient_state() .Done; TestState.CreateAccount(TestItem.AddressD, 1.Ether()); - Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); - TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); // Return the result received from the contract byte[] code = Prepare.EvmCode @@ -463,15 +463,7 @@ public void revert_resets_transient_state_from_succesful_calls() .PushData(1) .Op(Instruction.ADD) - .DataOnStackToMemory(0) - .PushData(0) - .PushData(0) - .PushData(32) - .PushData(0) - .PushData(0) - .PushData(TestItem.AddressD) - .PushData(50000) - .Op(Instruction.CALL) + .CallWithInput(TestItem.AddressD, 50000) // TLOAD and return value .LoadDataFromTransientStorage(1) @@ -491,15 +483,7 @@ public void revert_resets_transient_state_from_succesful_calls() .PushData(1) .Op(Instruction.ADD) - .DataOnStackToMemory(0) - .PushData(0) - .PushData(0) - .PushData(32) - .PushData(0) - .PushData(0) - .PushData(TestItem.AddressD) - .PushData(50000) - .Op(Instruction.CALL) + .CallWithInput(TestItem.AddressD, 50000) .Op(Instruction.REVERT) @@ -509,8 +493,8 @@ public void revert_resets_transient_state_from_succesful_calls() .Done; TestState.CreateAccount(TestItem.AddressD, 1.Ether()); - Keccak selfDestructCodeHash = TestState.UpdateCode(contractCode); - TestState.UpdateCodeHash(TestItem.AddressD, selfDestructCodeHash, Spec); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); // Return the result received from the contract byte[] code = Prepare.EvmCode @@ -529,5 +513,262 @@ public void revert_resets_transient_state_from_succesful_calls() // Should be original TSTORE value Assert.AreEqual(8, (int)result.ReturnValue.ToUInt256()); } + + /// + /// Transient storage cannot be manipulated in a static context + /// + [TestCase(Instruction.CALL, 1)] + [TestCase(Instruction.STATICCALL, 0)] + public void tstore_in_staticcall(Instruction callType, int expectedResult) + { + byte[] contractCode = Prepare.EvmCode + .StoreDataInTransientStorage(1, 8) + .PushData(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); + + // Return the result received from the contract (1 if successful) + byte[] code = Prepare.EvmCode + .StoreDataInTransientStorage(1, 7) + .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) + + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Transient storage cannot be manipulated in a static context when calling self + /// + [TestCase(Instruction.CALL, 9)] + [TestCase(Instruction.STATICCALL, 8)] + public void tstore_from_static_reentrant_call(Instruction callType, int expectedResult) + { + // If caller is self, TSTORE 9 and break recursion + // Else, TSTORE 8 and call self, return the result of TLOAD + byte[] contractCode = Prepare.EvmCode + // Check if caller is self + .Op(Instruction.CALLER) + .PushData(TestItem.AddressD) + .Op(Instruction.EQ) + .PushData(113) + .Op(Instruction.JUMPI) + + // Non-reentrant, call self after TSTORE 8 + .StoreDataInTransientStorage(1, 8) + .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) + // Return the TLOAD value + // Should be 8 if call fails, 9 if success + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Reentrant, TSTORE 9 + .Op(Instruction.JUMPDEST) // PC = 113 + .StoreDataInTransientStorage(1, 9) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressD, 50000) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Transient storage cannot be manipulated in a nested static context + /// + [TestCase(Instruction.CALL, 10)] + [TestCase(Instruction.STATICCALL, 8)] + public void tstore_from_nonstatic_reentrant_call_with_static_intermediary(Instruction callType, int expectedResult) + { + // If caller is self, TLOAD and return value (break recursion) + // Else, TSTORE and call self, return the response + byte[] contractCode = Prepare.EvmCode + // Check call depth + .PushData(0) + .Op(Instruction.CALLDATALOAD) + // Store input in mem and reload it to stack + .DataOnStackToMemory(5) + .PushData(5) + .Op(Instruction.MLOAD) + + // See if we're at call depth 1 + .PushData(1) + .Op(Instruction.EQ) + .PushData(84) + .Op(Instruction.JUMPI) + + // See if we're at call depth 2 + .PushData(5) + .Op(Instruction.MLOAD) + .PushData(2) + .Op(Instruction.EQ) + .PushData(140) + .Op(Instruction.JUMPI) + + // Call depth = 0, call self after TSTORE 8 + .StoreDataInTransientStorage(1, 8) + + // Recursive call with input + // Depth++ + .PushData(5) + .Op(Instruction.MLOAD) + .PushData(1) + .Op(Instruction.ADD) + + .DynamicCallWithInput(callType, TestItem.AddressD, 50000) + + // TLOAD and return value + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Call depth 1, TSTORE 9 but REVERT after recursion + .Op(Instruction.JUMPDEST) // PC = 84 + + // Recursive call with input + // Depth++ + .PushData(5) + .Op(Instruction.MLOAD) + .PushData(1) + .Op(Instruction.ADD) + .CallWithInput(TestItem.AddressD, 50000) + + // TLOAD and return value + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Call depth 2, TSTORE 10 and complete + .Op(Instruction.JUMPDEST) // PC = 140 + .StoreDataInTransientStorage(1, 10) // This will fail + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .CallWithInput(TestItem.AddressD, 50000, new byte[32]) + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + // Should be original TSTORE value + Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Delegatecall manipulates transient storage in the context of the current address + /// + [TestCase(Instruction.CALL, 7)] + [TestCase(Instruction.DELEGATECALL, 8)] + public void tstore_in_delegatecall(Instruction callType, int expectedResult) + { + byte[] contractCode = Prepare.EvmCode + .StoreDataInTransientStorage(1, 8) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); + + byte[] code = Prepare.EvmCode + .StoreDataInTransientStorage(1, 7) + .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) + // TLOAD and return value + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); + } + + /// + /// Delegatecall reads transient storage in the context of the current address + /// + [TestCase(Instruction.CALL, 0)] + [TestCase(Instruction.DELEGATECALL, 7)] + public void tload_in_delegatecall(Instruction callType, int expectedResult) + { + byte[] contractCode = Prepare.EvmCode + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); + + byte[] code = Prepare.EvmCode + .StoreDataInTransientStorage(1, 7) + .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) + // Return response from nested call + .PushData(32) + .PushData(0) + .PushData(0) + .Op(Instruction.RETURNDATACOPY) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); + } } } diff --git a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs index eba90660932..0c22cd39577 100644 --- a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs +++ b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs @@ -115,12 +115,20 @@ public Prepare CallWithInput(Address address, long gasLimit, string input) return CallWithInput(address, gasLimit, Bytes.FromHexString(input)); } - public Prepare CallWithInput(Address address, long gasLimit, byte[] input) + public Prepare CallWithInput(Address address, long gasLimit, byte[] input = null) { - StoreDataInMemory(0, input); + if (input != null) + { + StoreDataInMemory(0, input); + } + else + { + // Use top of stack as input + DataOnStackToMemory(0); + } PushData(0); PushData(0); - PushData(input.Length); + PushData(input != null ? input.Length : 32); PushData(0); PushData(0); PushData(address); @@ -179,6 +187,34 @@ public Prepare StaticCall(Address address, long gasLimit) return this; } + public Prepare DynamicCallWithInput(Instruction callType, Address address, long gasLimit, byte[] input = null) + { + if (callType != Instruction.CALL && + callType != Instruction.STATICCALL && + callType != Instruction.DELEGATECALL) + { + throw new Exception($"Unexpected call type {callType}"); + } + if (input != null) + { + StoreDataInMemory(0, input); + } + else + { + // Use top of stack as input + DataOnStackToMemory(0); + } + PushData(0); + PushData(0); + PushData(input != null ? input.Length : 32); + PushData(0); + PushData(0); + PushData(address); + PushData(gasLimit); + Op(callType); + return this; + } + public Prepare PushData(Address address) { PushData(address.Bytes); From 7f76c3b1bcf19ed384b5261196fec92435e7519b Mon Sep 17 00:00:00 2001 From: codyborn Date: Tue, 7 Jun 2022 08:52:09 +0200 Subject: [PATCH 08/21] Add test for gas refund and cross-tx state --- .../Nethermind.Evm.Test/Eip1153Tests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index feba9866e87..1f1893d4c17 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -770,5 +770,57 @@ public void tload_in_delegatecall(Instruction callType, int expectedResult) Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); } + + [Test] + public void Tstore_does_not_result_in_gasrefund() + { + byte[] code = Prepare.EvmCode + .StoreDataInTransientStorage(1, 7) + .StoreDataInTransientStorage(1, 0) + .Done; + + TestAllTracerWithOutput receipt = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(GasCostOf.Transaction + GasCostOf.VeryLow * 4 + GasCostOf.TStore * 2, receipt.GasSpent, "gas"); + } + + /// + /// Transient storage does not persist beyond a single transaction + /// + [Test] + public void transient_state_not_persisted_across_txs() + { + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .LoadDataFromTransientStorage(1) + // See if we're at call depth 1 + .PushData(1) + .Op(Instruction.EQ) + .PushData(24) + .Op(Instruction.JUMPI) + + // TSTORE 1 and Return 1 + .StoreDataInTransientStorage(1, 1) + .PushData(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + + // Return 0 + .Op(Instruction.JUMPDEST) // PC = 24 + .PushData(0) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(1, (int)result.ReturnValue.ToUInt256()); + + // If transient state persisted across txs, calling again would return 0 + result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + Assert.AreEqual(1, (int)result.ReturnValue.ToUInt256()); + } } } From d8badf0741c55a2593888a4ba05bdc788b1c2bba Mon Sep 17 00:00:00 2001 From: codyborn Date: Tue, 7 Jun 2022 12:30:19 +0200 Subject: [PATCH 09/21] Misc cleanup --- .../Nethermind.Evm.Test/VirtualMachineTestsBase.cs | 8 -------- .../Nethermind.Specs/Forks/13_ArrowGlacier.cs | 10 +++++----- src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs | 10 +++++----- .../Nethermind.State/TransientStorageProvider.cs | 1 - 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 54fc4ba63f9..5b27f00a280 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -259,14 +259,6 @@ protected void AssertStorage(StorageCell storageCell, UInt256 expectedValue) } } - //protected void AssertTransientStorage(StorageCell storageCell, UInt256 expectedValue) - //{ - // _callIndex++; - - // byte[] actualValue = Storage.GetTransientState(storageCell); - // Assert.AreEqual(expectedValue.ToBigEndian().WithoutLeadingZeros().ToArray(), actualValue, $"storage {storageCell}, call {_callIndex}"); - //} - protected void AssertCodeHash(Address address, Keccak codeHash) { Assert.AreEqual(codeHash, TestState.GetCodeHash(address), "code hash"); diff --git a/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs b/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs index 27982049251..ce8c8568534 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/13_ArrowGlacier.cs @@ -22,17 +22,17 @@ namespace Nethermind.Specs.Forks { - public class Shanghai : IReleaseSpec + public class ArrowGlacier : IReleaseSpec { private static IReleaseSpec _instance; - private Shanghai() + private ArrowGlacier() { } - public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new Shanghai()); + public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new ArrowGlacier()); - public string Name => "Shanghai"; + public string Name => "ArrowGlacier"; public long MaximumExtraDataSize => 32; public long MaxCodeSize => 24576; public long MinGasLimit => 5000; @@ -83,6 +83,6 @@ private Shanghai() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => 12965000; - public bool IsEip1153Enabled => true; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs b/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs index ce8c8568534..27982049251 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs @@ -22,17 +22,17 @@ namespace Nethermind.Specs.Forks { - public class ArrowGlacier : IReleaseSpec + public class Shanghai : IReleaseSpec { private static IReleaseSpec _instance; - private ArrowGlacier() + private Shanghai() { } - public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new ArrowGlacier()); + public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new Shanghai()); - public string Name => "ArrowGlacier"; + public string Name => "Shanghai"; public long MaximumExtraDataSize => 32; public long MaxCodeSize => 24576; public long MinGasLimit => 5000; @@ -83,6 +83,6 @@ private ArrowGlacier() public bool IsEip3607Enabled => true; public bool IsEip3675Enabled => false; public long Eip1559TransitionBlock => 12965000; - public bool IsEip1153Enabled => false; + public bool IsEip1153Enabled => true; } } diff --git a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs index 6b277be031f..dfcc247f549 100644 --- a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs @@ -21,7 +21,6 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Resettables; using Nethermind.Logging; -using Nethermind.Trie.Pruning; namespace Nethermind.State { From 1bd6aca52c12613ead584202cccbe952538a68fd Mon Sep 17 00:00:00 2001 From: codyborn Date: Tue, 7 Jun 2022 23:56:28 +0200 Subject: [PATCH 10/21] Refactor and create common abstraction for Storage Providrs --- .../PartialStorageProviderBase.cs | 254 +++++++++++++++++ .../PersistentStorageProvider.cs | 266 ++---------------- .../TransientStorageProvider.cs | 229 +-------------- 3 files changed, 292 insertions(+), 457 deletions(-) create mode 100644 src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs diff --git a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs new file mode 100644 index 00000000000..2b450fec83f --- /dev/null +++ b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs @@ -0,0 +1,254 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . + +using System; +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Resettables; +using Nethermind.Logging; +using Nethermind.Trie.Pruning; + +namespace Nethermind.State +{ + public abstract class PartialStorageProviderBase : IPartialStorageProvider + { + protected readonly ResettableDictionary> _intraBlockCache = new(); + + /// + /// EIP-1283 + /// + protected readonly ResettableDictionary _originalValues = new(); + + protected readonly ResettableHashSet _committedThisRound = new(); + + protected readonly ILogger _logger; + protected readonly ILogManager _logManager; + + protected readonly ResettableDictionary _storages = new(); + + protected const int StartCapacity = Resettable.StartCapacity; + protected int _capacity = StartCapacity; + protected Change?[] _changes = new Change[StartCapacity]; + protected int _currentPosition = Resettable.EmptyPosition; + + // stack of snapshot indexes on changes for start of each transaction + // this is needed for OriginalValues for new transactions + protected readonly Stack _transactionChangesSnapshots = new(); + + public PartialStorageProviderBase(ILogManager? logManager) + { + _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); + _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); + } + + public byte[] GetOriginal(StorageCell storageCell) + { + if (!_originalValues.ContainsKey(storageCell)) + { + throw new InvalidOperationException("Get original should only be called after get within the same caching round"); + } + + if (_transactionChangesSnapshots.TryPeek(out int snapshot)) + { + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) + { + if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) + { + return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; + } + } + } + + return _originalValues[storageCell]; + } + + public byte[] Get(StorageCell storageCell) + { + return GetCurrentValue(storageCell); + } + + public void Set(StorageCell storageCell, byte[] newValue) + { + PushUpdate(storageCell, newValue); + } + + int IPartialStorageProvider.TakeSnapshot(bool newTransactionStart) + { + if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); + if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) + { + _transactionChangesSnapshots.Push(_currentPosition); + } + + return _currentPosition; + } + + public void Restore(int snapshot) + { + if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); + + if (snapshot > _currentPosition) + { + throw new InvalidOperationException($"{nameof(PartialStorageProviderBase)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); + } + + if (snapshot == _currentPosition) + { + return; + } + + List keptInCache = new(); + + for (int i = 0; i < _currentPosition - snapshot; i++) + { + Change change = _changes[_currentPosition - i]; + if (_intraBlockCache[change!.StorageCell].Count == 1) + { + if (_changes[_intraBlockCache[change.StorageCell].Peek()]!.ChangeType == ChangeType.JustCache) + { + int actualPosition = _intraBlockCache[change.StorageCell].Pop(); + if (actualPosition != _currentPosition - i) + { + throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {_currentPosition} - {i}"); + } + + keptInCache.Add(change); + _changes[actualPosition] = null; + continue; + } + } + + int forAssertion = _intraBlockCache[change.StorageCell].Pop(); + if (forAssertion != _currentPosition - i) + { + throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); + } + + _changes[_currentPosition - i] = null; + + if (_intraBlockCache[change.StorageCell].Count == 0) + { + _intraBlockCache.Remove(change.StorageCell); + } + } + + _currentPosition = snapshot; + foreach (Change kept in keptInCache) + { + _currentPosition++; + _changes[_currentPosition] = kept; + _intraBlockCache[kept.StorageCell].Push(_currentPosition); + } + + while (_transactionChangesSnapshots.TryPeek(out int lastOriginalSnapshot) && lastOriginalSnapshot > snapshot) + { + _transactionChangesSnapshots.Pop(); + } + + } + + public void Commit() + { + Commit(NullStorageTracer.Instance); + } + + protected static readonly byte[] _zeroValue = {0}; + + protected readonly struct ChangeTrace + { + public ChangeTrace(byte[]? before, byte[]? after) + { + After = after ?? _zeroValue; + Before = before ?? _zeroValue; + } + + public ChangeTrace(byte[]? after) + { + After = after ?? _zeroValue; + Before = _zeroValue; + } + + public byte[] Before { get; } + public byte[] After { get; } + } + + public abstract void Commit(IStorageTracer tracer); + + public void Reset() + { + if (_logger.IsTrace) _logger.Trace("Resetting storage"); + + _intraBlockCache.Clear(); + _originalValues.Clear(); + _transactionChangesSnapshots.Clear(); + _currentPosition = -1; + _committedThisRound.Clear(); + Array.Clear(_changes, 0, _changes.Length); + _storages.Reset(); + } + + public abstract void CommitTrees(long blockNumber); + + protected abstract byte[] GetCurrentValue(StorageCell storageCell); + + private void PushUpdate(StorageCell cell, byte[] value) + { + SetupRegistry(cell); + IncrementChangePosition(); + _intraBlockCache[cell].Push(_currentPosition); + _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); + } + + protected void IncrementChangePosition() + { + Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); + } + + protected void SetupRegistry(StorageCell cell) + { + if (!_intraBlockCache.ContainsKey(cell)) + { + _intraBlockCache[cell] = new StackList(); + } + } + + public abstract void ClearStorage(Address address); + + protected class Change + { + public Change(ChangeType changeType, StorageCell storageCell, byte[] value) + { + StorageCell = storageCell; + Value = value; + ChangeType = changeType; + } + + public ChangeType ChangeType { get; } + public StorageCell StorageCell { get; } + public byte[] Value { get; } + } + + protected enum ChangeType + { + JustCache, + Update, + Destroy, + } + } +} diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index 55516737110..e68b3dfb902 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -26,182 +26,31 @@ namespace Nethermind.State { - public class PersistentStorageProvider : IPartialStorageProvider + public class PersistentStorageProvider : PartialStorageProviderBase, IPartialStorageProvider { - private readonly ResettableDictionary> _intraBlockCache = new(); - /// - /// EIP-1283 - /// - private readonly ResettableDictionary _originalValues = new(); - - private readonly ResettableHashSet _committedThisRound = new(); - - private readonly ILogger _logger; - - private readonly ITrieStore _trieStore; - - private readonly IStateProvider _stateProvider; - private readonly ILogManager _logManager; - - private readonly ResettableDictionary _storages = new(); - - private const int StartCapacity = Resettable.StartCapacity; - private int _capacity = StartCapacity; - private Change?[] _changes = new Change[StartCapacity]; - private int _currentPosition = Resettable.EmptyPosition; - - // stack of snapshot indexes on changes for start of each transaction - // this is needed for OriginalValues for new transactions - private readonly Stack _transactionChangesSnapshots = new(); + protected readonly ITrieStore _trieStore; + protected readonly IStateProvider _stateProvider; public PersistentStorageProvider(ITrieStore? trieStore, IStateProvider? stateProvider, ILogManager? logManager) + : base(logManager) { - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _trieStore = trieStore ?? throw new ArgumentNullException(nameof(trieStore)); _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); } - public byte[] GetOriginal(StorageCell storageCell) - { - if (!_originalValues.ContainsKey(storageCell)) - { - throw new InvalidOperationException("Get original should only be called after get within the same caching round"); - } - - if (_transactionChangesSnapshots.TryPeek(out int snapshot)) - { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) - { - return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; - } - } - } - - return _originalValues[storageCell]; - } - - public byte[] Get(StorageCell storageCell) - { - return GetCurrentValue(storageCell); - } - - public void Set(StorageCell storageCell, byte[] newValue) - { - PushUpdate(storageCell, newValue); - } - - private Keccak RecalculateRootHash(Address address) - { - StorageTree storageTree = GetOrCreateStorage(address); - storageTree.UpdateRootHash(); - return storageTree.RootHash; - } - - int IPartialStorageProvider.TakeSnapshot(bool newTransactionStart) - { - if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); - if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) - { - _transactionChangesSnapshots.Push(_currentPosition); - } - - return _currentPosition; - } - - public void Restore(int snapshot) + protected override byte[] GetCurrentValue(StorageCell storageCell) { - if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); - - if (snapshot > _currentPosition) - { - throw new InvalidOperationException($"{nameof(PersistentStorageProvider)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); - } - - if (snapshot == _currentPosition) - { - return; - } - - List keptInCache = new(); - - for (int i = 0; i < _currentPosition - snapshot; i++) - { - Change change = _changes[_currentPosition - i]; - if (_intraBlockCache[change!.StorageCell].Count == 1) - { - if (_changes[_intraBlockCache[change.StorageCell].Peek()]!.ChangeType == ChangeType.JustCache) - { - int actualPosition = _intraBlockCache[change.StorageCell].Pop(); - if (actualPosition != _currentPosition - i) - { - throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {_currentPosition} - {i}"); - } - - keptInCache.Add(change); - _changes[actualPosition] = null; - continue; - } - } - - int forAssertion = _intraBlockCache[change.StorageCell].Pop(); - if (forAssertion != _currentPosition - i) - { - throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); - } - - _changes[_currentPosition - i] = null; - - if (_intraBlockCache[change.StorageCell].Count == 0) - { - _intraBlockCache.Remove(change.StorageCell); - } - } - - _currentPosition = snapshot; - foreach (Change kept in keptInCache) - { - _currentPosition++; - _changes[_currentPosition] = kept; - _intraBlockCache[kept.StorageCell].Push(_currentPosition); - } - - while (_transactionChangesSnapshots.TryPeek(out int lastOriginalSnapshot) && lastOriginalSnapshot > snapshot) - { - _transactionChangesSnapshots.Pop(); - } - - } - - public void Commit() - { - Commit(NullStorageTracer.Instance); - } - - private static readonly byte[] _zeroValue = {0}; - - private readonly struct ChangeTrace - { - public ChangeTrace(byte[]? before, byte[]? after) - { - After = after ?? _zeroValue; - Before = before ?? _zeroValue; - } - - public ChangeTrace(byte[]? after) + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) { - After = after ?? _zeroValue; - Before = _zeroValue; + int lastChangeIndex = stack.Peek(); + return _changes[lastChangeIndex]!.Value; } - public byte[] Before { get; } - public byte[] After { get; } + return LoadFromTree(storageCell); } - public void Commit(IStorageTracer tracer) + public override void Commit(IStorageTracer tracer) { if (_currentPosition == -1) { @@ -213,12 +62,12 @@ public void Commit(IStorageTracer tracer) if (_changes[_currentPosition] is null) { - throw new InvalidOperationException($"Change at current position {_currentPosition} was null when commiting {nameof(PersistentStorageProvider)}"); + throw new InvalidOperationException($"Change at current position {_currentPosition} was null when commiting {nameof(PartialStorageProviderBase)}"); } if (_changes[_currentPosition + 1] != null) { - throw new InvalidOperationException($"Change after current position ({_currentPosition} + 1) was not null when commiting {nameof(PersistentStorageProvider)}"); + throw new InvalidOperationException($"Change after current position ({_currentPosition} + 1) was not null when commiting {nameof(PartialStorageProviderBase)}"); } HashSet
toUpdateRoots = new(); @@ -300,7 +149,7 @@ public void Commit(IStorageTracer tracer) if (_stateProvider.AccountExists(address)) { Keccak root = RecalculateRootHash(address); - + // _logger.Warn($"Recalculating storage root {address}->{root} ({toUpdateRoots.Count})"); _stateProvider.UpdateStorageRoot(address, root); } @@ -318,34 +167,7 @@ public void Commit(IStorageTracer tracer) } } - private static void ReportChanges(IStorageTracer tracer, Dictionary trace) - { - foreach ((StorageCell address, ChangeTrace change) in trace) - { - byte[] before = change.Before; - byte[] after = change.After; - - if (!Bytes.AreEqual(before, after)) - { - tracer.ReportStorageChange(address, before, after); - } - } - } - - public void Reset() - { - if (_logger.IsTrace) _logger.Trace("Resetting storage"); - - _intraBlockCache.Clear(); - _originalValues.Clear(); - _transactionChangesSnapshots.Clear(); - _currentPosition = -1; - _committedThisRound.Clear(); - Array.Clear(_changes, 0, _changes.Length); - _storages.Reset(); - } - - public void CommitTrees(long blockNumber) + public override void CommitTrees(long blockNumber) { // _logger.Warn($"Storage block commit {blockNumber}"); foreach (KeyValuePair storage in _storages) @@ -370,17 +192,6 @@ private StorageTree GetOrCreateStorage(Address address) return _storages[address]; } - private byte[] GetCurrentValue(StorageCell storageCell) - { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - int lastChangeIndex = stack.Peek(); - return _changes[lastChangeIndex]!.Value; - } - - return LoadFromTree(storageCell); - } - private byte[] LoadFromTree(StorageCell storageCell) { StorageTree tree = GetOrCreateStorage(storageCell.Address); @@ -400,42 +211,28 @@ private void PushToRegistryOnly(StorageCell cell, byte[] value) _changes[_currentPosition] = new Change(ChangeType.JustCache, cell, value); } - private void PushUpdate(StorageCell cell, byte[] value) - { - SetupRegistry(cell); - IncrementChangePosition(); - _intraBlockCache[cell].Push(_currentPosition); - _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); - } - - private void IncrementChangePosition() - { - Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); - } - - private void SetupRegistry(StorageCell cell) + private static void ReportChanges(IStorageTracer tracer, Dictionary trace) { - if (!_intraBlockCache.ContainsKey(cell)) + foreach ((StorageCell address, ChangeTrace change) in trace) { - _intraBlockCache[cell] = new StackList(); + byte[] before = change.Before; + byte[] after = change.After; + + if (!Bytes.AreEqual(before, after)) + { + tracer.ReportStorageChange(address, before, after); + } } } - private class Change + private Keccak RecalculateRootHash(Address address) { - public Change(ChangeType changeType, StorageCell storageCell, byte[] value) - { - StorageCell = storageCell; - Value = value; - ChangeType = changeType; - } - - public ChangeType ChangeType { get; } - public StorageCell StorageCell { get; } - public byte[] Value { get; } + StorageTree storageTree = GetOrCreateStorage(address); + storageTree.UpdateRootHash(); + return storageTree.RootHash; } - public void ClearStorage(Address address) + public override void ClearStorage(Address address) { /* we are setting cached values to zero so we do not use previously set values when the contract is revived with CREATE2 inside the same block */ @@ -453,12 +250,5 @@ by means of CREATE 2 - notice that the cached trie may carry information about i // TODO: how does it work with pruning? _storages[address] = new StorageTree(_trieStore, Keccak.EmptyTreeHash, _logManager); } - - private enum ChangeType - { - JustCache, - Update, - Destroy, - } } } diff --git a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs index dfcc247f549..da309ce24fe 100644 --- a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using Nethermind.Core; using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Resettables; using Nethermind.Logging; @@ -28,146 +29,18 @@ namespace Nethermind.State /// EIP-1153 provides a transient store for contracts that doesn't persist /// storage across calls. Reverts will rollback any transient state changes. /// - public class TransientStorageProvider : IPartialStorageProvider + public class TransientStorageProvider : PartialStorageProviderBase, IPartialStorageProvider { - private readonly ResettableDictionary> _intraBlockCache = new(); - - /// - /// EIP-1283 - /// - private readonly ResettableDictionary _originalValues = new(); - - private readonly ResettableHashSet _committedThisRound = new(); - - private readonly ILogger _logger; - private readonly ILogManager _logManager; - - private readonly ResettableDictionary _storages = new(); - - private const int StartCapacity = Resettable.StartCapacity; - private int _capacity = StartCapacity; - private Change?[] _changes = new Change[StartCapacity]; - private int _currentPosition = Resettable.EmptyPosition; - - // stack of snapshot indexes on changes for start of each transaction - // this is needed for OriginalValues for new transactions - private readonly Stack _transactionChangesSnapshots = new(); - public TransientStorageProvider(ILogManager? logManager) - { - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - } + : base(logManager) { } - public byte[] GetOriginal(StorageCell storageCell) + private Keccak RecalculateRootHash(Address address) { - if (!_originalValues.ContainsKey(storageCell)) - { - throw new InvalidOperationException("Get original should only be called after get within the same caching round"); - } - - if (_transactionChangesSnapshots.TryPeek(out int snapshot)) - { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) - { - return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; - } - } - } - - return _originalValues[storageCell]; + throw new NotImplementedException(); } - - public byte[] Get(StorageCell storageCell) + public override void CommitTrees(long blockNumber) { - return GetCurrentValue(storageCell); - } - - public void Set(StorageCell storageCell, byte[] newValue) - { - PushUpdate(storageCell, newValue); - } - - int IPartialStorageProvider.TakeSnapshot(bool newTransactionStart) - { - if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); - if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) - { - _transactionChangesSnapshots.Push(_currentPosition); - } - return _currentPosition; - } - - /// - /// Restore transient state to the snapshot index - /// - /// - /// - public void Restore(int snapshot) - { - if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); - - if (snapshot > _currentPosition) - { - throw new InvalidOperationException($"{nameof(TransientStorageProvider)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); - } - - if (snapshot == _currentPosition) - { - return; - } - - for (int i = 0; i < _currentPosition - snapshot; i++) - { - Change change = _changes[_currentPosition - i]; - - int forAssertion = _intraBlockCache[change.StorageCell].Pop(); - if (forAssertion != _currentPosition - i) - { - throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); - } - - _changes[_currentPosition - i] = null; - - if (_intraBlockCache[change.StorageCell].Count == 0) - { - _intraBlockCache.Remove(change.StorageCell); - } - } - - _currentPosition = snapshot; - - while (_transactionChangesSnapshots.TryPeek(out int lastOriginalSnapshot) && lastOriginalSnapshot > snapshot) - { - _transactionChangesSnapshots.Pop(); - } - } - - public void Commit() - { - Commit(NullStorageTracer.Instance); - } - - private static readonly byte[] _zeroValue = {0}; - - private readonly struct ChangeTrace - { - public ChangeTrace(byte[]? before, byte[]? after) - { - After = after ?? _zeroValue; - Before = before ?? _zeroValue; - } - - public ChangeTrace(byte[]? after) - { - After = after ?? _zeroValue; - Before = _zeroValue; - } - - public byte[] Before { get; } - public byte[] After { get; } + throw new NotImplementedException(); } /// @@ -176,7 +49,7 @@ public ChangeTrace(byte[]? after) /// /// /// - public void Commit(IStorageTracer tracer) + public override void Commit(IStorageTracer tracer) { if (_currentPosition == -1) { @@ -193,48 +66,7 @@ public void Commit(IStorageTracer tracer) _transactionChangesSnapshots.Clear(); } - private static void ReportChanges(IStorageTracer tracer, Dictionary trace) - { - foreach ((StorageCell address, ChangeTrace change) in trace) - { - byte[] before = change.Before; - byte[] after = change.After; - - if (!Bytes.AreEqual(before, after)) - { - tracer.ReportStorageChange(address, before, after); - } - } - } - - public void Reset() - { - if (_logger.IsTrace) _logger.Trace("Resetting storage"); - - _intraBlockCache.Clear(); - _originalValues.Clear(); - _transactionChangesSnapshots.Clear(); - _currentPosition = -1; - _committedThisRound.Clear(); - Array.Clear(_changes, 0, _changes.Length); - _storages.Reset(); - } - - public void CommitTrees(long blockNumber) - { - // _logger.Warn($"Storage block commit {blockNumber}"); - foreach (KeyValuePair storage in _storages) - { - storage.Value.Commit(blockNumber); - } - - // TODO: maybe I could update storage roots only now? - - // only needed here as there is no control over cached storage size otherwise - _storages.Reset(); - } - - private byte[] GetCurrentValue(StorageCell storageCell) + protected override byte[] GetCurrentValue(StorageCell storageCell) { if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) { @@ -245,43 +77,7 @@ private byte[] GetCurrentValue(StorageCell storageCell) return _zeroValue; } - - private void PushUpdate(StorageCell cell, byte[] value) - { - SetupRegistry(cell); - IncrementChangePosition(); - _intraBlockCache[cell].Push(_currentPosition); - _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); - } - - private void IncrementChangePosition() - { - Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); - } - - private void SetupRegistry(StorageCell cell) - { - if (!_intraBlockCache.ContainsKey(cell)) - { - _intraBlockCache[cell] = new StackList(); - } - } - - private class Change - { - public Change(ChangeType changeType, StorageCell storageCell, byte[] value) - { - StorageCell = storageCell; - Value = value; - ChangeType = changeType; - } - - public ChangeType ChangeType { get; } - public StorageCell StorageCell { get; } - public byte[] Value { get; } - } - - public void ClearStorage(Address address) + public override void ClearStorage(Address address) { /* we are setting cached values to zero so we do not use previously set values when the contract is revived with CREATE2 inside the same block */ @@ -293,10 +89,5 @@ when the contract is revived with CREATE2 inside the same block */ } } } - - private enum ChangeType - { - Update, - } } } From aed0f283b0ecc3410c4db0909f76bec8fefdeecb Mon Sep 17 00:00:00 2001 From: codyborn Date: Wed, 8 Jun 2022 07:42:37 +0200 Subject: [PATCH 11/21] Minor tweaks --- src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs | 8 +++++++- .../Nethermind.State.Test/StorageProviderTests.cs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index 1f1893d4c17..74610888068 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -45,11 +45,17 @@ public void before_shanghai_can_not_call_tstore_tload() { byte[] code = Prepare.EvmCode .StoreDataInTransientStorage(1, 8) - .LoadDataFromTransientStorage(1) .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber - 1, 100000, code); Assert.AreEqual(StatusCode.Failure, result.StatusCode); + + code = Prepare.EvmCode + .LoadDataFromTransientStorage(1) + .Done; + + result = Execute(MainnetSpecProvider.ShanghaiBlockNumber - 1, 100000, code); + Assert.AreEqual(StatusCode.Failure, result.StatusCode); } [Test] diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index da5865860c1..8061fa36917 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -317,7 +317,7 @@ public void Reset_resets_transient_state() [TestCase(0)] [TestCase(1)] [TestCase(2)] - public void Transient_state_restores_independed_of_permanent_state(int snapshot) + public void Transient_state_restores_independent_of_permanent_state(int snapshot) { Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); @@ -361,7 +361,7 @@ public void Transient_state_restores_independed_of_permanent_state(int snapshot) [TestCase(0)] [TestCase(1)] [TestCase(2)] - public void Permanent_state_restores_independed_of_transient_state(int snapshot) + public void Permanent_state_restores_independent_of_transient_state(int snapshot) { Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); From dc0eeecf4e6b5a51e59ec1dcf1de454368f7cdd0 Mon Sep 17 00:00:00 2001 From: codyborn Date: Wed, 8 Jun 2022 08:04:45 +0200 Subject: [PATCH 12/21] Add test case and simplify common pattern --- .../Nethermind.Evm.Test/Eip1153Tests.cs | 154 +++++++----------- .../Nethermind.Evm/ByteCodeBuilder.cs | 12 ++ 2 files changed, 73 insertions(+), 93 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index 74610888068..7e0c3839428 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -135,13 +135,7 @@ public void tload_from_different_contract() byte[] code = Prepare.EvmCode .StoreDataInTransientStorage(1, 8) .Call(TestItem.AddressD, 50000) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -170,13 +164,7 @@ public void tload_from_reentrant_call() .StoreDataInTransientStorage(1, 8) .Call(TestItem.AddressD, 50000) // Return the response - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() // Reentrant, TLOAD and return value .Op(Instruction.JUMPDEST) @@ -194,13 +182,7 @@ public void tload_from_reentrant_call() // Return the result received from the contract byte[] code = Prepare.EvmCode .Call(TestItem.AddressD, 50000) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -228,13 +210,7 @@ public void tstore_from_reentrant_call() .StoreDataInTransientStorage(1, 8) .Call(TestItem.AddressD, 50000) // Return the response - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() // Reentrant, TLOAD and return value .Op(Instruction.JUMPDEST) // PC = 78 @@ -253,13 +229,7 @@ public void tstore_from_reentrant_call() // Return the result received from the contract byte[] code = Prepare.EvmCode .Call(TestItem.AddressD, 50000) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -305,13 +275,7 @@ public void tstore_from_reentrant_call_read_by_caller() // Return the result received from the contract byte[] code = Prepare.EvmCode .Call(TestItem.AddressD, 50000) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -358,13 +322,7 @@ public void revert_resets_transient_state() // Return the result received from the contract byte[] code = Prepare.EvmCode .Call(TestItem.AddressD, 50000) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -413,13 +371,7 @@ public void revert_resets_all_transient_state() // Return the result received from the contract byte[] code = Prepare.EvmCode .Call(TestItem.AddressD, 50000) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -505,13 +457,7 @@ public void revert_resets_transient_state_from_succesful_calls() // Return the result received from the contract byte[] code = Prepare.EvmCode .CallWithInput(TestItem.AddressD, 50000, new byte[32]) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -543,15 +489,8 @@ public void tstore_in_staticcall(Instruction callType, int expectedResult) // Return the result received from the contract (1 if successful) byte[] code = Prepare.EvmCode .StoreDataInTransientStorage(1, 7) - .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) - - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -599,13 +538,7 @@ public void tstore_from_static_reentrant_call(Instruction callType, int expected // Return the result received from the contract byte[] code = Prepare.EvmCode .Call(TestItem.AddressD, 50000) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -694,13 +627,7 @@ public void tstore_from_nonstatic_reentrant_call_with_static_intermediary(Instru // Return the result received from the contract byte[] code = Prepare.EvmCode .CallWithInput(TestItem.AddressD, 50000, new byte[32]) - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -763,13 +690,7 @@ public void tload_in_delegatecall(Instruction callType, int expectedResult) .StoreDataInTransientStorage(1, 7) .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) // Return response from nested call - .PushData(32) - .PushData(0) - .PushData(0) - .Op(Instruction.RETURNDATACOPY) - .PushData(32) - .PushData(0) - .Op(Instruction.RETURN) + .ReturnInnerCallResult() .Done; TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); @@ -828,5 +749,52 @@ public void transient_state_not_persisted_across_txs() result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); Assert.AreEqual(1, (int)result.ReturnValue.ToUInt256()); } + + + /// + /// Transient storage can be accessed in a static context when calling self + /// + [TestCase(Instruction.CALL, 8)] + [TestCase(Instruction.STATICCALL, 8)] + public void tload_from_static_reentrant_call(Instruction callType, int expectedResult) + { + // If caller is self, TLOAD and break recursion + // Else, TSTORE 8 and call self, return the result of the inner call + byte[] contractCode = Prepare.EvmCode + // Check if caller is self + .Op(Instruction.CALLER) + .PushData(TestItem.AddressD) + .Op(Instruction.EQ) + .PushData(114) + .Op(Instruction.JUMPI) + + // Non-reentrant, call self after TSTORE 8 + .StoreDataInTransientStorage(1, 8) + .DynamicCallWithInput(callType, TestItem.AddressD, 50000, new byte[32]) + .ReturnInnerCallResult() + + // Reentrant, TLOAD and return + .Op(Instruction.JUMPDEST) // PC = 114 + .LoadDataFromTransientStorage(1) + .DataOnStackToMemory(0) + .PushData(32) + .PushData(0) + .Op(Instruction.RETURN) + .Done; + + TestState.CreateAccount(TestItem.AddressD, 1.Ether()); + Keccak contractCodeHash = TestState.UpdateCode(contractCode); + TestState.UpdateCodeHash(TestItem.AddressD, contractCodeHash, Spec); + + // Return the result received from the contract + byte[] code = Prepare.EvmCode + .Call(TestItem.AddressD, 50000) + .ReturnInnerCallResult() + .Done; + + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, 100000, code); + + Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); + } } } diff --git a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs index 0c22cd39577..cc9cac2609c 100644 --- a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs +++ b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs @@ -337,5 +337,17 @@ public Prepare Return(int size, int position) Op(Instruction.RETURN); return this; } + + public Prepare ReturnInnerCallResult() + { + PushData(32); + PushData(0); + PushData(0); + Op(Instruction.RETURNDATACOPY); + PushData(32); + PushData(0); + Op(Instruction.RETURN); + return this; + } } } From 86a525033d7ab240af7ac75cc64574554547d1c0 Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 20 Jun 2022 12:03:34 +0200 Subject: [PATCH 13/21] Add performance test --- .../Nethermind.Evm.Test/CoinbaseTests.cs | 6 ++-- .../Nethermind.Evm.Test/Eip1153Tests.cs | 30 ++++++++++++++++++- .../Nethermind.Evm.Test/Sha3Tests.cs | 6 ++-- .../VirtualMachineTestsBase.cs | 14 +++++---- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/CoinbaseTests.cs b/src/Nethermind/Nethermind.Evm.Test/CoinbaseTests.cs index c8eb7b2321d..e937b0b5d43 100644 --- a/src/Nethermind/Nethermind.Evm.Test/CoinbaseTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/CoinbaseTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -28,10 +28,10 @@ public class CoinbaseTests : VirtualMachineTestsBase private bool _setAuthor; - protected override Block BuildBlock(long blockNumber, SenderRecipientAndMiner senderRecipientAndMiner, Transaction transaction) + protected override Block BuildBlock(long blockNumber, SenderRecipientAndMiner senderRecipientAndMiner, Transaction transaction, long blockGasLimit = DefaultBlockGasLimit) { senderRecipientAndMiner ??= new SenderRecipientAndMiner(); - Block block = base.BuildBlock(blockNumber, senderRecipientAndMiner, transaction); + Block block = base.BuildBlock(blockNumber, senderRecipientAndMiner, transaction, blockGasLimit); if(_setAuthor) block.Header.Author = TestItem.AddressC; block.Header.Beneficiary = senderRecipientAndMiner.Recipient; return block; diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index 7e0c3839428..7935b62bc36 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -20,6 +20,7 @@ using Nethermind.Specs; using Nethermind.Core.Test.Builders; using NUnit.Framework; +using System.Diagnostics; namespace Nethermind.Evm.Test { @@ -74,6 +75,33 @@ public void tload_uninitialized_returns_zero() Assert.AreEqual(0, (int)result.ReturnValue.ToUInt256()); } + /// + /// Simple performance test + /// + [Ignore("Depends on hardware")] + [Test] + public void transient_storage_performance_test() + { + Stopwatch stopwatch = new Stopwatch(); + long blockGasLimit = 30000000; + long numOfOps = (long)(blockGasLimit * .95) / (GasCostOf.TLoad + GasCostOf.TStore + GasCostOf.VeryLow * 4); + Prepare prepare = Prepare.EvmCode; + for (long i = 0; i < numOfOps; i++) + { + prepare.StoreDataInTransientStorage(1, 8); + prepare.LoadDataFromTransientStorage(1); + prepare.Op(Instruction.POP); + } + + byte[] code = prepare.Done; + + stopwatch.Start(); + TestAllTracerWithOutput result = Execute(MainnetSpecProvider.ShanghaiBlockNumber, blockGasLimit, code, blockGasLimit); + Assert.AreEqual(StatusCode.Success, result.StatusCode); + stopwatch.Stop(); + Assert.IsTrue(stopwatch.ElapsedMilliseconds < 5000); + } + [Test] public void tload_after_tstore() { @@ -699,7 +727,7 @@ public void tload_in_delegatecall(Instruction callType, int expectedResult) } [Test] - public void Tstore_does_not_result_in_gasrefund() + public void tstore_does_not_result_in_gasrefund() { byte[] code = Prepare.EvmCode .StoreDataInTransientStorage(1, 7) diff --git a/src/Nethermind/Nethermind.Evm.Test/Sha3Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Sha3Tests.cs index 1b78f3b639c..2cb2500b901 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Sha3Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Sha3Tests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -29,9 +29,9 @@ public class Sha3Tests : VirtualMachineTestsBase private bool _setAuthor; - protected override Block BuildBlock(long blockNumber, SenderRecipientAndMiner senderRecipientAndMiner, Transaction transaction) + protected override Block BuildBlock(long blockNumber, SenderRecipientAndMiner senderRecipientAndMiner, Transaction transaction, long blockGasLimit = DefaultBlockGasLimit) { - Block block = base.BuildBlock(blockNumber, senderRecipientAndMiner, transaction); + Block block = base.BuildBlock(blockNumber, senderRecipientAndMiner, transaction, blockGasLimit); if(_setAuthor) block.Header.Author = TestItem.AddressC; block.Header.Beneficiary = TestItem.AddressB; return block; diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 5b27f00a280..60761ab726f 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -40,6 +40,7 @@ public class VirtualMachineTestsBase protected const string SampleHexData1 = "a01234"; protected const string SampleHexData2 = "b15678"; protected const string HexZero = "00"; + protected const long DefaultBlockGasLimit = 8000000; private IEthereumEcdsa _ethereumEcdsa; protected ITransactionProcessor _processor; @@ -116,9 +117,9 @@ protected T Execute(T tracer, params byte[] code) where T : ITxTracer return tracer; } - protected TestAllTracerWithOutput Execute(long blockNumber, long gasLimit, byte[] code) + protected TestAllTracerWithOutput Execute(long blockNumber, long gasLimit, byte[] code, long blockGasLimit = DefaultBlockGasLimit) { - (Block block, Transaction transaction) = PrepareTx(blockNumber, gasLimit, code); + (Block block, Transaction transaction) = PrepareTx(blockNumber, gasLimit, code, blockGasLimit: blockGasLimit); TestAllTracerWithOutput tracer = CreateTracer(); _processor.Execute(transaction, block.Header, tracer); return tracer; @@ -129,7 +130,8 @@ protected TestAllTracerWithOutput Execute(long blockNumber, long gasLimit, byte[ long gasLimit, byte[] code, SenderRecipientAndMiner senderRecipientAndMiner = null, - int value = 1) + int value = 1, + long blockGasLimit = DefaultBlockGasLimit) { senderRecipientAndMiner ??= SenderRecipientAndMiner.Default; TestState.CreateAccount(senderRecipientAndMiner.Sender, 100.Ether()); @@ -152,7 +154,7 @@ protected TestAllTracerWithOutput Execute(long blockNumber, long gasLimit, byte[ .SignedAndResolved(_ethereumEcdsa, senderRecipientAndMiner.SenderKey) .TestObject; - Block block = BuildBlock(blockNumber, senderRecipientAndMiner, transaction); + Block block = BuildBlock(blockNumber, senderRecipientAndMiner, transaction, blockGasLimit); return (block, transaction); } @@ -202,10 +204,10 @@ protected Block BuildBlock(long blockNumber, SenderRecipientAndMiner senderRecip return BuildBlock(blockNumber, senderRecipientAndMiner, null); } - protected virtual Block BuildBlock(long blockNumber, SenderRecipientAndMiner senderRecipientAndMiner, Transaction tx) + protected virtual Block BuildBlock(long blockNumber, SenderRecipientAndMiner senderRecipientAndMiner, Transaction tx, long blockGasLimit = DefaultBlockGasLimit) { senderRecipientAndMiner ??= SenderRecipientAndMiner.Default; - return Build.A.Block.WithNumber(blockNumber).WithTransactions(tx == null ? new Transaction[0] : new[] {tx}).WithGasLimit(8000000).WithBeneficiary(senderRecipientAndMiner.Miner).TestObject; + return Build.A.Block.WithNumber(blockNumber).WithTransactions(tx == null ? new Transaction[0] : new[] {tx}).WithGasLimit(blockGasLimit).WithBeneficiary(senderRecipientAndMiner.Miner).TestObject; } protected void AssertGas(TestAllTracerWithOutput receipt, long gas) From 4b18aeefcc9a654b449734172ecc1e17cbeac078 Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 20 Jun 2022 13:45:30 +0200 Subject: [PATCH 14/21] Fix issues with upstream merge --- .../ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs | 2 +- .../ChainSpecStyle/ChainSpecLoaderTests.cs | 2 +- src/Nethermind/Nethermind.Specs/Forks/14_GrayGlacier.cs | 1 + .../Nethermind.Specs/Forks/{14_Shanghai.cs => 15_Shanghai.cs} | 0 src/int256 | 2 +- 5 files changed, 4 insertions(+), 3 deletions(-) rename src/Nethermind/Nethermind.Specs/Forks/{14_Shanghai.cs => 15_Shanghai.cs} (100%) diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs index 4dd2c5c76f3..00f877adc10 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs index 0c7cc4b9a56..d141b7d3339 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecLoaderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify diff --git a/src/Nethermind/Nethermind.Specs/Forks/14_GrayGlacier.cs b/src/Nethermind/Nethermind.Specs/Forks/14_GrayGlacier.cs index 2bff0d592f2..5bfdb3a38d1 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/14_GrayGlacier.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/14_GrayGlacier.cs @@ -80,5 +80,6 @@ private GrayGlacier() { } public bool IsEip3541Enabled => true; public bool IsEip3607Enabled => true; public long Eip1559TransitionBlock => 12965000; + public bool IsEip1153Enabled => false; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs b/src/Nethermind/Nethermind.Specs/Forks/15_Shanghai.cs similarity index 100% rename from src/Nethermind/Nethermind.Specs/Forks/14_Shanghai.cs rename to src/Nethermind/Nethermind.Specs/Forks/15_Shanghai.cs diff --git a/src/int256 b/src/int256 index b59900f0fc9..89ab8c0ea74 160000 --- a/src/int256 +++ b/src/int256 @@ -1 +1 @@ -Subproject commit b59900f0fc96637ce73d9d759e4549fa6c2fc9e9 +Subproject commit 89ab8c0ea74c8cdea305afb97abbb16ad5e62c28 From d7ce80c918443b409460af3f98a146d25a1d6fdb Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 20 Jun 2022 15:01:04 +0200 Subject: [PATCH 15/21] Remove IPartialStorageProvider interface --- .../IPartialStorageProvider.cs | 52 ------------------- .../PartialStorageProviderBase.cs | 28 +--------- .../PersistentStorageProvider.cs | 25 ++++++++- .../Nethermind.State/StorageProvider.cs | 8 +-- .../TransientStorageProvider.cs | 11 +--- 5 files changed, 30 insertions(+), 94 deletions(-) delete mode 100644 src/Nethermind/Nethermind.State/IPartialStorageProvider.cs diff --git a/src/Nethermind/Nethermind.State/IPartialStorageProvider.cs b/src/Nethermind/Nethermind.State/IPartialStorageProvider.cs deleted file mode 100644 index b5b9263a1a6..00000000000 --- a/src/Nethermind/Nethermind.State/IPartialStorageProvider.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2021 Demerzel Solutions Limited -// This file is part of the Nethermind library. -// -// The Nethermind library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Nethermind library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Nethermind. If not, see . - -using Nethermind.Core; - -namespace Nethermind.State -{ - public interface IPartialStorageProvider : IJournal - { - byte[] GetOriginal(StorageCell storageCell); - - byte[] Get(StorageCell storageCell); - - void Set(StorageCell storageCell, byte[] newValue); - - void Reset(); - - void CommitTrees(long blockNumber); - - void Commit(); - - void Commit(IStorageTracer stateTracer); - - /// - /// Creates a restartable snapshot. - /// - /// Indicates new transaction will start here. - /// Snapshot index - /// - /// If is true and there are already changes in then next call to - /// will use changes before this snapshot as original values for this new transaction. - /// - int TakeSnapshot(bool newTransactionStart = false); - - int IJournal.TakeSnapshot() => TakeSnapshot(); - - void ClearStorage(Address address); - } -} diff --git a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs index 2b450fec83f..1826668876b 100644 --- a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs +++ b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs @@ -26,7 +26,7 @@ namespace Nethermind.State { - public abstract class PartialStorageProviderBase : IPartialStorageProvider + public abstract class PartialStorageProviderBase { protected readonly ResettableDictionary> _intraBlockCache = new(); @@ -57,27 +57,6 @@ public PartialStorageProviderBase(ILogManager? logManager) _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); } - public byte[] GetOriginal(StorageCell storageCell) - { - if (!_originalValues.ContainsKey(storageCell)) - { - throw new InvalidOperationException("Get original should only be called after get within the same caching round"); - } - - if (_transactionChangesSnapshots.TryPeek(out int snapshot)) - { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) - { - return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; - } - } - } - - return _originalValues[storageCell]; - } - public byte[] Get(StorageCell storageCell) { return GetCurrentValue(storageCell); @@ -88,7 +67,7 @@ public void Set(StorageCell storageCell, byte[] newValue) PushUpdate(storageCell, newValue); } - int IPartialStorageProvider.TakeSnapshot(bool newTransactionStart) + public int TakeSnapshot(bool newTransactionStart) { if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) @@ -202,9 +181,6 @@ public void Reset() Array.Clear(_changes, 0, _changes.Length); _storages.Reset(); } - - public abstract void CommitTrees(long blockNumber); - protected abstract byte[] GetCurrentValue(StorageCell storageCell); private void PushUpdate(StorageCell cell, byte[] value) diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index e68b3dfb902..a4e3a05136a 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -26,7 +26,7 @@ namespace Nethermind.State { - public class PersistentStorageProvider : PartialStorageProviderBase, IPartialStorageProvider + public class PersistentStorageProvider : PartialStorageProviderBase { protected readonly ITrieStore _trieStore; @@ -50,6 +50,27 @@ protected override byte[] GetCurrentValue(StorageCell storageCell) return LoadFromTree(storageCell); } + public byte[] GetOriginal(StorageCell storageCell) + { + if (!_originalValues.ContainsKey(storageCell)) + { + throw new InvalidOperationException("Get original should only be called after get within the same caching round"); + } + + if (_transactionChangesSnapshots.TryPeek(out int snapshot)) + { + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) + { + if (stack.TryGetSearchedItem(snapshot, out int lastChangeIndexBeforeOriginalSnapshot)) + { + return _changes[lastChangeIndexBeforeOriginalSnapshot]!.Value; + } + } + } + + return _originalValues[storageCell]; + } + public override void Commit(IStorageTracer tracer) { if (_currentPosition == -1) @@ -167,7 +188,7 @@ public override void Commit(IStorageTracer tracer) } } - public override void CommitTrees(long blockNumber) + public void CommitTrees(long blockNumber) { // _logger.Warn($"Storage block commit {blockNumber}"); foreach (KeyValuePair storage in _storages) diff --git a/src/Nethermind/Nethermind.State/StorageProvider.cs b/src/Nethermind/Nethermind.State/StorageProvider.cs index 615361c016b..adfcedbb67a 100644 --- a/src/Nethermind/Nethermind.State/StorageProvider.cs +++ b/src/Nethermind/Nethermind.State/StorageProvider.cs @@ -85,8 +85,8 @@ public void Restore(int snapshot) } public void Restore(Snapshot snapshot) { - ((IPartialStorageProvider)_persistentStorageProvider).Restore(snapshot.PersistentStorageSnapshot); - ((IPartialStorageProvider)_transientStorageProvider).Restore(snapshot.TransientStorageSnapshot); + _persistentStorageProvider.Restore(snapshot.PersistentStorageSnapshot); + _transientStorageProvider.Restore(snapshot.TransientStorageSnapshot); } public void Set(StorageCell storageCell, byte[] newValue) @@ -101,8 +101,8 @@ public void SetTransientState(StorageCell storageCell, byte[] newValue) Snapshot IStorageProvider.TakeSnapshot(bool newTransactionStart) { - int persistentSnapshot = ((IPartialStorageProvider)_persistentStorageProvider).TakeSnapshot(newTransactionStart); - int transientSnapshot = ((IPartialStorageProvider)_transientStorageProvider).TakeSnapshot(newTransactionStart); + int persistentSnapshot = _persistentStorageProvider.TakeSnapshot(newTransactionStart); + int transientSnapshot = _transientStorageProvider.TakeSnapshot(newTransactionStart); return new Snapshot(Snapshot.EmptyPosition, persistentSnapshot, transientSnapshot); } diff --git a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs index da309ce24fe..02eb33e30e2 100644 --- a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs @@ -29,20 +29,11 @@ namespace Nethermind.State /// EIP-1153 provides a transient store for contracts that doesn't persist /// storage across calls. Reverts will rollback any transient state changes. /// - public class TransientStorageProvider : PartialStorageProviderBase, IPartialStorageProvider + public class TransientStorageProvider : PartialStorageProviderBase { public TransientStorageProvider(ILogManager? logManager) : base(logManager) { } - private Keccak RecalculateRootHash(Address address) - { - throw new NotImplementedException(); - } - public override void CommitTrees(long blockNumber) - { - throw new NotImplementedException(); - } - /// /// Nothing to commit to permanent storage /// Reset the caches and return From 656d50fe95011b4cd381e5ce3a455b5ba2dc8f62 Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 20 Jun 2022 16:21:18 +0200 Subject: [PATCH 16/21] Add Snapshot.Storage nested struct --- .../Nethermind.Evm.Test/EvmStateTests.cs | 4 ++-- .../StorageProviderTests.cs | 8 +++---- .../Nethermind.State.Test/WorldStateTests.cs | 12 +++++------ .../Nethermind.State/IStorageProvider.cs | 8 +++---- src/Nethermind/Nethermind.State/Snapshot.cs | 21 ++++++++++++++----- .../Nethermind.State/StorageProvider.cs | 8 +++---- src/Nethermind/Nethermind.State/WorldState.cs | 8 +++---- 7 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs index a569587a7a8..8d37c61267b 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs @@ -250,13 +250,13 @@ parentEvmState is null new ExecutionEnvironment(), ExecutionType.Call, true, - new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition, Snapshot.EmptyPosition), + new Snapshot(Snapshot.EmptyPosition, new Snapshot.Storage(Snapshot.EmptyPosition, Snapshot.EmptyPosition)), isContinuation) : new EvmState(10000, new ExecutionEnvironment(), ExecutionType.Call, false, - new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition, Snapshot.EmptyPosition), + new Snapshot(Snapshot.EmptyPosition, new Snapshot.Storage(Snapshot.EmptyPosition, Snapshot.EmptyPosition)), 0, 0, false, diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index 8061fa36917..e3827f28fbc 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -57,7 +57,7 @@ public void Empty_commit_restore() Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); provider.Commit(); - provider.Restore(new Snapshot(Snapshot.EmptyPosition, Snapshot.EmptyPosition, Snapshot.EmptyPosition)); + provider.Restore(new Snapshot.Storage(Snapshot.EmptyPosition, Snapshot.EmptyPosition)); } private StorageProvider BuildStorageProvider(Context ctx) @@ -270,7 +270,7 @@ public void Tload_same_address_same_index_different_values_restore(int snapshot) { Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); - Snapshot[] snapshots = new Snapshot[4]; + Snapshot.Storage[] snapshots = new Snapshot.Storage[4]; snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[1]); snapshots[1] = ((IStorageProvider)provider).TakeSnapshot(); @@ -321,7 +321,7 @@ public void Transient_state_restores_independent_of_permanent_state(int snapshot { Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); - Snapshot[] snapshots = new Snapshot[4]; + Snapshot.Storage[] snapshots = new Snapshot.Storage[4]; // No updates snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); @@ -365,7 +365,7 @@ public void Permanent_state_restores_independent_of_transient_state(int snapshot { Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); - Snapshot[] snapshots = new Snapshot[4]; + Snapshot.Storage[] snapshots = new Snapshot.Storage[4]; // No updates snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); diff --git a/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs b/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs index 3e91774c88b..a68a70703f8 100644 --- a/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs +++ b/src/Nethermind/Nethermind.State.Test/WorldStateTests.cs @@ -48,7 +48,7 @@ public void When_taking_a_snapshot_return_the_same_value_as_both() Snapshot snapshot = worldState.TakeSnapshot(); snapshot.StateSnapshot.Should().Be(0); - snapshot.PersistentStorageSnapshot.Should().Be(0); + snapshot.StorageSnapshot.PersistentStorageSnapshot.Should().Be(0); } [Test] @@ -60,12 +60,12 @@ public void When_taking_a_snapshot_can_return_non_zero_snapshot_value() WorldState worldState = new(stateProvider, storageProvider); stateProvider.TakeSnapshot().Returns(1); - storageProvider.TakeSnapshot().Returns(new Snapshot(1, 2, 3)); + storageProvider.TakeSnapshot().Returns(new Snapshot.Storage(2, 3)); Snapshot snapshot = worldState.TakeSnapshot(); snapshot.StateSnapshot.Should().Be(1); - snapshot.PersistentStorageSnapshot.Should().Be(2); - snapshot.TransientStorageSnapshot.Should().Be(3); + snapshot.StorageSnapshot.PersistentStorageSnapshot.Should().Be(2); + snapshot.StorageSnapshot.TransientStorageSnapshot.Should().Be(3); } [Test] @@ -86,9 +86,9 @@ public void Can_restore_snapshot() IStorageProvider storageProvider = Substitute.For(); WorldState worldState = new(stateProvider, storageProvider); - worldState.Restore(new Snapshot(1, 2, 1)); + worldState.Restore(new Snapshot(1, new Snapshot.Storage(2, 1))); stateProvider.Received().Restore(1); - storageProvider.Received().Restore(new Snapshot(1, 2, 1)); + storageProvider.Received().Restore(new Snapshot.Storage(2, 1)); } } } diff --git a/src/Nethermind/Nethermind.State/IStorageProvider.cs b/src/Nethermind/Nethermind.State/IStorageProvider.cs index 69b991edea8..4b134e0e5e3 100644 --- a/src/Nethermind/Nethermind.State/IStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/IStorageProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ namespace Nethermind.State { - public interface IStorageProvider : IJournal + public interface IStorageProvider : IJournal { byte[] GetOriginal(StorageCell storageCell); @@ -47,9 +47,9 @@ public interface IStorageProvider : IJournal /// If is true and there are already changes in then next call to /// will use changes before this snapshot as original values for this new transaction. /// - Snapshot TakeSnapshot(bool newTransactionStart = false); + Snapshot.Storage TakeSnapshot(bool newTransactionStart = false); - Snapshot IJournal.TakeSnapshot() => TakeSnapshot(); + Snapshot.Storage IJournal.TakeSnapshot() => TakeSnapshot(); void ClearStorage(Address address); } diff --git a/src/Nethermind/Nethermind.State/Snapshot.cs b/src/Nethermind/Nethermind.State/Snapshot.cs index cd4cc040fd5..626494bdd7b 100644 --- a/src/Nethermind/Nethermind.State/Snapshot.cs +++ b/src/Nethermind/Nethermind.State/Snapshot.cs @@ -26,17 +26,28 @@ namespace Nethermind.State /// public readonly struct Snapshot { - public Snapshot(int stateSnapshot, int storageSnapshot, int transientStorageSnapshot) + public Snapshot(int stateSnapshot, Storage storageSnapshot) { StateSnapshot = stateSnapshot; - PersistentStorageSnapshot = storageSnapshot; - TransientStorageSnapshot = transientStorageSnapshot; + StorageSnapshot = storageSnapshot; + } + + public struct Storage + { + + public int PersistentStorageSnapshot { get; } + public int TransientStorageSnapshot { get; } + + public Storage(int storageSnapshot, int transientStorageSnapshot) + { + PersistentStorageSnapshot = storageSnapshot; + TransientStorageSnapshot = transientStorageSnapshot; + } } public int StateSnapshot { get; } + public Storage StorageSnapshot { get; } - public int PersistentStorageSnapshot { get; } - public int TransientStorageSnapshot { get; } public const int EmptyPosition = -1; } } diff --git a/src/Nethermind/Nethermind.State/StorageProvider.cs b/src/Nethermind/Nethermind.State/StorageProvider.cs index adfcedbb67a..1b8045c80d9 100644 --- a/src/Nethermind/Nethermind.State/StorageProvider.cs +++ b/src/Nethermind/Nethermind.State/StorageProvider.cs @@ -81,9 +81,9 @@ public void Reset() /// public void Restore(int snapshot) { - Restore(new Snapshot(Snapshot.EmptyPosition, snapshot, Snapshot.EmptyPosition)); + Restore(new Snapshot.Storage(snapshot, Snapshot.EmptyPosition)); } - public void Restore(Snapshot snapshot) + public void Restore(Snapshot.Storage snapshot) { _persistentStorageProvider.Restore(snapshot.PersistentStorageSnapshot); _transientStorageProvider.Restore(snapshot.TransientStorageSnapshot); @@ -99,12 +99,12 @@ public void SetTransientState(StorageCell storageCell, byte[] newValue) _transientStorageProvider.Set(storageCell, newValue); } - Snapshot IStorageProvider.TakeSnapshot(bool newTransactionStart) + Snapshot.Storage IStorageProvider.TakeSnapshot(bool newTransactionStart) { int persistentSnapshot = _persistentStorageProvider.TakeSnapshot(newTransactionStart); int transientSnapshot = _transientStorageProvider.TakeSnapshot(newTransactionStart); - return new Snapshot(Snapshot.EmptyPosition, persistentSnapshot, transientSnapshot); + return new Snapshot.Storage(persistentSnapshot, transientSnapshot); } } } diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index f04e1444bd5..37ab5b1d0e2 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -25,14 +25,14 @@ public class WorldState : IWorldState public Snapshot TakeSnapshot(bool newTransactionStart = false) { - Snapshot storageSnapshot = StorageProvider.TakeSnapshot(newTransactionStart); - return new (StateProvider.TakeSnapshot(), storageSnapshot.PersistentStorageSnapshot, storageSnapshot.TransientStorageSnapshot); + Snapshot.Storage storageSnapshot = StorageProvider.TakeSnapshot(newTransactionStart); + return new (StateProvider.TakeSnapshot(), storageSnapshot); } public void Restore(Snapshot snapshot) { StateProvider.Restore(snapshot.StateSnapshot); - StorageProvider.Restore(snapshot); + StorageProvider.Restore(snapshot.StorageSnapshot); } public IStorageProvider StorageProvider { get; } From b19fc503db8360eb60f7a4bd731f498505dc2f43 Mon Sep 17 00:00:00 2001 From: codyborn Date: Mon, 20 Jun 2022 16:46:50 +0200 Subject: [PATCH 17/21] Addressing minor PR feedback --- src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs | 4 ++-- src/Nethermind/Nethermind.Evm/Instruction.cs | 6 ++++-- src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs | 5 ----- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 8 ++++---- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs index cc9cac2609c..d21a3cfc7d0 100644 --- a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs +++ b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs @@ -115,7 +115,7 @@ public Prepare CallWithInput(Address address, long gasLimit, string input) return CallWithInput(address, gasLimit, Bytes.FromHexString(input)); } - public Prepare CallWithInput(Address address, long gasLimit, byte[] input = null) + public Prepare CallWithInput(Address address, long gasLimit, byte[]? input = null) { if (input != null) { @@ -187,7 +187,7 @@ public Prepare StaticCall(Address address, long gasLimit) return this; } - public Prepare DynamicCallWithInput(Instruction callType, Address address, long gasLimit, byte[] input = null) + public Prepare DynamicCallWithInput(Instruction callType, Address address, long gasLimit, byte[]? input = null) { if (callType != Instruction.CALL && callType != Instruction.STATICCALL && diff --git a/src/Nethermind/Nethermind.Evm/Instruction.cs b/src/Nethermind/Nethermind.Evm/Instruction.cs index 5acb28b1d24..d162919ffb3 100644 --- a/src/Nethermind/Nethermind.Evm/Instruction.cs +++ b/src/Nethermind/Nethermind.Evm/Instruction.cs @@ -84,8 +84,6 @@ public enum Instruction : byte MSTORE8 = 0x53, SLOAD = 0x54, SSTORE = 0x55, - TLOAD = 0xb3, - TSTORE = 0xb4, JUMP = 0x56, JUMPI = 0x57, PC = 0x58, @@ -169,6 +167,10 @@ public enum Instruction : byte LOG3 = 0xa3, LOG4 = 0xa4, + // EIP-1153 + TLOAD = 0xb3, + TSTORE = 0xb4, + CREATE = 0xf0, CALL = 0xf1, CALLCODE = 0xf2, diff --git a/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs b/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs index 11b469a137d..2031da65e3a 100644 --- a/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/ReleaseSpecExtensions.cs @@ -97,10 +97,5 @@ public static long GetExpByteCost(this IReleaseSpec spec) => spec.UseExpDDosProtection ? GasCostOf.ExpByteEip160 : GasCostOf.ExpByte; - - public static long GetTLoadCost(this IReleaseSpec spec) => - GasCostOf.TLoad; - public static long GetTStoreCost(this IReleaseSpec spec) => - GasCostOf.TStore; } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f479e70ecac..f426bec5e5c 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2019,13 +2019,13 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) } case Instruction.TLOAD: { + Metrics.TloadOpcode++; if (!spec.TransientStorageEnabled) { EndInstructionTraceError(EvmExceptionType.BadInstruction); return CallResult.InvalidInstructionException; } - Metrics.TloadOpcode++; - var gasCost = spec.GetTLoadCost(); + var gasCost = GasCostOf.TLoad; if (!UpdateGas(gasCost, ref gasAvailable)) { @@ -2043,12 +2043,12 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) } case Instruction.TSTORE: { + Metrics.TstoreOpcode++; if (!spec.TransientStorageEnabled) { EndInstructionTraceError(EvmExceptionType.BadInstruction); return CallResult.InvalidInstructionException; } - Metrics.TstoreOpcode++; if (vmState.IsStatic) { @@ -2056,7 +2056,7 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) return CallResult.StaticCallViolationException; } - var gasCost = spec.GetTStoreCost(); + var gasCost = GasCostOf.TStore; if (!UpdateGas(gasCost, ref gasAvailable)) { EndInstructionTraceError(EvmExceptionType.OutOfGas); From d0e8d0dd5743f8c08774077ca0285110c7da5dc7 Mon Sep 17 00:00:00 2001 From: "lukasz.rozmej" Date: Mon, 20 Jun 2022 23:01:41 +0200 Subject: [PATCH 18/21] Refactorings --- .../Nethermind.Evm.Test/Eip1153Tests.cs | 2 +- .../Nethermind.Evm.Test/EvmStateTests.cs | 4 +- .../Nethermind.Evm/Tracing/ITxTracer.cs | 2 + .../Nethermind.Evm/VirtualMachine.cs | 15 +++- .../StorageProviderTests.cs | 39 +++++---- .../PartialStorageProviderBase.cs | 79 +++++++++++++------ .../PersistentStorageProvider.cs | 59 ++++++-------- src/Nethermind/Nethermind.State/Snapshot.cs | 7 +- .../Nethermind.State/StorageProvider.cs | 3 +- .../TransientStorageProvider.cs | 48 +---------- src/int256 | 2 +- 11 files changed, 127 insertions(+), 133 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index 7935b62bc36..e3e4d86e182 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -78,7 +78,7 @@ public void tload_uninitialized_returns_zero() /// /// Simple performance test /// - [Ignore("Depends on hardware")] + [Explicit("Depends on hardware")] [Test] public void transient_storage_performance_test() { diff --git a/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs b/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs index 8d37c61267b..71c5cd6a8ee 100644 --- a/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/EvmStateTests.cs @@ -250,13 +250,13 @@ parentEvmState is null new ExecutionEnvironment(), ExecutionType.Call, true, - new Snapshot(Snapshot.EmptyPosition, new Snapshot.Storage(Snapshot.EmptyPosition, Snapshot.EmptyPosition)), + Snapshot.Empty, isContinuation) : new EvmState(10000, new ExecutionEnvironment(), ExecutionType.Call, false, - new Snapshot(Snapshot.EmptyPosition, new Snapshot.Storage(Snapshot.EmptyPosition, Snapshot.EmptyPosition)), + Snapshot.Empty, 0, 0, false, diff --git a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs index 09b29a81abe..58e8933e5be 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/ITxTracer.cs @@ -127,8 +127,10 @@ void ReportMemoryChange(long offset, in ZeroPaddedMemory data) void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value); void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue); + void SetOperationTransientStorage(Address storageCellAddress, UInt256 storageIndex, Span newValue, byte[] currentValue) { } void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value); + void LoadOperationTransientStorage(Address storageCellAddress, UInt256 storageIndex, byte[] value) { } void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress); diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index f426bec5e5c..cfcf4629693 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2039,6 +2039,11 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) byte[] value = _storage.GetTransientState(storageCell); stack.PushBytes(value); + if (_txTracer.IsTracingOpLevelStorage) + { + _txTracer.LoadOperationTransientStorage(storageCell.Address, storageIndex, value); + } + break; } case Instruction.TSTORE: @@ -2056,7 +2061,7 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) return CallResult.StaticCallViolationException; } - var gasCost = GasCostOf.TStore; + long gasCost = GasCostOf.TStore; if (!UpdateGas(gasCost, ref gasAvailable)) { EndInstructionTraceError(EvmExceptionType.OutOfGas); @@ -2076,7 +2081,13 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) } StorageCell storageCell = new(env.ExecutingAccount, storageIndex); - _storage.SetTransientState(storageCell, newValue.ToArray()); + byte[] currentValue = newValue.ToArray(); + _storage.SetTransientState(storageCell, currentValue); + + if (_txTracer.IsTracingOpLevelStorage) + { + _txTracer.SetOperationTransientStorage(storageCell.Address, storageIndex, newValue, currentValue); + } break; } diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index e3827f28fbc..2c69cb541c7 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -15,6 +15,7 @@ // along with the Nethermind. If not, see . using System; +using FluentAssertions; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; @@ -57,7 +58,7 @@ public void Empty_commit_restore() Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); provider.Commit(); - provider.Restore(new Snapshot.Storage(Snapshot.EmptyPosition, Snapshot.EmptyPosition)); + provider.Restore(Snapshot.Storage.Empty); } private StorageProvider BuildStorageProvider(Context ctx) @@ -325,27 +326,19 @@ public void Transient_state_restores_independent_of_permanent_state(int snapshot // No updates snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[0].TransientStorageSnapshot, -1); - Assert.AreEqual(snapshots[0].PersistentStorageSnapshot, -1); // Only update transient provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[1]); snapshots[1] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[1].TransientStorageSnapshot, 0); - Assert.AreEqual(snapshots[1].PersistentStorageSnapshot, -1); // Update both provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[2]); provider.Set(new StorageCell(ctx.Address1, 1), _values[9]); snapshots[2] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[2].TransientStorageSnapshot, 1); - Assert.AreEqual(snapshots[2].PersistentStorageSnapshot, 0); // Only update persistent provider.Set(new StorageCell(ctx.Address1, 1), _values[8]); snapshots[3] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[3].TransientStorageSnapshot, 1); - Assert.AreEqual(snapshots[3].PersistentStorageSnapshot, 1); provider.Restore(snapshots[snapshot + 1]); @@ -354,7 +347,14 @@ public void Transient_state_restores_independent_of_permanent_state(int snapshot { snapshot--; } - Assert.AreEqual(_values[snapshot + 1], provider.GetTransientState(new StorageCell(ctx.Address1, 1))); + + snapshots.Should().Equal( + Snapshot.Storage.Empty, + new Snapshot.Storage(Snapshot.EmptyPosition, 0), + new Snapshot.Storage(0, 1), + new Snapshot.Storage(1, 1)); + + _values[snapshot + 1].Should().BeEquivalentTo(provider.GetTransientState(new StorageCell(ctx.Address1, 1))); } [TestCase(-1)] @@ -369,27 +369,19 @@ public void Permanent_state_restores_independent_of_transient_state(int snapshot // No updates snapshots[0] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[0].TransientStorageSnapshot, -1); - Assert.AreEqual(snapshots[0].PersistentStorageSnapshot, -1); // Only update persistent provider.Set(new StorageCell(ctx.Address1, 1), _values[1]); snapshots[1] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[1].PersistentStorageSnapshot, 0); - Assert.AreEqual(snapshots[1].TransientStorageSnapshot, -1); // Update both provider.Set(new StorageCell(ctx.Address1, 1), _values[2]); provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[9]); snapshots[2] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[2].PersistentStorageSnapshot, 1); - Assert.AreEqual(snapshots[2].TransientStorageSnapshot, 0); // Only update transient provider.SetTransientState(new StorageCell(ctx.Address1, 1), _values[8]); snapshots[3] = ((IStorageProvider)provider).TakeSnapshot(); - Assert.AreEqual(snapshots[3].PersistentStorageSnapshot, 1); - Assert.AreEqual(snapshots[3].TransientStorageSnapshot, 1); provider.Restore(snapshots[snapshot + 1]); @@ -398,7 +390,14 @@ public void Permanent_state_restores_independent_of_transient_state(int snapshot { snapshot--; } - Assert.AreEqual(_values[snapshot + 1], provider.Get(new StorageCell(ctx.Address1, 1))); + + snapshots.Should().Equal( + Snapshot.Storage.Empty, + new Snapshot.Storage(0, Snapshot.EmptyPosition), + new Snapshot.Storage(1, 0), + new Snapshot.Storage(1, 1)); + + _values[snapshot + 1].Should().BeEquivalentTo(provider.Get(new StorageCell(ctx.Address1, 1))); } private class Context diff --git a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs index 1826668876b..e027c2a9416 100644 --- a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs +++ b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs @@ -30,20 +30,10 @@ public abstract class PartialStorageProviderBase { protected readonly ResettableDictionary> _intraBlockCache = new(); - /// - /// EIP-1283 - /// - protected readonly ResettableDictionary _originalValues = new(); - - protected readonly ResettableHashSet _committedThisRound = new(); - protected readonly ILogger _logger; - protected readonly ILogManager _logManager; - - protected readonly ResettableDictionary _storages = new(); - protected const int StartCapacity = Resettable.StartCapacity; - protected int _capacity = StartCapacity; + private const int StartCapacity = Resettable.StartCapacity; + private int _capacity = StartCapacity; protected Change?[] _changes = new Change[StartCapacity]; protected int _currentPosition = Resettable.EmptyPosition; @@ -51,10 +41,11 @@ public abstract class PartialStorageProviderBase // this is needed for OriginalValues for new transactions protected readonly Stack _transactionChangesSnapshots = new(); - public PartialStorageProviderBase(ILogManager? logManager) + protected static readonly byte[] _zeroValue = {0}; + + protected PartialStorageProviderBase(ILogManager? logManager) { - _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); - _logger = logManager.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); + _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); } public byte[] Get(StorageCell storageCell) @@ -84,7 +75,7 @@ public void Restore(int snapshot) if (snapshot > _currentPosition) { - throw new InvalidOperationException($"{nameof(PartialStorageProviderBase)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); + throw new InvalidOperationException($"{GetType().Name} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); } if (snapshot == _currentPosition) @@ -147,8 +138,6 @@ public void Commit() Commit(NullStorageTracer.Instance); } - protected static readonly byte[] _zeroValue = {0}; - protected readonly struct ChangeTrace { public ChangeTrace(byte[]? before, byte[]? after) @@ -167,20 +156,51 @@ public ChangeTrace(byte[]? after) public byte[] After { get; } } - public abstract void Commit(IStorageTracer tracer); + public void Commit(IStorageTracer tracer) + { + if (_currentPosition == Snapshot.EmptyPosition) + { + if (_logger.IsTrace) _logger.Trace("No storage changes to commit"); + } + else + { + CommitCore(tracer); + } + } - public void Reset() + protected virtual void CommitCore(IStorageTracer tracer) + { + Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition); + _intraBlockCache.Reset(); + _transactionChangesSnapshots.Clear(); + } + + + public virtual void Reset() { if (_logger.IsTrace) _logger.Trace("Resetting storage"); _intraBlockCache.Clear(); - _originalValues.Clear(); _transactionChangesSnapshots.Clear(); _currentPosition = -1; - _committedThisRound.Clear(); Array.Clear(_changes, 0, _changes.Length); - _storages.Reset(); } + + protected bool TryGetCachedValue(StorageCell storageCell, out byte[]? bytes) + { + if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) + { + int lastChangeIndex = stack.Peek(); + { + bytes = _changes[lastChangeIndex]!.Value; + return true; + } + } + + bytes = null; + return false; + } + protected abstract byte[] GetCurrentValue(StorageCell storageCell); private void PushUpdate(StorageCell cell, byte[] value) @@ -204,7 +224,18 @@ protected void SetupRegistry(StorageCell cell) } } - public abstract void ClearStorage(Address address); + public virtual void ClearStorage(Address address) + { + // We are setting cached values to zero so we do not use previously set values + // when the contract is revived with CREATE2 inside the same block + foreach (KeyValuePair> cellByAddress in _intraBlockCache) + { + if (cellByAddress.Key.Address == address) + { + Set(cellByAddress.Key, _zeroValue); + } + } + } protected class Change { diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index a4e3a05136a..3b14b4294e2 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -28,28 +28,35 @@ namespace Nethermind.State { public class PersistentStorageProvider : PartialStorageProviderBase { - - protected readonly ITrieStore _trieStore; - protected readonly IStateProvider _stateProvider; + private readonly ITrieStore _trieStore; + private readonly IStateProvider _stateProvider; + private readonly ILogManager? _logManager; + private readonly ResettableDictionary _storages = new(); + /// + /// EIP-1283 + /// + private readonly ResettableDictionary _originalValues = new(); + private readonly ResettableHashSet _committedThisRound = new(); public PersistentStorageProvider(ITrieStore? trieStore, IStateProvider? stateProvider, ILogManager? logManager) : base(logManager) { _trieStore = trieStore ?? throw new ArgumentNullException(nameof(trieStore)); _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); + _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); } - protected override byte[] GetCurrentValue(StorageCell storageCell) + public override void Reset() { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - int lastChangeIndex = stack.Peek(); - return _changes[lastChangeIndex]!.Value; - } - - return LoadFromTree(storageCell); + base.Reset(); + _storages.Reset(); + _originalValues.Clear(); + _committedThisRound.Clear(); } + protected override byte[] GetCurrentValue(StorageCell storageCell) => + TryGetCachedValue(storageCell, out byte[] bytes) ? bytes : LoadFromTree(storageCell); + public byte[] GetOriginal(StorageCell storageCell) { if (!_originalValues.ContainsKey(storageCell)) @@ -71,14 +78,8 @@ public byte[] GetOriginal(StorageCell storageCell) return _originalValues[storageCell]; } - public override void Commit(IStorageTracer tracer) + protected override void CommitCore(IStorageTracer tracer) { - if (_currentPosition == -1) - { - if (_logger.IsTrace) _logger.Trace("No storage changes to commit"); - return; - } - if (_logger.IsTrace) _logger.Trace("Committing storage changes"); if (_changes[_currentPosition] is null) @@ -176,11 +177,9 @@ public override void Commit(IStorageTracer tracer) } } - Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition, StartCapacity); - _committedThisRound.Reset(); - _intraBlockCache.Reset(); + base.CommitCore(tracer); _originalValues.Reset(); - _transactionChangesSnapshots.Clear(); + _committedThisRound.Reset(); if (isTracing) { @@ -255,19 +254,11 @@ private Keccak RecalculateRootHash(Address address) public override void ClearStorage(Address address) { - /* we are setting cached values to zero so we do not use previously set values - when the contract is revived with CREATE2 inside the same block */ - foreach (var cellByAddress in _intraBlockCache) - { - if (cellByAddress.Key.Address == address) - { - Set(cellByAddress.Key, _zeroValue); - } - } + base.ClearStorage(address); - /* here it is important to make sure that we will not reuse the same tree when the contract is revived - by means of CREATE 2 - notice that the cached trie may carry information about items that were not - touched in this block, hence were not zeroed above */ + // here it is important to make sure that we will not reuse the same tree when the contract is revived + // by means of CREATE 2 - notice that the cached trie may carry information about items that were not + // touched in this block, hence were not zeroed above // TODO: how does it work with pruning? _storages[address] = new StorageTree(_trieStore, Keccak.EmptyTreeHash, _logManager); } diff --git a/src/Nethermind/Nethermind.State/Snapshot.cs b/src/Nethermind/Nethermind.State/Snapshot.cs index 626494bdd7b..01785492778 100644 --- a/src/Nethermind/Nethermind.State/Snapshot.cs +++ b/src/Nethermind/Nethermind.State/Snapshot.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -26,14 +26,17 @@ namespace Nethermind.State /// public readonly struct Snapshot { + public static readonly Snapshot Empty = new(EmptyPosition, Storage.Empty); + public Snapshot(int stateSnapshot, Storage storageSnapshot) { StateSnapshot = stateSnapshot; StorageSnapshot = storageSnapshot; } - public struct Storage + public readonly struct Storage { + public static readonly Storage Empty = new(EmptyPosition, EmptyPosition); public int PersistentStorageSnapshot { get; } public int TransientStorageSnapshot { get; } diff --git a/src/Nethermind/Nethermind.State/StorageProvider.cs b/src/Nethermind/Nethermind.State/StorageProvider.cs index 1b8045c80d9..ace96841820 100644 --- a/src/Nethermind/Nethermind.State/StorageProvider.cs +++ b/src/Nethermind/Nethermind.State/StorageProvider.cs @@ -79,10 +79,11 @@ public void Reset() /// Convenience for test cases /// /// - public void Restore(int snapshot) + internal void Restore(int snapshot) { Restore(new Snapshot.Storage(snapshot, Snapshot.EmptyPosition)); } + public void Restore(Snapshot.Storage snapshot) { _persistentStorageProvider.Restore(snapshot.PersistentStorageSnapshot); diff --git a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs index 02eb33e30e2..b006fe96369 100644 --- a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs @@ -34,51 +34,7 @@ public class TransientStorageProvider : PartialStorageProviderBase public TransientStorageProvider(ILogManager? logManager) : base(logManager) { } - /// - /// Nothing to commit to permanent storage - /// Reset the caches and return - /// - /// - /// - public override void Commit(IStorageTracer tracer) - { - if (_currentPosition == -1) - { - if (_logger.IsTrace) _logger.Trace("No storage changes to commit"); - return; - } - - if (_logger.IsTrace) _logger.Trace("Committing transient storage changes"); - - Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition, StartCapacity); - _committedThisRound.Reset(); - _intraBlockCache.Reset(); - _originalValues.Reset(); - _transactionChangesSnapshots.Clear(); - } - - protected override byte[] GetCurrentValue(StorageCell storageCell) - { - if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) - { - int lastChangeIndex = stack.Peek(); - return _changes[lastChangeIndex]!.Value; - } - - return _zeroValue; - } - - public override void ClearStorage(Address address) - { - /* we are setting cached values to zero so we do not use previously set values - when the contract is revived with CREATE2 inside the same block */ - foreach (var cellByAddress in _intraBlockCache) - { - if (cellByAddress.Key.Address == address) - { - Set(cellByAddress.Key, _zeroValue); - } - } - } + protected override byte[] GetCurrentValue(StorageCell storageCell) => + TryGetCachedValue(storageCell, out byte[]? bytes) ? bytes! : _zeroValue; } } diff --git a/src/int256 b/src/int256 index 89ab8c0ea74..b59900f0fc9 160000 --- a/src/int256 +++ b/src/int256 @@ -1 +1 @@ -Subproject commit 89ab8c0ea74c8cdea305afb97abbb16ad5e62c28 +Subproject commit b59900f0fc96637ce73d9d759e4549fa6c2fc9e9 From cc724be28ba19c6d90a582e01e44c29c9d90e796 Mon Sep 17 00:00:00 2001 From: "lukasz.rozmej" Date: Mon, 20 Jun 2022 23:06:26 +0200 Subject: [PATCH 19/21] fix warning --- src/Nethermind/Nethermind.State/PersistentStorageProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index 3b14b4294e2..10b4de0f8bd 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -55,7 +55,7 @@ public override void Reset() } protected override byte[] GetCurrentValue(StorageCell storageCell) => - TryGetCachedValue(storageCell, out byte[] bytes) ? bytes : LoadFromTree(storageCell); + TryGetCachedValue(storageCell, out byte[]? bytes) ? bytes! : LoadFromTree(storageCell); public byte[] GetOriginal(StorageCell storageCell) { From 6daf335a49b3ec2436fdb6d10df09e735a6197db Mon Sep 17 00:00:00 2001 From: "lukasz.rozmej" Date: Mon, 20 Jun 2022 23:07:03 +0200 Subject: [PATCH 20/21] fix namespaces --- .../Nethermind.State/PartialStorageProviderBase.cs | 3 --- src/Nethermind/Nethermind.State/TransientStorageProvider.cs | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs index e027c2a9416..b40fa2a052f 100644 --- a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs +++ b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs @@ -18,11 +18,8 @@ using System.Collections.Generic; using Nethermind.Core; using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Resettables; using Nethermind.Logging; -using Nethermind.Trie.Pruning; namespace Nethermind.State { diff --git a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs index b006fe96369..25822d1ee81 100644 --- a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs @@ -14,13 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -using System; -using System.Collections.Generic; using Nethermind.Core; -using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; -using Nethermind.Core.Resettables; using Nethermind.Logging; namespace Nethermind.State From 8268705573e29382e130ff34b8f7edc64dac67ba Mon Sep 17 00:00:00 2001 From: codyborn Date: Tue, 21 Jun 2022 16:34:57 +0200 Subject: [PATCH 21/21] Add comments --- .../Nethermind.Evm.Test/Eip1153Tests.cs | 22 ++++++ .../VirtualMachineTests.cs | 7 +- .../Nethermind.Evm/ByteCodeBuilder.cs | 39 ++++++++++ src/Nethermind/Nethermind.Evm/GasCostOf.cs | 4 +- .../Nethermind.Evm/VirtualMachine.cs | 2 +- .../StorageProviderTests.cs | 30 +++++++- .../Nethermind.State/IStorageProvider.cs | 47 ++++++++++++ .../PartialStorageProviderBase.cs | 72 ++++++++++++++++++- .../PersistentStorageProvider.cs | 31 ++++++++ src/Nethermind/Nethermind.State/Snapshot.cs | 5 +- .../TransientStorageProvider.cs | 5 ++ 11 files changed, 255 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index e3e4d86e182..988fc1af92a 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -24,11 +24,17 @@ namespace Nethermind.Evm.Test { + /// + /// Tests functionality of Transient Storage + /// internal class Eip1153Tests : VirtualMachineTestsBase { protected override long BlockNumber => MainnetSpecProvider.ShanghaiBlockNumber; protected override ISpecProvider SpecProvider => MainnetSpecProvider.Instance; + /// + /// Transient storage should be activated after Shanghai hardfork + /// [Test] public void after_shanghai_can_call_tstore_tload() { @@ -41,6 +47,9 @@ public void after_shanghai_can_call_tstore_tload() Assert.AreEqual(StatusCode.Success, result.StatusCode); } + /// + /// Transient storage should not be activated until after Shanghai hardfork + /// [Test] public void before_shanghai_can_not_call_tstore_tload() { @@ -59,6 +68,9 @@ public void before_shanghai_can_not_call_tstore_tload() Assert.AreEqual(StatusCode.Failure, result.StatusCode); } + /// + /// Uninitialized transient storage is zero + /// [Test] public void tload_uninitialized_returns_zero() { @@ -102,6 +114,9 @@ public void transient_storage_performance_test() Assert.IsTrue(stopwatch.ElapsedMilliseconds < 5000); } + /// + /// Simple functionality test + /// [Test] public void tload_after_tstore() { @@ -118,6 +133,10 @@ public void tload_after_tstore() Assert.AreEqual(8, (int)result.ReturnValue.ToUInt256()); } + /// + /// Testing transient data store/load from different locations + /// + /// Location [TestCase(2)] [TestCase(3)] [TestCase(4)] @@ -726,6 +745,9 @@ public void tload_in_delegatecall(Instruction callType, int expectedResult) Assert.AreEqual(expectedResult, (int)result.ReturnValue.ToUInt256()); } + /// + /// Zeroing out a transient storage slot does not result in gas refund + /// [Test] public void tstore_does_not_result_in_gasrefund() { diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs index c30c218454d..5e1654cbbb7 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTests.cs @@ -19,7 +19,6 @@ using Nethermind.Core; using Nethermind.Core.Extensions; using Nethermind.Evm.Tracing.GethStyle; -using Nethermind.State; using Nethermind.Int256; using NUnit.Framework; using Nethermind.Specs; @@ -426,6 +425,9 @@ public void Sstore_twice_0_same_storage_should_refund_only_once() Assert.AreEqual(BigInteger.Zero.ToBigEndianByteArray(), Storage.Get(new StorageCell(Recipient, 0)), "storage"); } + /// + /// TLoad gas cost check + /// [Test] public void Tload() { @@ -438,6 +440,9 @@ public void Tload() Assert.AreEqual(GasCostOf.Transaction + GasCostOf.VeryLow * 1 + GasCostOf.TLoad, receipt.GasSpent, "gas"); } + /// + /// TStore gas cost check + /// [Test] public void Tstore() { diff --git a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs index d21a3cfc7d0..17520644947 100644 --- a/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs +++ b/src/Nethermind/Nethermind.Evm/ByteCodeBuilder.cs @@ -22,6 +22,9 @@ namespace Nethermind.Evm { + /// + /// A utility class to easily construct common patterns of EVM byte code + /// public class Prepare { private readonly List _byteCode = new(); @@ -187,6 +190,15 @@ public Prepare StaticCall(Address address, long gasLimit) return this; } + /// + /// Call the address with the specified callType + /// + /// CALL, STATICCALL, DELEGATECALL + /// Address of the contract + /// Gas limit of the call + /// Optional 32 byte input + /// Prepare with call bytecode + /// Throws exception if callType is incorrect public Prepare DynamicCallWithInput(Instruction callType, Address address, long gasLimit, byte[]? input = null) { if (callType != Instruction.CALL && @@ -308,6 +320,12 @@ private Prepare StoreDataInMemory(int position, byte[] data) return this; } + /// + /// Take the data already on stack and store it in memory + /// at specified position + /// + /// Memory position + /// Prepare with requested bytecode public Prepare DataOnStackToMemory(int position) { PushData(position); @@ -315,6 +333,12 @@ public Prepare DataOnStackToMemory(int position) return this; } + /// + /// Store input value at specified key in transient storage + /// + /// Storage key + /// Value to store + /// Prepare with requested bytecode public Prepare StoreDataInTransientStorage(int key, int value) { PushData(value); @@ -323,6 +347,11 @@ public Prepare StoreDataInTransientStorage(int key, int value) return this; } + /// + /// Load value from specified key in transient storage + /// + /// Storage key + /// Prepare with requested bytecode public Prepare LoadDataFromTransientStorage(int key) { PushData(key); @@ -330,6 +359,12 @@ public Prepare LoadDataFromTransientStorage(int key) return this; } + /// + /// Return the data in memory at position + /// + /// Data size + /// Memory position + /// Prepare with requested bytecode public Prepare Return(int size, int position) { PushData(size); @@ -338,6 +373,10 @@ public Prepare Return(int size, int position) return this; } + /// + /// Returns the result from a call made immediately prior + /// + /// Prepare with requested bytecode public Prepare ReturnInnerCallResult() { PushData(32); diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index 562586e8931..c43d4469ae7 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -70,7 +70,7 @@ public static class GasCostOf public const long AccessAccountListEntry = 2400; // eip-2930 public const long AccessStorageListEntry = 1900; // eip-2930 - public const long TLoad = WarmStateRead; - public const long TStore = WarmStateRead; + public const long TLoad = WarmStateRead; // eip-1153 + public const long TStore = WarmStateRead; // eip-1153 } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index cfcf4629693..7c1f805f2c4 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -2045,7 +2045,7 @@ void UpdateMemoryCost(in UInt256 position, in UInt256 length) } break; - } + } case Instruction.TSTORE: { Metrics.TstoreOpcode++; diff --git a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs index 2c69cb541c7..2769c197740 100644 --- a/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StorageProviderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -237,6 +237,9 @@ public void Can_commit_when_exactly_at_capacity_regression() Assert.AreEqual(_values[(Resettable.StartCapacity + 1) % 2], valueAfter); } + /// + /// Transient storage should be zero if uninitialized + /// [Test] public void Can_tload_uninitialized_locations() { @@ -253,6 +256,9 @@ public void Can_tload_uninitialized_locations() Assert.True(provider.GetTransientState(new StorageCell(ctx.Address2, 1)).IsZero()); } + /// + /// Simple transient storage test + /// [Test] public void Can_tload_after_tstore() { @@ -263,6 +269,10 @@ public void Can_tload_after_tstore() Assert.AreEqual(_values[1], provider.GetTransientState(new StorageCell(ctx.Address1, 2))); } + /// + /// Transient storage can be updated and restored + /// + /// Snapshot to restore to [TestCase(-1)] [TestCase(0)] [TestCase(1)] @@ -288,6 +298,9 @@ public void Tload_same_address_same_index_different_values_restore(int snapshot) Assert.AreEqual(_values[snapshot + 1], provider.GetTransientState(new StorageCell(ctx.Address1, 1))); } + /// + /// Commit will reset transient state + /// [Test] public void Commit_resets_transient_state() { @@ -301,6 +314,9 @@ public void Commit_resets_transient_state() Assert.True(provider.GetTransientState(new StorageCell(ctx.Address1, 2)).IsZero()); } + /// + /// Reset will reset transient state + /// [Test] public void Reset_resets_transient_state() { @@ -314,11 +330,15 @@ public void Reset_resets_transient_state() Assert.True(provider.GetTransientState(new StorageCell(ctx.Address1, 2)).IsZero()); } + /// + /// Transient state does not impact persistent state + /// + /// Snapshot to restore to [TestCase(-1)] [TestCase(0)] [TestCase(1)] [TestCase(2)] - public void Transient_state_restores_independent_of_permanent_state(int snapshot) + public void Transient_state_restores_independent_of_persistent_state(int snapshot) { Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); @@ -357,11 +377,15 @@ public void Transient_state_restores_independent_of_permanent_state(int snapshot _values[snapshot + 1].Should().BeEquivalentTo(provider.GetTransientState(new StorageCell(ctx.Address1, 1))); } + /// + /// Persistent state does not impact transient state + /// + /// Snapshot to restore to [TestCase(-1)] [TestCase(0)] [TestCase(1)] [TestCase(2)] - public void Permanent_state_restores_independent_of_transient_state(int snapshot) + public void Persistent_state_restores_independent_of_transient_state(int snapshot) { Context ctx = new(); StorageProvider provider = BuildStorageProvider(ctx); diff --git a/src/Nethermind/Nethermind.State/IStorageProvider.cs b/src/Nethermind/Nethermind.State/IStorageProvider.cs index 4b134e0e5e3..eb2f45cb5f1 100644 --- a/src/Nethermind/Nethermind.State/IStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/IStorageProvider.cs @@ -18,24 +18,67 @@ namespace Nethermind.State { + /// + /// Interface for the StorageProvider + /// Includes both persistent and transient storage + /// public interface IStorageProvider : IJournal { + /// + /// Return the original persistent storage value from the storage cell + /// + /// + /// byte[] GetOriginal(StorageCell storageCell); + /// + /// Get the persistent storage value at the specified storage cell + /// + /// Storage location + /// Value at cell byte[] Get(StorageCell storageCell); + /// + /// Set the provided value to persistent storage at the specified storage cell + /// + /// Storage location + /// Value to store void Set(StorageCell storageCell, byte[] newValue); + /// + /// Get the transient storage value at the specified storage cell + /// + /// Storage location + /// Value at cell byte[] GetTransientState(StorageCell storageCell); + /// + /// Set the provided value to transient storage at the specified storage cell + /// + /// Storage location + /// Value to store void SetTransientState(StorageCell storageCell, byte[] newValue); + /// + /// Reset all storage + /// void Reset(); + /// + /// Commit persisent storage trees + /// + /// Current block number void CommitTrees(long blockNumber); + /// + /// Commit persistent storage + /// void Commit(); + /// + /// Commit persistent storage + /// + /// State tracer void Commit(IStorageTracer stateTracer); /// @@ -51,6 +94,10 @@ public interface IStorageProvider : IJournal Snapshot.Storage IJournal.TakeSnapshot() => TakeSnapshot(); + /// + /// Clear all storage at specified address + /// + /// Contract address void ClearStorage(Address address); } } diff --git a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs index b40fa2a052f..823472d1436 100644 --- a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs +++ b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs @@ -23,6 +23,9 @@ namespace Nethermind.State { + /// + /// Contains common code for both Persistent and Transient storage providers + /// public abstract class PartialStorageProviderBase { protected readonly ResettableDictionary> _intraBlockCache = new(); @@ -45,16 +48,31 @@ protected PartialStorageProviderBase(ILogManager? logManager) _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); } + /// + /// Get the storage value at the specified storage cell + /// + /// Storage location + /// Value at cell public byte[] Get(StorageCell storageCell) { return GetCurrentValue(storageCell); } + /// + /// Set the provided value to storage at the specified storage cell + /// + /// Storage location + /// Value to store public void Set(StorageCell storageCell, byte[] newValue) { PushUpdate(storageCell, newValue); } + /// + /// Creates a restartable snapshot. + /// + /// Indicates new transaction will start here. + /// Snapshot index public int TakeSnapshot(bool newTransactionStart) { if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); @@ -66,6 +84,11 @@ public int TakeSnapshot(bool newTransactionStart) return _currentPosition; } + /// + /// Restore the state to the provided snapshot + /// + /// Snapshot index + /// Throws exception if snapshot is invalid public void Restore(int snapshot) { if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); @@ -130,6 +153,9 @@ public void Restore(int snapshot) } + /// + /// Commit persistent storage + /// public void Commit() { Commit(NullStorageTracer.Instance); @@ -153,6 +179,10 @@ public ChangeTrace(byte[]? after) public byte[] After { get; } } + /// + /// Commit persistent storage + /// + /// State tracer public void Commit(IStorageTracer tracer) { if (_currentPosition == Snapshot.EmptyPosition) @@ -165,6 +195,11 @@ public void Commit(IStorageTracer tracer) } } + /// + /// Called by Commit + /// Used for storage-specific logic + /// + /// Storage tracer protected virtual void CommitCore(IStorageTracer tracer) { Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition); @@ -172,7 +207,9 @@ protected virtual void CommitCore(IStorageTracer tracer) _transactionChangesSnapshots.Clear(); } - + /// + /// Reset the storage state + /// public virtual void Reset() { if (_logger.IsTrace) _logger.Trace("Resetting storage"); @@ -183,6 +220,12 @@ public virtual void Reset() Array.Clear(_changes, 0, _changes.Length); } + /// + /// Attempt to get the current value at the storage cell + /// + /// Storage location + /// Resulting value + /// True if value has been set protected bool TryGetCachedValue(StorageCell storageCell, out byte[]? bytes) { if (_intraBlockCache.TryGetValue(storageCell, out StackList stack)) @@ -198,8 +241,18 @@ protected bool TryGetCachedValue(StorageCell storageCell, out byte[]? bytes) return false; } + /// + /// Get the current value at the specified location + /// + /// Storage location + /// Value at location protected abstract byte[] GetCurrentValue(StorageCell storageCell); + /// + /// Update the storage cell with provided value + /// + /// Storage location + /// Value to set private void PushUpdate(StorageCell cell, byte[] value) { SetupRegistry(cell); @@ -208,11 +261,18 @@ private void PushUpdate(StorageCell cell, byte[] value) _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); } + /// + /// Increment position and size (if needed) of _changes + /// protected void IncrementChangePosition() { Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); } + /// + /// Initialize the StackList at the storage cell position if needed + /// + /// protected void SetupRegistry(StorageCell cell) { if (!_intraBlockCache.ContainsKey(cell)) @@ -221,6 +281,10 @@ protected void SetupRegistry(StorageCell cell) } } + /// + /// Clear all storage at specified address + /// + /// Contract address public virtual void ClearStorage(Address address) { // We are setting cached values to zero so we do not use previously set values @@ -234,6 +298,9 @@ public virtual void ClearStorage(Address address) } } + /// + /// Used for tracking each change to storage + /// protected class Change { public Change(ChangeType changeType, StorageCell storageCell, byte[] value) @@ -248,6 +315,9 @@ public Change(ChangeType changeType, StorageCell storageCell, byte[] value) public byte[] Value { get; } } + /// + /// Type of change to track + /// protected enum ChangeType { JustCache, diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index 10b4de0f8bd..1a61a142a77 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -26,6 +26,10 @@ namespace Nethermind.State { + /// + /// Manages persistent storage allowing for snapshotting and restoring + /// Persists data to ITrieStore + /// public class PersistentStorageProvider : PartialStorageProviderBase { private readonly ITrieStore _trieStore; @@ -46,6 +50,9 @@ public PersistentStorageProvider(ITrieStore? trieStore, IStateProvider? statePro _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); } + /// + /// Reset the storage state + /// public override void Reset() { base.Reset(); @@ -54,9 +61,19 @@ public override void Reset() _committedThisRound.Clear(); } + /// + /// Get the current value at the specified location + /// + /// Storage location + /// Value at location protected override byte[] GetCurrentValue(StorageCell storageCell) => TryGetCachedValue(storageCell, out byte[]? bytes) ? bytes! : LoadFromTree(storageCell); + /// + /// Return the original persistent storage value from the storage cell + /// + /// + /// public byte[] GetOriginal(StorageCell storageCell) { if (!_originalValues.ContainsKey(storageCell)) @@ -78,6 +95,12 @@ public byte[] GetOriginal(StorageCell storageCell) return _originalValues[storageCell]; } + + /// + /// Called by Commit + /// Used for persistent storage specific logic + /// + /// Storage tracer protected override void CommitCore(IStorageTracer tracer) { if (_logger.IsTrace) _logger.Trace("Committing storage changes"); @@ -187,6 +210,10 @@ protected override void CommitCore(IStorageTracer tracer) } } + /// + /// Commit persisent storage trees + /// + /// Current block number public void CommitTrees(long blockNumber) { // _logger.Warn($"Storage block commit {blockNumber}"); @@ -252,6 +279,10 @@ private Keccak RecalculateRootHash(Address address) return storageTree.RootHash; } + /// + /// Clear all storage at specified address + /// + /// Contract address public override void ClearStorage(Address address) { base.ClearStorage(address); diff --git a/src/Nethermind/Nethermind.State/Snapshot.cs b/src/Nethermind/Nethermind.State/Snapshot.cs index 01785492778..a6429daef46 100644 --- a/src/Nethermind/Nethermind.State/Snapshot.cs +++ b/src/Nethermind/Nethermind.State/Snapshot.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Demerzel Solutions Limited +// Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. // // The Nethermind library is free software: you can redistribute it and/or modify @@ -34,6 +34,9 @@ public Snapshot(int stateSnapshot, Storage storageSnapshot) StorageSnapshot = storageSnapshot; } + /// + /// Tracks snapshot positions for Persistent and Transient storage + /// public readonly struct Storage { public static readonly Storage Empty = new(EmptyPosition, EmptyPosition); diff --git a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs index 25822d1ee81..73cdd945f7f 100644 --- a/src/Nethermind/Nethermind.State/TransientStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/TransientStorageProvider.cs @@ -28,6 +28,11 @@ public class TransientStorageProvider : PartialStorageProviderBase public TransientStorageProvider(ILogManager? logManager) : base(logManager) { } + /// + /// Get the storage value at the specified storage cell + /// + /// Storage location + /// Value at cell protected override byte[] GetCurrentValue(StorageCell storageCell) => TryGetCachedValue(storageCell, out byte[]? bytes) ? bytes! : _zeroValue; }