@hazae41/bobine 0.0.10 → 0.0.12

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.
package/README.md CHANGED
@@ -132,150 +132,45 @@ declare function add(x: externref, y: externref): externref
132
132
 
133
133
  You can pass bytes between modules by storing them in the blob storage and loading them via reference
134
134
 
135
- - `blobs.save(offset: i32, length: i32): blobref` = save `length` bytes at `offset` of your memory to the blob storage
136
-
137
- - `blobs.load(blob: blobref, offset: i32): void` = load some blob into your memory at `offset`
138
-
139
- - `blobs.equals(left: blobref, right: blobref): bool` = check if two blobs are equals without loading them into memory
140
-
141
- - `blobs.concat(left: blobref, right: blobref): blobref` = concatenate two blobs without loading them into memory
142
-
143
- - `blob.to_hex/from_hex/to_base64/from_base64(blob: blobref): blobref` = convert blobs to/from hex/base64 without loading them into memory
144
-
145
135
  #### BigInts module
146
136
 
147
- You can work with infinite-precision bigints
148
-
149
- - `bigints.add(left: bigintref, right: bigintref): bigintref` = add two bigints
150
-
151
- - `bigints.sub(left: bigintref, right: bigintref): bigintref` = subtract two bigints
152
-
153
- - `bigints.mul(left: bigintref, right: bigintref): bigintref` = multiply two bigints
154
-
155
- - `bigints.div(left: bigintref, right: bigintref): bigintref` = divide two bigints
137
+ You can work with infinite-precision bigints and convert them with blobs and texts
156
138
 
157
- - `bigints.pow(left: bigintref, right: bigintref): bigintref` = left ** right
158
-
159
- - `bigints.encode(bigint: bigintref): blobref` = convert bigint to bytes
160
-
161
- - `bigints.decode(base16: blobref): bigintref` = convert bytes to bigint
162
-
163
- - `bigints.to_base16(bigint: bigintref): blobref` = convert bigint to hex utf8 bytes
164
-
165
- - `bigints.from_base16(base16: blobref): bigintref` = convert hex utf8 bytes to bigint
166
-
167
- - `bigints.to_base10(bigint: bigintref): blobref` = convert bigint to base10 utf8 bytes
168
-
169
- - `bigints.from_base10(base16: blobref): bigintref` = convert base10 utf8 bytes to bigint
139
+ And many others
170
140
 
171
141
  #### Packs module
172
142
 
173
143
  You can pack various arguments (numbers, refs) into a pack which can be passed between modules and/or encoded/decoded into bytes
174
144
 
175
- - `packs.create(...values: any[]): packref` = create a new pack from the provided values (number, blobref, packref, null)
176
-
177
- - `packs.encode(pack: packref): blobref` = encodes values into bytes using the following pseudocode
178
-
179
- ```tsx
180
- function writePack(pack: packref) {
181
- for (const value of values) {
182
- if (isNull(value)) {
183
- writeUint8(1)
184
- continue
185
- }
186
-
187
- if (isNumber(value)) {
188
- writeUint8(2)
189
- writeFloat64(value, "little-endian")
190
- continue
191
- }
192
-
193
- if (isBigInt(value)) {
194
- writeUint8(3)
195
- writeUint32(value.toHex().length, "little-endian")
196
- writeBytes(value.toHex())
197
- continue
198
- }
199
-
200
- if (isBlobref(value)) {
201
- writeUint8(4)
202
- writeUint32(value.length, "little-endian")
203
- writeBytes(value)
204
- continue
205
- }
206
-
207
- if (isPackref(value)) {
208
- writeUint8(5)
209
- writePack(value)
210
- continue
211
- }
212
-
213
- throw new Error()
214
- }
215
-
216
- writeUint8(0)
217
- }
218
- ```
219
-
220
- - `packs.decode(blob: blobref): packref` = decodes bytes into a pack of values using the same pseudocode but for reading
221
-
222
- - `packs.concat(left: packref, right: packref)` = concatenate two packs into one (basically does `[...left, ...right]`)
223
-
224
- - `packs.get<T>(pack: packref, index: i32): T` = get the value of a pack at `index` (throws if not found)
225
-
226
- - `packs.length(pack: packref): i32` = get the length of a pack
227
-
228
145
  #### Environment module
229
146
 
230
147
  Get infos about the executing environment
231
148
 
232
- - `env.mode: i32` = `1` if execution, `2` is simulation
233
-
234
- - `env.uuid(): blobref` = get the unique uuid of this environment (similar to a chain id)
235
-
236
149
  #### Modules module
237
150
 
238
151
  Modules are identified by their address as a blob of bytes (pure sha256-output 32-length bytes without any encoding)
239
152
 
240
- - `modules.load(module: blobref): blobref` = get the code of module as a blob
241
-
242
- - `modules.call(module: blobref, method: blobref, params: packref): packref` = dynamically call a module method with the given params as pack and return value as a 1-length pack
243
-
244
- - `modules.create(code: blobref, salt: blobref): blobref` = dynamically create a new module with the given code and salt, returns the module address
245
-
246
- - `modules.self(): blobref` = get your module address as blob
153
+ You can dynamically create modules, call modules, get their bytecode
247
154
 
248
155
  #### Storage module
249
156
 
250
- You can use a private storage (it works like storage and events at the same time)
251
-
252
- - `storage.set(key: blobref, value: blobref): void` = set some value to storage at key
253
-
254
- - `storage.get(key: blobref): blobref` = get the latest value from storage at key
157
+ You can use a private key-value storage (it works like storage and events at the same time)
255
158
 
256
159
  #### SHA-256 module
257
160
 
258
161
  Use the SHA-256 hashing algorithm
259
162
 
260
- - `sha256.digest(payload: blobref): blobref` = hash the payload and returns the digest
261
-
262
163
  #### Ed25519 module
263
164
 
264
- Use the Ed25519 signing algorithm
265
-
266
- - `ed25519.verify(pubkey: blobref, signature: blobref, payload: blobref): boolean` = verify a signature
267
-
268
- - `ed25519.sign(payload: blobref): blobref` = (experimental) sign payload using the miner's private key
165
+ Use the Ed25519 signing algorithm to verify any signature and (experimentally) sign payload using the miner's private key
269
166
 
270
167
  #### Symbols module (experimental)
271
168
 
272
- - `symbols.create(): symbolref` = create a unique reference that can be passed around
169
+ Create unique references that can be passed around
273
170
 
274
171
  #### References module (experimental)
275
172
 
276
- - `refs.numerize(ref: symbolref/blobref/packref): i32` = translate any reference into a unique private pointer that can be stored into data structures
277
-
278
- - `refs.denumerize(pointer: i32): symbolref/blobref/packref` = get the exact same reference back from your private pointer
173
+ Translate any reference into a unique private pointer that can be stored into data structures
279
174
 
280
175
  This can be useful if you want to check a reference for authenticity
281
176
 
@@ -6,6 +6,8 @@ export declare class Pack {
6
6
  writeOrThrow(cursor: Cursor): void;
7
7
  }
8
8
  export declare namespace Pack {
9
- type Value = null | Pack | number | Uint8Array | string | bigint;
10
- function readOrThrow(cursor: Cursor): Pack;
9
+ type Value = null | number | Uint8Array | string | bigint | Array<Value>;
10
+ function readOrThrow(cursor: Cursor): Array<Value>;
11
+ function sizeOrThrow(values: Array<Value>): number;
12
+ function writeOrThrow(values: Array<Value>, cursor: Cursor): void;
11
13
  }
@@ -5,14 +5,64 @@ export class Pack {
5
5
  this.values = values;
6
6
  }
7
7
  sizeOrThrow() {
8
+ return Pack.sizeOrThrow(this.values);
9
+ }
10
+ writeOrThrow(cursor) {
11
+ Pack.writeOrThrow(this.values, cursor);
12
+ }
13
+ }
14
+ (function (Pack) {
15
+ function readOrThrow(cursor) {
16
+ const values = [];
17
+ while (true) {
18
+ const type = cursor.readUint8OrThrow();
19
+ if (type === 0)
20
+ break;
21
+ if (type === 1) {
22
+ values.push(null);
23
+ continue;
24
+ }
25
+ if (type === 2) {
26
+ values.push(Pack.readOrThrow(cursor));
27
+ continue;
28
+ }
29
+ if (type === 3) {
30
+ values.push(cursor.readFloat64OrThrow(true));
31
+ continue;
32
+ }
33
+ if (type === 4) {
34
+ const size = cursor.readUint32OrThrow(true);
35
+ values.push(cursor.readOrThrow(size));
36
+ continue;
37
+ }
38
+ if (type === 5) {
39
+ const size = cursor.readUint32OrThrow(true);
40
+ const data = cursor.readOrThrow(size);
41
+ values.push(new TextDecoder().decode(data));
42
+ continue;
43
+ }
44
+ if (type === 6) {
45
+ const negative = cursor.readUint8OrThrow();
46
+ const size = cursor.readUint32OrThrow(true);
47
+ const data = cursor.readOrThrow(size);
48
+ const absolute = BigInt("0x" + data.toHex());
49
+ values.push(negative ? -absolute : absolute);
50
+ continue;
51
+ }
52
+ throw new Error("Unknown pack type");
53
+ }
54
+ return values;
55
+ }
56
+ Pack.readOrThrow = readOrThrow;
57
+ function sizeOrThrow(values) {
8
58
  let size = 0;
9
- for (const value of this.values) {
59
+ for (const value of values) {
10
60
  if (value == null) {
11
61
  size += 1;
12
62
  continue;
13
63
  }
14
- if (value instanceof Pack) {
15
- size += 1 + value.sizeOrThrow();
64
+ if (Array.isArray(value)) {
65
+ size += 1 + sizeOrThrow(value);
16
66
  continue;
17
67
  }
18
68
  if (typeof value === "number") {
@@ -29,9 +79,10 @@ export class Pack {
29
79
  continue;
30
80
  }
31
81
  if (typeof value === "bigint") {
32
- const text = value.toString(16);
82
+ const absolute = value < 0n ? -value : value;
83
+ const text = absolute.toString(16);
33
84
  const data = Uint8Array.fromHex(text.length % 2 === 1 ? "0" + text : text);
34
- size += 1 + 4 + data.length;
85
+ size += 1 + 1 + 4 + data.length;
35
86
  continue;
36
87
  }
37
88
  throw new Error("Unknown pack value");
@@ -39,15 +90,16 @@ export class Pack {
39
90
  size += 1;
40
91
  return size;
41
92
  }
42
- writeOrThrow(cursor) {
43
- for (const value of this.values) {
93
+ Pack.sizeOrThrow = sizeOrThrow;
94
+ function writeOrThrow(values, cursor) {
95
+ for (const value of values) {
44
96
  if (value == null) {
45
97
  cursor.writeUint8OrThrow(1);
46
98
  continue;
47
99
  }
48
- if (value instanceof Pack) {
100
+ if (Array.isArray(value)) {
49
101
  cursor.writeUint8OrThrow(2);
50
- value.writeOrThrow(cursor);
102
+ writeOrThrow(value, cursor);
51
103
  continue;
52
104
  }
53
105
  if (typeof value === "number") {
@@ -70,8 +122,10 @@ export class Pack {
70
122
  }
71
123
  if (typeof value === "bigint") {
72
124
  cursor.writeUint8OrThrow(6);
73
- const text = value.toString(16);
125
+ const [negative, absolute] = value < 0n ? [1, -value] : [0, value];
126
+ const text = absolute.toString(16);
74
127
  const data = Uint8Array.fromHex(text.length % 2 === 1 ? "0" + text : text);
128
+ cursor.writeUint8OrThrow(negative);
75
129
  cursor.writeUint32OrThrow(data.length, true);
76
130
  cursor.writeOrThrow(data);
77
131
  continue;
@@ -81,46 +135,5 @@ export class Pack {
81
135
  cursor.writeUint8OrThrow(0);
82
136
  return;
83
137
  }
84
- }
85
- (function (Pack) {
86
- function readOrThrow(cursor) {
87
- const values = [];
88
- while (true) {
89
- const type = cursor.readUint8OrThrow();
90
- if (type === 0)
91
- break;
92
- if (type === 1) {
93
- values.push(null);
94
- continue;
95
- }
96
- if (type === 2) {
97
- values.push(Pack.readOrThrow(cursor));
98
- continue;
99
- }
100
- if (type === 3) {
101
- values.push(cursor.readFloat64OrThrow(true));
102
- continue;
103
- }
104
- if (type === 4) {
105
- const size = cursor.readUint32OrThrow(true);
106
- values.push(cursor.readOrThrow(size));
107
- continue;
108
- }
109
- if (type === 5) {
110
- const size = cursor.readUint32OrThrow(true);
111
- const data = cursor.readOrThrow(size);
112
- values.push(new TextDecoder().decode(data));
113
- continue;
114
- }
115
- if (type === 6) {
116
- const size = cursor.readUint32OrThrow(true);
117
- const data = cursor.readOrThrow(size);
118
- values.push(BigInt("0x" + data.toHex()));
119
- continue;
120
- }
121
- throw new Error("Unknown pack type");
122
- }
123
- return new Pack(values);
124
- }
125
- Pack.readOrThrow = readOrThrow;
138
+ Pack.writeOrThrow = writeOrThrow;
126
139
  })(Pack || (Pack = {}));
@@ -92,7 +92,7 @@ export async function serve(config) {
92
92
 
93
93
  module TEXT NOT NULL,
94
94
 
95
- key BLOB NOT NULL,
95
+ key TEXT NOT NULL,
96
96
  value BLOB NOT NULL
97
97
  );`);
98
98
  await database.exec(`CREATE TABLE IF NOT EXISTS moments (
@@ -4,7 +4,6 @@ import { RpcErr, RpcError, RpcMethodNotFoundError, RpcOk } from "@hazae41/jsonrp
4
4
  import * as Wasm from "@hazae41/wasm";
5
5
  import { Buffer } from "node:buffer";
6
6
  import { existsSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
7
- import { log } from "../../libs/debug/mod.js";
8
7
  import { meter } from "../../libs/metering/mod.js";
9
8
  import { Pack } from "../../libs/packs/mod.js";
10
9
  const config = await fetch(self.name).then(res => res.json());
@@ -12,10 +11,12 @@ const helper = new Worker(import.meta.resolve("../helper/bin.js"), { name: self.
12
11
  function run(module, method, params, mode, maxsparks) {
13
12
  let sparks = 0n;
14
13
  const exports = {};
14
+ const logs = new Array();
15
15
  const caches = new Map();
16
+ const reads = new Array();
16
17
  const writes = new Array();
17
18
  const pack_encode = (pack) => {
18
- return Writable.writeToBytesOrThrow(pack);
19
+ return Writable.writeToBytesOrThrow(new Pack(pack));
19
20
  };
20
21
  const pack_decode = (bytes) => {
21
22
  return Readable.readFromBytesOrThrow(Pack, bytes);
@@ -48,7 +49,7 @@ function run(module, method, params, mode, maxsparks) {
48
49
  };
49
50
  const ed25519_sign = (subpayload) => {
50
51
  sparks_consume(BigInt(subpayload.length) * 256n);
51
- const payload = pack_encode(new Pack([Uint8Array.fromHex(module), subpayload]));
52
+ const payload = pack_encode([Uint8Array.fromHex(module), subpayload]);
52
53
  const result = new Int32Array(new SharedArrayBuffer(4 + 64));
53
54
  helper.postMessage({ method: "ed25519_sign", params: [payload], result });
54
55
  if (Atomics.wait(result, 0, 0) !== "ok")
@@ -79,8 +80,8 @@ function run(module, method, params, mode, maxsparks) {
79
80
  }
80
81
  };
81
82
  imports["console"] = {
82
- log: (blob) => {
83
- log?.(new TextDecoder().decode(blob));
83
+ log: (text) => {
84
+ logs.push(text);
84
85
  }
85
86
  };
86
87
  imports["blobs"] = {
@@ -159,16 +160,16 @@ function run(module, method, params, mode, maxsparks) {
159
160
  };
160
161
  imports["packs"] = {
161
162
  create: (...values) => {
162
- return new Pack(values);
163
+ return values;
163
164
  },
164
165
  concat: (left, right) => {
165
- return new Pack([...left.values, ...right.values]);
166
+ return [...left, ...right];
166
167
  },
167
168
  length: (pack) => {
168
- return pack.values.length;
169
+ return pack.length;
169
170
  },
170
171
  get(pack, index) {
171
- const value = pack.values[index >>> 0];
172
+ const value = pack[index >>> 0];
172
173
  if (value === undefined)
173
174
  throw new Error("Not found");
174
175
  return value;
@@ -181,6 +182,9 @@ function run(module, method, params, mode, maxsparks) {
181
182
  }
182
183
  };
183
184
  imports["bigints"] = {
185
+ identity: (value) => {
186
+ return value;
187
+ },
184
188
  zero: () => {
185
189
  return 0n;
186
190
  },
@@ -235,30 +239,22 @@ function run(module, method, params, mode, maxsparks) {
235
239
  pow: (left, right) => {
236
240
  return left ** right;
237
241
  },
238
- encode: (bigint) => {
239
- const text = bigint.toString(16);
240
- const data = Uint8Array.fromHex(text.length % 2 === 1 ? "0" + text : text);
241
- return data;
242
- },
243
- decode: (bytes) => {
244
- return BigInt("0x" + bytes.toHex());
245
- },
246
242
  from_base16: (text) => {
247
- return BigInt("0x" + new TextDecoder().decode(text));
243
+ return BigInt("0x" + text);
248
244
  },
249
245
  to_base16: (bigint) => {
250
- return new TextEncoder().encode(bigint.toString(16));
246
+ return bigint.toString(16);
251
247
  },
252
248
  from_base10: (text) => {
253
- return BigInt(new TextDecoder().decode(text));
249
+ return BigInt(text);
254
250
  },
255
251
  to_base10: (bigint) => {
256
- return new TextEncoder().encode(bigint.toString());
252
+ return bigint.toString();
257
253
  }
258
254
  };
259
255
  imports["modules"] = {
260
256
  create: (wasmAsBytes, saltAsBytes) => {
261
- const packAsBytes = pack_encode(new Pack([wasmAsBytes, saltAsBytes]));
257
+ const packAsBytes = pack_encode([wasmAsBytes, saltAsBytes]);
262
258
  const digestOfWasmAsBytes = sha256_digest(wasmAsBytes);
263
259
  const digestOfPackAsBytes = sha256_digest(packAsBytes);
264
260
  const digestOfWasmAsHex = digestOfWasmAsBytes.toHex();
@@ -286,9 +282,10 @@ function run(module, method, params, mode, maxsparks) {
286
282
  }
287
283
  };
288
284
  imports["storage"] = {
289
- set: (key, value) => {
285
+ set: (key, fresh) => {
290
286
  const cache = caches.get(module);
291
- cache.set(key, value);
287
+ cache.set(key, fresh);
288
+ const value = pack_encode(fresh);
292
289
  writes.push([module, key, value]);
293
290
  return;
294
291
  },
@@ -305,8 +302,10 @@ function run(module, method, params, mode, maxsparks) {
305
302
  throw new Error("Internal error");
306
303
  if (result[1] === 2)
307
304
  return null;
308
- const fresh = new Uint8Array(result.buffer, 4 + 4 + 4, result[2]).slice();
305
+ const value = new Uint8Array(result.buffer, 4 + 4 + 4, result[2]).slice();
306
+ const fresh = pack_decode(value);
309
307
  cache.set(key, fresh);
308
+ reads.push([module, key, value]);
310
309
  return fresh;
311
310
  }
312
311
  };
@@ -380,37 +379,33 @@ function run(module, method, params, mode, maxsparks) {
380
379
  const { instance } = load(module);
381
380
  if (typeof instance.exports[method] !== "function")
382
381
  throw new Error("Not found");
383
- const result = pack_encode(new Pack([instance.exports[method](...pack_decode(params).values)]));
384
- return { result, writes, sparks };
382
+ const returned = [instance.exports[method](...pack_decode(params))];
383
+ if (mode !== 2)
384
+ return pack_encode([logs, reads, writes, returned, sparks]);
385
+ if (writes.length) {
386
+ const result = new Int32Array(new SharedArrayBuffer(4 + 4));
387
+ helper.postMessage({ method: "storage_set", params: [module, method, params, writes], result });
388
+ if (Atomics.wait(result, 0, 0) !== "ok")
389
+ throw new Error("Failed to wait");
390
+ if (result[0] === 2)
391
+ throw new Error("Internal error");
392
+ // NOOP
393
+ }
394
+ return pack_encode([logs, reads, writes, returned, sparks]);
385
395
  }
386
396
  self.addEventListener("message", (event) => {
387
397
  try {
388
398
  const request = event.data;
389
399
  if (request.method === "execute") {
390
400
  const [module, method, params, maxsparks] = request.params;
391
- const start = performance.now();
392
- const { result, writes, sparks } = run(module, method, params, 1, maxsparks);
393
- const until = performance.now();
394
- log?.(`Evaluated ${(until - start).toFixed(2)}ms with ${sparks} sparks`);
395
- if (writes.length) {
396
- const result = new Int32Array(new SharedArrayBuffer(4 + 4));
397
- helper.postMessage({ method: "storage_set", params: [module, method, params, writes], result });
398
- if (Atomics.wait(result, 0, 0) !== "ok")
399
- throw new Error("Failed to wait");
400
- if (result[0] === 2)
401
- throw new Error("Internal error");
402
- log?.(`Wrote ${writes.length} events to storage`);
403
- }
404
- self.postMessage(new RpcOk(request.id, result));
401
+ const proof = run(module, method, params, 1, maxsparks);
402
+ self.postMessage(new RpcOk(request.id, proof));
405
403
  return;
406
404
  }
407
405
  if (request.method === "simulate") {
408
406
  const [module, method, params, maxsparks] = request.params;
409
- const start = performance.now();
410
- const { result, sparks } = run(module, method, params, 2, maxsparks);
411
- const until = performance.now();
412
- log?.(`Evaluated ${(until - start).toFixed(2)}ms with ${sparks} sparks`);
413
- self.postMessage(new RpcOk(request.id, result));
407
+ const proof = run(module, method, params, 2, maxsparks);
408
+ self.postMessage(new RpcOk(request.id, proof));
414
409
  return;
415
410
  }
416
411
  if (request.method === "verify") {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@hazae41/bobine",
4
- "version": "0.0.10",
4
+ "version": "0.0.12",
5
5
  "description": "A blockchain in your garage",
6
6
  "repository": "github:hazae41/bobine",
7
7
  "author": "hazae41",
@@ -1 +0,0 @@
1
- export declare const log: (message: string) => void;
@@ -1,4 +0,0 @@
1
- import process from "node:process";
2
- export const log = process.env.NODE_ENV === "development"
3
- ? (message) => console.debug(message)
4
- : null;