koilib 5.5.3 → 5.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,378 @@
1
+ import { sha256 } from "@noble/hashes/sha256";
2
+ import {
3
+ DictionaryGenesisData,
4
+ GenesisDataDecoded,
5
+ GenesisDataEncoded,
6
+ } from "./interface";
7
+ import { Serializer } from "./Serializer";
8
+ import {
9
+ multihash,
10
+ encodeBase64,
11
+ decodeBase64,
12
+ encodeBase64url,
13
+ decodeBase64url,
14
+ encodeBase58,
15
+ decodeBase58,
16
+ } from "./utils";
17
+ import chainJson from "./jsonDescriptors/chain-proto.json";
18
+
19
+ const defaultAlias: DictionaryGenesisData = {
20
+ "object_key::head_block": { typeName: "block" },
21
+ "object_key::chain_id": {},
22
+ "object_key::genesis_key": { isAddress: true },
23
+ "object_key::resource_limit_data": { typeName: "resource_limit_data" },
24
+ "object_key::max_account_resources": { typeName: "max_account_resources" },
25
+ "object_key::protocol_descriptor": {},
26
+ "object_key::compute_bandwidth_registry": {
27
+ typeName: "compute_bandwidth_registry",
28
+ },
29
+ "object_key::block_hash_code": {},
30
+ };
31
+
32
+ function prepareDictionary(
33
+ dictionary: DictionaryGenesisData
34
+ ): DictionaryGenesisData {
35
+ const serializerChain = new Serializer(chainJson, { bytesConversion: true });
36
+
37
+ const defaultDictionary: DictionaryGenesisData = {};
38
+ Object.keys(defaultAlias).forEach((alias) => {
39
+ const key = encodeBase64(multihash(sha256(alias)));
40
+ defaultDictionary[key] = {
41
+ serializer: serializerChain,
42
+ alias,
43
+ ...defaultAlias[alias],
44
+ };
45
+ });
46
+
47
+ const dic = {
48
+ ...defaultDictionary,
49
+ ...dictionary,
50
+ };
51
+
52
+ return dic;
53
+ }
54
+
55
+ /**
56
+ * Function to encode genesis data in order to launch a
57
+ * new blockchain. The different values are serialized using
58
+ * protobuffers. One of the arguments is the dictionary which
59
+ * contains the relevant information to perform the serialization.
60
+ * By default the function contains the dictionary for the
61
+ * following keys:
62
+ *
63
+ * - "object_key::head_block"
64
+ * - "object_key::chain_id"
65
+ * - "object_key::genesis_key"
66
+ * - "object_key::resource_limit_data"
67
+ * - "object_key::max_account_resources"
68
+ * - "object_key::protocol_descriptor"
69
+ * - "object_key::compute_bandwidth_registry"
70
+ * - "object_key::block_hash_code"
71
+ *
72
+ * @param genesisDataDecoded - Genesis data where the values are
73
+ * objects.
74
+ * @param dictionary - Set of keys which contains the relevant
75
+ * information to perform the serialization
76
+ *
77
+ * @example
78
+ *
79
+ * ```ts
80
+ * const signer = Signer.fromSeed("seed");
81
+ * const genesisDataDecoded = {
82
+ * entries: [
83
+ * {
84
+ * space: { system: true },
85
+ * alias: "object_key::genesis_key",
86
+ * value: signer.address,
87
+ * },
88
+ * ],
89
+ * };
90
+ *
91
+ * const genesisData = await encodeGenesisData(genesisDataDecoded);
92
+ * console.log(genesisData);
93
+ *
94
+ * // {
95
+ * // entries: [
96
+ * // {
97
+ * // space: { system: true },
98
+ * // key: "EiC3nO+XbeKg4C8ugW7M7XdfmJKY4i3l91KoJWxosQPImA==",
99
+ * // value: "AMpASH7CjUHBpl2QR8E5lGKVjVLAvJRg5g==",
100
+ * // },
101
+ * // ],
102
+ * // }
103
+ * ```
104
+ *
105
+ * @example adding a custom dictionary
106
+ *
107
+ * ```ts
108
+ * const contractId = Signer.fromSeed("seed").address;
109
+ * const zone = encodeBase64(decodeBase58(contractId));
110
+ * const genesisDataDecoded = {
111
+ * entries: [
112
+ * {
113
+ * space: { system: true, zone, id: 1 },
114
+ * key: "difficulty_metadata_key",
115
+ * value: {
116
+ * target: encodeBase64url(toUint8Array("F".repeat(64))),
117
+ * last_block_time: "1641038400000",
118
+ * difficulty: encodeBase64url(toUint8Array("1".repeat(64))),
119
+ * target_block_interval: "10",
120
+ * },
121
+ * },
122
+ * ],
123
+ * };
124
+ *
125
+ * const powJson = {
126
+ * nested: {
127
+ * mypackage: {
128
+ * nested: {
129
+ * difficulty_metadata: {
130
+ * "fields": {
131
+ * "target": { "type": "bytes", "id": 1 },
132
+ * "last_block_time": { "type": "uint64", "id": 2,
133
+ * "options": { "jstype": "JS_STRING" }
134
+ * },
135
+ * "difficulty": { "type": "bytes", "id": 3 },
136
+ * "target_block_interval": { "type": "uint64", "id": 4,
137
+ * "options": { "jstype": "JS_STRING" }
138
+ * }
139
+ * }
140
+ * },
141
+ * }
142
+ * }
143
+ * }
144
+ * }
145
+ *
146
+ * const dic = {
147
+ * difficulty_metadata_key: {
148
+ * serializer: new Serializer(powJson),
149
+ * typeName: "difficulty_metadata",
150
+ * },
151
+ * };
152
+ *
153
+ * const genesisData = await encodeGenesisData(genesisDataDecoded, dic);
154
+ * console.log(genesisData);
155
+ *
156
+ * // {
157
+ * // entries: [
158
+ * // {
159
+ * // key: "difficulty_metadata_key",
160
+ * // space: {
161
+ * // id: 1,
162
+ * // system: true,
163
+ * // zone: "AMpASH7CjUHBpl2QR8E5lGKVjVLAvJRg5g==",
164
+ * // },
165
+ * // value:
166
+ * // "CiD//////////////////////////////////////////xCAlIus4S8aIBERERERERERERERERERERERERERERERERERERERERERIAo=",
167
+ * // },
168
+ * // ],
169
+ * // };
170
+ * ```
171
+ */
172
+ export async function encodeGenesisData(
173
+ genesisDataDecoded: GenesisDataDecoded,
174
+ dictionary: DictionaryGenesisData = {}
175
+ ): Promise<GenesisDataEncoded> {
176
+ const genesisData: GenesisDataEncoded = {};
177
+ if (!genesisDataDecoded || !genesisDataDecoded.entries) return genesisData;
178
+ const dic = prepareDictionary(dictionary);
179
+
180
+ genesisData.entries = await Promise.all(
181
+ genesisDataDecoded.entries.map(async (entry) => {
182
+ const key = Object.keys(dic).find(
183
+ (k) => k === entry.key || (entry.alias && dic[k].alias === entry.alias)
184
+ );
185
+ if (!key)
186
+ return {
187
+ error: `key ${entry.key!} not found in the dictionary`,
188
+ space: entry.space,
189
+ key: entry.key,
190
+ value: encodeBase64(new Uint8Array()),
191
+ };
192
+
193
+ const { isAddress, serializer, typeName } = dic[key];
194
+ let valueBytes: Uint8Array;
195
+ let error = "";
196
+ if (isAddress) {
197
+ valueBytes = decodeBase58(entry.value as string);
198
+ } else if (serializer && typeName) {
199
+ valueBytes = await serializer.serialize(
200
+ entry.value as Record<string, unknown>,
201
+ typeName
202
+ );
203
+ } else {
204
+ valueBytes = new Uint8Array();
205
+ error = "no serializer or typeName defined in the dictionary";
206
+ }
207
+
208
+ return {
209
+ ...(error && { error }),
210
+ space: entry.space,
211
+ key,
212
+ value: encodeBase64(valueBytes),
213
+ };
214
+ })
215
+ );
216
+
217
+ return genesisData;
218
+ }
219
+
220
+ /**
221
+ * Function to decode genesis data used to launch a
222
+ * new blockchain. The different values are deserialized using
223
+ * protobuffers. One of the arguments is the dictionary which
224
+ * contains the relevant information for the deserialization.
225
+ * By default the function contains the dictionary for the
226
+ * following keys:
227
+ *
228
+ * - "object_key::head_block"
229
+ * - "object_key::chain_id"
230
+ * - "object_key::genesis_key"
231
+ * - "object_key::resource_limit_data"
232
+ * - "object_key::max_account_resources"
233
+ * - "object_key::protocol_descriptor"
234
+ * - "object_key::compute_bandwidth_registry"
235
+ * - "object_key::block_hash_code"
236
+ *
237
+ * @param genesisData - Genesis data
238
+ * @param dictionary - Set of keys which contains the relevant
239
+ * information to perform the deserialization
240
+ *
241
+ * @example
242
+ *
243
+ * ```ts
244
+ * const genesisData = {
245
+ * entries: [
246
+ * {
247
+ * space: { system: true },
248
+ * key: "EiC3nO+XbeKg4C8ugW7M7XdfmJKY4i3l91KoJWxosQPImA==",
249
+ * value: "AMpASH7CjUHBpl2QR8E5lGKVjVLAvJRg5g==",
250
+ * },
251
+ * ],
252
+ * }
253
+ *
254
+ * const genesisDataDecoded = await decodeGenesisData(genesisData);
255
+ * console.log(genesisDataDecoded);
256
+ *
257
+ * // {
258
+ * // entries: [
259
+ * // {
260
+ * // space: { system: true },
261
+ * // key: "EiC3nO+XbeKg4C8ugW7M7XdfmJKY4i3l91KoJWxosQPImA==",
262
+ * // alias: "object_key::genesis_key",
263
+ * // value: "1KSQWDyUnFZ48Pf2hsW8Akh1b5fKUWc8Z3",
264
+ * // },
265
+ * // ],
266
+ * // };
267
+ * ```
268
+ *
269
+ * @example adding a custom dictionary
270
+ *
271
+ * ```ts
272
+ * const genesisData = {
273
+ * entries: [
274
+ * {
275
+ * key: "difficulty_metadata_key",
276
+ * space: {
277
+ * id: 1,
278
+ * system: true,
279
+ * zone: "AMpASH7CjUHBpl2QR8E5lGKVjVLAvJRg5g==",
280
+ * },
281
+ * value:
282
+ * "CiD//////////////////////////////////////////xCAlIus4S8aIBERERERERERERERERERERERERERERERERERERERERERIAo=",
283
+ * },
284
+ * ],
285
+ * };
286
+ *
287
+ * const powJson = {
288
+ * nested: {
289
+ * mypackage: {
290
+ * nested: {
291
+ * difficulty_metadata: {
292
+ * "fields": {
293
+ * "target": { "type": "bytes", "id": 1 },
294
+ * "last_block_time": { "type": "uint64", "id": 2,
295
+ * "options": { "jstype": "JS_STRING" }
296
+ * },
297
+ * "difficulty": { "type": "bytes", "id": 3 },
298
+ * "target_block_interval": { "type": "uint64", "id": 4,
299
+ * "options": { "jstype": "JS_STRING" }
300
+ * }
301
+ * }
302
+ * },
303
+ * }
304
+ * }
305
+ * }
306
+ * }
307
+ *
308
+ * const dic = {
309
+ * difficulty_metadata_key: {
310
+ * serializer: new Serializer(powJson),
311
+ * typeName: "difficulty_metadata",
312
+ * },
313
+ * };
314
+ *
315
+ * const genesisDataDecoded = await decodeGenesisData(genesisData, dic);
316
+ * console.log(genesisData);
317
+ *
318
+ * // {
319
+ * // entries: [
320
+ * // {
321
+ * // space: { system: true, zone, id: 1 },
322
+ * // key: "difficulty_metadata_key",
323
+ * // value: {
324
+ * // target: "__________________________________________8=",
325
+ * // last_block_time: "1641038400000",
326
+ * // difficulty: "ERERERERERERERERERERERERERERERERERERERERERE=",
327
+ * // target_block_interval: "10",
328
+ * // },
329
+ * // },
330
+ * // ],
331
+ * // };
332
+ * ```
333
+ */
334
+ export async function decodeGenesisData(
335
+ genesisData: GenesisDataEncoded,
336
+ dictionary: DictionaryGenesisData = {}
337
+ ): Promise<GenesisDataDecoded> {
338
+ const genesisDataDecoded: GenesisDataDecoded = {};
339
+ if (!genesisData || !genesisData.entries) return genesisDataDecoded;
340
+ const dic = prepareDictionary(dictionary);
341
+
342
+ genesisDataDecoded.entries = await Promise.all(
343
+ genesisData.entries.map(async (entry) => {
344
+ const key = Object.keys(dic).find((k) => k === entry.key);
345
+ if (!key)
346
+ return {
347
+ error: `key ${entry.key!} not found in the dictionary`,
348
+ ...entry,
349
+ };
350
+
351
+ const { isAddress, serializer, typeName, alias } = dic[key];
352
+
353
+ const valueBase64url = encodeBase64url(decodeBase64(entry.value));
354
+ let value: string | Record<string, unknown>;
355
+ let error = "";
356
+ if (isAddress) {
357
+ value = encodeBase58(decodeBase64url(valueBase64url));
358
+ } else if (serializer && typeName) {
359
+ value = await serializer.deserialize(valueBase64url, typeName);
360
+ } else {
361
+ value = valueBase64url;
362
+ error = "no serializer or typeName defined in the dictionary";
363
+ }
364
+
365
+ return {
366
+ ...(error && { error }),
367
+ space: entry.space,
368
+ key,
369
+ value,
370
+ ...(alias && { alias }),
371
+ };
372
+ })
373
+ );
374
+
375
+ return genesisDataDecoded;
376
+ }
377
+
378
+ export const ChainTypes = chainJson;