@ar.io/sdk 3.12.0-beta.6 → 3.12.0-beta.7

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.
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hashBufferToB64Url = exports.hashReadableStreamToB64Url = exports.hashReadableToB64Url = void 0;
6
+ exports.convertDataStreamToDataRoot = exports.hashDataStreamToB64Url = exports.readableStreamToAsyncIterable = exports.isReadableStream = exports.isAsyncIterable = void 0;
4
7
  /**
5
8
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
9
  *
@@ -16,41 +19,90 @@ exports.hashBufferToB64Url = exports.hashReadableStreamToB64Url = exports.hashRe
16
19
  * See the License for the specific language governing permissions and
17
20
  * limitations under the License.
18
21
  */
22
+ const arweave_1 = __importDefault(require("arweave"));
23
+ const merkle_js_1 = require("arweave/node/lib/merkle.js");
19
24
  const crypto_1 = require("crypto");
20
25
  const base64_js_1 = require("./base64.js");
21
- const hashReadableToB64Url = (stream, algorithm = 'sha256') => {
22
- return new Promise((resolve, reject) => {
23
- const hash = (0, crypto_1.createHash)(algorithm);
24
- stream.on('data', (chunk) => hash.update(chunk));
25
- stream.on('end', () => resolve((0, base64_js_1.toB64Url)(hash.digest())));
26
- stream.on('error', (err) => reject(err));
27
- });
26
+ const isAsyncIterable = (x) => {
27
+ return x && typeof x[Symbol.asyncIterator] === 'function';
28
+ };
29
+ exports.isAsyncIterable = isAsyncIterable;
30
+ const isReadableStream = (x) => {
31
+ return x && typeof x.getReader === 'function';
28
32
  };
29
- exports.hashReadableToB64Url = hashReadableToB64Url;
30
- const hashReadableStreamToB64Url = (stream, algorithm = 'sha256') => {
31
- return new Promise((resolve, reject) => {
32
- const hash = (0, crypto_1.createHash)(algorithm);
33
+ exports.isReadableStream = isReadableStream;
34
+ // convert ReadableStream to async iterable
35
+ const readableStreamToAsyncIterable = (stream) => ({
36
+ async *[Symbol.asyncIterator]() {
33
37
  const reader = stream.getReader();
34
- const read = async () => {
35
- try {
38
+ try {
39
+ while (true) {
36
40
  const { done, value } = await reader.read();
37
- if (done) {
38
- resolve((0, base64_js_1.toB64Url)(hash.digest()));
39
- }
40
- else {
41
- hash.update(value);
42
- read();
43
- }
41
+ if (done)
42
+ break;
43
+ if (value !== undefined)
44
+ yield value;
44
45
  }
45
- catch (err) {
46
- reject(err);
47
- }
48
- };
49
- read().catch(reject);
50
- });
46
+ }
47
+ finally {
48
+ reader.releaseLock();
49
+ }
50
+ },
51
+ });
52
+ exports.readableStreamToAsyncIterable = readableStreamToAsyncIterable;
53
+ const hashDataStreamToB64Url = async (stream, algorithm = 'sha256') => {
54
+ const asyncIterable = (0, exports.isAsyncIterable)(stream)
55
+ ? stream
56
+ : (0, exports.readableStreamToAsyncIterable)(stream);
57
+ const hash = (0, crypto_1.createHash)(algorithm);
58
+ for await (const chunk of asyncIterable) {
59
+ hash.update(chunk);
60
+ }
61
+ return (0, base64_js_1.toB64Url)(hash.digest());
51
62
  };
52
- exports.hashReadableStreamToB64Url = hashReadableStreamToB64Url;
53
- const hashBufferToB64Url = (buffer, algorithm = 'sha256') => {
54
- return (0, base64_js_1.toB64Url)((0, crypto_1.createHash)(algorithm).update(buffer).digest());
63
+ exports.hashDataStreamToB64Url = hashDataStreamToB64Url;
64
+ const convertDataStreamToDataRoot = async ({ dataStream, }) => {
65
+ const chunks = [];
66
+ let leftover = new Uint8Array(0);
67
+ let cursor = 0;
68
+ const asyncIterable = (0, exports.isAsyncIterable)(dataStream)
69
+ ? dataStream
70
+ : (0, exports.readableStreamToAsyncIterable)(dataStream);
71
+ for await (const data of asyncIterable) {
72
+ const inputChunk = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
73
+ const combined = new Uint8Array(leftover.length + inputChunk.length);
74
+ combined.set(leftover, 0);
75
+ combined.set(inputChunk, leftover.length);
76
+ let startIndex = 0;
77
+ while (combined.length - startIndex >= merkle_js_1.MAX_CHUNK_SIZE) {
78
+ let chunkSize = merkle_js_1.MAX_CHUNK_SIZE;
79
+ const remainderAfterThis = combined.length - startIndex - merkle_js_1.MAX_CHUNK_SIZE;
80
+ if (remainderAfterThis > 0 && remainderAfterThis < merkle_js_1.MIN_CHUNK_SIZE) {
81
+ chunkSize = Math.ceil((combined.length - startIndex) / 2);
82
+ }
83
+ const chunkData = combined.slice(startIndex, startIndex + chunkSize);
84
+ const dataHash = await arweave_1.default.crypto.hash(chunkData);
85
+ chunks.push({
86
+ dataHash,
87
+ minByteRange: cursor,
88
+ maxByteRange: cursor + chunkSize,
89
+ });
90
+ cursor += chunkSize;
91
+ startIndex += chunkSize;
92
+ }
93
+ leftover = combined.slice(startIndex);
94
+ }
95
+ if (leftover.length > 0) {
96
+ // TODO: ensure a web friendly crypto hash function is used in web
97
+ const dataHash = await arweave_1.default.crypto.hash(leftover);
98
+ chunks.push({
99
+ dataHash,
100
+ minByteRange: cursor,
101
+ maxByteRange: cursor + leftover.length,
102
+ });
103
+ }
104
+ const leaves = await (0, merkle_js_1.generateLeaves)(chunks);
105
+ const root = await (0, merkle_js_1.buildLayers)(leaves);
106
+ return (0, base64_js_1.toB64Url)(Buffer.from(root.id));
55
107
  };
56
- exports.hashBufferToB64Url = hashBufferToB64Url;
108
+ exports.convertDataStreamToDataRoot = convertDataStreamToDataRoot;
@@ -17,4 +17,4 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.version = void 0;
19
19
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
20
- exports.version = '3.12.0-beta.6';
20
+ exports.version = '3.12.0-beta.7';
@@ -15,39 +15,16 @@
15
15
  */
16
16
  import Arweave from 'arweave';
17
17
  import { MAX_CHUNK_SIZE, MIN_CHUNK_SIZE, buildLayers, generateLeaves, } from 'arweave/node/lib/merkle.js';
18
- import { Readable } from 'node:stream';
19
18
  import { toB64Url } from '../../../../utils/base64.js';
20
- export async function convertBufferToDataRoot({ buffer, }) {
21
- const chunks = [];
22
- let cursor = 0;
23
- let offset = 0;
24
- while (offset < buffer.byteLength) {
25
- let chunkSize = Math.min(MAX_CHUNK_SIZE, buffer.byteLength - offset);
26
- const remainder = buffer.byteLength - offset - chunkSize;
27
- if (remainder > 0 && remainder < MIN_CHUNK_SIZE) {
28
- chunkSize = Math.ceil((buffer.byteLength - offset) / 2);
29
- }
30
- // subarray does not exist on web Buffer type
31
- const slice = buffer.subarray(offset, offset + chunkSize);
32
- const hash = await crypto.subtle.digest('SHA-256', slice);
33
- const hashArray = new Uint8Array(hash);
34
- chunks.push({
35
- dataHash: hashArray,
36
- minByteRange: cursor,
37
- maxByteRange: cursor + chunkSize,
38
- });
39
- cursor += chunkSize;
40
- offset += chunkSize;
41
- }
42
- const leaves = await generateLeaves(chunks);
43
- const result = await buildLayers(leaves);
44
- return Buffer.from(result.id).toString('base64url');
45
- }
46
- export const convertReadableToDataRoot = async ({ iterable, }) => {
19
+ import { isAsyncIterable, readableStreamToAsyncIterable, } from '../../../../utils/hash.js';
20
+ export const convertDataStreamToDataRoot = async ({ dataStream, }) => {
47
21
  const chunks = [];
48
22
  let leftover = new Uint8Array(0);
49
23
  let cursor = 0;
50
- for await (const data of iterable) {
24
+ const asyncIterable = isAsyncIterable(dataStream)
25
+ ? dataStream
26
+ : readableStreamToAsyncIterable(dataStream);
27
+ for await (const data of asyncIterable) {
51
28
  const inputChunk = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
52
29
  const combined = new Uint8Array(leftover.length + inputChunk.length);
53
30
  combined.set(leftover, 0);
@@ -72,6 +49,7 @@ export const convertReadableToDataRoot = async ({ iterable, }) => {
72
49
  leftover = combined.slice(startIndex);
73
50
  }
74
51
  if (leftover.length > 0) {
52
+ // TODO: ensure a web friendly crypto hash function is used in web
75
53
  const dataHash = await Arweave.crypto.hash(leftover);
76
54
  chunks.push({
77
55
  dataHash,
@@ -89,20 +67,14 @@ export class DataRootVerificationStrategy {
89
67
  this.trustedDataRootProvider = trustedDataRootProvider;
90
68
  }
91
69
  async verifyData({ data, txId, }) {
92
- const trustedDataRootPromise = this.trustedDataRootProvider.getDataRoot({
93
- txId,
94
- });
95
- let computedDataRoot;
96
- if (Buffer.isBuffer(data)) {
97
- computedDataRoot = await convertBufferToDataRoot({ buffer: data });
98
- }
99
- else if (data instanceof Readable || data instanceof ReadableStream) {
100
- computedDataRoot = await convertReadableToDataRoot({ iterable: data });
101
- }
102
- if (computedDataRoot === undefined) {
103
- throw new Error('Data root could not be computed');
104
- }
105
- const trustedDataRoot = await trustedDataRootPromise;
70
+ const [computedDataRoot, trustedDataRoot] = await Promise.all([
71
+ convertDataStreamToDataRoot({
72
+ dataStream: data,
73
+ }),
74
+ this.trustedDataRootProvider.getDataRoot({
75
+ txId,
76
+ }),
77
+ ]);
106
78
  if (computedDataRoot !== trustedDataRoot) {
107
79
  throw new Error('Data root does not match', {
108
80
  cause: { computedDataRoot, trustedDataRoot },
@@ -1,45 +1,22 @@
1
- /**
2
- * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
- import { Readable } from 'node:stream';
17
- import { hashBufferToB64Url, hashReadableStreamToB64Url, hashReadableToB64Url, } from '../../../../utils/hash.js';
1
+ import { hashDataStreamToB64Url } from '../../../../utils/hash.js';
18
2
  export class HashVerificationStrategy {
19
3
  trustedHashProvider;
20
4
  constructor({ trustedHashProvider, }) {
21
5
  this.trustedHashProvider = trustedHashProvider;
22
6
  }
23
7
  async verifyData({ data, txId, }) {
24
- const hashPromise = this.trustedHashProvider.getHash({ txId });
25
- let computedHash;
26
- if (Buffer.isBuffer(data)) {
27
- computedHash = hashBufferToB64Url(data);
28
- }
29
- else if (data instanceof Readable) {
30
- computedHash = await hashReadableToB64Url(data);
31
- }
32
- else if (data instanceof ReadableStream) {
33
- computedHash = await hashReadableStreamToB64Url(data);
34
- }
8
+ // kick off the hash computation, but don't wait for it until we compute our own hash
9
+ const [computedHash, fetchedHash] = await Promise.all([
10
+ hashDataStreamToB64Url(data),
11
+ this.trustedHashProvider.getHash({ txId }),
12
+ ]);
35
13
  // await on the hash promise and compare to get a little concurrency when computing hashes over larger data
36
- const { hash } = await hashPromise;
37
14
  if (computedHash === undefined) {
38
15
  throw new Error('Hash could not be computed');
39
16
  }
40
- if (computedHash !== hash) {
17
+ if (computedHash !== fetchedHash.hash) {
41
18
  throw new Error('Hash does not match', {
42
- cause: { computedHash, trustedHash: hash },
19
+ cause: { computedHash, trustedHash: fetchedHash },
43
20
  });
44
21
  }
45
22
  }