Skip to content

Commit c1f6870

Browse files
authored
contract id introduced (#482)
1 parent bcd2237 commit c1f6870

File tree

7 files changed

+41
-24
lines changed

7 files changed

+41
-24
lines changed

src/Paprika.Tests/Store/BasePageTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public override void RegisterForFutureReuse(Page page, bool possibleImmediateReu
6565
.BeTrue($"Page at {addr} should not be registered as reusable before");
6666
}
6767

68-
public override Dictionary<Keccak, uint> IdCache { get; } = new();
68+
public override Dictionary<Keccak, ContractId> IdCache { get; } = new();
6969

7070
public override string ToString() => $"Batch context used {_pageCount} pages to write the data";
7171

src/Paprika/Store/BatchContextBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public Page GetWritableCopy(Page page)
4343

4444
public virtual void NoticeAbandonedPageReused(Page page) { }
4545

46-
public abstract IDictionary<Keccak, uint> IdCache { get; }
46+
public abstract IDictionary<Keccak, ContractId> IdCache { get; }
4747

4848
/// <summary>
4949
/// Assigns the batch identifier to a given page, marking it writable by this batch.

src/Paprika/Store/ContractId.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Paprika.Store;
2+
3+
/// <summary>
4+
/// Represents a contract id.
5+
/// </summary>
6+
/// <remarks>
7+
/// A contract id is an uint uniquely assigned to a contract. It's done by persisting a mapping between
8+
/// between its address' Keccak and an auto-incremented uint.
9+
/// </remarks>
10+
/// <param name="Value">The underlying value.</param>
11+
public readonly record struct ContractId(uint Value)
12+
{
13+
private const uint NullValue = 0;
14+
public bool IsNull => Value == NullValue;
15+
16+
public static readonly ContractId Null = new(NullValue);
17+
}

src/Paprika/Store/IBatchContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public interface IReadOnlyBatchContext : IPageResolver
122122
/// </summary>
123123
uint BatchId { get; }
124124

125-
IDictionary<Keccak, uint> IdCache { get; }
125+
IDictionary<Keccak, ContractId> IdCache { get; }
126126
}
127127

128128
/// <summary>

src/Paprika/Store/PagedDb.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,9 @@ private DbAddress SetNewRoot(RootPage root)
471471
private sealed class ReadOnlyBatch(PagedDb db, RootPage root, string name)
472472
: IVisitableReadOnlyBatch, IReadOnlyBatchContext
473473
{
474-
[ThreadStatic] private static ConcurrentDictionary<Keccak, uint>? s_cache;
474+
[ThreadStatic] private static ConcurrentDictionary<Keccak, ContractId>? s_cache;
475475

476-
private ConcurrentDictionary<Keccak, uint> _idCache = Interlocked.Exchange(ref s_cache, null) ?? new(
476+
private ConcurrentDictionary<Keccak, ContractId> _idCache = Interlocked.Exchange(ref s_cache, null) ?? new(
477477
Environment.ProcessorCount,
478478
RootPage.IdCacheLimit);
479479

@@ -522,7 +522,7 @@ public bool TryGet(scoped in Key key, out ReadOnlySpan<byte> result)
522522

523523
public uint BatchId => root.Header.BatchId;
524524

525-
public IDictionary<Keccak, uint> IdCache
525+
public IDictionary<Keccak, ContractId> IdCache
526526
{
527527
get
528528
{
@@ -835,7 +835,7 @@ public override void NoticeAbandonedPageReused(Page page)
835835
#endif
836836
}
837837

838-
public override Dictionary<Keccak, uint> IdCache { get; }
838+
public override Dictionary<Keccak, ContractId> IdCache { get; }
839839

840840
public void Dispose()
841841
{
@@ -865,11 +865,11 @@ public unsafe Context()
865865
Page = new((byte*)NativeMemory.AlignedAlloc(Page.PageSize, (UIntPtr)UIntPtr.Size));
866866
Abandoned = new List<DbAddress>();
867867
Written = new HashSet<DbAddress>();
868-
IdCache = new Dictionary<Keccak, uint>();
868+
IdCache = new Dictionary<Keccak, ContractId>();
869869
ReusedImmediately = new Stack<DbAddress>();
870870
}
871871

872-
public Dictionary<Keccak, uint> IdCache { get; }
872+
public Dictionary<Keccak, ContractId> IdCache { get; }
873873

874874
public Page Page { get; }
875875

src/Paprika/Store/RootPage.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public bool TryGet(scoped in Key key, IReadOnlyBatchContext batch, out ReadOnlyS
123123

124124
if (cache.TryGetValue(keccak, out var id))
125125
{
126-
if (id == 0)
126+
if (id.IsNull)
127127
{
128128
result = default;
129129
return false;
@@ -168,7 +168,7 @@ public void SetRaw(in Key key, IBatchContext batch, ReadOnlySpan<byte> rawData)
168168
Data.AccountCounter++;
169169

170170
// memoize in cache
171-
batch.IdCache[keccak] = id = Data.AccountCounter;
171+
batch.IdCache[keccak] = id = new ContractId(Data.AccountCounter);
172172

173173
// update root
174174
Data.Storage.SetId(keccak, id, batch);
@@ -192,7 +192,7 @@ public void Destroy(IBatchContext batch, in NibblePath account)
192192
var keccak = account.UnsafeAsKeccak;
193193

194194
// Destroy the Id entry about it
195-
Data.Storage.SetId(keccak, 0, batch);
195+
Data.Storage.SetId(keccak, ContractId.Null, batch);
196196

197197
// Destroy the account entry
198198
SetAtRoot(batch, account, ReadOnlySpan<byte>.Empty, ref Data.StateRoot);

src/Paprika/Store/StorageFanOut.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,26 +120,27 @@ public void Accept(IPageVisitor visitor, IPageResolver resolver)
120120
}
121121
}
122122

123-
public bool TryGetId(in Keccak keccak, out uint id, IPageResolver batch)
123+
public bool TryGetId(in Keccak keccak, out ContractId id, IPageResolver batch)
124124
{
125+
Unsafe.SkipInit(out id);
126+
125127
var at = BuildIdIndex(NibblePath.FromKey(keccak), out var sliced);
126128

127129
if (TryGet(batch, at, sliced, Type.Id, out var result))
128130
{
129-
id = BinaryPrimitives.ReadUInt32LittleEndian(result);
131+
id = new ContractId(BinaryPrimitives.ReadUInt32LittleEndian(result));
130132
return true;
131133
}
132134

133-
id = default;
134135
return false;
135136
}
136137

137-
public void SetId(Keccak keccak, uint id, IBatchContext batch)
138+
public void SetId(Keccak keccak, ContractId id, IBatchContext batch)
138139
{
139140
var at = BuildIdIndex(NibblePath.FromKey(keccak), out var sliced);
140141

141142
Span<byte> span = stackalloc byte[4];
142-
BinaryPrimitives.WriteUInt32LittleEndian(span, id);
143+
BinaryPrimitives.WriteUInt32LittleEndian(span, id.Value);
143144

144145
Set(batch, at, sliced, Type.Id, span);
145146
}
@@ -170,18 +171,18 @@ private static uint BuildIdIndex(in NibblePath path, out NibblePath sliced)
170171
/// </summary>
171172
public static int NormalizeAtForId(uint at) => (int)(at >> IdShift);
172173

173-
public bool TryGetStorage(uint id, scoped in NibblePath path, out ReadOnlySpan<byte> result,
174+
public bool TryGetStorage(ContractId id, scoped in NibblePath path, out ReadOnlySpan<byte> result,
174175
IReadOnlyBatchContext batch) =>
175-
TryGet(batch, id, path, Type.Storage, out result);
176+
TryGet(batch, id.Value, path, Type.Storage, out result);
176177

177-
public void SetStorage(uint id, scoped in NibblePath path, ReadOnlySpan<byte> data, IBatchContext batch)
178+
public void SetStorage(ContractId id, scoped in NibblePath path, ReadOnlySpan<byte> data, IBatchContext batch)
178179
{
179-
Set(batch, id, path, Type.Storage, data);
180+
Set(batch, id.Value, path, Type.Storage, data);
180181
}
181182

182-
public void DeleteStorageByPrefix(uint id, scoped in NibblePath prefix, IBatchContext batch)
183+
public void DeleteStorageByPrefix(ContractId id, scoped in NibblePath prefix, IBatchContext batch)
183184
{
184-
var (next, index) = GetIndex(id, Level);
185+
var (next, index) = GetIndex(id.Value, Level);
185186
var addr = _addresses[index];
186187

187188
if (addr.IsNull)
@@ -453,7 +454,6 @@ public Page Set(uint at, in NibblePath key, in ReadOnlySpan<byte> data, IBatchCo
453454
var addr = Data.Addresses[index];
454455

455456
var child = addr.IsNull
456-
457457
? batch.GetNewCleanPage<BottomPage>(out addr, StartLevel).AsPage()
458458
: batch.EnsureWritableCopy(ref addr);
459459

0 commit comments

Comments
 (0)