wao 0.3.1 → 0.4.1

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/esm/aoconnect.js CHANGED
@@ -1,764 +1,28 @@
1
- let _txs = {}
2
-
3
- import Arweave from "arweave"
4
-
5
- const KB = 1024
6
- const MB = KB * 1024
7
- const CACHE_SZ = 32 * KB
8
- const CHUNK_SZ = 128 * MB
9
- const NOTIFY_SZ = 512 * MB
10
-
11
- function WeaveDrive(mod, FS) {
12
- return {
13
- reset(fd) {
14
- //console.log("WeaveDrive: Resetting fd: ", fd)
15
- FS.streams[fd].node.position = 0
16
- FS.streams[fd].node.cache = new Uint8Array(0)
17
- },
18
-
19
- joinUrl({ url, path }) {
20
- if (!path) return url
21
- if (path.startsWith("/"))
22
- return this.joinUrl({ url, path: path.slice(1) })
23
-
24
- url = new URL(url)
25
- url.pathname += path
26
- return url.toString()
27
- },
28
-
29
- async customFetch(path, options) {
30
- txs[path] = true
31
- /**
32
- * mod.ARWEAVE may be a comma-delimited list of urls.
33
- * So we parse it into an array that we sequentially consume
34
- * using fetch, and return the first successful response.
35
- *
36
- * The first url is considered "primary". So if all urls fail
37
- * to produce a successful response, then we return the primary's
38
- * error response
39
- */
40
- const urlList = mod.ARWEAVE.includes(",")
41
- ? mod.ARWEAVE.split(",").map(url => url.trim())
42
- : [mod.ARWEAVE]
43
-
44
- let p
45
- for (const url of urlList) {
46
- //txs[this.joinUrl({ url, path })] = true
47
- const res = fetch(this.joinUrl({ url, path }), options)
48
- if (await res.then(r => r.ok).catch(() => false)) return res
49
- if (!p) p = res
50
- }
51
-
52
- /**
53
- * None succeeded so fallback to the primary and accept
54
- * whatever it returned
55
- */
56
- return p
57
- },
58
-
59
- async create(id) {
60
- var properties = { isDevice: false, contents: null }
61
-
62
- if (!(await this.checkAdmissible(id))) {
63
- //console.log("WeaveDrive: Arweave ID is not admissable! ", id)
64
- return 0
65
- }
66
-
67
- // Create the file in the emscripten FS
68
-
69
- // This check/mkdir was added for AOP 6 Boot loader because create is
70
- // called first because were only loading Data, we needed to create
71
- // the directory. See: https://github.com/permaweb/aos/issues/342
72
- if (!FS.analyzePath("/data/").exists) {
73
- FS.mkdir("/data/")
74
- }
75
-
76
- var node = FS.createFile("/", "data/" + id, properties, true, false)
77
- // Set initial parameters
78
- /*
79
- var bytesLength = await this.customFetch(`/${id}`, {
80
- method: "HEAD",
81
- }).then(res => res.headers.get("Content-Length"))
82
- */
83
- const bytesLength = _txs[id]
84
- ? new TextEncoder().encode(_txs[id]).length
85
- : 100
86
- node.total_size = Number(bytesLength)
87
- node.cache = new Uint8Array(0)
88
- node.position = 0
89
-
90
- // Add a function that defers querying the file size until it is asked the first time.
91
- Object.defineProperties(node, {
92
- usedBytes: {
93
- get: function () {
94
- return bytesLength
95
- },
96
- },
97
- })
98
-
99
- // Now we have created the file in the emscripten FS, we can open it as a stream
100
- var stream = FS.open("/data/" + id, "r")
101
-
102
- //console.log("JS: Created file: ", id, " fd: ", stream.fd);
103
- return stream
104
- },
105
- async createBlockHeader(id) {
106
- const customFetch = this.customFetch
107
- // todo: add a bunch of retries
108
- async function retry(x) {
109
- return new Promise(r => {
110
- setTimeout(function () {
111
- r(customFetch(`/block/height/${id}`))
112
- }, x * 10000)
113
- })
114
- }
115
- var result = await this.customFetch(`/block/height/${id}`)
116
- .then(res => (!res.ok ? retry(1) : res))
117
- .then(res => (!res.ok ? retry(2) : res))
118
- .then(res => (!res.ok ? retry(3) : res))
119
- .then(res => (!res.ok ? retry(4) : res))
120
- .then(res => res.text())
121
-
122
- var bytesLength = result.length
123
-
124
- var node = FS.createDataFile(
125
- "/",
126
- "block/" + id,
127
- Buffer.from(result, "utf-8"),
128
- true,
129
- false,
130
- )
131
-
132
- var stream = FS.open("/block/" + id, "r")
133
- return stream
134
- },
135
- async createTxHeader(id) {
136
- const customFetch = this.customFetch
137
- async function toAddress(owner) {
138
- return Arweave.utils.bufferTob64Url(
139
- await Arweave.crypto.hash(Arweave.utils.b64UrlToBuffer(owner)),
140
- )
141
- }
142
- async function retry(x) {
143
- return new Promise(r => {
144
- setTimeout(function () {
145
- r(customFetch(`/tx/${id}`))
146
- }, x * 10000)
147
- })
148
- }
149
- // todo: add a bunch of retries
150
- var result = await this.customFetch(`/tx/${id}`)
151
- .then(res => (!res.ok ? retry(1) : res))
152
- .then(res => (!res.ok ? retry(2) : res))
153
- .then(res => (!res.ok ? retry(3) : res))
154
- .then(res => (!res.ok ? retry(4) : res))
155
- .then(res => res.json())
156
- .then(async entry => ({
157
- ...entry,
158
- ownerAddress: await toAddress(entry.owner),
159
- }))
160
- //.then(x => (console.error(x), x))
161
- .then(x => JSON.stringify(x))
162
-
163
- var node = FS.createDataFile(
164
- "/",
165
- "tx/" + id,
166
- Buffer.from(result, "utf-8"),
167
- true,
168
- false,
169
- )
170
- var stream = FS.open("/tx/" + id, "r")
171
- return stream
172
- },
173
- async createDataItemTxHeader(id) {
174
- const gqlQuery = this.gqlQuery
175
- var GET_TRANSACTION_QUERY = `
176
- query GetTransactions ($transactionIds: [ID!]!) {
177
- transactions(ids: $transactionIds) {
178
- edges {
179
- node {
180
- id
181
- anchor
182
- data {
183
- size
184
- }
185
- signature
186
- recipient
187
- owner {
188
- address
189
- key
190
- }
191
- fee {
192
- ar
193
- winston
194
- }
195
- quantity {
196
- winston
197
- ar
198
- }
199
- tags {
200
- name
201
- value
202
- }
203
- bundledIn {
204
- id
205
- }
206
- block {
207
- id
208
- timestamp
209
- height
210
- previous
211
- }
212
- }
213
- }
214
- }
215
- }`
216
- var variables = { transactionIds: [id] }
217
- async function retry(x) {
218
- return new Promise(r => {
219
- setTimeout(function () {
220
- r(gqlQuery(GET_TRANSACTION_QUERY, variables))
221
- }, x * 10000)
222
- })
223
- }
224
-
225
- const gqlExists = await this.gqlExists()
226
- if (!gqlExists) {
227
- return "GQL Not Found!"
228
- }
229
-
230
- // todo: add a bunch of retries
231
- var result = await this.gqlQuery(GET_TRANSACTION_QUERY, variables)
232
- .then(res => (!res.ok ? retry(1) : res))
233
- .then(res => (!res.ok ? retry(2) : res))
234
- .then(res => (!res.ok ? retry(3) : res))
235
- .then(res => (!res.ok ? retry(4) : res))
236
- .then(res => res.json())
237
- .then(res => {
238
- return res?.data?.transactions?.edges?.[0]?.node
239
- ? res.data.transactions.edges[0].node
240
- : "No results"
241
- })
242
- .then(async entry => {
243
- return typeof entry == "string"
244
- ? entry
245
- : {
246
- format: 3,
247
- ...entry,
248
- }
249
- })
250
- .then(x => {
251
- return typeof x == "string" ? x : JSON.stringify(x)
252
- })
253
-
254
- if (result === "No results") {
255
- return result
256
- }
257
- FS.createDataFile(
258
- "/",
259
- "tx2/" + id,
260
- Buffer.from(result, "utf-8"),
261
- true,
262
- false,
263
- )
264
- var stream = FS.open("/tx2/" + id, "r")
265
-
266
- return stream
267
- },
268
- async open(filename) {
269
- const pathCategory = filename.split("/")[1]
270
- const id = filename.split("/")[2]
271
- console.log("JS: Opening ID: ", id)
272
- if (pathCategory === "tx") {
273
- FS.createPath("/", "tx", true, false)
274
- if (FS.analyzePath(filename).exists) {
275
- var stream = FS.open(filename, "r")
276
- if (stream.fd) return stream.fd
277
- return 0
278
- } else {
279
- const stream = await this.createTxHeader(id)
280
- return stream.fd
281
- }
282
- }
283
- if (pathCategory === "tx2") {
284
- FS.createPath("/", "tx2", true, false)
285
- if (FS.analyzePath(filename).exists) {
286
- var stream = FS.open(filename, "r")
287
- if (stream.fd) return stream.fd
288
- return 0
289
- } else {
290
- const stream = await this.createDataItemTxHeader(id)
291
- if (stream.fd) return stream.fd
292
- return 0
293
- }
294
- }
295
- if (pathCategory === "block") {
296
- FS.createPath("/", "block", true, false)
297
- if (FS.analyzePath(filename).exists) {
298
- var stream = FS.open(filename, "r")
299
- if (stream.fd) return stream.fd
300
- return 0
301
- } else {
302
- const stream = await this.createBlockHeader(id)
303
- return stream.fd
304
- }
305
- }
306
- if (pathCategory === "data") {
307
- if (FS.analyzePath(filename).exists) {
308
- var stream = FS.open(filename, "r")
309
- if (stream.fd) return stream.fd
310
- console.log("JS: File not found: ", filename)
311
- return 0
312
- } else {
313
- //console.log("JS: Open => Creating file: ", id);
314
- const stream = await this.create(id)
315
- //console.log("JS: Open => Created file: ", id, " fd: ", stream.fd);
316
- return stream.fd
317
- }
318
- } else if (pathCategory === "headers") {
319
- console.log("Header access not implemented yet.")
320
- return 0
321
- } else {
322
- console.log("JS: Invalid path category: ", pathCategory)
323
- return 0
324
- }
325
- },
326
- async read(fd, raw_dst_ptr, raw_length) {
327
- // Note: The length and dst_ptr are 53 bit integers in JS, so this _should_ be ok into a large memspace.
328
- var to_read = Number(raw_length)
329
- var dst_ptr = Number(raw_dst_ptr)
330
-
331
- var stream = 0
332
- for (var i = 0; i < FS.streams.length; i++) {
333
- if (FS.streams[i].fd === fd) {
334
- stream = FS.streams[i]
335
- }
336
- }
337
- // read block headers
338
- if (stream.path.includes("/block")) {
339
- mod.HEAP8.set(stream.node.contents.subarray(0, to_read), dst_ptr)
340
- return to_read
341
- }
342
- // read tx headers
343
- if (stream.path.includes("/tx")) {
344
- mod.HEAP8.set(stream.node.contents.subarray(0, to_read), dst_ptr)
345
- return to_read
346
- }
347
- // Satisfy what we can with the cache first
348
- var bytes_read = this.readFromCache(stream, dst_ptr, to_read)
349
- stream.position += bytes_read
350
- stream.lastReadPosition = stream.position
351
- dst_ptr += bytes_read
352
- to_read -= bytes_read
353
-
354
- // Return if we have satisfied the request
355
- if (to_read === 0) {
356
- //console.log("WeaveDrive: Satisfied request with cache. Returning...")
357
- return bytes_read
358
- }
359
- //console.log("WeaveDrive: Read from cache: ", bytes_read, " Remaining to read: ", to_read)
360
-
361
- const chunk_download_sz = Math.max(to_read, CACHE_SZ)
362
- const to = Math.min(
363
- stream.node.total_size,
364
- stream.position + chunk_download_sz,
365
- )
366
- //console.log("WeaveDrive: fd: ", fd, " Read length: ", to_read, " Reading ahead:", to - to_read - stream.position)
367
-
368
- // Fetch with streaming
369
- /*
370
- const response = await this.customFetch(`/${stream.node.name}`, {
371
- method: "GET",
372
- redirect: "follow",
373
- headers: { Range: `bytes=${stream.position}-${to}` },
374
- })
375
-
376
- const reader = response.body.getReader()
377
- */
378
- const data = new TextEncoder().encode(_txs[stream.node.name] ?? "")
379
-
380
- // Extract the Range header to determine the start and end of the requested chunk
381
- const start = 0
382
- const end = data.length
383
-
384
- // Create a ReadableStream for the requested chunk
385
- const chunk = data.subarray(start, end)
386
- const response = new Response(
387
- new ReadableStream({
388
- start(controller) {
389
- controller.enqueue(chunk) // Push the chunk to the stream
390
- controller.close() // Close the stream when done
391
- },
392
- }),
393
- {
394
- headers: { "Content-Length": chunk.length.toString() },
395
- },
396
- )
397
- const reader = response.body.getReader()
398
- var bytes_until_cache = CHUNK_SZ
399
- var bytes_until_notify = NOTIFY_SZ
400
- var downloaded_bytes = 0
401
- var cache_chunks = []
402
-
403
- try {
404
- while (true) {
405
- const { done, value: chunk_bytes } = await reader.read()
406
- if (done) break
407
- // Update the number of downloaded bytes to be _all_, not just the write length
408
- downloaded_bytes += chunk_bytes.length
409
- bytes_until_cache -= chunk_bytes.length
410
- bytes_until_notify -= chunk_bytes.length
411
-
412
- // Write bytes from the chunk and update the pointer if necessary
413
- const write_length = Math.min(chunk_bytes.length, to_read)
414
- if (write_length > 0) {
415
- //console.log("WeaveDrive: Writing: ", write_length, " bytes to: ", dst_ptr)
416
- mod.HEAP8.set(chunk_bytes.subarray(0, write_length), dst_ptr)
417
- dst_ptr += write_length
418
- bytes_read += write_length
419
- stream.position += write_length
420
- to_read -= write_length
421
- }
422
-
423
- if (to_read == 0) {
424
- // Add excess bytes to our cache
425
- const chunk_to_cache = chunk_bytes.subarray(write_length)
426
- //console.log("WeaveDrive: Cacheing excess: ", chunk_to_cache.length)
427
- cache_chunks.push(chunk_to_cache)
428
- }
429
-
430
- if (bytes_until_cache <= 0) {
431
- console.log("WeaveDrive: Chunk size reached. Compressing cache...")
432
- stream.node.cache = this.addChunksToCache(
433
- stream.node.cache,
434
- cache_chunks,
435
- )
436
- cache_chunks = []
437
- bytes_until_cache = CHUNK_SZ
438
- }
439
-
440
- if (bytes_until_notify <= 0) {
441
- console.log(
442
- "WeaveDrive: Downloaded: ",
443
- (downloaded_bytes / stream.node.total_size) * 100,
444
- "%",
445
- )
446
- bytes_until_notify = NOTIFY_SZ
447
- }
448
- }
449
- } catch (error) {
450
- console.error("WeaveDrive: Error reading the stream: ", error)
451
- } finally {
452
- reader.releaseLock()
453
- }
454
- // If we have no cache, or we have not satisfied the full request, we need to download the rest
455
- // Rebuild the cache from the new cache chunks
456
- stream.node.cache = this.addChunksToCache(stream.node.cache, cache_chunks)
457
-
458
- // Update the last read position
459
- stream.lastReadPosition = stream.position
460
- return bytes_read
461
- },
462
- close(fd) {
463
- var stream = 0
464
- for (var i = 0; i < FS.streams.length; i++) {
465
- if (FS.streams[i].fd === fd) {
466
- stream = FS.streams[i]
467
- }
468
- }
469
- FS.close(stream)
470
- },
471
-
472
- // Readahead cache functions
473
- readFromCache(stream, dst_ptr, length) {
474
- // Check if the cache has been invalidated by a seek
475
- if (stream.lastReadPosition !== stream.position) {
476
- //console.log("WeaveDrive: Invalidating cache for fd: ", stream.fd, " Current pos: ", stream.position, " Last read pos: ", stream.lastReadPosition)
477
- stream.node.cache = new Uint8Array(0)
478
- return 0
479
- }
480
- // Calculate the bytes of the request that can be satisfied with the cache
481
- var cache_part_length = Math.min(length, stream.node.cache.length)
482
- var cache_part = stream.node.cache.subarray(0, cache_part_length)
483
- mod.HEAP8.set(cache_part, dst_ptr)
484
- // Set the new cache to the remainder of the unused cache and update pointers
485
- stream.node.cache = stream.node.cache.subarray(cache_part_length)
486
-
487
- return cache_part_length
488
- },
489
-
490
- addChunksToCache(old_cache, chunks) {
491
- // Make a new cache array of the old cache length + the sum of the chunk lengths, capped by the max cache size
492
- var new_cache_length = Math.min(
493
- old_cache.length + chunks.reduce((acc, chunk) => acc + chunk.length, 0),
494
- CACHE_SZ,
495
- )
496
- var new_cache = new Uint8Array(new_cache_length)
497
- // Copy the old cache to the new cache
498
- new_cache.set(old_cache, 0)
499
- // Load the cache chunks into the new cache
500
- var current_offset = old_cache.length
501
- for (let chunk of chunks) {
502
- if (current_offset < new_cache_length) {
503
- new_cache.set(
504
- chunk.subarray(0, new_cache_length - current_offset),
505
- current_offset,
506
- )
507
- current_offset += chunk.length
508
- }
509
- }
510
- return new_cache
511
- },
512
-
513
- // General helpder functions
514
- async checkAdmissible(ID) {
515
- if (mod.mode && mod.mode == "test") {
516
- // CAUTION: If the module is initiated with `mode = test` we don't check availability.
517
- return true
518
- }
519
-
520
- // Check if we are attempting to load the On-Boot id, if so allow it
521
- // this was added for AOP 6 Boot loader See: https://github.com/permaweb/aos/issues/342
522
- const bootTag = this.getTagValue("On-Boot", mod.spawn.tags)
523
- if (bootTag && bootTag === ID) return true
524
-
525
- // Check that this module or process set the WeaveDrive tag on spawn
526
- const blockHeight = mod.blockHeight
527
- const moduleExtensions = this.getTagValues("Extension", mod.module.tags)
528
- const moduleHasWeaveDrive = moduleExtensions.includes("WeaveDrive")
529
- const processExtensions = this.getTagValues("Extension", mod.spawn.tags)
530
- const processHasWeaveDrive =
531
- moduleHasWeaveDrive || processExtensions.includes("WeaveDrive")
532
-
533
- if (!processHasWeaveDrive) {
534
- console.log(
535
- "WeaveDrive: Process tried to call WeaveDrive, but extension not set!",
536
- )
537
- return false
538
- }
539
-
540
- const modes = ["Assignments", "Individual", "Library"]
541
- // Get the Availability-Type from the spawned process's Module or Process item
542
- // First check the module for its defaults
543
- const moduleAvailabilityType = this.getTagValue(
544
- "Availability-Type",
545
- mod.module.tags,
546
- )
547
- const moduleMode = moduleAvailabilityType
548
- ? moduleAvailabilityType
549
- : "Assignments" // Default to assignments
550
-
551
- // Now check the process's spawn item. These settings override Module item settings.
552
- const processAvailabilityType = this.getTagValue(
553
- "Availability-Type",
554
- mod.spawn.tags,
555
- )
556
- const processMode = processAvailabilityType
557
- ? processAvailabilityType
558
- : moduleMode
559
-
560
- if (!modes.includes(processMode)) {
561
- throw `Unsupported WeaveDrive mode: ${processMode}`
562
- }
563
-
564
- const attestors = this.serializeStringArr(
565
- [
566
- this.getTagValue("Scheduler", mod.spawn.tags),
567
- ...this.getTagValues("Attestor", mod.spawn.tags),
568
- ].filter(t => !!t),
569
- )
570
-
571
- // Init a set of GraphQL queries to run in order to find a valid attestation
572
- // Every WeaveDrive process has at least the "Assignments" availability check form.
573
- const assignmentsHaveID = await this.queryHasResult(
574
- `query {
575
- transactions(
576
- owners: ${attestors},
577
- block: {min: 0, max: ${blockHeight}},
578
- tags: [
579
- { name: "Type", values: ["Attestation"] },
580
- { name: "Message", values: ["${ID}"]}
581
- { name: "Data-Protocol", values: ["ao"] },
582
- ]
583
- )
584
- {
585
- edges {
586
- node {
587
- tags {
588
- name
589
- value
590
- }
591
- }
592
- }
593
- }
594
- }`,
595
- )
596
-
597
- if (assignmentsHaveID) {
598
- return true
599
- }
600
-
601
- if (processMode == "Individual") {
602
- const individualsHaveID = await this.queryHasResult(
603
- `query {
604
- transactions(
605
- owners: ${attestors},
606
- block: {min: 0, max: ${blockHeight}},
607
- tags: [
608
- { name: "Type", values: ["Available"]},
609
- { name: "ID", values: ["${ID}"]}
610
- { name: "Data-Protocol", values: ["WeaveDrive"] },
611
- ]
612
- )
613
- {
614
- edges {
615
- node {
616
- tags {
617
- name
618
- value
619
- }
620
- }
621
- }
622
- }
623
- }`,
624
- )
625
-
626
- if (individualsHaveID) {
627
- return true
628
- }
629
- }
630
-
631
- // Halt message processing if the process requires Library mode.
632
- // This should signal 'Cannot Process' to the CU, not that the message itself is
633
- // invalid. Subsequently, the CU should not be slashable for saying that the process
634
- // execution failed on this message. The CU must also not continue to execute further
635
- // messages on this process. Attesting to them would be slashable, as the state would
636
- // be incorrect.
637
- if (processMode == "Library") {
638
- throw "This WeaveDrive implementation does not support Library attestations yet!"
639
- }
640
-
641
- return false
642
- },
643
-
644
- serializeStringArr(arr = []) {
645
- return `[${arr.map(s => `"${s}"`).join(", ")}]`
646
- },
647
-
648
- getTagValues(key, tags) {
649
- var values = []
650
- for (i = 0; i < tags.length; i++) {
651
- if (tags[i].name == key) {
652
- values.push(tags[i].value)
653
- }
654
- }
655
- return values
656
- },
657
-
658
- getTagValue(key, tags) {
659
- const values = this.getTagValues(key, tags)
660
- return values.pop()
661
- },
662
-
663
- async queryHasResult(query, variables) {
664
- const json = await this.gqlQuery(query, variables).then(res => res.json())
665
-
666
- return !!json?.data?.transactions?.edges?.length
667
- },
668
-
669
- async gqlExists() {
670
- const query = `query {
671
- transactions(
672
- first: 1
673
- ) {
674
- pageInfo {
675
- hasNextPage
676
- }
677
- }
678
- }
679
- `
680
-
681
- const gqlExists = await this.gqlQuery(query, {}).then(res => res.ok)
682
- return gqlExists
683
- },
684
-
685
- async gqlQuery(query, variables) {
686
- const options = {
687
- method: "POST",
688
- body: JSON.stringify({ query, variables }),
689
- headers: { "Content-Type": "application/json" },
690
- }
691
-
692
- return this.customFetch("graphql", options)
693
- },
694
- }
695
- }
696
-
697
1
  import { DataItem } from "warp-arbundles"
698
2
  import base64url from "base64url"
3
+ import {
4
+ tags,
5
+ action,
6
+ tag,
7
+ buildTags,
8
+ isJSON,
9
+ jsonToStr,
10
+ dirname,
11
+ } from "./utils.js"
12
+ import ArMem from "./armem.js"
13
+ import weavedrive from "./weavedrive.js"
699
14
  import AoLoader from "@permaweb/ao-loader"
700
15
  import { readFileSync } from "fs"
701
16
  import { resolve } from "path"
17
+ import { scheduler, mu, su, cu, acc } from "./test.js"
702
18
  import { is, clone, fromPairs, map, mergeLeft, isNil } from "ramda"
703
- import accounts from "./accounts.js"
704
- import { tags, action, tag, buildTags } from "./utils.js"
705
-
706
- function isJSON(obj) {
707
- if (obj === null || obj === undefined) return false
708
- if (
709
- typeof obj !== "object" ||
710
- obj instanceof Buffer ||
711
- obj instanceof ArrayBuffer ||
712
- Array.isArray(obj)
713
- ) {
714
- return false
715
- }
716
-
717
- try {
718
- const str = JSON.stringify(obj)
719
- const parsed = JSON.parse(str)
720
- const isjson = typeof parsed === "object" && parsed !== null
721
- return isjson ? str : false
722
- } catch (e) {
723
- return false
724
- }
725
- }
726
-
727
- const jsonToStr = obj =>
728
- isJSON(obj) || (is(Number, obj) ? Number(obj).toString() : obj)
729
-
730
- const dirname = async () =>
731
- typeof __dirname != "undefined"
732
- ? __dirname
733
- : (await import("./dirname.js")).default
19
+ import AR from "./tar.js"
734
20
 
735
- export const acc = accounts.users
736
- export const mu = accounts.mu
737
- const scheduler = "GQ33BkPtZrqxA84vM8Zk-N2aO0toNNu_C-l-rawrBA"
738
- let env = {}
21
+ export const connect = mem => {
22
+ mem ??= new ArMem()
23
+ const ar = new AR({ mem })
24
+ const WeaveDrive = new weavedrive(ar).drive
739
25
 
740
- export const connect = () => {
741
- let wasms = {
742
- "Do_Uc2Sju_ffp6Ev0AnLVdPtot15rvMjP-a9VVaA5fM": {
743
- file: "aos2_0_1",
744
- format: "wasm64-unknown-emscripten-draft_2024_02_15",
745
- },
746
- cNlipBptaF9JeFAf4wUmpi43EojNanIBos3EfNrEOWo: {
747
- file: "aos_1",
748
- format: "wasm64-unknown-emscripten-draft_2024_02_15",
749
- },
750
- ghSkge2sIUD_F00ym5sEimC63BDBuBrq4b5OcwxOjiw: {
751
- file: "sqlite",
752
- format: "wasm64-unknown-emscripten-draft_2024_02_15",
753
- },
754
- }
755
- let modules = {
756
- aos2_0_1: "Do_Uc2Sju_ffp6Ev0AnLVdPtot15rvMjP-a9VVaA5fM",
757
- aos1: "cNlipBptaF9JeFAf4wUmpi43EojNanIBos3EfNrEOWo",
758
- sqlite: "ghSkge2sIUD_F00ym5sEimC63BDBuBrq4b5OcwxOjiw",
759
- }
760
- let modmap = {}
761
- let msgs = {}
762
26
  const transform = input => {
763
27
  const output = { Tags: [] }
764
28
  if (input.Data) output.Data = input.Data
@@ -817,16 +81,6 @@ export const connect = () => {
817
81
  }
818
82
  }
819
83
 
820
- const parse = async opt => {
821
- const item = await opt.signer({ data: opt.data ?? "", tags: opt.tags })
822
- const rowner = new DataItem(item.raw).rawOwner
823
- const hashBuffer = Buffer.from(
824
- await crypto.subtle.digest("SHA-256", rowner),
825
- )
826
- const owner = base64url.encode(hashBuffer)
827
- return { id: item.id, owner }
828
- }
829
-
830
84
  const postModule = async ({ data, tags = {}, signer }) => {
831
85
  const t = mergeLeft(tags, {
832
86
  "Data-Protocol": "ao",
@@ -838,42 +92,47 @@ export const connect = () => {
838
92
  "Memory-Limit": "1-gb",
839
93
  "Compute-Limit": "9000000000000",
840
94
  })
841
- const _tags = buildTags(t)
842
- const { id, owner } = await parse({ tags: _tags, data, signer })
95
+ const _tags = buildTags(null, t)
96
+ const { id, owner } = await ar.dataitem({ tags: _tags, data, signer })
97
+ await ar.post({ data, signer, tags: t })
843
98
  const handle = await AoLoader(data, {
844
99
  format: t["Module-Format"],
845
100
  mode: "test",
846
101
  WeaveDrive,
847
102
  })
848
- modmap[id] = { handle, id }
103
+ mem.modmap[id] = { handle, id }
849
104
  return id
850
105
  }
851
106
 
852
107
  const spawn = async (opt = {}) => {
853
108
  if (!opt.module) throw Error("module missing")
854
109
  if (!opt.scheduler) throw Error("scheduler missing")
855
- let mod = opt.module ?? modules["aos2_0_1"]
856
- if (!modmap[mod] && wasms[mod]) {
110
+ let mod = opt.module ?? mem.modules["aos2_0_1"]
111
+ if (!mem.modmap[mod] && mem.wasms[mod]) {
857
112
  const __dirname = await dirname()
858
113
  const wasm = readFileSync(
859
- resolve(__dirname, `lua/${wasms[mod].file}.wasm`),
114
+ resolve(__dirname, `lua/${mem.wasms[mod].file}.wasm`),
860
115
  )
861
116
  const handle = await AoLoader(wasm, {
862
- format: wasms[mod].format,
117
+ format: mem.wasms[mod].format,
863
118
  mode: "test",
864
119
  WeaveDrive,
865
120
  })
866
- modmap[mod] = { handle, id: mod }
121
+ mem.modmap[mod] = { handle, id: mod }
867
122
  }
868
123
  if (!mod) throw Error("module not found")
869
- const _module = modmap[mod]
124
+ const _module = mem.modmap[mod]
870
125
  let ex = false
871
126
  opt.tags ??= []
872
127
  for (let v of opt.tags) if (v.name === "Type") ex = true
873
128
  if (!ex) opt.tags.push({ name: "Type", value: "Process" })
874
- const { id, owner } = await parse(opt)
129
+ const { id, owner, item } = await ar.dataitem({
130
+ data: opt.data,
131
+ signer: opt.signer,
132
+ tags: tags(opt.tags),
133
+ })
134
+ await ar.postItem(item, su.jwk)
875
135
  const _tags = tags(opt.tags)
876
- _txs[id] = opt.data ?? null
877
136
  let res = null
878
137
  let memory = null
879
138
  let p = {
@@ -891,7 +150,7 @@ export const connect = () => {
891
150
  if (_tags["On-Boot"]) {
892
151
  let data = ""
893
152
  if (_tags["On-Boot"] === "Data") data = opt.data ?? ""
894
- else data = msgs[_tags["On-Boot"]]?.data ?? ""
153
+ else data = mem.msgs[_tags["On-Boot"]]?.data ?? ""
895
154
  let msg = genMsg(p, data, opt.tags, owner, mu.addr, true)
896
155
  const _env = genEnv({
897
156
  pid: p.id,
@@ -908,8 +167,8 @@ export const connect = () => {
908
167
  } else {
909
168
  p.height += 1
910
169
  }
911
- msgs[id] = opt
912
- env[id] = p
170
+ mem.msgs[id] = opt
171
+ mem.env[id] = p
913
172
  if (_tags["Cron-Interval"]) {
914
173
  let [num, unit] = _tags["Cron-Interval"].split("-")
915
174
  let int = 0
@@ -942,16 +201,22 @@ export const connect = () => {
942
201
  cronTags.push({ name: k.replace(/Cron-Tag-/, ""), value: _tags[k] })
943
202
  }
944
203
  }
945
- env[id].cronTags = cronTags
946
- env[id].span = int
204
+ mem.env[id].cronTags = cronTags
205
+ mem.env[id].span = int
947
206
  }
948
207
  return id
949
208
  }
950
209
 
951
210
  const assign = async opt => {
952
- const p = env[opt.process]
953
- let _opt = clone(msgs[opt.message])
954
- const { id, owner } = await parse(_opt)
211
+ const p = mem.env[opt.process]
212
+ let _opt = clone(mem.msgs[opt.message])
213
+ const { id, owner, item } = await ar.dataitem({
214
+ data: _opt.data,
215
+ signer: _opt.signer,
216
+ tags: tags(_opt.tags),
217
+ target: opt.process,
218
+ })
219
+ await ar.postItem(item, su.jwk)
955
220
  try {
956
221
  const msg = genMsg(
957
222
  p,
@@ -972,9 +237,9 @@ export const connect = () => {
972
237
  p.res[id] = res
973
238
  p.results.push(id)
974
239
  p.txs.unshift({ id: id, ..._opt })
975
- msgs[id] = _opt
240
+ mem.msgs[id] = _opt
976
241
  for (const v of res.Messages ?? []) {
977
- if (env[v.Target]) {
242
+ if (mem.env[v.Target]) {
978
243
  await message({
979
244
  process: v.Target,
980
245
  tags: v.Tags,
@@ -992,12 +257,18 @@ export const connect = () => {
992
257
  }
993
258
 
994
259
  const message = async opt => {
995
- const p = env[opt.process]
260
+ const p = mem.env[opt.process]
996
261
  let ex = false
997
262
  for (let v of opt.tags) if (v.name === "Type") ex = true
998
263
 
999
264
  if (!ex) opt.tags.push({ name: "Type", value: "Message" })
1000
- const { id, owner } = await parse(opt)
265
+ const { item, id, owner } = await ar.dataitem({
266
+ data: opt.data,
267
+ signer: opt.signer,
268
+ tags: tags(opt.tags),
269
+ target: opt.process,
270
+ })
271
+ await ar.postItem(item, su.jwk)
1001
272
  try {
1002
273
  const msg = genMsg(
1003
274
  p,
@@ -1013,15 +284,14 @@ export const connect = () => {
1013
284
  auth: mu.addr,
1014
285
  })
1015
286
  const res = await p.handle(p.memory, msg, _env)
1016
- _txs[id] = opt.data ?? null
1017
287
  p.memory = res.Memory
1018
288
  delete res.Memory
1019
289
  p.res[id] = res
1020
290
  p.results.push(id)
1021
291
  p.txs.unshift({ id: id, ...opt })
1022
- msgs[id] = opt
292
+ mem.msgs[id] = opt
1023
293
  for (const v of res.Messages ?? []) {
1024
- if (env[v.Target]) {
294
+ if (mem.env[v.Target]) {
1025
295
  await message({
1026
296
  process: v.Target,
1027
297
  tags: v.Tags,
@@ -1060,20 +330,16 @@ export const connect = () => {
1060
330
  }
1061
331
 
1062
332
  return {
1063
- scheduler,
1064
- modules,
1065
- accounts: acc,
1066
- mu,
1067
333
  message,
1068
334
  unmonitor: async opt => {
1069
- const p = env[opt.process]
335
+ const p = mem.env[opt.process]
1070
336
  try {
1071
337
  clearInterval(p.cron)
1072
338
  p.cron = null
1073
339
  } catch (e) {}
1074
340
  },
1075
341
  monitor: async opt => {
1076
- const p = env[opt.process]
342
+ const p = mem.env[opt.process]
1077
343
  if (isNil(p.cron)) {
1078
344
  p.cron = setInterval(async () => {
1079
345
  await message({
@@ -1085,16 +351,11 @@ export const connect = () => {
1085
351
  }, p.span)
1086
352
  }
1087
353
  },
1088
- txs: async pid => {
1089
- let _txs = []
1090
- for (let v of env[pid].txs) _txs.push({ tags: v.tags, id: v.id })
1091
- return _txs
1092
- },
1093
354
  spawn,
1094
355
  assign,
1095
- result: async opt => env[opt.process].res[opt.message],
356
+ result: async opt => mem.env[opt.process].res[opt.message],
1096
357
  results: async opt => {
1097
- const p = env[opt.process]
358
+ const p = mem.env[opt.process]
1098
359
  let results = []
1099
360
  const limit = opt.limit ?? 25
1100
361
  if (opt.sort === "DESC") {
@@ -1111,8 +372,8 @@ export const connect = () => {
1111
372
  return { edges: results }
1112
373
  },
1113
374
  dryrun: async opt => {
1114
- const p = env[opt.process]
1115
- const { id, owner } = await parse(opt)
375
+ const p = mem.env[opt.process]
376
+ const { id, owner } = await ar.dataitem({ ...opt, target: opt.process })
1116
377
  try {
1117
378
  const msg = genMsg(p, opt.data ?? "", opt.tags, owner, mu.addr, true)
1118
379
  const _env = genEnv({
@@ -1128,9 +389,6 @@ export const connect = () => {
1128
389
  }
1129
390
  return null
1130
391
  },
1131
- getProcesses: () => env,
1132
- blueprint: async pkg => {
1133
- return readFileSync(resolve(await dirname(), `lua/${pkg}.lua`), "utf8")
1134
- },
392
+ mem,
1135
393
  }
1136
394
  }