@shelby-protocol/sdk 0.3.1 → 0.4.0
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/dist/browser/index.d.ts +9 -9
- package/dist/browser/index.mjs +1068 -589
- package/dist/core/clients/ShelbyBlobClient.d.ts +155 -40
- package/dist/core/clients/ShelbyBlobClient.mjs +314 -112
- package/dist/core/clients/ShelbyClient.d.ts +27 -4
- package/dist/core/clients/ShelbyClient.mjs +869 -551
- package/dist/core/clients/ShelbyClientConfig.d.ts +9 -1
- package/dist/core/clients/ShelbyMetadataClient.d.ts +38 -6
- package/dist/core/clients/ShelbyMetadataClient.mjs +95 -13
- package/dist/core/clients/ShelbyMicropaymentChannelClient.d.ts +42 -0
- package/dist/core/clients/ShelbyMicropaymentChannelClient.mjs +62 -7
- package/dist/core/clients/ShelbyPlacementGroupClient.mjs +10 -7
- package/dist/core/clients/ShelbyRPCClient.d.ts +55 -66
- package/dist/core/clients/ShelbyRPCClient.mjs +344 -360
- package/dist/core/clients/index.d.ts +3 -2
- package/dist/core/clients/index.mjs +933 -551
- package/dist/core/clients/utils.mjs +1 -1
- package/dist/core/commitments.d.ts +12 -1
- package/dist/core/commitments.mjs +7 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.mjs +1 -1
- package/dist/core/erasure/constants.d.ts +16 -1
- package/dist/core/erasure/constants.mjs +15 -1
- package/dist/core/erasure/index.d.ts +1 -1
- package/dist/core/erasure/index.mjs +15 -1
- package/dist/core/errors.d.ts +20 -1
- package/dist/core/errors.mjs +29 -0
- package/dist/core/index.d.ts +9 -9
- package/dist/core/index.mjs +1068 -589
- package/dist/core/operations/generated/sdk.d.ts +798 -42
- package/dist/core/operations/generated/sdk.mjs +93 -9
- package/dist/core/operations/index.d.ts +1 -1
- package/dist/core/operations/index.mjs +94 -10
- package/dist/core/rpc-responses.d.ts +1 -57
- package/dist/core/rpc-responses.mjs +1 -21
- package/dist/core/sp/chunk_proof.d.ts +23 -0
- package/dist/core/sp/chunk_proof.mjs +113 -0
- package/dist/core/sp/index.d.ts +3 -0
- package/dist/core/sp/index.mjs +402 -0
- package/dist/core/sp/sp_write_client.d.ts +53 -0
- package/dist/core/sp/sp_write_client.mjs +302 -0
- package/dist/core/types/blobs.d.ts +24 -5
- package/dist/core/types/blobs.mjs +24 -0
- package/dist/core/types/index.d.ts +2 -2
- package/dist/core/types/index.mjs +46 -2
- package/dist/core/types/payments.mjs +1 -1
- package/dist/core/types/storage_providers.d.ts +32 -6
- package/dist/core/types/storage_providers.mjs +22 -0
- package/dist/gen/rpc_server_pb.d.ts +295 -0
- package/dist/gen/rpc_server_pb.mjs +28 -0
- package/dist/node/clients/ShelbyNodeClient.d.ts +1 -0
- package/dist/node/clients/ShelbyNodeClient.mjs +869 -551
- package/dist/node/clients/index.d.ts +1 -0
- package/dist/node/clients/index.mjs +867 -551
- package/dist/node/index.d.ts +9 -9
- package/dist/node/index.mjs +1068 -589
- package/dist/node/parallel/commitment_worker.mjs +36 -249
- package/dist/node/parallel/commitment_worker_pool.mjs +56 -3
- package/dist/node/parallel/coverage_flush.d.ts +16 -0
- package/dist/node/parallel/coverage_flush.mjs +43 -0
- package/dist/node/parallel/default_commitment_worker_pool.mjs +56 -3
- package/dist/node/parallel/index.d.ts +1 -0
- package/dist/node/parallel/index.mjs +72 -4
- package/dist/node/parallel/parallel_commitments.mjs +56 -3
- package/dist/node/parallel/worker_pool.mjs +56 -3
- package/package.json +11 -4
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// src/core/sp/chunk_proof.ts
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
|
|
4
|
+
// src/core/utils.ts
|
|
5
|
+
import {
|
|
6
|
+
AccountAddress,
|
|
7
|
+
Hex
|
|
8
|
+
} from "@aptos-labs/ts-sdk";
|
|
9
|
+
async function concatHashes(parts) {
|
|
10
|
+
const chunks = parts.map((part) => Hex.fromHexInput(part).toUint8Array());
|
|
11
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
12
|
+
const combined = new Uint8Array(totalLength);
|
|
13
|
+
let offset = 0;
|
|
14
|
+
for (const chunk of chunks) {
|
|
15
|
+
combined.set(chunk, offset);
|
|
16
|
+
offset += chunk.byteLength;
|
|
17
|
+
}
|
|
18
|
+
return Hex.fromHexInput(
|
|
19
|
+
new Uint8Array(await crypto.subtle.digest("SHA-256", combined))
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/core/sp/chunk_proof.ts
|
|
24
|
+
var SP_WRITE_SUCCESS_ERROR_CODE = 1n;
|
|
25
|
+
function concatHashesSync(left, right) {
|
|
26
|
+
const combined = new Uint8Array(64);
|
|
27
|
+
combined.set(left, 0);
|
|
28
|
+
combined.set(right, 32);
|
|
29
|
+
const hash = createHash("sha256");
|
|
30
|
+
hash.update(combined);
|
|
31
|
+
return new Uint8Array(hash.digest());
|
|
32
|
+
}
|
|
33
|
+
async function generateChunksetInclusionProof(chunksetRoots, chunksetIndex) {
|
|
34
|
+
if (chunksetRoots.length === 0) {
|
|
35
|
+
throw new Error("Cannot generate inclusion proof for empty chunkset roots");
|
|
36
|
+
}
|
|
37
|
+
if (chunksetIndex < 0 || chunksetIndex >= chunksetRoots.length) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Chunkset index ${chunksetIndex} out of range [0, ${chunksetRoots.length})`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (chunksetRoots.length === 1) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const zeroHash = new Uint8Array(32);
|
|
46
|
+
const siblings = [];
|
|
47
|
+
let currentLeaves = chunksetRoots.map((h) => h.toUint8Array());
|
|
48
|
+
let currentIdx = chunksetIndex;
|
|
49
|
+
while (currentLeaves.length > 1) {
|
|
50
|
+
if (currentLeaves.length % 2 !== 0) {
|
|
51
|
+
currentLeaves.push(zeroHash);
|
|
52
|
+
}
|
|
53
|
+
const siblingIdx = currentIdx % 2 === 0 ? currentIdx + 1 : currentIdx - 1;
|
|
54
|
+
siblings.push(currentLeaves[siblingIdx]);
|
|
55
|
+
const nextLeaves = [];
|
|
56
|
+
for (let i = 0; i < currentLeaves.length; i += 2) {
|
|
57
|
+
const combined = await concatHashes([
|
|
58
|
+
currentLeaves[i],
|
|
59
|
+
currentLeaves[i + 1]
|
|
60
|
+
]);
|
|
61
|
+
nextLeaves.push(combined.toUint8Array());
|
|
62
|
+
}
|
|
63
|
+
currentLeaves = nextLeaves;
|
|
64
|
+
currentIdx = Math.floor(currentIdx / 2);
|
|
65
|
+
}
|
|
66
|
+
return siblings;
|
|
67
|
+
}
|
|
68
|
+
function serializeInclusionProofSiblings(siblings) {
|
|
69
|
+
const totalLength = siblings.reduce((sum, s) => sum + s.length, 0);
|
|
70
|
+
const proof = new Uint8Array(totalLength);
|
|
71
|
+
let offset = 0;
|
|
72
|
+
for (const sibling of siblings) {
|
|
73
|
+
proof.set(sibling, offset);
|
|
74
|
+
offset += sibling.length;
|
|
75
|
+
}
|
|
76
|
+
return proof;
|
|
77
|
+
}
|
|
78
|
+
function buildChunkProof(chunkIndex, chunkRoots, chunksetToBlobProof) {
|
|
79
|
+
const chunkToChunksetSiblings = [];
|
|
80
|
+
let currentIdx = chunkIndex;
|
|
81
|
+
let currentLevel = [...chunkRoots];
|
|
82
|
+
while (currentLevel.length > 1) {
|
|
83
|
+
if (currentLevel.length % 2 !== 0) {
|
|
84
|
+
currentLevel.push(new Uint8Array(32));
|
|
85
|
+
}
|
|
86
|
+
const siblingIdx = currentIdx % 2 === 0 ? currentIdx + 1 : currentIdx - 1;
|
|
87
|
+
if (siblingIdx < currentLevel.length) {
|
|
88
|
+
chunkToChunksetSiblings.push(currentLevel[siblingIdx]);
|
|
89
|
+
}
|
|
90
|
+
const nextLevel = [];
|
|
91
|
+
for (let i = 0; i < currentLevel.length; i += 2) {
|
|
92
|
+
nextLevel.push(concatHashesSync(currentLevel[i], currentLevel[i + 1]));
|
|
93
|
+
}
|
|
94
|
+
currentLevel = nextLevel;
|
|
95
|
+
currentIdx = Math.floor(currentIdx / 2);
|
|
96
|
+
}
|
|
97
|
+
const totalLength = chunkToChunksetSiblings.reduce((sum, s) => sum + s.length, 0) + chunksetToBlobProof.length;
|
|
98
|
+
const fullProof = new Uint8Array(totalLength);
|
|
99
|
+
let offset = 0;
|
|
100
|
+
for (const sibling of chunkToChunksetSiblings) {
|
|
101
|
+
fullProof.set(sibling, offset);
|
|
102
|
+
offset += sibling.length;
|
|
103
|
+
}
|
|
104
|
+
fullProof.set(chunksetToBlobProof, offset);
|
|
105
|
+
return fullProof;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/core/sp/sp_write_client.ts
|
|
109
|
+
import * as net from "net";
|
|
110
|
+
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
111
|
+
|
|
112
|
+
// src/gen/rpc_server_pb.ts
|
|
113
|
+
import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
|
|
114
|
+
var file_rpc_server = /* @__PURE__ */ fileDesc("ChBycGNfc2VydmVyLnByb3RvEgNjYXYiGgoHUGF5bWVudBIPCgdwYXltZW50GAEgASgMIhkKCVByb29mTm9kZRIMCgRoYXNoGAEgASgMIiwKBVByb29mEiMKC3Byb29mX25vZGVzGAEgAygLMg4uY2F2LlByb29mTm9kZSJFCghDaHVua0tleRIQCghibG9iX3VpZBgBIAEoBBIUCgxjaHVua3NldF9pZHgYAiABKA0SEQoJY2h1bmtfaWR4GAMgASgNIk8KC1JlYWRSZXF1ZXN0EiEKCmlkZW50aWZpZXIYASABKAsyDS5jYXYuQ2h1bmtLZXkSHQoHcGF5bWVudBgCIAEoCzIMLmNhdi5QYXltZW50InEKGUNsYXlIZWxwZXJEYXRhUmVhZFJlcXVlc3QSIQoKaWRlbnRpZmllchgBIAEoCzINLmNhdi5DaHVua0tleRIYChB0YXJnZXRfY2h1bmtfaWR4GAIgASgNEhcKD2hlbHBlcl9zZXRfbWFzaxgDIAEoBCJgCgxXcml0ZVJlcXVlc3QSIQoKaWRlbnRpZmllchgBIAEoCzINLmNhdi5DaHVua0tleRIMCgRib2R5GAIgASgMEhkKBXByb29mGAMgASgLMgouY2F2LlByb29mSgQIBBAFIsQBCgdSZXF1ZXN0EiQKCHJlYWRfcmVxGAEgASgLMhAuY2F2LlJlYWRSZXF1ZXN0SAASJgoJd3JpdGVfcmVxGAIgASgLMhEuY2F2LldyaXRlUmVxdWVzdEgAEjkKD2hlbHBlcl9yZWFkX3JlcRgFIAEoCzIeLmNhdi5DbGF5SGVscGVyRGF0YVJlYWRSZXF1ZXN0SAASEgoKcmVxdWVzdF9pZBgDIAEoBBIRCglzaWduYXR1cmUYBCABKAxCCQoHcmVxdWVzdCJXCgxSZWFkUmVzcG9uc2USDAoEYm9keRgBIAEoDBIZCgVwcm9vZhgCIAEoCzIKLmNhdi5Qcm9vZhIeChZoZWxwZXJfY2h1bmtfcm9vdF9oYXNoGAMgASgMIk0KDVdyaXRlUmVzcG9uc2USGgoSYmxvYl9hY2tfc2lnbmF0dXJlGAIgASgMEiAKGG1pc3NpbmdfY2h1bmtzZXRfaW5kaWNlcxgDIAMoDSKWAQoIUmVzcG9uc2USEgoKcmVxdWVzdF9pZBgBIAEoBBISCgplcnJvcl9jb2RlGAIgASgEEiYKCXJlYWRfcmVzcBgDIAEoCzIRLmNhdi5SZWFkUmVzcG9uc2VIABIoCgp3cml0ZV9yZXNwGAQgASgLMhIuY2F2LldyaXRlUmVzcG9uc2VIAEIKCghyZXNwb25zZUoECAUQBmIGcHJvdG8z");
|
|
115
|
+
var ProofNodeSchema = /* @__PURE__ */ messageDesc(file_rpc_server, 1);
|
|
116
|
+
var ProofSchema = /* @__PURE__ */ messageDesc(file_rpc_server, 2);
|
|
117
|
+
var ChunkKeySchema = /* @__PURE__ */ messageDesc(file_rpc_server, 3);
|
|
118
|
+
var ReadRequestSchema = /* @__PURE__ */ messageDesc(file_rpc_server, 4);
|
|
119
|
+
var WriteRequestSchema = /* @__PURE__ */ messageDesc(file_rpc_server, 6);
|
|
120
|
+
var RequestSchema = /* @__PURE__ */ messageDesc(file_rpc_server, 7);
|
|
121
|
+
var ResponseSchema = /* @__PURE__ */ messageDesc(file_rpc_server, 10);
|
|
122
|
+
|
|
123
|
+
// src/core/sp/sp_write_client.ts
|
|
124
|
+
var SP_CLIENT_LOG = process.env.SHELBY_SP_CLIENT_LOG === "1";
|
|
125
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
126
|
+
var DEFAULT_CONNECTIONS = 4;
|
|
127
|
+
var SpWriteClient = class {
|
|
128
|
+
constructor(host, port, numConnections = DEFAULT_CONNECTIONS, requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
|
|
129
|
+
this.host = host;
|
|
130
|
+
this.port = port;
|
|
131
|
+
this.numConnections = numConnections;
|
|
132
|
+
this.requestTimeoutMs = requestTimeoutMs;
|
|
133
|
+
}
|
|
134
|
+
connections = [];
|
|
135
|
+
nextConnectionIdx = 0;
|
|
136
|
+
nextRequestId = 1n;
|
|
137
|
+
connected = false;
|
|
138
|
+
async connect() {
|
|
139
|
+
if (this.connected) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const connectPromises = [];
|
|
143
|
+
for (let i = 0; i < this.numConnections; i++) {
|
|
144
|
+
connectPromises.push(this.createConnection());
|
|
145
|
+
}
|
|
146
|
+
await Promise.all(connectPromises);
|
|
147
|
+
this.connected = true;
|
|
148
|
+
}
|
|
149
|
+
createConnection() {
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
const socket = new net.Socket();
|
|
152
|
+
let settled = false;
|
|
153
|
+
const state = {
|
|
154
|
+
socket,
|
|
155
|
+
responseBuffer: Buffer.alloc(0),
|
|
156
|
+
expectedSize: null,
|
|
157
|
+
pending: /* @__PURE__ */ new Map(),
|
|
158
|
+
writeQueue: [],
|
|
159
|
+
draining: false,
|
|
160
|
+
closed: false
|
|
161
|
+
};
|
|
162
|
+
socket.on("connect", () => {
|
|
163
|
+
this.connections.push(state);
|
|
164
|
+
settled = true;
|
|
165
|
+
resolve();
|
|
166
|
+
});
|
|
167
|
+
socket.on("data", (data) => {
|
|
168
|
+
this.handleData(state, data);
|
|
169
|
+
});
|
|
170
|
+
socket.on("error", (err) => {
|
|
171
|
+
if (SP_CLIENT_LOG)
|
|
172
|
+
console.error(
|
|
173
|
+
`[SpWriteClient] socket error ${this.host}:${this.port}: ${err.message}`
|
|
174
|
+
);
|
|
175
|
+
if (!settled) {
|
|
176
|
+
settled = true;
|
|
177
|
+
reject(err);
|
|
178
|
+
}
|
|
179
|
+
this.handleDisconnect(state, err);
|
|
180
|
+
});
|
|
181
|
+
socket.on("close", (hadError) => {
|
|
182
|
+
if (SP_CLIENT_LOG)
|
|
183
|
+
console.error(
|
|
184
|
+
`[SpWriteClient] socket closed ${this.host}:${this.port} (hadError=${hadError})`
|
|
185
|
+
);
|
|
186
|
+
this.handleDisconnect(state, new Error("SP connection closed"));
|
|
187
|
+
});
|
|
188
|
+
socket.on("timeout", () => {
|
|
189
|
+
if (SP_CLIENT_LOG)
|
|
190
|
+
console.error(
|
|
191
|
+
`[SpWriteClient] socket idle timeout ${this.host}:${this.port} after ${this.requestTimeoutMs}ms`
|
|
192
|
+
);
|
|
193
|
+
socket.destroy(new Error("SP connection timed out"));
|
|
194
|
+
});
|
|
195
|
+
socket.connect(this.port, this.host);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
handleData(state, data) {
|
|
199
|
+
state.responseBuffer = Buffer.concat([state.responseBuffer, data]);
|
|
200
|
+
while (true) {
|
|
201
|
+
if (state.expectedSize === null) {
|
|
202
|
+
if (state.responseBuffer.length < 8) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
state.expectedSize = Number(state.responseBuffer.readBigUInt64LE(0));
|
|
206
|
+
}
|
|
207
|
+
const totalNeeded = 8 + state.expectedSize;
|
|
208
|
+
if (state.responseBuffer.length < totalNeeded) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const responseBytes = state.responseBuffer.subarray(8, totalNeeded);
|
|
212
|
+
state.responseBuffer = state.responseBuffer.subarray(totalNeeded);
|
|
213
|
+
state.expectedSize = null;
|
|
214
|
+
try {
|
|
215
|
+
const response = fromBinary(ResponseSchema, responseBytes);
|
|
216
|
+
const pending = state.pending.get(response.requestId);
|
|
217
|
+
if (pending) {
|
|
218
|
+
clearTimeout(pending.timeoutId);
|
|
219
|
+
state.pending.delete(response.requestId);
|
|
220
|
+
const success = response.errorCode === SP_WRITE_SUCCESS_ERROR_CODE;
|
|
221
|
+
if (pending.resolver.kind === "write") {
|
|
222
|
+
const blobAckSignature = response.response.case === "writeResp" ? response.response.value.blobAckSignature : new Uint8Array(0);
|
|
223
|
+
pending.resolver.resolve({
|
|
224
|
+
success,
|
|
225
|
+
errorCode: response.errorCode,
|
|
226
|
+
blobAckSignature
|
|
227
|
+
});
|
|
228
|
+
} else {
|
|
229
|
+
const body = response.response.case === "readResp" ? response.response.value.body : new Uint8Array(0);
|
|
230
|
+
pending.resolver.resolve({
|
|
231
|
+
success,
|
|
232
|
+
errorCode: response.errorCode,
|
|
233
|
+
bytesRead: body.byteLength
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
for (const [, pending] of state.pending) {
|
|
239
|
+
clearTimeout(pending.timeoutId);
|
|
240
|
+
pending.reject(
|
|
241
|
+
error instanceof Error ? error : new Error(String(error))
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
state.pending.clear();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
handleDisconnect(state, error) {
|
|
249
|
+
if (state.closed) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
state.closed = true;
|
|
253
|
+
for (const [, pending] of state.pending) {
|
|
254
|
+
clearTimeout(pending.timeoutId);
|
|
255
|
+
pending.reject(error);
|
|
256
|
+
}
|
|
257
|
+
state.pending.clear();
|
|
258
|
+
const idx = this.connections.indexOf(state);
|
|
259
|
+
if (idx !== -1) {
|
|
260
|
+
this.connections.splice(idx, 1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
drainConnection(state) {
|
|
264
|
+
if (state.draining || state.writeQueue.length === 0 || state.closed) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
state.draining = true;
|
|
268
|
+
const frame = state.writeQueue.shift();
|
|
269
|
+
const canContinue = state.socket.write(frame, () => {
|
|
270
|
+
state.draining = false;
|
|
271
|
+
this.drainConnection(state);
|
|
272
|
+
});
|
|
273
|
+
if (!canContinue) {
|
|
274
|
+
state.socket.once("drain", () => {
|
|
275
|
+
state.draining = false;
|
|
276
|
+
this.drainConnection(state);
|
|
277
|
+
});
|
|
278
|
+
} else {
|
|
279
|
+
state.draining = false;
|
|
280
|
+
this.drainConnection(state);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
selectConnection() {
|
|
284
|
+
if (this.connections.length === 0) {
|
|
285
|
+
throw new Error("Not connected to SP");
|
|
286
|
+
}
|
|
287
|
+
for (let i = 0; i < this.connections.length; i++) {
|
|
288
|
+
const idx = (this.nextConnectionIdx + i) % this.connections.length;
|
|
289
|
+
const conn = this.connections[idx];
|
|
290
|
+
if (!conn.closed) {
|
|
291
|
+
this.nextConnectionIdx = (idx + 1) % this.connections.length;
|
|
292
|
+
return conn;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
throw new Error("No available SP connections");
|
|
296
|
+
}
|
|
297
|
+
async writeChunk(params) {
|
|
298
|
+
if (!this.connected) {
|
|
299
|
+
await this.connect();
|
|
300
|
+
}
|
|
301
|
+
const conn = this.selectConnection();
|
|
302
|
+
const requestId = this.nextRequestId++;
|
|
303
|
+
const chunkKey = create(ChunkKeySchema, {
|
|
304
|
+
blobUid: params.blobUid,
|
|
305
|
+
chunksetIdx: params.chunksetIdx,
|
|
306
|
+
chunkIdx: params.chunkIdx
|
|
307
|
+
});
|
|
308
|
+
const proofNodes = [];
|
|
309
|
+
for (let i = 0; i < params.proof.length; i += 32) {
|
|
310
|
+
proofNodes.push(
|
|
311
|
+
create(ProofNodeSchema, {
|
|
312
|
+
hash: params.proof.slice(i, i + 32)
|
|
313
|
+
})
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const request = create(RequestSchema, {
|
|
317
|
+
request: {
|
|
318
|
+
case: "writeReq",
|
|
319
|
+
value: create(WriteRequestSchema, {
|
|
320
|
+
identifier: chunkKey,
|
|
321
|
+
body: params.chunkData,
|
|
322
|
+
proof: create(ProofSchema, { proofNodes })
|
|
323
|
+
})
|
|
324
|
+
},
|
|
325
|
+
requestId,
|
|
326
|
+
signature: new Uint8Array(64)
|
|
327
|
+
});
|
|
328
|
+
const requestBytes = toBinary(RequestSchema, request);
|
|
329
|
+
const lengthBuffer = Buffer.alloc(8);
|
|
330
|
+
lengthBuffer.writeBigUInt64LE(BigInt(requestBytes.length), 0);
|
|
331
|
+
const frame = Buffer.concat([lengthBuffer, Buffer.from(requestBytes)]);
|
|
332
|
+
return new Promise((resolve, reject) => {
|
|
333
|
+
const timeoutId = setTimeout(() => {
|
|
334
|
+
conn.pending.delete(requestId);
|
|
335
|
+
reject(new Error(`SP write request ${requestId} timed out`));
|
|
336
|
+
}, this.requestTimeoutMs);
|
|
337
|
+
conn.pending.set(requestId, {
|
|
338
|
+
resolver: { kind: "write", resolve },
|
|
339
|
+
reject,
|
|
340
|
+
timeoutId
|
|
341
|
+
});
|
|
342
|
+
conn.writeQueue.push(frame);
|
|
343
|
+
this.drainConnection(conn);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
async readChunk(params) {
|
|
347
|
+
if (!this.connected) {
|
|
348
|
+
await this.connect();
|
|
349
|
+
}
|
|
350
|
+
const conn = this.selectConnection();
|
|
351
|
+
const requestId = this.nextRequestId++;
|
|
352
|
+
const chunkKey = create(ChunkKeySchema, {
|
|
353
|
+
blobUid: params.blobUid,
|
|
354
|
+
chunksetIdx: params.chunksetIdx,
|
|
355
|
+
chunkIdx: params.chunkIdx
|
|
356
|
+
});
|
|
357
|
+
const request = create(RequestSchema, {
|
|
358
|
+
request: {
|
|
359
|
+
case: "readReq",
|
|
360
|
+
value: create(ReadRequestSchema, {
|
|
361
|
+
identifier: chunkKey
|
|
362
|
+
})
|
|
363
|
+
},
|
|
364
|
+
requestId,
|
|
365
|
+
signature: new Uint8Array(64)
|
|
366
|
+
});
|
|
367
|
+
const requestBytes = toBinary(RequestSchema, request);
|
|
368
|
+
const lengthBuffer = Buffer.alloc(8);
|
|
369
|
+
lengthBuffer.writeBigUInt64LE(BigInt(requestBytes.length), 0);
|
|
370
|
+
const frame = Buffer.concat([lengthBuffer, Buffer.from(requestBytes)]);
|
|
371
|
+
return new Promise((resolve, reject) => {
|
|
372
|
+
const timeoutId = setTimeout(() => {
|
|
373
|
+
conn.pending.delete(requestId);
|
|
374
|
+
reject(new Error(`SP read request ${requestId} timed out`));
|
|
375
|
+
}, this.requestTimeoutMs);
|
|
376
|
+
conn.pending.set(requestId, {
|
|
377
|
+
resolver: { kind: "read", resolve },
|
|
378
|
+
reject,
|
|
379
|
+
timeoutId
|
|
380
|
+
});
|
|
381
|
+
conn.writeQueue.push(frame);
|
|
382
|
+
this.drainConnection(conn);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
// biome-ignore lint/suspicious/useAwait: callers rely on Promise return
|
|
386
|
+
async close() {
|
|
387
|
+
this.connected = false;
|
|
388
|
+
for (const conn of this.connections) {
|
|
389
|
+
conn.closed = true;
|
|
390
|
+
conn.socket.destroy();
|
|
391
|
+
}
|
|
392
|
+
this.connections = [];
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
export {
|
|
396
|
+
SP_WRITE_SUCCESS_ERROR_CODE,
|
|
397
|
+
SpWriteClient,
|
|
398
|
+
buildChunkProof,
|
|
399
|
+
concatHashesSync,
|
|
400
|
+
generateChunksetInclusionProof,
|
|
401
|
+
serializeInclusionProofSiblings
|
|
402
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
interface SpWriteChunkParams {
|
|
2
|
+
blobUid: bigint;
|
|
3
|
+
chunksetIdx: number;
|
|
4
|
+
chunkIdx: number;
|
|
5
|
+
chunkData: Uint8Array;
|
|
6
|
+
proof: Uint8Array;
|
|
7
|
+
}
|
|
8
|
+
interface SpWriteResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
errorCode: bigint;
|
|
11
|
+
/**
|
|
12
|
+
* BLS blob-ack signature returned by the SP when it has received all
|
|
13
|
+
* chunksets for the blob. Empty (zero-length) if the blob is not yet
|
|
14
|
+
* complete on this SP. Present only for the last write that completes the
|
|
15
|
+
* blob on this particular SP.
|
|
16
|
+
*/
|
|
17
|
+
blobAckSignature: Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
interface SpReadChunkParams {
|
|
20
|
+
blobUid: bigint;
|
|
21
|
+
chunksetIdx: number;
|
|
22
|
+
chunkIdx: number;
|
|
23
|
+
}
|
|
24
|
+
interface SpReadResult {
|
|
25
|
+
success: boolean;
|
|
26
|
+
errorCode: bigint;
|
|
27
|
+
bytesRead: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Client for writing erasure chunks directly to a Storage Provider over TCP/protobuf.
|
|
31
|
+
*/
|
|
32
|
+
declare class SpWriteClient {
|
|
33
|
+
private readonly host;
|
|
34
|
+
private readonly port;
|
|
35
|
+
private readonly numConnections;
|
|
36
|
+
private readonly requestTimeoutMs;
|
|
37
|
+
private connections;
|
|
38
|
+
private nextConnectionIdx;
|
|
39
|
+
private nextRequestId;
|
|
40
|
+
private connected;
|
|
41
|
+
constructor(host: string, port: number, numConnections?: number, requestTimeoutMs?: number);
|
|
42
|
+
connect(): Promise<void>;
|
|
43
|
+
private createConnection;
|
|
44
|
+
private handleData;
|
|
45
|
+
private handleDisconnect;
|
|
46
|
+
private drainConnection;
|
|
47
|
+
private selectConnection;
|
|
48
|
+
writeChunk(params: SpWriteChunkParams): Promise<SpWriteResult>;
|
|
49
|
+
readChunk(params: SpReadChunkParams): Promise<SpReadResult>;
|
|
50
|
+
close(): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { type SpReadChunkParams, type SpReadResult, type SpWriteChunkParams, SpWriteClient, type SpWriteResult };
|