Skip to content

Commit bcccaf5

Browse files
AA bundler storage (rocksdb), bls aggregator, reputation extensions, plus further test coverage
1 parent e692fc1 commit bcccaf5

33 files changed

Lines changed: 3146 additions & 36 deletions
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
using RocksDbSharp;
2+
3+
namespace Nethereum.AccountAbstraction.Bundler.RocksDB
4+
{
5+
public class BundlerRocksDbManager : IDisposable
6+
{
7+
public const string CF_USEROP_PENDING = "userop_pending";
8+
public const string CF_USEROP_SUBMITTED = "userop_submitted";
9+
public const string CF_USEROP_INCLUDED = "userop_included";
10+
public const string CF_USEROP_FAILED = "userop_failed";
11+
public const string CF_SENDER_INDEX = "sender_index";
12+
public const string CF_TX_MAPPING = "tx_mapping";
13+
public const string CF_REPUTATION = "reputation";
14+
public const string CF_METADATA = "metadata";
15+
16+
private static readonly string[] ColumnFamilyNames = new[]
17+
{
18+
CF_USEROP_PENDING,
19+
CF_USEROP_SUBMITTED,
20+
CF_USEROP_INCLUDED,
21+
CF_USEROP_FAILED,
22+
CF_SENDER_INDEX,
23+
CF_TX_MAPPING,
24+
CF_REPUTATION,
25+
CF_METADATA
26+
};
27+
28+
private readonly RocksDb _database;
29+
private readonly Dictionary<string, ColumnFamilyHandle> _columnFamilies;
30+
private readonly BundlerRocksDbOptions _options;
31+
private bool _disposed;
32+
33+
public BundlerRocksDbManager(BundlerRocksDbOptions options)
34+
{
35+
_options = options ?? new BundlerRocksDbOptions();
36+
_columnFamilies = new Dictionary<string, ColumnFamilyHandle>();
37+
38+
var dbOptions = CreateDbOptions();
39+
var cfOptions = CreateColumnFamilyOptions();
40+
41+
var columnFamilies = new ColumnFamilies();
42+
foreach (var cfName in ColumnFamilyNames)
43+
{
44+
columnFamilies.Add(cfName, cfOptions);
45+
}
46+
47+
_database = RocksDb.Open(dbOptions, _options.DatabasePath, columnFamilies);
48+
49+
foreach (var cfName in ColumnFamilyNames)
50+
{
51+
_columnFamilies[cfName] = _database.GetColumnFamily(cfName);
52+
}
53+
}
54+
55+
public RocksDb Database => _database;
56+
57+
public ColumnFamilyHandle GetColumnFamily(string name)
58+
{
59+
if (_columnFamilies.TryGetValue(name, out var handle))
60+
{
61+
return handle;
62+
}
63+
throw new ArgumentException($"Column family '{name}' not found");
64+
}
65+
66+
public WriteBatch CreateWriteBatch()
67+
{
68+
return new WriteBatch();
69+
}
70+
71+
public Snapshot CreateSnapshot()
72+
{
73+
return _database.CreateSnapshot();
74+
}
75+
76+
public void Write(WriteBatch batch, WriteOptions? writeOptions = null)
77+
{
78+
_database.Write(batch, writeOptions);
79+
}
80+
81+
public byte[]? Get(string columnFamily, byte[] key, ReadOptions? readOptions = null)
82+
{
83+
var cf = GetColumnFamily(columnFamily);
84+
return _database.Get(key, cf, readOptions);
85+
}
86+
87+
public void Put(string columnFamily, byte[] key, byte[] value, WriteOptions? writeOptions = null)
88+
{
89+
var cf = GetColumnFamily(columnFamily);
90+
_database.Put(key, value, cf, writeOptions);
91+
}
92+
93+
public void Delete(string columnFamily, byte[] key, WriteOptions? writeOptions = null)
94+
{
95+
var cf = GetColumnFamily(columnFamily);
96+
_database.Remove(key, cf, writeOptions);
97+
}
98+
99+
public bool KeyExists(string columnFamily, byte[] key, ReadOptions? readOptions = null)
100+
{
101+
var cf = GetColumnFamily(columnFamily);
102+
var value = _database.Get(key, cf, readOptions);
103+
return value != null;
104+
}
105+
106+
public Iterator CreateIterator(string columnFamily, ReadOptions? readOptions = null)
107+
{
108+
var cf = GetColumnFamily(columnFamily);
109+
return _database.NewIterator(cf, readOptions);
110+
}
111+
112+
public void Flush()
113+
{
114+
_database.Flush(new FlushOptions());
115+
}
116+
117+
public void Compact()
118+
{
119+
foreach (var cf in _columnFamilies.Values)
120+
{
121+
_database.CompactRange((byte[]?)null, (byte[]?)null, cf);
122+
}
123+
}
124+
125+
private DbOptions CreateDbOptions()
126+
{
127+
var options = new DbOptions()
128+
.SetCreateIfMissing(true)
129+
.SetCreateMissingColumnFamilies(true)
130+
.SetMaxBackgroundCompactions(_options.MaxBackgroundCompactions)
131+
.SetMaxBackgroundFlushes(_options.MaxBackgroundFlushes);
132+
133+
if (_options.EnableStatistics)
134+
{
135+
options.EnableStatistics();
136+
}
137+
138+
return options;
139+
}
140+
141+
private ColumnFamilyOptions CreateColumnFamilyOptions()
142+
{
143+
var blockBasedOptions = new BlockBasedTableOptions()
144+
.SetBlockCache(Cache.CreateLru((ulong)_options.BlockCacheSize))
145+
.SetFilterPolicy(BloomFilterPolicy.Create(_options.BloomFilterBitsPerKey));
146+
147+
var cfOptions = new ColumnFamilyOptions()
148+
.SetBlockBasedTableFactory(blockBasedOptions)
149+
.SetWriteBufferSize((ulong)_options.WriteBufferSize)
150+
.SetMaxWriteBufferNumber(_options.MaxWriteBufferNumber)
151+
.SetCompression(Compression.Lz4);
152+
153+
return cfOptions;
154+
}
155+
156+
public void Dispose()
157+
{
158+
Dispose(true);
159+
GC.SuppressFinalize(this);
160+
}
161+
162+
protected virtual void Dispose(bool disposing)
163+
{
164+
if (!_disposed)
165+
{
166+
if (disposing)
167+
{
168+
_database?.Dispose();
169+
}
170+
_disposed = true;
171+
}
172+
}
173+
}
174+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Nethereum.AccountAbstraction.Bundler.RocksDB
2+
{
3+
public class BundlerRocksDbOptions
4+
{
5+
public string DatabasePath { get; set; } = "./bundlerdata";
6+
public long BlockCacheSize { get; set; } = 64 * 1024 * 1024; // 64MB
7+
public int MaxBackgroundCompactions { get; set; } = 2;
8+
public int MaxBackgroundFlushes { get; set; } = 1;
9+
public int BloomFilterBitsPerKey { get; set; } = 10;
10+
public bool EnableStatistics { get; set; } = false;
11+
public long WriteBufferSize { get; set; } = 32 * 1024 * 1024; // 32MB
12+
public int MaxWriteBufferNumber { get; set; } = 2;
13+
public int MaxMempoolSize { get; set; } = 10000;
14+
public TimeSpan EntryTtl { get; set; } = TimeSpan.FromMinutes(30);
15+
public TimeSpan IncludedEntryRetention { get; set; } = TimeSpan.FromMinutes(5);
16+
}
17+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Nethereum.AccountAbstraction.Bundler.Mempool;
3+
using Nethereum.AccountAbstraction.Bundler.Reputation;
4+
using Nethereum.AccountAbstraction.Bundler.RocksDB.Serialization;
5+
using Nethereum.AccountAbstraction.Bundler.RocksDB.Stores;
6+
7+
namespace Nethereum.AccountAbstraction.Bundler.RocksDB
8+
{
9+
public static class BundlerRocksDbServiceCollectionExtensions
10+
{
11+
public static IServiceCollection AddBundlerRocksDbStorage(
12+
this IServiceCollection services,
13+
string databasePath)
14+
{
15+
return services.AddBundlerRocksDbStorage(new BundlerRocksDbOptions { DatabasePath = databasePath });
16+
}
17+
18+
public static IServiceCollection AddBundlerRocksDbStorage(
19+
this IServiceCollection services,
20+
BundlerRocksDbOptions? options = null,
21+
ReputationConfig? reputationConfig = null)
22+
{
23+
options ??= new BundlerRocksDbOptions();
24+
reputationConfig ??= new ReputationConfig();
25+
26+
services.AddSingleton(options);
27+
services.AddSingleton(reputationConfig);
28+
29+
services.AddSingleton<BundlerRocksDbManager>(sp =>
30+
{
31+
var opts = sp.GetRequiredService<BundlerRocksDbOptions>();
32+
return new BundlerRocksDbManager(opts);
33+
});
34+
35+
services.AddSingleton<IUserOpMempool>(sp =>
36+
{
37+
var manager = sp.GetRequiredService<BundlerRocksDbManager>();
38+
var opts = sp.GetRequiredService<BundlerRocksDbOptions>();
39+
return new RocksDbUserOpMempool(manager, opts);
40+
});
41+
42+
services.AddSingleton<IReputationStore>(sp =>
43+
{
44+
var manager = sp.GetRequiredService<BundlerRocksDbManager>();
45+
var config = sp.GetRequiredService<ReputationConfig>();
46+
return new RocksDbReputationStore(manager, config);
47+
});
48+
49+
return services;
50+
}
51+
52+
public static IServiceCollection AddBundlerRocksDbMempoolOnly(
53+
this IServiceCollection services,
54+
BundlerRocksDbOptions? options = null)
55+
{
56+
options ??= new BundlerRocksDbOptions();
57+
58+
services.AddSingleton(options);
59+
60+
services.AddSingleton<BundlerRocksDbManager>(sp =>
61+
{
62+
var opts = sp.GetRequiredService<BundlerRocksDbOptions>();
63+
return new BundlerRocksDbManager(opts);
64+
});
65+
66+
services.AddSingleton<IUserOpMempool>(sp =>
67+
{
68+
var manager = sp.GetRequiredService<BundlerRocksDbManager>();
69+
var opts = sp.GetRequiredService<BundlerRocksDbOptions>();
70+
return new RocksDbUserOpMempool(manager, opts);
71+
});
72+
73+
return services;
74+
}
75+
76+
public static IServiceCollection AddBundlerRocksDbReputationOnly(
77+
this IServiceCollection services,
78+
BundlerRocksDbOptions? options = null,
79+
ReputationConfig? reputationConfig = null)
80+
{
81+
options ??= new BundlerRocksDbOptions();
82+
reputationConfig ??= new ReputationConfig();
83+
84+
services.AddSingleton(options);
85+
services.AddSingleton(reputationConfig);
86+
87+
if (!services.Any(s => s.ServiceType == typeof(BundlerRocksDbManager)))
88+
{
89+
services.AddSingleton<BundlerRocksDbManager>(sp =>
90+
{
91+
var opts = sp.GetRequiredService<BundlerRocksDbOptions>();
92+
return new BundlerRocksDbManager(opts);
93+
});
94+
}
95+
96+
services.AddSingleton<IReputationStore>(sp =>
97+
{
98+
var manager = sp.GetRequiredService<BundlerRocksDbManager>();
99+
var config = sp.GetRequiredService<ReputationConfig>();
100+
return new RocksDbReputationStore(manager, config);
101+
});
102+
103+
return services;
104+
}
105+
}
106+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
3+
<PropertyGroup>
4+
<Description>Nethereum Account Abstraction Bundler RocksDB - Persistent storage for ERC-4337 bundler mempool and reputation</Description>
5+
<AssemblyTitle>Nethereum.AccountAbstraction.Bundler.RocksDB</AssemblyTitle>
6+
<Version>$(NethereumVersion)</Version>
7+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
8+
<AssemblyName>Nethereum.AccountAbstraction.Bundler.RocksDB</AssemblyName>
9+
<PackageId>Nethereum.AccountAbstraction.Bundler.RocksDB</PackageId>
10+
<ImplicitUsings>enable</ImplicitUsings>
11+
<Nullable>enable</Nullable>
12+
</PropertyGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="RocksDB" Version="10.4.2.62149" />
16+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\Nethereum.AccountAbstraction.Bundler\Nethereum.AccountAbstraction.Bundler.csproj" />
21+
<ProjectReference Include="..\Nethereum.RLP\Nethereum.RLP.csproj" />
22+
</ItemGroup>
23+
24+
<PropertyGroup>
25+
<SignAssembly>false</SignAssembly>
26+
</PropertyGroup>
27+
28+
</Project>

0 commit comments

Comments
 (0)