-
Notifications
You must be signed in to change notification settings - Fork 104
/
Copy pathSmartContractStorage.cs
253 lines (210 loc) · 8.3 KB
/
SmartContractStorage.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
using Neo.Json;
using System;
using System.Buffers.Binary;
using System.Numerics;
namespace Neo.SmartContract.Testing.Storage
{
public class SmartContractStorage
{
private readonly SmartContract _smartContract;
private int? _contractId;
/// <summary>
/// Storage Id
/// </summary>
public int Id => GetContractId();
/// <summary>
/// Constructor
/// </summary>
/// <param name="smartContract">Smart Contract</param>
/// <param name="contractId">Contract id, can be null</param>
internal SmartContractStorage(SmartContract smartContract, int? contractId = null)
{
_smartContract = smartContract;
_contractId = contractId;
}
private int GetContractId()
{
// If it was not initialized checking the contract, we need to query the contract id
if (_contractId is not null) return _contractId.Value;
var state = _smartContract.Engine.Native.ContractManagement.GetContract(_smartContract.Hash)
?? throw new Exception($"The contract {_smartContract.Hash} is not deployed, so it's not possible to get the storage id.");
_contractId = state.Id;
return _contractId.Value;
}
/// <summary>
/// Check if the entry exist
/// </summary>
/// <param name="key">Key</param>
public bool Contains(byte key) => Contains(new byte[] { key });
/// <summary>
/// Check if the entry exist
/// </summary>
/// <param name="key">Key</param>
public bool Contains(string key) => Contains(Utility.StrictUTF8.GetBytes(key));
/// <summary>
/// Check if the entry exist
/// </summary>
/// <param name="key">Key</param>
public bool Contains(ReadOnlyMemory<byte> key)
{
var skey = new StorageKey() { Id = GetContractId(), Key = key };
var entry = _smartContract.Engine.Storage.Snapshot.TryGet(skey);
return entry != null;
}
/// <summary>
/// Read an entry from the smart contract storage
/// </summary>
/// <param name="key">Key</param>
public ReadOnlyMemory<byte> Get(byte key) => Get(new byte[] { key });
/// <summary>
/// Read an entry from the smart contract storage
/// </summary>
/// <param name="key">Key</param>
public ReadOnlyMemory<byte> Get(string key) => Get(Utility.StrictUTF8.GetBytes(key));
/// <summary>
/// Read an entry from the smart contract storage
/// </summary>
/// <param name="key">Key</param>
public ReadOnlyMemory<byte> Get(ReadOnlyMemory<byte> key)
{
var skey = new StorageKey() { Id = GetContractId(), Key = key };
var entry = _smartContract.Engine.Storage.Snapshot.TryGet(skey);
if (entry != null)
{
return entry.Value;
}
return null;
}
/// <summary>
/// Put an entry in the smart contract storage
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
public void Put(byte key, ReadOnlyMemory<byte> value) => Put(new byte[] { key }, value);
/// <summary>
/// Put an entry in the smart contract storage
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
public void Put(string key, ReadOnlyMemory<byte> value) => Put(Utility.StrictUTF8.GetBytes(key), value);
/// <summary>
/// Put an entry in the smart contract storage
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
public void Put(ReadOnlyMemory<byte> key, ReadOnlyMemory<byte> value)
{
var skey = new StorageKey() { Id = GetContractId(), Key = key };
var entry = _smartContract.Engine.Storage.Snapshot.GetAndChange(skey, () => new StorageItem() { Value = value });
entry.Value = value;
}
/// <summary>
/// Put an entry in the smart contract storage
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
public void Put(byte key, BigInteger value) => Put(new byte[] { key }, value);
/// <summary>
/// Put an entry in the smart contract storage
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
public void Put(string key, BigInteger value) => Put(Utility.StrictUTF8.GetBytes(key), value);
/// <summary>
/// Put an entry in the smart contract storage
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
public void Put(ReadOnlyMemory<byte> key, BigInteger value)
{
var skey = new StorageKey() { Id = GetContractId(), Key = key };
var entry = _smartContract.Engine.Storage.Snapshot.GetAndChange(skey, () => new StorageItem(value));
entry.Set(value);
}
/// <summary>
/// Remove an entry from the smart contract storage
/// </summary>
/// <param name="key">Key</param>
public void Remove(byte key) => Remove(new byte[] { key });
/// <summary>
/// Remove an entry from the smart contract storage
/// </summary>
/// <param name="key">Key</param>
public void Remove(string key) => Remove(Utility.StrictUTF8.GetBytes(key));
/// <summary>
/// Remove an entry from the smart contract storage
/// </summary>
/// <param name="key">Key</param>
public void Remove(ReadOnlyMemory<byte> key)
{
var skey = new StorageKey() { Id = GetContractId(), Key = key };
_smartContract.Engine.Storage.Snapshot.Delete(skey);
}
/// <summary>
/// Import data from json, expected data (in base64):
/// - "prefix" : { "key":"value" }
/// </summary>
/// <param name="json">Json Object</param>
public void Import(string json)
{
if (JToken.Parse(json) is not JObject jo)
{
throw new FormatException("The json is not a valid JObject");
}
Import(jo);
}
/// <summary>
/// Import data from json, expected data (in base64):
/// - "prefix" : { "key":"value" }
/// </summary>
/// <param name="json">Json Object</param>
public void Import(JObject json)
{
var buffer = new byte[(sizeof(int))];
BinaryPrimitives.WriteInt32LittleEndian(buffer, GetContractId());
var keyId = Convert.ToBase64String(buffer);
JObject prefix;
// Find prefix
if (json.ContainsProperty(keyId))
{
if (json[keyId] is not JObject jo)
{
throw new FormatException("Invalid json");
}
prefix = jo;
}
else
{
return;
}
// Read values
foreach (var entry in prefix.Properties)
{
if (entry.Value is JString str)
{
// "key":"value" in base64
Put(Convert.FromBase64String(entry.Key), Convert.FromBase64String(str.Value));
}
}
}
/// <summary>
/// Export data to json
/// </summary>
public JObject Export()
{
var buffer = new byte[(sizeof(int))];
BinaryPrimitives.WriteInt32LittleEndian(buffer, GetContractId());
var keyId = Convert.ToBase64String(buffer);
// Write prefix
JObject ret = new();
JObject prefix = new();
ret[keyId] = prefix;
foreach (var entry in _smartContract.Engine.Storage.Snapshot.Seek(Array.Empty<byte>(), Persistence.SeekDirection.Forward))
{
// "key":"value" in base64
prefix[Convert.ToBase64String(entry.Key.Key.ToArray())] = Convert.ToBase64String(entry.Value.Value.ToArray());
}
return ret;
}
}
}