diff --git a/src/Paprika.Runner/Program.cs b/src/Paprika.Runner/Program.cs index ff3d3c0a..e0fb89c5 100644 --- a/src/Paprika.Runner/Program.cs +++ b/src/Paprika.Runner/Program.cs @@ -129,7 +129,7 @@ public static async Task Main(String[] args) ctx.Refresh(); })); - await using (var blockchain = new Blockchain(db, FlushEvery, 1000, reporter.Observe)) + await using (var blockchain = new Blockchain(db, null, FlushEvery, 1000, reporter.Observe)) { counter = Writer(blockchain, bigStorageAccount, random, layout[writing]); } diff --git a/src/Paprika/Chain/Blockchain.cs b/src/Paprika/Chain/Blockchain.cs index 82350eb3..3e04e61a 100644 --- a/src/Paprika/Chain/Blockchain.cs +++ b/src/Paprika/Chain/Blockchain.cs @@ -44,6 +44,7 @@ public class Blockchain : IAsyncDisposable private readonly MetricsExtensions.IAtomicIntGauge _flusherQueueCount; private readonly PagedDb _db; + private readonly IPreCommitBehavior? _preCommit; private readonly TimeSpan _minFlushDelay; private readonly Action? _beforeMetricsDisposed; private readonly Task _flusher; @@ -52,9 +53,10 @@ public class Blockchain : IAsyncDisposable private static readonly TimeSpan DefaultFlushDelay = TimeSpan.FromSeconds(1); - public Blockchain(PagedDb db, TimeSpan? minFlushDelay = null, int? finalizationQueueLimit = null, Action? beforeMetricsDisposed = null) + public Blockchain(PagedDb db, IPreCommitBehavior? preCommit = null, TimeSpan? minFlushDelay = null, int? finalizationQueueLimit = null, Action? beforeMetricsDisposed = null) { _db = db; + _preCommit = preCommit; _minFlushDelay = minFlushDelay ?? DefaultFlushDelay; _beforeMetricsDisposed = beforeMetricsDisposed; @@ -270,7 +272,7 @@ public bool TryGet(in Key key, out ReadOnlySpan result) /// /// Represents a block that is a result of ExecutionPayload, storing it in a in-memory trie /// - private class Block : RefCountingDisposable, IWorldState + private class Block : RefCountingDisposable, IWorldState, ICommit { public Keccak Hash { get; } public Keccak ParentHash { get; } @@ -316,6 +318,9 @@ public void Commit() // acquires one more lease for this block as it is stored in the blockchain AcquireLease(); + // run pre-commit + _blockchain._preCommit?.BeforeCommit(this); + // set to blocks in number and in blocks by hash _blockchain._blocksByNumber.AddOrUpdate(BlockNumber, static (_, block) => new[] { block }, @@ -391,7 +396,9 @@ public void SetStorage(in Keccak key, in Keccak address, UInt256 value) Set(Key.StorageCell(path, address), payload); } - private void Set(in Key key, in ReadOnlySpan payload) + public bool TryGet(in Key key, out ReadOnlySpanOwner result) => throw new NotImplementedException("Not implemented yet"); + + public void Set(in Key key, in ReadOnlySpan payload) { InBlockMap map; @@ -417,6 +424,8 @@ private void Set(in Key key, in ReadOnlySpan payload) map.TrySet(key, payload); } + public IKeyEnumerator GetEnumerator() => throw new NotImplementedException("Not implemented yet"); + private ReadOnlySpanOwner Get(int bloom, in Key key) { if (TryAcquireLease() == false) diff --git a/src/Paprika/Chain/IPreCommitBehavior.cs b/src/Paprika/Chain/IPreCommitBehavior.cs new file mode 100644 index 00000000..b44fe142 --- /dev/null +++ b/src/Paprika/Chain/IPreCommitBehavior.cs @@ -0,0 +1,74 @@ +using Paprika.Data; +using Paprika.Utils; + +namespace Paprika.Chain; + +/// +/// A pre-commit behavior run by component just before commiting a +/// with . Useful to provide concerns, like the Merkle construct and others. +/// +public interface IPreCommitBehavior +{ + /// + /// Executed just before commit. + /// + /// The object representing the commit. + public void BeforeCommit(ICommit commit); +} + +/// +/// The set of changes applied by . +/// Allows for additional modifications of the data just before the commit. +/// +/// +/// To access all the keys use the enumerator: +/// +/// public static void Foreach(this ICommit commit) +/// { +/// foreach (var key in commit) +/// { +/// key. +/// } +/// } +/// +public interface ICommit +{ + /// + /// Tries to retrieve the result stored under the given key. + /// + /// + /// Whether the retrieval was successful. + /// + /// + /// If successful, returns a result as an owner. Must be disposed properly. + /// + public bool TryGet(in Key key, out ReadOnlySpanOwner result); + + /// + /// Sets the value under the given key. + /// + void Set(in Key key, in ReadOnlySpan payload); + + /// + /// Gets the enumerator for the keys in the given commit. + /// + /// + IKeyEnumerator GetEnumerator(); +} + +/// +/// The enumerator. +/// +public interface IKeyEnumerator : IDisposable +{ + /// + /// The current key. + /// + ref readonly Key Current { get; } + + /// + /// Moves to the next. + /// + public bool MoveNext(); +} +