wao 0.6.0 → 0.6.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.
package/esm/weavedrive.js CHANGED
@@ -1,16 +1,13 @@
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
- this.drive = function WeaveDrive(mod, FS) {
10
+ this.ext = (mod, FS) => {
14
11
  return {
15
12
  reset(fd) {
16
13
  //console.log("WeaveDrive: Resetting fd: ", fd)
@@ -18,67 +15,22 @@ 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
 
62
- if (!(await this.checkAdmissible(id))) {
63
- //console.log("WeaveDrive: Arweave ID is not admissable! ", id)
64
- return 0
65
- }
21
+ //console.log("WeaveDrive: Arweave ID is not admissable! ", id)
22
+ if (!(await this.checkAdmissible(id))) return 0
66
23
 
67
24
  // Create the file in the emscripten FS
68
25
 
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
- }
26
+ if (!FS.analyzePath("/data/").exists) FS.mkdir("/data/")
75
27
 
76
28
  var node = FS.createFile("/", "data/" + id, properties, true, false)
77
29
  // Set initial parameters
78
30
  /*
79
- var bytesLength = await this.customFetch(`/${id}`, {
80
- method: "HEAD",
81
- }).then(res => res.headers.get("Content-Length"))
31
+ var bytesLength = await this.customFetch(`/${id}`, {
32
+ method: "HEAD",
33
+ }).then(res => res.headers.get("Content-Length"))
82
34
  */
83
35
  let data = await ar.data(id)
84
36
  const bytesLength = data?.length ?? 0
@@ -88,11 +40,7 @@ export default class WeaveDrive {
88
40
 
89
41
  // Add a function that defers querying the file size until it is asked the first time.
90
42
  Object.defineProperties(node, {
91
- usedBytes: {
92
- get: function () {
93
- return bytesLength
94
- },
95
- },
43
+ usedBytes: { get: () => bytesLength },
96
44
  })
97
45
 
98
46
  // Now we have created the file in the emscripten FS, we can open it as a stream
@@ -102,23 +50,21 @@ export default class WeaveDrive {
102
50
  return stream
103
51
  },
104
52
  async createBlockHeader(id) {
105
- const customFetch = this.customFetch
106
- // todo: add a bunch of retries
107
- async function retry(x) {
108
- return new Promise(r => {
109
- setTimeout(function () {
110
- r(customFetch(`/block/height/${id}`))
111
- }, x * 10000)
112
- })
113
- }
114
- var result = await this.customFetch(`/block/height/${id}`)
115
- .then(res => (!res.ok ? retry(1) : res))
116
- .then(res => (!res.ok ? retry(2) : res))
117
- .then(res => (!res.ok ? retry(3) : res))
118
- .then(res => (!res.ok ? retry(4) : res))
119
- .then(res => res.text())
120
-
121
- var bytesLength = result.length
53
+ var result = ""
54
+ try {
55
+ // todo: implement indep_hash
56
+ // fetch(`/block/height/${id}`)
57
+ const block = ar.mem.blockmap[ar.mem.blocks[id]]
58
+ if (block) {
59
+ result = JSON.stringify({
60
+ id: block.id,
61
+ timestamp: block.timestamp,
62
+ previous_block: block.previous,
63
+ txs: block.txs,
64
+ height: id,
65
+ })
66
+ }
67
+ } catch (e) {}
122
68
 
123
69
  var node = FS.createDataFile(
124
70
  "/",
@@ -132,33 +78,12 @@ export default class WeaveDrive {
132
78
  return stream
133
79
  },
134
80
  async createTxHeader(id) {
135
- const customFetch = this.customFetch
136
- async function toAddress(owner) {
137
- return Arweave.utils.bufferTob64Url(
138
- await Arweave.crypto.hash(Arweave.utils.b64UrlToBuffer(owner)),
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
-
81
+ var result = ""
82
+ // fetch(`/tx/${id}`)
83
+ try {
84
+ let tx = ar.mem.txs[id]
85
+ if (tx) result = JSON.stringify(tx)
86
+ } catch (e) {}
162
87
  var node = FS.createDataFile(
163
88
  "/",
164
89
  "tx/" + id,
@@ -170,89 +95,25 @@ export default class WeaveDrive {
170
95
  return stream
171
96
  },
172
97
  async createDataItemTxHeader(id) {
173
- const gqlQuery = this.gqlQuery
174
- var GET_TRANSACTION_QUERY = `
175
- query GetTransactions ($transactionIds: [ID!]!) {
176
- transactions(ids: $transactionIds) {
177
- edges {
178
- node {
179
- id
180
- anchor
181
- data {
182
- size
183
- }
184
- signature
185
- recipient
186
- owner {
187
- address
188
- key
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)
221
- })
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)
98
+ let result = (
99
+ await ar.gql.txs({
100
+ id,
101
+ fields: [
102
+ "id",
103
+ "anchor",
104
+ { data: ["size"] },
105
+ "signature",
106
+ "recipient",
107
+ "owner",
108
+ "fee",
109
+ "quantity",
110
+ "tags",
111
+ "bundledIn",
112
+ "block",
113
+ ],
251
114
  })
252
-
253
- if (result === "No results") {
254
- return result
255
- }
115
+ )[0]
116
+ result = result ? JSON.stringify(result) : "No results"
256
117
  FS.createDataFile(
257
118
  "/",
258
119
  "tx2/" + id,
@@ -351,10 +212,10 @@ export default class WeaveDrive {
351
212
  to_read -= bytes_read
352
213
 
353
214
  // Return if we have satisfied the request
354
- if (to_read === 0) {
355
- //console.log("WeaveDrive: Satisfied request with cache. Returning...")
356
- return bytes_read
357
- }
215
+
216
+ //console.log("WeaveDrive: Satisfied request with cache. Returning...")
217
+ if (to_read === 0) return bytes_read
218
+
358
219
  //console.log("WeaveDrive: Read from cache: ", bytes_read, " Remaining to read: ", to_read)
359
220
 
360
221
  const chunk_download_sz = Math.max(to_read, CACHE_SZ)
@@ -364,17 +225,9 @@ export default class WeaveDrive {
364
225
  )
365
226
  //console.log("WeaveDrive: fd: ", fd, " Read length: ", to_read, " Reading ahead:", to - to_read - stream.position)
366
227
 
367
- // Fetch with streaming
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
- */
228
+ // fetch(`/${stream.node.name}`)
377
229
  const data = await ar.data(stream.node.name)
230
+
378
231
  // Extract the Range header to determine the start and end of the requested chunk
379
232
  const start = 0
380
233
  const end = data.length
@@ -465,9 +318,7 @@ export default class WeaveDrive {
465
318
  close(fd) {
466
319
  var stream = 0
467
320
  for (var i = 0; i < FS.streams.length; i++) {
468
- if (FS.streams[i].fd === fd) {
469
- stream = FS.streams[i]
470
- }
321
+ if (FS.streams[i].fd === fd) stream = FS.streams[i]
471
322
  }
472
323
  FS.close(stream)
473
324
  },
@@ -516,10 +367,9 @@ export default class WeaveDrive {
516
367
 
517
368
  // General helpder functions
518
369
  async checkAdmissible(ID) {
519
- if (mod.mode && mod.mode == "test") {
520
- // CAUTION: If the module is initiated with `mode = test` we don't check availability.
521
- return true
522
- }
370
+ // CAUTION: If the module is initiated with `mode = test` we don't check availability.
371
+ if (mod.mode && mod.mode == "test") return true
372
+
523
373
  // Check if we are attempting to load the On-Boot id, if so allow it
524
374
  // this was added for AOP 6 Boot loader See: https://github.com/permaweb/aos/issues/342
525
375
  const bootTag = this.getTagValue("On-Boot", mod.spawn.tags)
@@ -546,7 +396,6 @@ export default class WeaveDrive {
546
396
  )
547
397
  return false
548
398
  }
549
-
550
399
  const modes = ["Assignments", "Individual", "Library"]
551
400
  // Get the Availability-Type from the spawned process's Module or Process item
552
401
  // First check the module for its defaults
@@ -579,66 +428,30 @@ export default class WeaveDrive {
579
428
  )
580
429
  // Init a set of GraphQL queries to run in order to find a valid attestation
581
430
  // Every WeaveDrive process has at least the "Assignments" availability check form.
582
-
583
- const assignmentsHaveID = await this.queryHasResult(
584
- `query {
585
- transactions(
586
- owners: ${attestors},
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
- }
431
+ const exists = async tags => {
432
+ const common = {
433
+ owners: attestors,
434
+ block: [0, blockHeight],
435
+ fields: ["tags"],
602
436
  }
437
+ return (await ar.gql.txs({ ...common, tags })).length > 0
603
438
  }
604
- }`,
605
- )
439
+ const assignmentsHaveID = await exists({
440
+ Type: "Attestation",
441
+ Message: ID,
442
+ "Data-Protocol": "ao",
443
+ })
606
444
  if (assignmentsHaveID) return true
607
445
 
608
446
  if (processMode == "Individual") {
609
- const individualsHaveID = await this.queryHasResult(
610
- `query {
611
- transactions(
612
- owners: ${attestors},
613
- block: {min: 0, max: ${blockHeight}},
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
-
447
+ const individualsHaveID = await exists({
448
+ Type: "Available",
449
+ ID,
450
+ "Data-Protocol": "WeaveDrive",
451
+ })
633
452
  if (individualsHaveID) return true
634
453
  }
635
454
 
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
455
  if (processMode == "Library") {
643
456
  throw "This WeaveDrive implementation does not support Library attestations yet!"
644
457
  }
@@ -653,9 +466,7 @@ export default class WeaveDrive {
653
466
  getTagValues(key, tags) {
654
467
  var values = []
655
468
  for (let i = 0; i < tags.length; i++) {
656
- if (tags[i].name == key) {
657
- values.push(tags[i].value)
658
- }
469
+ if (tags[i].name == key) values.push(tags[i].value)
659
470
  }
660
471
  return values
661
472
  },
@@ -664,51 +475,6 @@ export default class WeaveDrive {
664
475
  const values = this.getTagValues(key, tags)
665
476
  return values.pop()
666
477
  },
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
478
  }
713
479
  }
714
480
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wao",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",