wao 0.9.1 → 0.9.2

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,482 @@
1
+ import * as WarpArBundles from "warp-arbundles"
2
+ const pkg = WarpArBundles.default ?? WarpArBundles
3
+ const { DataItem } = pkg
4
+ import crypto from "crypto"
5
+ import base64url from "base64url"
6
+
7
+ import {
8
+ tags,
9
+ action,
10
+ tag,
11
+ buildTags,
12
+ isJSON,
13
+ jsonToStr,
14
+ dirname,
15
+ } from "./utils.js"
16
+ import weavedrive from "./weavedrive.js"
17
+ import { is, clone, fromPairs, map, mergeLeft, isNil } from "ramda"
18
+
19
+ export default ({ AR, scheduler, mu, su, cu, acc, AoLoader, ArMem } = {}) => {
20
+ return (mem, { cache, log = false, extensions = {} } = {}) => {
21
+ const isMem = mem?.__type__ === "mem"
22
+ if (!isMem) {
23
+ let args = { cache }
24
+ if (mem?.SU_URL) {
25
+ args = mergeLeft(mem, args)
26
+ args.scheduler = scheduler
27
+ }
28
+ mem = new ArMem(args)
29
+ }
30
+ const ar = new AR({ mem, log })
31
+ for (const k in extensions) extensions[k] = new extensions[k](ar).ext
32
+ extensions.WeaveDrive = new weavedrive(ar).ext
33
+
34
+ const transform = input => {
35
+ const output = { Tags: [] }
36
+ if (input.Data) output.Data = input.Data
37
+ Object.entries(input).forEach(([key, value]) => {
38
+ if (key !== "Data" && key !== "Tags" && typeof value === "string") {
39
+ output.Tags.push({ name: key, value })
40
+ }
41
+ })
42
+ input.Tags?.forEach(tag => {
43
+ const tagKey = Object.keys(tag)[0]
44
+ const tagValue = tag[tagKey]
45
+ if (typeof tagValue === "string") {
46
+ output.Tags.push({ name: tagKey, value: tagValue })
47
+ }
48
+ })
49
+
50
+ return output
51
+ }
52
+
53
+ const genMsg = async (Id, p, data, Tags, from, Owner, dry = false) => {
54
+ if (!dry) p.height += 1
55
+ return {
56
+ Id,
57
+ Target: p.id,
58
+ Owner,
59
+ Data: data?.length ? data : "",
60
+ "Block-Height": (await mem.get("height")).toString(),
61
+ Timestamp: Date.now().toString(),
62
+ Module: p.module,
63
+ From: from,
64
+ Cron: false,
65
+ Tags: Tags?.length ? Tags : [],
66
+ }
67
+ }
68
+
69
+ const genEnv = async ({ pid, owner = "", module = "" }) => {
70
+ return {
71
+ Process: {
72
+ Id: pid,
73
+ Tags: (await mem.get("txs", pid))?.tags ?? [],
74
+ Owner: owner,
75
+ },
76
+ Module: {
77
+ Id: module,
78
+ Tags: (await mem.get("txs", module))?.tags ?? [],
79
+ },
80
+ }
81
+ }
82
+ const spawn = async (opt = {}) => {
83
+ if (!opt.module) throw Error("module missing")
84
+ if (!opt.scheduler) throw Error("scheduler missing")
85
+ const { mod, wasm, format } = await mem.getWasm(opt.module, mem)
86
+ opt.tags = buildTags(
87
+ null,
88
+ mergeLeft(tags(opt.tags ?? []), {
89
+ "Data-Protocol": "ao",
90
+ Variant: "ao.TN.1",
91
+ Type: "Process",
92
+ SDK: "aoconnect",
93
+ Module: mod,
94
+ Scheduler: opt.scheduler,
95
+ "Content-Type": "text/plain",
96
+ Authority: mu.addr,
97
+ }),
98
+ )
99
+ let ex = false
100
+ for (let v of opt.tags) if (v.name === "Type") ex = true
101
+ if (!ex) opt.tags.push({ name: "Type", value: "Process" })
102
+ if (opt.for) opt.tags.push({ name: "Pushed-For", value: opt.for })
103
+ const {
104
+ id,
105
+ owner,
106
+ item,
107
+ tags: __tags,
108
+ } = await ar.dataitem({
109
+ item: opt.item,
110
+ data: opt.data,
111
+ signer: opt.signer,
112
+ tags: tags(opt.tags),
113
+ })
114
+ opt.tags = buildTags(null, __tags)
115
+ if (opt.item) opt.data = base64url.decode(item.data)
116
+ await ar.postItems(item, su.jwk)
117
+ const now = Date.now
118
+ const t = tags(opt.tags)
119
+ const ext = t.Extension || "WeaveDrive"
120
+ const wdrive = extensions[ext]
121
+ let handle = null
122
+ try {
123
+ handle = await AoLoader(wasm, {
124
+ format,
125
+ WeaveDrive: wdrive,
126
+ spawn: item,
127
+ module: await mem.get("txs", mod),
128
+ })
129
+ } catch (e) {}
130
+ if (!handle) return null
131
+ let _module = null
132
+ _module = { handle, id: mod }
133
+ Date.now = now
134
+ const _tags = tags(opt.tags)
135
+ let res = null
136
+ let memory = opt.memory ?? null
137
+ let p = {
138
+ extension: ext,
139
+ item: item?.binary,
140
+ format,
141
+ id: id,
142
+ epochs: [],
143
+ handle: _module.handle,
144
+ module: _module.id,
145
+ hash: id,
146
+ memory,
147
+ owner,
148
+ height: 0,
149
+ res: { [id]: res },
150
+ results: [id],
151
+ txs: [],
152
+ opt,
153
+ }
154
+ if (memory) {
155
+ // forking...
156
+ } else if (_tags["On-Boot"] || true) {
157
+ let data = ""
158
+ if (_tags["On-Boot"] === "Data") data = opt.data ?? ""
159
+ else data = (await mem.get("msgs", _tags["On-Boot"]))?.data ?? ""
160
+ let msg = await genMsg(id, p, data, opt.tags, owner, mu.addr, true)
161
+ const _env = await genEnv({
162
+ pid: p.id,
163
+ owner: p.owner,
164
+ module: p.module,
165
+ })
166
+ res = await _module.handle(null, msg, _env)
167
+ p.memory = res.Memory
168
+ delete res.Memory
169
+ p.res[id] = res
170
+ } else {
171
+ p.height += 1
172
+ }
173
+ await mem.set(opt, "msgs", id)
174
+ if (_tags["Cron-Interval"]) {
175
+ let [num, unit] = _tags["Cron-Interval"].split("-")
176
+ let int = 0
177
+ switch (unit.replace(/s$/, "")) {
178
+ case "millisecond":
179
+ int = num
180
+ break
181
+ case "second":
182
+ int = num * 1000
183
+ break
184
+ case "minute":
185
+ int = num * 1000 * 60
186
+ break
187
+ case "hour":
188
+ int = num * 1000 * 60 * 60
189
+ break
190
+ case "day":
191
+ int = num * 1000 * 60 * 60 * 24
192
+ break
193
+ case "month":
194
+ int = num * 1000 * 60 * 60 * 24 * 30
195
+ break
196
+ case "year":
197
+ int = num * 1000 * 60 * 60 * 24 * 365
198
+ break
199
+ }
200
+ let cronTags = []
201
+ for (const k in _tags) {
202
+ if (/^Cron-Tag-/.test(k)) {
203
+ cronTags.push({ name: k.replace(/Cron-Tag-/, ""), value: _tags[k] })
204
+ }
205
+ }
206
+ p.cronTags = cronTags
207
+ p.span = int
208
+ }
209
+ await mem.set(p, "env", id)
210
+ return id
211
+ }
212
+
213
+ function genHashChain(previousHash, previousMessageId = null) {
214
+ const hasher = crypto.createHash("sha256")
215
+ hasher.update(Buffer.from(previousHash, "base64"))
216
+ if (previousMessageId) {
217
+ hasher.update(Buffer.from(previousMessageId, "base64"))
218
+ }
219
+ return base64url(hasher.digest())
220
+ }
221
+
222
+ const assign = async opt => {
223
+ const p = await mem.get("env", opt.process)
224
+ if (!p) return null
225
+ let _opt = await mem.get("msgs", opt.message)
226
+ let hash = genHashChain(p.hash, opt.message)
227
+ p.hash = hash
228
+ opt.tags = buildTags(
229
+ null,
230
+ mergeLeft(tags(opt.tags ?? []), {
231
+ Timestamp: Date.now(),
232
+ Epoch: p.epochs.length,
233
+ Nonce: "0",
234
+ "Data-Protocol": "ao",
235
+ Variant: "ao.TN.1",
236
+ SDK: "aoconnect",
237
+ Type: "Assignment",
238
+ "Block-Height": await mem.get("height"),
239
+ Process: opt.process,
240
+ Message: opt.message,
241
+ "Hash-Chain": hash,
242
+ }),
243
+ )
244
+ p.epochs.push([opt.message])
245
+ const { id, owner, item } = await ar.dataitem({
246
+ data: opt.data,
247
+ signer: opt.signer,
248
+ tags: tags(opt.tags),
249
+ target: opt.process,
250
+ })
251
+ if (opt.message_item) {
252
+ await ar.postItems([opt.message_item, item], su.jwk)
253
+ } else {
254
+ await ar.postItems(item, su.jwk)
255
+ }
256
+ try {
257
+ let data = _opt.data ?? ""
258
+ let _tags = _opt.tags
259
+ let from = _opt.from ?? opt.from ?? owner
260
+ if (_opt.item) {
261
+ try {
262
+ data = base64url.decode(_opt.item.data)
263
+ _tags = _opt.item.tags
264
+ if (!from) {
265
+ const raw_owner = _opt.item.rawOwner
266
+ const hashBuffer = Buffer.from(
267
+ await crypto.subtle.digest("SHA-256", raw_owner),
268
+ )
269
+ from = base64url.encode(hashBuffer)
270
+ }
271
+ } catch (e) {
272
+ console.log(e)
273
+ }
274
+ }
275
+ // check: is owner=mu.addr right?
276
+ const msg = await genMsg(opt.message, p, data, _tags, from, mu.addr)
277
+ const _env = await genEnv({
278
+ pid: p.id,
279
+ owner: p.owner,
280
+ module: p.module,
281
+ })
282
+ if (!p.handle) {
283
+ const { format, mod, wasm } = await mem.getWasm(p.modulea)
284
+ const wdrive = extensions[p.extention]
285
+ p.handle = await AoLoader(wasm, {
286
+ format,
287
+ WeaveDrive: wdrive,
288
+ spawn: new DataItem(p.item),
289
+ module: await mem.get("txs", mod),
290
+ })
291
+ mem.env[opt.process].handle = p.handle
292
+ }
293
+ const res = await p.handle(p.memory, msg, _env)
294
+ p.memory = res.Memory
295
+ delete res.Memory
296
+ p.res[opt.message] = res
297
+ p.results.push(opt.message)
298
+ p.txs.unshift({ id, ...opt })
299
+ await mem.set(p, "env", opt.process)
300
+ await mem.set(_opt, "msgs", opt.message)
301
+ for (const v of res.Messages ?? []) {
302
+ if (await mem.get("env", v.Target)) {
303
+ await message({
304
+ for: opt.message,
305
+ process: v.Target,
306
+ tags: v.Tags,
307
+ data: v.Data,
308
+ signer: mu.signer,
309
+ from: opt.process,
310
+ })
311
+ }
312
+ }
313
+ for (const v of res.Spawns ?? []) {
314
+ const __tags = tags(v.Tags)
315
+ await spawn({
316
+ for: opt.message,
317
+ module: __tags.Module,
318
+ scheduler,
319
+ tags: v.Tags,
320
+ data: v.Data,
321
+ from: __tags["From-Process"],
322
+ signer: mu.signer,
323
+ })
324
+ }
325
+ for (const v of res.Assignments ?? []) {
326
+ for (const v2 of v.Processes) {
327
+ await assign({
328
+ message: v.Message,
329
+ process: v2,
330
+ from: opt.process,
331
+ signer: mu.signer,
332
+ })
333
+ }
334
+ }
335
+
336
+ return id
337
+ } catch (e) {
338
+ console.log(e)
339
+ }
340
+ return null
341
+ }
342
+
343
+ const message = async opt => {
344
+ const p = await mem.get("env", opt.process)
345
+ if (!p) return null
346
+ let ex = false
347
+ let id = opt?.item?.id ?? ""
348
+ let owner = opt.owner ?? ""
349
+ let item = opt.item
350
+ if (!opt.item && opt.signer) {
351
+ for (let v of opt.tags) if (v.name === "Type") ex = true
352
+ opt.tags = buildTags(
353
+ null,
354
+ mergeLeft(tags(opt.tags ?? []), {
355
+ "Data-Protocol": "ao",
356
+ Variant: "ao.TN.1",
357
+ Type: "Message",
358
+ SDK: "aoconnect",
359
+ }),
360
+ )
361
+ if (opt.for) {
362
+ opt.tags.push({ name: "Pushed-For", value: opt.for })
363
+ opt.tags.push({ name: "From-Process", value: opt.from })
364
+ const pr = (await mem.get("txs", opt.from))?.tags ?? []
365
+ const module = tags(pr).Module
366
+ if (module) opt.tags.push({ name: "From-Module", value: module })
367
+ }
368
+ ;({ item, id, owner } = await ar.dataitem({
369
+ data: opt.data,
370
+ signer: opt.signer,
371
+ tags: tags(opt.tags),
372
+ target: opt.process,
373
+ }))
374
+ }
375
+ await mem.set(opt, "msgs", id)
376
+ await assign({
377
+ message_item: item,
378
+ message: id,
379
+ process: opt.process,
380
+ from: owner,
381
+ signer: mu.signer,
382
+ })
383
+ return id
384
+ }
385
+
386
+ return {
387
+ message,
388
+ unmonitor: async opt => {
389
+ const p = await mem.get("env", opt.process)
390
+ try {
391
+ clearInterval(p.cron)
392
+ p.cron = null
393
+ } catch (e) {}
394
+ },
395
+ monitor: async opt => {
396
+ const p = await mem.get("env", opt.process)
397
+ if (isNil(p.cron)) {
398
+ p.cron = setInterval(async () => {
399
+ await message({
400
+ tags: p.cronTags,
401
+ process: opt.process,
402
+ signer: mu.signer,
403
+ from: mu.addr,
404
+ })
405
+ }, p.span)
406
+ }
407
+ },
408
+ spawn,
409
+ assign,
410
+ ar,
411
+ result: async opt =>
412
+ (await mem.get("env", opt.process))?.res[opt.message],
413
+ results: async opt => {
414
+ const p = await mem.get("env", opt.process)
415
+ let results = []
416
+ const limit = opt.limit ?? 25
417
+ if (opt.sort === "DESC") {
418
+ for (let i = p.results.length - 1; 0 < i; i--) {
419
+ results.push({ cursor: p.results[i], node: p.res[p.results[i]] })
420
+ if (results.length >= limit) break
421
+ }
422
+ } else {
423
+ for (let i = 0; i < p.results.length; i++) {
424
+ results.push({ node: p.res[p.results[i]] })
425
+ if (results.length >= limit) break
426
+ }
427
+ }
428
+ return { edges: results }
429
+ },
430
+ dryrun: async opt => {
431
+ const p = await mem.get("env", opt.process)
432
+ if (!p) return null
433
+ let id = opt.id ?? ""
434
+ let owner = opt.owner ?? ""
435
+ if (!opt.id && opt.signer) {
436
+ ;({ id, owner } = await ar.dataitem({ ...opt, target: opt.process }))
437
+ }
438
+ try {
439
+ const msg = await genMsg(
440
+ id,
441
+ p,
442
+ opt.data ?? "",
443
+ opt.tags,
444
+ owner,
445
+ mu.addr,
446
+ true,
447
+ )
448
+ const _env = await genEnv({
449
+ pid: p.id,
450
+ owner: p.owner,
451
+ module: p.module,
452
+ })
453
+ function cloneMemory(memory) {
454
+ const buffer = memory.buffer.slice(0)
455
+ return new WebAssembly.Memory({
456
+ initial: memory.buffer.byteLength / 65536,
457
+ maximum: memory.maximum || undefined,
458
+ shared: memory.shared || false,
459
+ })
460
+ }
461
+ if (!p.handle) {
462
+ const { format, mod, wasm } = await mem.getWasm(p.modulea)
463
+ const wdrive = extensions[p.extention]
464
+ p.handle = await AoLoader(wasm, {
465
+ format,
466
+ WeaveDrive: wdrive,
467
+ spawn: new DataItem(p.item),
468
+ module: await mem.get("txs", mod),
469
+ })
470
+ mem.env[opt.process].handle = p.handle
471
+ }
472
+ const res = await p.handle(p.memory, msg, _env)
473
+ return res
474
+ } catch (e) {
475
+ console.log(e)
476
+ }
477
+ return null
478
+ },
479
+ mem,
480
+ }
481
+ }
482
+ }