wao 0.31.2 → 0.32.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/hb2.js ADDED
@@ -0,0 +1,439 @@
1
+ import { connect, createSigner } from "@permaweb/aoconnect"
2
+ import { isEmpty, last, isNotNil, mergeLeft, clone } from "ramda"
3
+ import { rsaid, hmacid, toAddr, buildTags } from "./utils.js"
4
+ import { sign, signer } from "./signer.js"
5
+ import { send as _send } from "./send.js"
6
+ import hyper_aos from "./lua/hyper-aos.js"
7
+ import aos_wamr from "./lua/aos_wamr.js"
8
+ import { from } from "./httpsig.js"
9
+
10
+ const seed = num => {
11
+ const array = new Array(num)
12
+ for (let i = 0; i < num; i++) array[i] = Math.floor(Math.random() * 256)
13
+ return Buffer.from(array).toString("base64")
14
+ }
15
+
16
+ class HB {
17
+ constructor({
18
+ url = "http://localhost:10001",
19
+ cu = "http://localhost:6363",
20
+ jwk,
21
+ } = {}) {
22
+ this.cu = cu
23
+ this.url = url
24
+ if (jwk) this._init(jwk)
25
+ }
26
+ async signEncoded(encoded) {
27
+ const { path, ...msg } = encoded
28
+ return await sign({
29
+ jwk: this.jwk,
30
+ msg,
31
+ path,
32
+ url: this.url,
33
+ })
34
+ }
35
+
36
+ _init(jwk) {
37
+ this.jwk = jwk
38
+ this.signer = createSigner(jwk, this.url)
39
+ this.addr = toAddr(jwk.n)
40
+ this.sign = signer({ signer: this.signer, url: this.url })
41
+
42
+ const { request } = connect({
43
+ MODE: "mainnet",
44
+ URL: this.url,
45
+ device: "",
46
+ signer: this.signer,
47
+ })
48
+
49
+ this._request = request
50
+ }
51
+
52
+ async setInfo() {
53
+ if (!this._info) {
54
+ try {
55
+ this._info = await this.g("/~meta@1.0/info")
56
+ } catch (e) {}
57
+ }
58
+ }
59
+
60
+ async init(jwk) {
61
+ this._init(jwk)
62
+ await this.setInfo()
63
+ return this
64
+ }
65
+
66
+ async send(msg) {
67
+ return await _send(msg)
68
+ }
69
+
70
+ async getImage() {
71
+ const wasm = Buffer.from(aos_wamr, "base64")
72
+ const id = await this.cacheBinary(wasm, "application/wasm")
73
+ this.image ??= id
74
+ return id
75
+ }
76
+
77
+ async getLua() {
78
+ const lua = Buffer.from(hyper_aos, "base64")
79
+ const id = await this.cacheScript(lua, "application/lua")
80
+ this.lua ??= id
81
+ return id
82
+ }
83
+
84
+ async scheduleAOS({ pid, action = "Eval", tags = {}, data }) {
85
+ pid ??= this.pid
86
+ let _tags = mergeLeft(tags, {
87
+ device: "process@1.0",
88
+ method: "POST",
89
+ type: "Message",
90
+ Action: action,
91
+ target: pid,
92
+ })
93
+
94
+ if (data) _tags.data = data
95
+ const msg = { body: _tags, path: `/${pid}~process@1.0/schedule` }
96
+ let res = await this.post(msg)
97
+ const slot = res.headers.slot
98
+ return { slot, res, pid }
99
+ }
100
+
101
+ async messageAOS(args) {
102
+ const { slot, pid } = await this.scheduleAOS(args)
103
+ return { slot, outbox: await this.computeAOS({ pid, slot }) }
104
+ }
105
+
106
+ async messageLegacy(args) {
107
+ const { slot, pid } = await this.scheduleLegacy(args)
108
+ return { slot, res: await this.computeLegacy({ pid, slot }) }
109
+ }
110
+
111
+ async computeAOS({ pid, slot }) {
112
+ return await this.getJSON({ path: `/${pid}/compute/results/outbox`, slot })
113
+ }
114
+
115
+ async computeLua({ pid, slot }) {
116
+ return await this.getJSON({ path: `/${pid}/compute/results`, slot })
117
+ }
118
+
119
+ async compute({ pid, slot, path = "" }) {
120
+ if (path && !/^\//.test(path)) path = "/" + path
121
+ return await this.getJSON({ path: `/${pid}/compute${path}`, slot })
122
+ }
123
+
124
+ async computeLegacy({ pid, slot }) {
125
+ const json = await this.compute({ pid, slot })
126
+ return JSON.parse(json.results.json.body)
127
+ }
128
+
129
+ async spawn(tags = {}) {
130
+ const addr = await this.g("/~meta@1.0/info/address")
131
+ this.scheduler ??= addr
132
+ const _tags = mergeLeft(tags, {
133
+ device: "process@1.0",
134
+ scheduler: this.scheduler,
135
+ "random-seed": seed(16),
136
+ type: "Process",
137
+ "execution-device": "test-device@1.0",
138
+ })
139
+ const msg = {
140
+ device: "process@1.0",
141
+ path: "schedule",
142
+ body: _tags,
143
+ scheduler: this.scheduler,
144
+ }
145
+ const res = await this.post(msg)
146
+ return { res, pid: res.headers.process }
147
+ }
148
+
149
+ async cacheScript(data, type = "application/lua") {
150
+ if (!this.cache) {
151
+ const { pid } = await this.spawn({})
152
+ this.cache = pid
153
+ }
154
+ const { slot } = await this.schedule({
155
+ data,
156
+ pid: this.cache,
157
+ "content-type": type,
158
+ })
159
+ const msgs = await this.messages({ pid: this.cache, from: slot, limit: 1 })
160
+ return msgs.edges[0].node.message.Id
161
+ }
162
+
163
+ async cacheBinary(data, type) {
164
+ const res = await this.post({ path: "/~wao@1.0/cache_module", data, type })
165
+ return res.headers.id
166
+ }
167
+
168
+ async message(args) {
169
+ const pid = args.pid
170
+ const { slot } = await this.schedule(args)
171
+ const res = await this.compute({ pid, slot })
172
+ return { slot, pid, res }
173
+ }
174
+
175
+ async scheduleLegacy({
176
+ pid,
177
+ action = "Eval",
178
+ tags = {},
179
+ data,
180
+ scheduler,
181
+ } = {}) {
182
+ if (action) tags.Action = action
183
+ return await this.schedule({ pid, tags, data, scheduler })
184
+ }
185
+
186
+ async scheduleLua(...args) {
187
+ return await this.scheduleLegacy(...args)
188
+ }
189
+
190
+ async schedule({ pid, tags = {}, data } = {}) {
191
+ pid ??= this.pid
192
+ let _tags = mergeLeft(tags, { type: "Message", target: pid })
193
+ if (data) _tags.data = data
194
+ const msg = {
195
+ device: "process@1.0",
196
+ method: "POST",
197
+ path: "schedule",
198
+ body: _tags,
199
+ }
200
+
201
+ let res = await this.post(msg)
202
+ return { slot: res.headers.slot, res, pid }
203
+ }
204
+
205
+ async spawnAOS(image) {
206
+ const addr = await this.g("/~meta@1.0/info/address")
207
+ this.scheduler ??= addr
208
+ image ??= this.image ?? (await this.getImage())
209
+ const _tags = {
210
+ device: "process@1.0",
211
+ scheduler: this.scheduler,
212
+ "data-protocol": "ao",
213
+ variant: "ao.N.1",
214
+ "scheduler-location": this.scheduler,
215
+ authority: this.scheduler,
216
+ "random-seed": seed(16),
217
+ type: "Process",
218
+ image,
219
+ "execution-device": "stack@1.0",
220
+ "push-device": "push@1.0",
221
+ "device-stack": [
222
+ "wasi@1.0",
223
+ "json-iface@1.0",
224
+ "wasm-64@1.0",
225
+ "patch@1.0",
226
+ "multipass@1.0",
227
+ ],
228
+ "output-prefix": "wasm",
229
+ "patch-from": "/results/outbox",
230
+ "patch-mode": "patches",
231
+ "stack-keys": ["init", "compute", "snapshot", "normalize"],
232
+ passes: 2,
233
+ }
234
+ const msg = {
235
+ device: "process@1.0",
236
+ path: "schedule",
237
+ body: _tags,
238
+ scheduler: this.scheduler,
239
+ }
240
+
241
+ const res = await this.post(msg)
242
+ const pid = res.headers.process
243
+ this.pid ??= pid
244
+ return { pid, res }
245
+ }
246
+
247
+ async spawnLua(lua) {
248
+ const addr = await this.g("/~meta@1.0/info/address")
249
+ this.scheduler ??= addr
250
+ lua ??= this.lua ?? (await this.getLua())
251
+ const _tags = {
252
+ device: "process@1.0",
253
+ scheduler: this.scheduler,
254
+ "data-protocol": "ao",
255
+ variant: "ao.N.1",
256
+ "scheduler-location": this.scheduler,
257
+ authority: this.scheduler,
258
+ "random-seed": seed(16),
259
+ type: "Process",
260
+ module: lua,
261
+ "execution-device": "lua@5.3a",
262
+ "push-device": "push@1.0",
263
+ "patch-from": "/results/outbox",
264
+ }
265
+ const msg = {
266
+ device: "process@1.0",
267
+ path: "schedule",
268
+ body: _tags,
269
+ }
270
+
271
+ const res = await this.post(msg)
272
+ const pid = res.headers.process
273
+ this.pid ??= pid
274
+ return { pid, res }
275
+ }
276
+
277
+ async now({ pid, path = "" }) {
278
+ if (path && !/^\//.test(path)) path = "/" + path
279
+ return await this.getJSON({ path: `/${pid}/now${path}` })
280
+ }
281
+
282
+ async slot({ pid, path = "" }) {
283
+ if (path && !/^\//.test(path)) path = "/" + path
284
+ return await this.getJSON({ path: `/${pid}/slot${path}` })
285
+ }
286
+
287
+ async messages({ pid, from, to } = {}) {
288
+ let params = `target=${pid}`
289
+ if (isNotNil(from)) params += `&from=${from}`
290
+ if (isNotNil(to)) params += `&to=${to}`
291
+ params += `&accept=application/aos-2`
292
+ let res = await fetch(`${this.url}/~scheduler@1.0/schedule?${params}`).then(
293
+ r => r.json()
294
+ )
295
+ if (res.page_info.has_next_page) {
296
+ res.next = async () => {
297
+ const from2 = last(res.edges).cursor + 1
298
+ return await this.messages({ pid, from: from2, to })
299
+ }
300
+ }
301
+ return res
302
+ }
303
+
304
+ async spawnLegacy({ module, tags = {}, data } = {}) {
305
+ await this.setInfo()
306
+ let t = {
307
+ type: "Process",
308
+ "data-protocol": "ao",
309
+ variant: "ao.TN.1",
310
+ scheduler: this._info.address,
311
+ authority: this._info.address,
312
+ "scheduler-location": this._info.address,
313
+ "random-seed": seed(16),
314
+ module: module ?? "ISShJH1ij-hPPt9St5UFFr_8Ys3Kj5cyg7zrMGt7H9s",
315
+ device: "process@1.0",
316
+ "scheduler-device": "scheduler@1.0",
317
+ "execution-device": "stack@1.0",
318
+ "push-device": "push@1.0",
319
+ "device-stack": ["genesis-wasm@1.0", "patch@1.0"],
320
+ "patch-from": "/results/outbox",
321
+ "stack-keys": ["init", "compute", "snapshot", "normalize"],
322
+ }
323
+ if (data) t.data = data
324
+ tags = mergeLeft(tags, t)
325
+ return await this.spawn(tags)
326
+ }
327
+
328
+ async results({ process, limit, sort = "DESC", from, to } = {}) {
329
+ let params = ""
330
+ const addParam = (key, val) => {
331
+ params += params === "" ? "?" : "&"
332
+ params += `${key}=${val}`
333
+ }
334
+ if (limit) addParam("limit", limit)
335
+ if (sort) addParam("sort", sort)
336
+ if (from) addParam("from", from)
337
+ if (to) addParam("to", to)
338
+ const res = await this.post({
339
+ path: "/~relay@1.0/call",
340
+ method: "GET",
341
+ "relay-path": `${this.cu}/results/${process}${params}`,
342
+ "content-type": "application/json",
343
+ })
344
+ return JSON.parse(res.body)
345
+ }
346
+
347
+ async dryrun({ tags = {}, pid, action, data } = {}) {
348
+ if (typeof action === "string") tags.Action = action
349
+ let json = { Tags: buildTags({ ...tags }), Owner: this.addr }
350
+ if (data) json.Data = data
351
+ const res = await this.post({
352
+ path: "/~relay@1.0/call",
353
+ method: "POST",
354
+ "relay-path": `${this.cu}/dry-run?process-id=${pid}`,
355
+ "content-type": "application/json",
356
+ "relay-body": JSON.stringify(json),
357
+ })
358
+ return JSON.parse(res.body)
359
+ }
360
+
361
+ async commit(obj, opts) {
362
+ const msg = await this.sign(obj, opts)
363
+ const hmacId = hmacid(msg.headers)
364
+ const rsaId = rsaid(msg.headers)
365
+ const committer = this.addr
366
+ const meta = { alg: "rsa-pss-sha512", "commitment-device": "httpsig@1.0" }
367
+ const meta2 = { alg: "hmac-sha256", "commitment-device": "httpsig@1.0" }
368
+ const sigs = {
369
+ signature: msg.headers.signature,
370
+ "signature-input": msg.headers["signature-input"],
371
+ }
372
+ return {
373
+ commitments: {
374
+ [rsaId]: { ...meta, committer, ...sigs },
375
+ [hmacId]: { ...meta2, ...sigs },
376
+ },
377
+ ...obj,
378
+ }
379
+ }
380
+
381
+ async p(path, ...args) {
382
+ let _args = clone(args)
383
+ _args[0] ??= {}
384
+ _args[0].path ??= path
385
+ return (await this.post(..._args))?.out ?? null
386
+ }
387
+
388
+ async post(obj, opt = {}) {
389
+ const _json = opt.json ? "/~json@1.0/serialize" : ""
390
+ obj.path += _json
391
+ const signed = await this.sign(obj, opt)
392
+ return await this.send(signed)
393
+ }
394
+
395
+ async g(path, ...args) {
396
+ let _args = clone(args)
397
+ _args[0] ??= {}
398
+ _args[0].path ??= path
399
+ return (await this.get(..._args))?.out ?? null
400
+ }
401
+
402
+ async get({ path, ...params }, opt = {}) {
403
+ const _json = opt.json ? "/~json@1.0/serialize" : ""
404
+ path ??= "/~message@1.0"
405
+ if (!/^\//.test(path)) path = "/" + path
406
+ let _params = ""
407
+ if (!isEmpty(params)) {
408
+ let i = 0
409
+ for (const k in params) {
410
+ _params += `${i === 0 ? "?" : "&"}${k}=${params[k]}`
411
+ i++
412
+ }
413
+ }
414
+ const response = await fetch(`${this.url}${path}${_json}${_params}`)
415
+ let headers = {}
416
+ response.headers.forEach((v, k) => (headers[k] = v))
417
+ const http = {
418
+ headers,
419
+ body: await response.text(),
420
+ status: response.status,
421
+ }
422
+ return {
423
+ ...from(http),
424
+ ...http,
425
+ }
426
+ }
427
+
428
+ async postJSON(args, opt = {}) {
429
+ const res = await this.post(args, { ...opt, json: true })
430
+ return JSON.parse(res.body)
431
+ }
432
+
433
+ async getJSON(args, opt = {}) {
434
+ const res = await this.get(args, { ...opt, json: true })
435
+ return JSON.parse(res.body)
436
+ }
437
+ }
438
+
439
+ export default HB