wao 0.6.0 → 0.6.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/cjs/ao.js +102 -81
- package/cjs/aoconnect.js +36 -32
- package/cjs/helpers.js +4 -12
- package/cjs/kv.js +335 -0
- package/cjs/utils.js +17 -1
- package/cjs/weavedrive.js +236 -588
- package/esm/ao.js +25 -13
- package/esm/aoconnect.js +8 -5
- package/esm/helpers.js +4 -12
- package/esm/kv.js +226 -0
- package/esm/utils.js +14 -0
- package/esm/weavedrive.js +67 -293
- package/package.json +1 -1
package/esm/weavedrive.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import Arweave from "arweave"
|
|
2
|
-
import { toGraphObj } from "./utils.js"
|
|
3
|
-
import { map } from "ramda"
|
|
4
|
-
|
|
5
1
|
const KB = 1024
|
|
6
2
|
const MB = KB * 1024
|
|
7
3
|
const CACHE_SZ = 32 * KB
|
|
8
4
|
const CHUNK_SZ = 128 * MB
|
|
9
5
|
const NOTIFY_SZ = 512 * MB
|
|
10
6
|
const log = console.log
|
|
7
|
+
|
|
11
8
|
export default class WeaveDrive {
|
|
12
9
|
constructor(ar) {
|
|
13
10
|
this.drive = function WeaveDrive(mod, FS) {
|
|
@@ -18,44 +15,6 @@ export default class WeaveDrive {
|
|
|
18
15
|
FS.streams[fd].node.cache = new Uint8Array(0)
|
|
19
16
|
},
|
|
20
17
|
|
|
21
|
-
joinUrl({ url, path }) {
|
|
22
|
-
if (!path) return url
|
|
23
|
-
if (path.startsWith("/"))
|
|
24
|
-
return this.joinUrl({ url, path: path.slice(1) })
|
|
25
|
-
|
|
26
|
-
url = new URL(url)
|
|
27
|
-
url.pathname += path
|
|
28
|
-
return url.toString()
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
async customFetch(path, options) {
|
|
32
|
-
/**
|
|
33
|
-
* mod.ARWEAVE may be a comma-delimited list of urls.
|
|
34
|
-
* So we parse it into an array that we sequentially consume
|
|
35
|
-
* using fetch, and return the first successful response.
|
|
36
|
-
*
|
|
37
|
-
* The first url is considered "primary". So if all urls fail
|
|
38
|
-
* to produce a successful response, then we return the primary's
|
|
39
|
-
* error response
|
|
40
|
-
*/
|
|
41
|
-
const urlList = mod.ARWEAVE.includes(",")
|
|
42
|
-
? mod.ARWEAVE.split(",").map(url => url.trim())
|
|
43
|
-
: [mod.ARWEAVE]
|
|
44
|
-
|
|
45
|
-
let p
|
|
46
|
-
for (const url of urlList) {
|
|
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
18
|
async create(id) {
|
|
60
19
|
var properties = { isDevice: false, contents: null }
|
|
61
20
|
|
|
@@ -66,19 +25,14 @@ export default class WeaveDrive {
|
|
|
66
25
|
|
|
67
26
|
// Create the file in the emscripten FS
|
|
68
27
|
|
|
69
|
-
|
|
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
|
-
}
|
|
28
|
+
if (!FS.analyzePath("/data/").exists) FS.mkdir("/data/")
|
|
75
29
|
|
|
76
30
|
var node = FS.createFile("/", "data/" + id, properties, true, false)
|
|
77
31
|
// Set initial parameters
|
|
78
32
|
/*
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
33
|
+
var bytesLength = await this.customFetch(`/${id}`, {
|
|
34
|
+
method: "HEAD",
|
|
35
|
+
}).then(res => res.headers.get("Content-Length"))
|
|
82
36
|
*/
|
|
83
37
|
let data = await ar.data(id)
|
|
84
38
|
const bytesLength = data?.length ?? 0
|
|
@@ -102,21 +56,21 @@ export default class WeaveDrive {
|
|
|
102
56
|
return stream
|
|
103
57
|
},
|
|
104
58
|
async createBlockHeader(id) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
59
|
+
var result = ""
|
|
60
|
+
try {
|
|
61
|
+
// todo: implement indep_hash
|
|
62
|
+
// fetch(`/block/height/${id}`)
|
|
63
|
+
const block = ar.mem.blockmap[ar.mem.blocks[id]]
|
|
64
|
+
if (block) {
|
|
65
|
+
result = JSON.stringify({
|
|
66
|
+
id: block.id,
|
|
67
|
+
timestamp: block.timestamp,
|
|
68
|
+
previous_block: block.previous,
|
|
69
|
+
txs: block.txs,
|
|
70
|
+
height: id,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {}
|
|
120
74
|
|
|
121
75
|
var bytesLength = result.length
|
|
122
76
|
|
|
@@ -132,33 +86,12 @@ export default class WeaveDrive {
|
|
|
132
86
|
return stream
|
|
133
87
|
},
|
|
134
88
|
async createTxHeader(id) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
async function retry(x) {
|
|
142
|
-
return new Promise(r => {
|
|
143
|
-
setTimeout(function () {
|
|
144
|
-
r(customFetch(`/tx/${id}`))
|
|
145
|
-
}, x * 10000)
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
// todo: add a bunch of retries
|
|
149
|
-
var result = await this.customFetch(`/tx/${id}`)
|
|
150
|
-
.then(res => (!res.ok ? retry(1) : res))
|
|
151
|
-
.then(res => (!res.ok ? retry(2) : res))
|
|
152
|
-
.then(res => (!res.ok ? retry(3) : res))
|
|
153
|
-
.then(res => (!res.ok ? retry(4) : res))
|
|
154
|
-
.then(res => res.json())
|
|
155
|
-
.then(async entry => ({
|
|
156
|
-
...entry,
|
|
157
|
-
ownerAddress: await toAddress(entry.owner),
|
|
158
|
-
}))
|
|
159
|
-
//.then(x => (console.error(x), x))
|
|
160
|
-
.then(x => JSON.stringify(x))
|
|
161
|
-
|
|
89
|
+
var result = ""
|
|
90
|
+
// fetch(`/tx/${id}`)
|
|
91
|
+
try {
|
|
92
|
+
let tx = ar.mem.txs[id]
|
|
93
|
+
if (tx) result = JSON.stringify(tx)
|
|
94
|
+
} catch (e) {}
|
|
162
95
|
var node = FS.createDataFile(
|
|
163
96
|
"/",
|
|
164
97
|
"tx/" + id,
|
|
@@ -170,89 +103,25 @@ export default class WeaveDrive {
|
|
|
170
103
|
return stream
|
|
171
104
|
},
|
|
172
105
|
async createDataItemTxHeader(id) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
fee {
|
|
191
|
-
ar
|
|
192
|
-
winston
|
|
193
|
-
}
|
|
194
|
-
quantity {
|
|
195
|
-
winston
|
|
196
|
-
ar
|
|
197
|
-
}
|
|
198
|
-
tags {
|
|
199
|
-
name
|
|
200
|
-
value
|
|
201
|
-
}
|
|
202
|
-
bundledIn {
|
|
203
|
-
id
|
|
204
|
-
}
|
|
205
|
-
block {
|
|
206
|
-
id
|
|
207
|
-
timestamp
|
|
208
|
-
height
|
|
209
|
-
previous
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}`
|
|
215
|
-
var variables = { transactionIds: [id] }
|
|
216
|
-
async function retry(x) {
|
|
217
|
-
return new Promise(r => {
|
|
218
|
-
setTimeout(function () {
|
|
219
|
-
r(gqlQuery(GET_TRANSACTION_QUERY, variables))
|
|
220
|
-
}, x * 10000)
|
|
106
|
+
let result = (
|
|
107
|
+
await ar.gql.txs({
|
|
108
|
+
id,
|
|
109
|
+
fields: [
|
|
110
|
+
"id",
|
|
111
|
+
"anchor",
|
|
112
|
+
{ data: ["size"] },
|
|
113
|
+
"signature",
|
|
114
|
+
"recipient",
|
|
115
|
+
"owner",
|
|
116
|
+
"fee",
|
|
117
|
+
"quantity",
|
|
118
|
+
"tags",
|
|
119
|
+
"bundledIn",
|
|
120
|
+
"block",
|
|
121
|
+
],
|
|
221
122
|
})
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const gqlExists = await this.gqlExists()
|
|
225
|
-
if (!gqlExists) {
|
|
226
|
-
return "GQL Not Found!"
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// todo: add a bunch of retries
|
|
230
|
-
var result = await this.gqlQuery(GET_TRANSACTION_QUERY, variables)
|
|
231
|
-
.then(res => (!res.ok ? retry(1) : res))
|
|
232
|
-
.then(res => (!res.ok ? retry(2) : res))
|
|
233
|
-
.then(res => (!res.ok ? retry(3) : res))
|
|
234
|
-
.then(res => (!res.ok ? retry(4) : res))
|
|
235
|
-
.then(res => res.json())
|
|
236
|
-
.then(res => {
|
|
237
|
-
return res?.data?.transactions?.edges?.[0]?.node
|
|
238
|
-
? res.data.transactions.edges[0].node
|
|
239
|
-
: "No results"
|
|
240
|
-
})
|
|
241
|
-
.then(async entry => {
|
|
242
|
-
return typeof entry == "string"
|
|
243
|
-
? entry
|
|
244
|
-
: {
|
|
245
|
-
format: 3,
|
|
246
|
-
...entry,
|
|
247
|
-
}
|
|
248
|
-
})
|
|
249
|
-
.then(x => {
|
|
250
|
-
return typeof x == "string" ? x : JSON.stringify(x)
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
if (result === "No results") {
|
|
254
|
-
return result
|
|
255
|
-
}
|
|
123
|
+
)[0]
|
|
124
|
+
result = result ? JSON.stringify(result) : "No results"
|
|
256
125
|
FS.createDataFile(
|
|
257
126
|
"/",
|
|
258
127
|
"tx2/" + id,
|
|
@@ -364,17 +233,9 @@ export default class WeaveDrive {
|
|
|
364
233
|
)
|
|
365
234
|
//console.log("WeaveDrive: fd: ", fd, " Read length: ", to_read, " Reading ahead:", to - to_read - stream.position)
|
|
366
235
|
|
|
367
|
-
//
|
|
368
|
-
/*
|
|
369
|
-
const response = await this.customFetch(`/${stream.node.name}`, {
|
|
370
|
-
method: "GET",
|
|
371
|
-
redirect: "follow",
|
|
372
|
-
headers: { Range: `bytes=${stream.position}-${to}` },
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
const reader = response.body.getReader()
|
|
376
|
-
*/
|
|
236
|
+
// fetch(`/${stream.node.name}`)
|
|
377
237
|
const data = await ar.data(stream.node.name)
|
|
238
|
+
|
|
378
239
|
// Extract the Range header to determine the start and end of the requested chunk
|
|
379
240
|
const start = 0
|
|
380
241
|
const end = data.length
|
|
@@ -465,9 +326,7 @@ export default class WeaveDrive {
|
|
|
465
326
|
close(fd) {
|
|
466
327
|
var stream = 0
|
|
467
328
|
for (var i = 0; i < FS.streams.length; i++) {
|
|
468
|
-
if (FS.streams[i].fd === fd)
|
|
469
|
-
stream = FS.streams[i]
|
|
470
|
-
}
|
|
329
|
+
if (FS.streams[i].fd === fd) stream = FS.streams[i]
|
|
471
330
|
}
|
|
472
331
|
FS.close(stream)
|
|
473
332
|
},
|
|
@@ -516,10 +375,9 @@ export default class WeaveDrive {
|
|
|
516
375
|
|
|
517
376
|
// General helpder functions
|
|
518
377
|
async checkAdmissible(ID) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
378
|
+
// CAUTION: If the module is initiated with `mode = test` we don't check availability.
|
|
379
|
+
if (mod.mode && mod.mode == "test") return true
|
|
380
|
+
|
|
523
381
|
// Check if we are attempting to load the On-Boot id, if so allow it
|
|
524
382
|
// this was added for AOP 6 Boot loader See: https://github.com/permaweb/aos/issues/342
|
|
525
383
|
const bootTag = this.getTagValue("On-Boot", mod.spawn.tags)
|
|
@@ -546,7 +404,6 @@ export default class WeaveDrive {
|
|
|
546
404
|
)
|
|
547
405
|
return false
|
|
548
406
|
}
|
|
549
|
-
|
|
550
407
|
const modes = ["Assignments", "Individual", "Library"]
|
|
551
408
|
// Get the Availability-Type from the spawned process's Module or Process item
|
|
552
409
|
// First check the module for its defaults
|
|
@@ -579,66 +436,30 @@ export default class WeaveDrive {
|
|
|
579
436
|
)
|
|
580
437
|
// Init a set of GraphQL queries to run in order to find a valid attestation
|
|
581
438
|
// Every WeaveDrive process has at least the "Assignments" availability check form.
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
block: {min: 0, max: ${blockHeight}},
|
|
588
|
-
tags: [
|
|
589
|
-
{ name: "Type", values: ["Attestation"] },
|
|
590
|
-
{ name: "Message", values: ["${ID}"]}
|
|
591
|
-
{ name: "Data-Protocol", values: ["ao"] },
|
|
592
|
-
]
|
|
593
|
-
)
|
|
594
|
-
{
|
|
595
|
-
edges {
|
|
596
|
-
node {
|
|
597
|
-
tags {
|
|
598
|
-
name
|
|
599
|
-
value
|
|
600
|
-
}
|
|
601
|
-
}
|
|
439
|
+
const exists = async tags => {
|
|
440
|
+
const common = {
|
|
441
|
+
owners: attestors,
|
|
442
|
+
block: [0, blockHeight],
|
|
443
|
+
fields: ["tags"],
|
|
602
444
|
}
|
|
445
|
+
return (await ar.gql.txs({ ...common, tags })).length > 0
|
|
603
446
|
}
|
|
604
|
-
|
|
605
|
-
|
|
447
|
+
const assignmentsHaveID = await exists({
|
|
448
|
+
Type: "Attestation",
|
|
449
|
+
Message: ID,
|
|
450
|
+
"Data-Protocol": "ao",
|
|
451
|
+
})
|
|
606
452
|
if (assignmentsHaveID) return true
|
|
607
453
|
|
|
608
454
|
if (processMode == "Individual") {
|
|
609
|
-
const individualsHaveID = await
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
tags: [
|
|
615
|
-
{ name: "Type", values: ["Available"]},
|
|
616
|
-
{ name: "ID", values: ["${ID}"]}
|
|
617
|
-
{ name: "Data-Protocol", values: ["WeaveDrive"] },
|
|
618
|
-
]
|
|
619
|
-
)
|
|
620
|
-
{
|
|
621
|
-
edges {
|
|
622
|
-
node {
|
|
623
|
-
tags {
|
|
624
|
-
name
|
|
625
|
-
value
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}`,
|
|
631
|
-
)
|
|
632
|
-
|
|
455
|
+
const individualsHaveID = await exists({
|
|
456
|
+
Type: "Available",
|
|
457
|
+
ID,
|
|
458
|
+
"Data-Protocol": "WeaveDrive",
|
|
459
|
+
})
|
|
633
460
|
if (individualsHaveID) return true
|
|
634
461
|
}
|
|
635
462
|
|
|
636
|
-
// Halt message processing if the process requires Library mode.
|
|
637
|
-
// This should signal 'Cannot Process' to the CU, not that the message itself is
|
|
638
|
-
// invalid. Subsequently, the CU should not be slashable for saying that the process
|
|
639
|
-
// execution failed on this message. The CU must also not continue to execute further
|
|
640
|
-
// messages on this process. Attesting to them would be slashable, as the state would
|
|
641
|
-
// be incorrect.
|
|
642
463
|
if (processMode == "Library") {
|
|
643
464
|
throw "This WeaveDrive implementation does not support Library attestations yet!"
|
|
644
465
|
}
|
|
@@ -653,9 +474,7 @@ export default class WeaveDrive {
|
|
|
653
474
|
getTagValues(key, tags) {
|
|
654
475
|
var values = []
|
|
655
476
|
for (let i = 0; i < tags.length; i++) {
|
|
656
|
-
if (tags[i].name == key)
|
|
657
|
-
values.push(tags[i].value)
|
|
658
|
-
}
|
|
477
|
+
if (tags[i].name == key) values.push(tags[i].value)
|
|
659
478
|
}
|
|
660
479
|
return values
|
|
661
480
|
},
|
|
@@ -664,51 +483,6 @@ export default class WeaveDrive {
|
|
|
664
483
|
const values = this.getTagValues(key, tags)
|
|
665
484
|
return values.pop()
|
|
666
485
|
},
|
|
667
|
-
|
|
668
|
-
async queryHasResult(query, variables) {
|
|
669
|
-
const json = await this.gqlQuery(query, variables).then(res =>
|
|
670
|
-
res.json(),
|
|
671
|
-
)
|
|
672
|
-
return !!json?.data?.transactions?.edges?.length
|
|
673
|
-
},
|
|
674
|
-
|
|
675
|
-
async gqlExists() {
|
|
676
|
-
const query = `query {
|
|
677
|
-
transactions(
|
|
678
|
-
first: 1
|
|
679
|
-
) {
|
|
680
|
-
pageInfo {
|
|
681
|
-
hasNextPage
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
`
|
|
686
|
-
|
|
687
|
-
const gqlExists = await this.gqlQuery(query, {}).then(res => res.ok)
|
|
688
|
-
return gqlExists
|
|
689
|
-
},
|
|
690
|
-
|
|
691
|
-
async gqlQuery(query, variables) {
|
|
692
|
-
let json = null
|
|
693
|
-
try {
|
|
694
|
-
const { tar, args } = toGraphObj({ query, variables })
|
|
695
|
-
let res2 = null
|
|
696
|
-
if (tar === "transactions") {
|
|
697
|
-
res2 = await ar.gql.txs({ ...args })
|
|
698
|
-
} else if (tar === "blocks") {
|
|
699
|
-
res2 = await ar.gql.blocks({ ...args })
|
|
700
|
-
}
|
|
701
|
-
const edges = map(v => ({ node: v, cursor: v.cursor }), res2)
|
|
702
|
-
json = {
|
|
703
|
-
data: {
|
|
704
|
-
transactions: { pageInfo: { hasNextPage: true }, edges },
|
|
705
|
-
},
|
|
706
|
-
}
|
|
707
|
-
} catch (e) {
|
|
708
|
-
log(e)
|
|
709
|
-
}
|
|
710
|
-
return { json: () => json }
|
|
711
|
-
},
|
|
712
486
|
}
|
|
713
487
|
}
|
|
714
488
|
}
|