@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.
- package/bundles/web.bundle.min.js +134 -134
- package/lib/cjs/common/wayfinder/verification/strategies/data-root-verifier.js +17 -46
- package/lib/cjs/common/wayfinder/verification/strategies/hash-verifier.js +7 -30
- package/lib/cjs/common/wayfinder/wayfinder.js +87 -184
- package/lib/cjs/utils/hash.js +83 -31
- package/lib/cjs/version.js +1 -1
- package/lib/esm/common/wayfinder/verification/strategies/data-root-verifier.js +15 -43
- package/lib/esm/common/wayfinder/verification/strategies/hash-verifier.js +8 -31
- package/lib/esm/common/wayfinder/wayfinder.js +85 -179
- package/lib/esm/utils/hash.js +74 -27
- package/lib/esm/version.js +1 -1
- package/lib/types/common/wayfinder/verification/strategies/data-root-verifier.d.ts +4 -8
- package/lib/types/common/wayfinder/verification/strategies/hash-verifier.d.ts +2 -3
- package/lib/types/common/wayfinder/wayfinder.d.ts +40 -58
- package/lib/types/types/wayfinder.d.ts +2 -2
- package/lib/types/utils/hash.d.ts +8 -4
- package/lib/types/version.d.ts +1 -1
- package/package.json +1 -1
package/lib/cjs/utils/hash.js
CHANGED
|
@@ -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.
|
|
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
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
35
|
-
|
|
38
|
+
try {
|
|
39
|
+
while (true) {
|
|
36
40
|
const { done, value } = await reader.read();
|
|
37
|
-
if (done)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
hash.update(value);
|
|
42
|
-
read();
|
|
43
|
-
}
|
|
41
|
+
if (done)
|
|
42
|
+
break;
|
|
43
|
+
if (value !== undefined)
|
|
44
|
+
yield value;
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
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.
|
|
53
|
-
const
|
|
54
|
-
|
|
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.
|
|
108
|
+
exports.convertDataStreamToDataRoot = convertDataStreamToDataRoot;
|
package/lib/cjs/version.js
CHANGED
|
@@ -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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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:
|
|
19
|
+
cause: { computedHash, trustedHash: fetchedHash },
|
|
43
20
|
});
|
|
44
21
|
}
|
|
45
22
|
}
|