@shelby-protocol/sdk 0.0.1-experimental.4 → 0.0.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/README.md +41 -0
- package/dist/browser/index.d.ts +17 -8
- package/dist/browser/index.mjs +84 -35
- package/dist/chunk-3ZDXWPYC.mjs +65 -0
- package/dist/chunk-75VHXY5P.mjs +48 -0
- package/dist/chunk-APML3CGJ.mjs +176 -0
- package/dist/chunk-DLMDDEWF.mjs +112 -0
- package/dist/{chunk-GGYTHP5F.mjs → chunk-FIFKKWXV.mjs} +19 -17
- package/dist/chunk-HPVCKAN2.mjs +41 -0
- package/dist/chunk-KBUWZXFA.mjs +92 -0
- package/dist/{chunk-C33H3C3N.mjs → chunk-LSNN4V5F.mjs} +1 -1
- package/dist/chunk-LTV26KU4.mjs +141 -0
- package/dist/{chunk-2FF5FICZ.mjs → chunk-NRNVAY72.mjs} +97 -43
- package/dist/chunk-NTJSNNA7.mjs +175 -0
- package/dist/{chunk-QEMIORTL.mjs → chunk-OTBLZL2S.mjs} +1 -1
- package/dist/chunk-QP2C6ORP.mjs +177 -0
- package/dist/chunk-RBFWGDMY.mjs +30 -0
- package/dist/chunk-VRLIOKWG.mjs +11 -0
- package/dist/chunk-XGMJ4CG4.mjs +384 -0
- package/dist/chunk-Z7RFCADT.mjs +0 -0
- package/dist/chunk-ZPW742E7.mjs +28 -0
- package/dist/clay-codes-Ce9EmXfa.d.ts +129 -0
- package/dist/core/blobs.mjs +1 -1
- package/dist/core/chunk.d.ts +34 -0
- package/dist/core/chunk.mjs +17 -0
- package/dist/core/clients/ShelbyBlobClient.d.ts +121 -89
- package/dist/core/clients/ShelbyBlobClient.mjs +11 -3
- package/dist/core/clients/ShelbyClient.d.ts +71 -30
- package/dist/core/clients/ShelbyClient.mjs +14 -9
- package/dist/core/clients/ShelbyClientConfig.d.ts +43 -9
- package/dist/core/clients/ShelbyClientConfig.mjs +1 -0
- package/dist/core/clients/ShelbyRPCClient.d.ts +47 -20
- package/dist/core/clients/ShelbyRPCClient.mjs +10 -3
- package/dist/core/clients/index.d.ts +10 -5
- package/dist/core/clients/index.mjs +18 -12
- package/dist/core/clients/utils.d.ts +7 -0
- package/dist/core/clients/utils.mjs +7 -0
- package/dist/core/commitments.d.ts +18 -18
- package/dist/core/commitments.mjs +7 -5
- package/dist/core/constants.d.ts +24 -19
- package/dist/core/constants.mjs +15 -15
- package/dist/core/erasure/clay-codes.d.ts +2 -0
- package/dist/core/erasure/clay-codes.mjs +9 -0
- package/dist/core/erasure/constants.d.ts +48 -0
- package/dist/core/erasure/constants.mjs +17 -0
- package/dist/core/erasure/default.d.ts +6 -0
- package/dist/core/erasure/default.mjs +9 -0
- package/dist/core/erasure/index.d.ts +4 -0
- package/dist/core/erasure/index.mjs +27 -0
- package/dist/core/erasure/provider.d.ts +2 -0
- package/dist/core/erasure/provider.mjs +0 -0
- package/dist/core/erasure/reed-solomon.d.ts +2 -0
- package/dist/core/erasure/reed-solomon.mjs +7 -0
- package/dist/core/erasure/utils.d.ts +12 -0
- package/dist/core/erasure/utils.mjs +33 -0
- package/dist/core/index.d.ts +17 -8
- package/dist/core/index.mjs +84 -35
- package/dist/core/layout.mjs +4 -2
- package/dist/core/networks.d.ts +7 -0
- package/dist/core/networks.mjs +9 -0
- package/dist/core/operations/generated/sdk.d.ts +686 -0
- package/dist/core/operations/generated/sdk.mjs +23 -0
- package/dist/core/operations/index.d.ts +31 -0
- package/dist/core/operations/index.mjs +12 -0
- package/dist/core/strings.d.ts +6 -0
- package/dist/core/strings.mjs +9 -0
- package/dist/core/types/blobs.d.ts +49 -79
- package/dist/core/types/encodings.d.ts +5 -13
- package/dist/core/types/encodings.mjs +0 -1
- package/dist/core/types/index.d.ts +4 -2
- package/dist/core/types/index.mjs +1 -1
- package/dist/core/types/placement_groups.d.ts +15 -0
- package/dist/core/types/placement_groups.mjs +0 -0
- package/dist/core/types/storage_providers.d.ts +31 -0
- package/dist/core/types/storage_providers.mjs +1 -0
- package/dist/core/utils.d.ts +15 -3
- package/dist/core/utils.mjs +5 -3
- package/dist/node/clients/ShelbyMetadataClient.d.ts +72 -0
- package/dist/node/clients/ShelbyMetadataClient.mjs +9 -0
- package/dist/node/clients/ShelbyNodeClient.d.ts +8 -3
- package/dist/node/clients/ShelbyNodeClient.mjs +15 -10
- package/dist/node/clients/index.d.ts +12 -5
- package/dist/node/clients/index.mjs +20 -11
- package/dist/node/index.d.ts +18 -8
- package/dist/node/index.mjs +90 -37
- package/package.json +17 -5
- package/dist/chunk-5Z3RVWU3.mjs +0 -67
- package/dist/chunk-B3CB2YEO.mjs +0 -318
- package/dist/chunk-D2FERD4A.mjs +0 -39
- package/dist/chunk-G263DBCY.mjs +0 -105
- package/dist/chunk-P7BVGLTV.mjs +0 -32
- package/dist/chunk-QM5BVKLD.mjs +0 -109
- package/dist/core/erasure.d.ts +0 -6
- package/dist/core/erasure.mjs +0 -7
- /package/dist/{chunk-7S6RVKYB.mjs → chunk-MB7C7VQF.mjs} +0 -0
- /package/dist/{chunk-IHTPXUYI.mjs → chunk-MQUVYMNQ.mjs} +0 -0
- /package/dist/{chunk-PLUDE5C3.mjs → chunk-RNXGC54D.mjs} +0 -0
- /package/dist/{chunk-QKT5R735.mjs → chunk-TUANYVZQ.mjs} +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
DEFAULT_CHUNKSET_SIZE_BYTES,
|
|
3
|
+
DEFAULT_CHUNK_SIZE_BYTES
|
|
4
|
+
} from "./chunk-APML3CGJ.mjs";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_ERASURE_K,
|
|
7
|
+
DEFAULT_ERASURE_N
|
|
8
|
+
} from "./chunk-ZPW742E7.mjs";
|
|
7
9
|
|
|
8
10
|
// src/core/layout.ts
|
|
9
11
|
import { AccountAddress } from "@aptos-labs/ts-sdk";
|
|
@@ -17,9 +19,10 @@ var ChunkKey = class _ChunkKey {
|
|
|
17
19
|
this.blobName = blobName;
|
|
18
20
|
this.chunksetIdx = chunksetIdx;
|
|
19
21
|
this.chunkIdx = chunkIdx;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
if (chunkIdx >= DEFAULT_ERASURE_N) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Cannot create a chunk with idx ${chunkIdx}. M+K=${DEFAULT_ERASURE_N}`
|
|
25
|
+
);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
key() {
|
|
@@ -27,14 +30,14 @@ var ChunkKey = class _ChunkKey {
|
|
|
27
30
|
}
|
|
28
31
|
// Returns the range in the blob that this key represents, or "partity" if this is parity chunk.
|
|
29
32
|
range() {
|
|
30
|
-
if (this.chunkIdx >=
|
|
33
|
+
if (this.chunkIdx >= DEFAULT_ERASURE_K) {
|
|
31
34
|
return "parity";
|
|
32
35
|
}
|
|
33
|
-
const chunksetStart = this.chunksetIdx *
|
|
34
|
-
const chunkStart = chunksetStart + this.chunkIdx *
|
|
36
|
+
const chunksetStart = this.chunksetIdx * DEFAULT_CHUNKSET_SIZE_BYTES;
|
|
37
|
+
const chunkStart = chunksetStart + this.chunkIdx * DEFAULT_CHUNK_SIZE_BYTES;
|
|
35
38
|
return {
|
|
36
39
|
start: chunkStart,
|
|
37
|
-
end: chunkStart +
|
|
40
|
+
end: chunkStart + DEFAULT_CHUNK_SIZE_BYTES
|
|
38
41
|
};
|
|
39
42
|
}
|
|
40
43
|
static fromJSON(json) {
|
|
@@ -58,18 +61,17 @@ function roundSize(size) {
|
|
|
58
61
|
let ret = 0;
|
|
59
62
|
let remain = size;
|
|
60
63
|
while (remain > 0) {
|
|
61
|
-
ret +=
|
|
62
|
-
remain -=
|
|
64
|
+
ret += DEFAULT_CHUNKSET_SIZE_BYTES;
|
|
65
|
+
remain -= DEFAULT_CHUNKSET_SIZE_BYTES;
|
|
63
66
|
}
|
|
64
67
|
return ret;
|
|
65
68
|
}
|
|
66
69
|
function allChunksForBlob(account, blobName, _size) {
|
|
67
70
|
const ret = [];
|
|
68
71
|
const size = roundSize(_size);
|
|
69
|
-
const nChunksets = size /
|
|
72
|
+
const nChunksets = size / DEFAULT_CHUNKSET_SIZE_BYTES;
|
|
70
73
|
for (let chunksetIdx = 0; chunksetIdx < nChunksets; ++chunksetIdx) {
|
|
71
|
-
|
|
72
|
-
for (let chunkIdx = 0; chunkIdx < n; ++chunkIdx) {
|
|
74
|
+
for (let chunkIdx = 0; chunkIdx < DEFAULT_ERASURE_N; ++chunkIdx) {
|
|
73
75
|
ret.push(new ChunkKey(account, blobName, chunksetIdx, chunkIdx));
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/core/constants.ts
|
|
2
|
+
import { Network } from "@aptos-labs/ts-sdk";
|
|
3
|
+
var DEFAULT_PROJECT_NAME = "shelby";
|
|
4
|
+
var DEFAULT_PROJECT_DESCRIPTION = "High performance, decentralized storage";
|
|
5
|
+
var NetworkToShelbyRPCBaseUrl = {
|
|
6
|
+
[Network.SHELBYNET]: "https://api.shelbynet.shelby.xyz/shelby",
|
|
7
|
+
[Network.DEVNET]: void 0,
|
|
8
|
+
[Network.TESTNET]: void 0,
|
|
9
|
+
[Network.MAINNET]: void 0,
|
|
10
|
+
[Network.LOCAL]: void 0,
|
|
11
|
+
[Network.CUSTOM]: void 0
|
|
12
|
+
};
|
|
13
|
+
var NetworkToShelbyBlobIndexerBaseUrl = {
|
|
14
|
+
[Network.SHELBYNET]: "https://api.shelbynet.aptoslabs.com/nocode/v1/public/cmforrguw0042s601fn71f9l2/v1/graphql",
|
|
15
|
+
[Network.DEVNET]: void 0,
|
|
16
|
+
[Network.TESTNET]: void 0,
|
|
17
|
+
[Network.MAINNET]: void 0,
|
|
18
|
+
[Network.LOCAL]: void 0,
|
|
19
|
+
[Network.CUSTOM]: void 0
|
|
20
|
+
};
|
|
21
|
+
var SHELBY_DEPLOYER = "0xc63d6a5efb0080a6029403131715bd4971e1149f7cc099aac69bb0069b3ddbf5";
|
|
22
|
+
var TOKEN_DEPLOYER = "0x33009e852be7f93762dd0bf303383c2cb2c5cab7a30d8238ca5f9f177ae75124";
|
|
23
|
+
var TOKEN_OBJECT_ADDRESS = "0x249f5c642a63885ff88a5113b3ba0079840af5a1357706f8c7f3bfc5dd12511f";
|
|
24
|
+
var SHELBYUSD_TOKEN_ADDRESS = TOKEN_OBJECT_ADDRESS;
|
|
25
|
+
var SHELBYUSD_TOKEN_MODULE = "shelby_usd";
|
|
26
|
+
var SHELBYUSD_TOKEN_NAME = "ShelbyUSD";
|
|
27
|
+
var SHELBYUSD_TOKEN_TYPE = `${TOKEN_OBJECT_ADDRESS}::${SHELBYUSD_TOKEN_MODULE}::${SHELBYUSD_TOKEN_NAME}`;
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
DEFAULT_PROJECT_NAME,
|
|
31
|
+
DEFAULT_PROJECT_DESCRIPTION,
|
|
32
|
+
NetworkToShelbyRPCBaseUrl,
|
|
33
|
+
NetworkToShelbyBlobIndexerBaseUrl,
|
|
34
|
+
SHELBY_DEPLOYER,
|
|
35
|
+
TOKEN_DEPLOYER,
|
|
36
|
+
TOKEN_OBJECT_ADDRESS,
|
|
37
|
+
SHELBYUSD_TOKEN_ADDRESS,
|
|
38
|
+
SHELBYUSD_TOKEN_MODULE,
|
|
39
|
+
SHELBYUSD_TOKEN_NAME,
|
|
40
|
+
SHELBYUSD_TOKEN_TYPE
|
|
41
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/core/utils.ts
|
|
2
|
+
import { Hex } from "@aptos-labs/ts-sdk";
|
|
3
|
+
async function* readInChunks(input, chunkSize) {
|
|
4
|
+
let idx = 0;
|
|
5
|
+
if (isReadableStream(input)) {
|
|
6
|
+
const reader = input.getReader();
|
|
7
|
+
let buffer = new Uint8Array(chunkSize);
|
|
8
|
+
let bufferWriteOffset = 0;
|
|
9
|
+
try {
|
|
10
|
+
while (true) {
|
|
11
|
+
const { value, done } = await reader.read();
|
|
12
|
+
if (done) break;
|
|
13
|
+
if (value === void 0) continue;
|
|
14
|
+
let srcOffset = 0;
|
|
15
|
+
while (srcOffset < value.length) {
|
|
16
|
+
const remainingCapacity = chunkSize - bufferWriteOffset;
|
|
17
|
+
const bytesToCopy = Math.min(
|
|
18
|
+
remainingCapacity,
|
|
19
|
+
value.length - srcOffset
|
|
20
|
+
);
|
|
21
|
+
buffer.set(
|
|
22
|
+
value.subarray(srcOffset, srcOffset + bytesToCopy),
|
|
23
|
+
bufferWriteOffset
|
|
24
|
+
);
|
|
25
|
+
bufferWriteOffset += bytesToCopy;
|
|
26
|
+
srcOffset += bytesToCopy;
|
|
27
|
+
if (bufferWriteOffset >= chunkSize) {
|
|
28
|
+
yield [idx++, buffer];
|
|
29
|
+
buffer = new Uint8Array(chunkSize);
|
|
30
|
+
bufferWriteOffset = 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} finally {
|
|
35
|
+
reader.releaseLock();
|
|
36
|
+
}
|
|
37
|
+
if (bufferWriteOffset > 0) {
|
|
38
|
+
yield [idx++, buffer.subarray(0, bufferWriteOffset)];
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const view = toUint8Array(input);
|
|
43
|
+
for (let offset = 0; offset < view.byteLength; offset += chunkSize) {
|
|
44
|
+
yield [
|
|
45
|
+
idx++,
|
|
46
|
+
view.subarray(offset, Math.min(offset + chunkSize, view.byteLength))
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function zeroPadBytes(buffer, desiredLength) {
|
|
51
|
+
if (buffer.byteLength === desiredLength) {
|
|
52
|
+
return buffer;
|
|
53
|
+
}
|
|
54
|
+
if (buffer.byteLength > desiredLength) {
|
|
55
|
+
return buffer.subarray(0, desiredLength);
|
|
56
|
+
}
|
|
57
|
+
const paddedBuffer = new Uint8Array(desiredLength);
|
|
58
|
+
paddedBuffer.set(buffer);
|
|
59
|
+
return paddedBuffer;
|
|
60
|
+
}
|
|
61
|
+
async function concatHashes(parts) {
|
|
62
|
+
const chunks = parts.map((part) => Hex.fromHexInput(part).toUint8Array());
|
|
63
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
64
|
+
const combined = new Uint8Array(totalLength);
|
|
65
|
+
let offset = 0;
|
|
66
|
+
for (const chunk of chunks) {
|
|
67
|
+
combined.set(chunk, offset);
|
|
68
|
+
offset += chunk.byteLength;
|
|
69
|
+
}
|
|
70
|
+
return Hex.fromHexInput(
|
|
71
|
+
new Uint8Array(await crypto.subtle.digest("SHA-256", combined))
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
function isReadableStream(value) {
|
|
75
|
+
return typeof value === "object" && value !== null && "getReader" in value && typeof value.getReader === "function";
|
|
76
|
+
}
|
|
77
|
+
function toUint8Array(view) {
|
|
78
|
+
return view instanceof Uint8Array ? view : new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
79
|
+
}
|
|
80
|
+
function buildRequestUrl(path, baseUrl) {
|
|
81
|
+
const baseHasSlash = baseUrl.endsWith("/");
|
|
82
|
+
const safeBase = baseHasSlash ? baseUrl : `${baseUrl}/`;
|
|
83
|
+
const safePath = path.replace(/^\/+/, "");
|
|
84
|
+
return new URL(safePath, safeBase);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
readInChunks,
|
|
89
|
+
zeroPadBytes,
|
|
90
|
+
concatHashes,
|
|
91
|
+
buildRequestUrl
|
|
92
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
concatHashes,
|
|
3
|
+
readInChunks,
|
|
4
|
+
zeroPadBytes
|
|
5
|
+
} from "./chunk-KBUWZXFA.mjs";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_CHUNKSET_SIZE_BYTES
|
|
8
|
+
} from "./chunk-APML3CGJ.mjs";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_ERASURE_K,
|
|
11
|
+
DEFAULT_ERASURE_M
|
|
12
|
+
} from "./chunk-ZPW742E7.mjs";
|
|
13
|
+
|
|
14
|
+
// src/core/commitments.ts
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
var ChunksetCommitmentSchema = z.object({
|
|
17
|
+
// Chunkset root (vector commitment of child chunks)
|
|
18
|
+
chunkset_root: z.string().nullable(),
|
|
19
|
+
// the size is known statically from the current configuration
|
|
20
|
+
chunk_commitments: z.array(z.string())
|
|
21
|
+
}).refine(
|
|
22
|
+
(data) => {
|
|
23
|
+
return data.chunk_commitments.length === DEFAULT_ERASURE_K + DEFAULT_ERASURE_M;
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
message: `Chunkset must have exactly ${DEFAULT_ERASURE_K + DEFAULT_ERASURE_M} chunks (ERASURE_K + ERASURE_M = ${DEFAULT_ERASURE_K} + ${DEFAULT_ERASURE_M})`,
|
|
27
|
+
path: ["chunk_commitments"]
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
function expectedTotalChunksets(rawSize, chunksetSize = DEFAULT_CHUNKSET_SIZE_BYTES) {
|
|
31
|
+
if (chunksetSize <= 0) {
|
|
32
|
+
throw new Error("chunksetSize must be positive");
|
|
33
|
+
}
|
|
34
|
+
if (rawSize === 0) return 1;
|
|
35
|
+
return Math.ceil(rawSize / chunksetSize);
|
|
36
|
+
}
|
|
37
|
+
var BlobCommitmentsSchema = z.object({
|
|
38
|
+
schema_version: z.string(),
|
|
39
|
+
raw_data_size: z.number(),
|
|
40
|
+
// FIXME I am not sure about this being here, or if it should be somewhere else
|
|
41
|
+
blob_merkle_root: z.string(),
|
|
42
|
+
chunkset_commitments: z.array(ChunksetCommitmentSchema)
|
|
43
|
+
}).refine(
|
|
44
|
+
(data) => {
|
|
45
|
+
return expectedTotalChunksets(data.raw_data_size) === data.chunkset_commitments.length;
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
message: "Total chunkset count mismatches with raw data size",
|
|
49
|
+
// FIXME put more details in here
|
|
50
|
+
path: ["chunkset_commitments"]
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
async function generateChunksetCommitments(shouldPad, chunksetIdx, chunksetData, expectedChunksetSize, provider, onChunk) {
|
|
54
|
+
const { erasure_n } = provider.config;
|
|
55
|
+
const chunkCommitments = [];
|
|
56
|
+
const chunksetPayload = shouldPad ? zeroPadBytes(chunksetData, expectedChunksetSize) : validatePrePaddedChunkset(
|
|
57
|
+
chunksetData,
|
|
58
|
+
expectedChunksetSize,
|
|
59
|
+
chunksetIdx
|
|
60
|
+
);
|
|
61
|
+
const { chunks } = provider.encode(chunksetPayload);
|
|
62
|
+
if (chunks.length !== erasure_n) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Erasure provider produced ${chunks.length} chunks, expected ${erasure_n}.`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
let chunkIdx = 0;
|
|
68
|
+
for (const chunkData of chunks) {
|
|
69
|
+
if (onChunk !== void 0) {
|
|
70
|
+
await onChunk(chunksetIdx, chunkIdx, chunkData);
|
|
71
|
+
}
|
|
72
|
+
const chunkHash = await concatHashes([chunkData]);
|
|
73
|
+
chunkCommitments.push(chunkHash);
|
|
74
|
+
chunkIdx += 1;
|
|
75
|
+
}
|
|
76
|
+
const h = await concatHashes(
|
|
77
|
+
chunkCommitments.map((chunk) => chunk.toUint8Array())
|
|
78
|
+
);
|
|
79
|
+
const entry = {
|
|
80
|
+
chunkset_root: h.toString(),
|
|
81
|
+
chunk_commitments: chunkCommitments.map((chunk) => chunk.toString())
|
|
82
|
+
};
|
|
83
|
+
return { h, entry };
|
|
84
|
+
}
|
|
85
|
+
async function generateCommitments(provider, fullData, onChunk, options) {
|
|
86
|
+
const expectedChunksetSize = DEFAULT_CHUNKSET_SIZE_BYTES;
|
|
87
|
+
const shouldPad = options?.pad ?? true;
|
|
88
|
+
const chunksetCommitments = [];
|
|
89
|
+
const chunksetCommitmentHashes = [];
|
|
90
|
+
let rawDataSize = 0;
|
|
91
|
+
const chunksetGen = readInChunks(fullData, expectedChunksetSize);
|
|
92
|
+
for await (const [chunksetIdx, chunksetData] of chunksetGen) {
|
|
93
|
+
rawDataSize += chunksetData.length;
|
|
94
|
+
const { h, entry } = await generateChunksetCommitments(
|
|
95
|
+
shouldPad,
|
|
96
|
+
chunksetIdx,
|
|
97
|
+
chunksetData,
|
|
98
|
+
expectedChunksetSize,
|
|
99
|
+
provider,
|
|
100
|
+
onChunk
|
|
101
|
+
);
|
|
102
|
+
chunksetCommitments.push(entry);
|
|
103
|
+
chunksetCommitmentHashes.push(h);
|
|
104
|
+
}
|
|
105
|
+
if (rawDataSize === 0) {
|
|
106
|
+
const zeroChunkset = new Uint8Array(expectedChunksetSize);
|
|
107
|
+
const { h, entry } = await generateChunksetCommitments(
|
|
108
|
+
shouldPad,
|
|
109
|
+
0,
|
|
110
|
+
zeroChunkset,
|
|
111
|
+
expectedChunksetSize,
|
|
112
|
+
provider,
|
|
113
|
+
onChunk
|
|
114
|
+
);
|
|
115
|
+
chunksetCommitments.push(entry);
|
|
116
|
+
chunksetCommitmentHashes.push(h);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
schema_version: "1.3",
|
|
120
|
+
raw_data_size: rawDataSize,
|
|
121
|
+
blob_merkle_root: (await concatHashes(
|
|
122
|
+
chunksetCommitmentHashes.map((chunk) => chunk.toUint8Array())
|
|
123
|
+
)).toString(),
|
|
124
|
+
chunkset_commitments: chunksetCommitments
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function validatePrePaddedChunkset(chunkset, expectedSize, chunksetIdx) {
|
|
128
|
+
if (chunkset.byteLength !== expectedSize) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Chunkset ${chunksetIdx} has size ${chunkset.byteLength} bytes but expected ${expectedSize} bytes. Enable padding or supply pre-padded data before calling generateCommitments.`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return chunkset;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
ChunksetCommitmentSchema,
|
|
138
|
+
expectedTotalChunksets,
|
|
139
|
+
BlobCommitmentsSchema,
|
|
140
|
+
generateCommitments
|
|
141
|
+
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BlobNameSchema
|
|
3
|
-
} from "./chunk-GGYTHP5F.mjs";
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_SHELBY_BASE_URL
|
|
6
|
-
} from "./chunk-D2FERD4A.mjs";
|
|
7
1
|
import {
|
|
8
2
|
sleep
|
|
9
3
|
} from "./chunk-I6NG5GNL.mjs";
|
|
4
|
+
import {
|
|
5
|
+
getShelbyIndexerClient
|
|
6
|
+
} from "./chunk-75VHXY5P.mjs";
|
|
7
|
+
import {
|
|
8
|
+
buildRequestUrl
|
|
9
|
+
} from "./chunk-KBUWZXFA.mjs";
|
|
10
|
+
import {
|
|
11
|
+
NetworkToShelbyRPCBaseUrl
|
|
12
|
+
} from "./chunk-HPVCKAN2.mjs";
|
|
13
|
+
import {
|
|
14
|
+
BlobNameSchema
|
|
15
|
+
} from "./chunk-FIFKKWXV.mjs";
|
|
10
16
|
|
|
11
17
|
// src/core/clients/ShelbyRPCClient.ts
|
|
12
18
|
import { AccountAddress } from "@aptos-labs/ts-sdk";
|
|
@@ -15,32 +21,43 @@ function encodeURIComponentKeepSlashes(str) {
|
|
|
15
21
|
}
|
|
16
22
|
var ShelbyRPCClient = class {
|
|
17
23
|
baseUrl;
|
|
24
|
+
apiKey;
|
|
25
|
+
rpcConfig;
|
|
26
|
+
indexer;
|
|
18
27
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* blockchain and downloading blobs.
|
|
28
|
+
* Creates a new ShelbyRPCClient for interacting with Shelby RPC nodes.
|
|
29
|
+
* This client handles blob storage operations including upload and download.
|
|
22
30
|
*
|
|
23
|
-
* @param config
|
|
24
|
-
* @param config.
|
|
31
|
+
* @param config - The client configuration object.
|
|
32
|
+
* @param config.network - The Shelby network to use.
|
|
25
33
|
*
|
|
26
34
|
* @example
|
|
27
35
|
* ```typescript
|
|
28
|
-
* const
|
|
29
|
-
*
|
|
36
|
+
* const client = new ShelbyRPCClient({
|
|
37
|
+
* network: Network.SHELBYNET,
|
|
38
|
+
* apiKey: "AG-***",
|
|
39
|
+
* });
|
|
30
40
|
* ```
|
|
31
41
|
*/
|
|
32
42
|
constructor(config) {
|
|
33
|
-
this.baseUrl = config.
|
|
43
|
+
this.baseUrl = config.rpc?.baseUrl ?? NetworkToShelbyRPCBaseUrl.shelbynet;
|
|
44
|
+
this.apiKey = config.apiKey ?? config.rpc?.apiKey;
|
|
45
|
+
this.rpcConfig = config.rpc ?? {};
|
|
46
|
+
this.indexer = getShelbyIndexerClient(config);
|
|
34
47
|
}
|
|
35
48
|
async #uploadPart(uploadId, partIdx, partData) {
|
|
36
49
|
const nRetries = 5;
|
|
37
50
|
for (let i = 0; i < nRetries; ++i) {
|
|
38
51
|
const partResponse = await fetch(
|
|
39
|
-
|
|
52
|
+
buildRequestUrl(
|
|
53
|
+
`/v1/multipart-uploads/${uploadId}/parts/${partIdx}`,
|
|
54
|
+
this.baseUrl
|
|
55
|
+
),
|
|
40
56
|
{
|
|
41
57
|
method: "PUT",
|
|
42
58
|
headers: {
|
|
43
|
-
"Content-Type": "application/octet-stream"
|
|
59
|
+
"Content-Type": "application/octet-stream",
|
|
60
|
+
...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
|
|
44
61
|
},
|
|
45
62
|
body: partData
|
|
46
63
|
}
|
|
@@ -54,17 +71,21 @@ var ShelbyRPCClient = class {
|
|
|
54
71
|
throw new Error(`Failed to upload part ${partIdx}.`);
|
|
55
72
|
}
|
|
56
73
|
async #putBlobMultipart(account, blobName, blobData, partSize = 5 * 1024 * 1024) {
|
|
57
|
-
const startResponse = await fetch(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
const startResponse = await fetch(
|
|
75
|
+
buildRequestUrl("/v1/multipart-uploads", this.baseUrl),
|
|
76
|
+
{
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
rawAccount: account.toString(),
|
|
84
|
+
rawBlobName: blobName,
|
|
85
|
+
rawPartSize: partSize
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
);
|
|
68
89
|
if (!startResponse.ok) {
|
|
69
90
|
let errorBodyText = "Could not read error body";
|
|
70
91
|
try {
|
|
@@ -84,11 +105,15 @@ var ShelbyRPCClient = class {
|
|
|
84
105
|
await this.#uploadPart(uploadId, partIdx, partData);
|
|
85
106
|
}
|
|
86
107
|
const completeResponse = await fetch(
|
|
87
|
-
|
|
108
|
+
buildRequestUrl(
|
|
109
|
+
`/v1/multipart-uploads/${uploadId}/complete`,
|
|
110
|
+
this.baseUrl
|
|
111
|
+
),
|
|
88
112
|
{
|
|
89
113
|
method: "POST",
|
|
90
114
|
headers: {
|
|
91
|
-
"Content-Type": "application/json"
|
|
115
|
+
"Content-Type": "application/json",
|
|
116
|
+
...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
|
|
92
117
|
}
|
|
93
118
|
}
|
|
94
119
|
);
|
|
@@ -104,18 +129,23 @@ var ShelbyRPCClient = class {
|
|
|
104
129
|
}
|
|
105
130
|
}
|
|
106
131
|
/**
|
|
107
|
-
* Uploads blob data to the Shelby RPC node
|
|
108
|
-
* commitments have been
|
|
132
|
+
* Uploads blob data to the Shelby RPC node for storage by storage providers.
|
|
133
|
+
* This method should be called after blob commitments have been registered on the blockchain.
|
|
134
|
+
* Uses multipart upload for efficient handling of large files.
|
|
109
135
|
*
|
|
110
|
-
* @param params.account - The account
|
|
111
|
-
* @param params.blobName - The name of the blob (e.g. "
|
|
112
|
-
* @param params.blobData - The data
|
|
136
|
+
* @param params.account - The account that owns the blob.
|
|
137
|
+
* @param params.blobName - The name/path of the blob (e.g. "folder/file.txt").
|
|
138
|
+
* @param params.blobData - The raw blob data as a Uint8Array.
|
|
113
139
|
*
|
|
114
140
|
* @example
|
|
115
141
|
* ```typescript
|
|
116
142
|
* const blobData = new TextEncoder().encode("Hello, world!");
|
|
117
143
|
*
|
|
118
|
-
* await client.putBlob({
|
|
144
|
+
* await client.putBlob({
|
|
145
|
+
* account: AccountAddress.from("0x1"),
|
|
146
|
+
* blobName: "greetings/hello.txt",
|
|
147
|
+
* blobData
|
|
148
|
+
* });
|
|
119
149
|
* ```
|
|
120
150
|
*/
|
|
121
151
|
async putBlob(params) {
|
|
@@ -129,24 +159,42 @@ var ShelbyRPCClient = class {
|
|
|
129
159
|
// FIXME make this possible to stream in put ^^^
|
|
130
160
|
/**
|
|
131
161
|
* Downloads a blob from the Shelby RPC node.
|
|
162
|
+
* Returns a streaming response with validation to ensure data integrity.
|
|
163
|
+
*
|
|
164
|
+
* @param params.account - The account that owns the blob.
|
|
165
|
+
* @param params.blobName - The name/path of the blob (e.g. "folder/file.txt").
|
|
166
|
+
* @param params.range - Optional byte range for partial downloads.
|
|
167
|
+
* @param params.range.start - Starting byte position (inclusive).
|
|
168
|
+
* @param params.range.end - Ending byte position (inclusive, optional).
|
|
132
169
|
*
|
|
133
|
-
* @
|
|
134
|
-
* @param params.blobName - The name of the blob (e.g. "foo/bar")
|
|
135
|
-
* @param params.range - The range of the blob to download.
|
|
170
|
+
* @returns A ShelbyBlob object containing the account, name, readable stream, and content length.
|
|
136
171
|
*
|
|
137
|
-
* @
|
|
172
|
+
* @throws Error if the download fails or content length doesn't match.
|
|
138
173
|
*
|
|
139
174
|
* @example
|
|
140
175
|
* ```typescript
|
|
176
|
+
* // Download entire blob
|
|
141
177
|
* const blob = await client.getBlob({
|
|
142
|
-
* account,
|
|
143
|
-
* blobName: "
|
|
178
|
+
* account: AccountAddress.from("0x1"),
|
|
179
|
+
* blobName: "documents/report.pdf"
|
|
180
|
+
* });
|
|
181
|
+
*
|
|
182
|
+
* // Download partial content (bytes 100-199)
|
|
183
|
+
* const partial = await client.getBlob({
|
|
184
|
+
* account: AccountAddress.from("0x1"),
|
|
185
|
+
* blobName: "large-file.bin",
|
|
186
|
+
* range: { start: 100, end: 199 }
|
|
144
187
|
* });
|
|
145
188
|
* ```
|
|
146
189
|
*/
|
|
147
190
|
async getBlob(params) {
|
|
148
191
|
BlobNameSchema.parse(params.blobName);
|
|
149
|
-
const url =
|
|
192
|
+
const url = buildRequestUrl(
|
|
193
|
+
`/v1/blobs/${params.account.toString()}/${encodeURIComponentKeepSlashes(
|
|
194
|
+
params.blobName
|
|
195
|
+
)}`,
|
|
196
|
+
this.baseUrl
|
|
197
|
+
);
|
|
150
198
|
const requestInit = {};
|
|
151
199
|
if (params.range !== void 0) {
|
|
152
200
|
const headers = new Headers();
|
|
@@ -161,7 +209,13 @@ var ShelbyRPCClient = class {
|
|
|
161
209
|
}
|
|
162
210
|
requestInit.headers = headers;
|
|
163
211
|
}
|
|
164
|
-
const response = await fetch(url,
|
|
212
|
+
const response = await fetch(url, {
|
|
213
|
+
...requestInit,
|
|
214
|
+
headers: {
|
|
215
|
+
...requestInit.headers,
|
|
216
|
+
...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
165
219
|
if (!response.ok) {
|
|
166
220
|
throw new Error(
|
|
167
221
|
`Failed to download blob: ${response.status} ${response.statusText}`
|