@shelby-protocol/sdk 0.3.0 → 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.
Files changed (126) hide show
  1. package/dist/browser/index.d.ts +19 -10
  2. package/dist/browser/index.mjs +4200 -146
  3. package/dist/core/aptos-explorer.mjs +46 -5
  4. package/dist/core/blobs.mjs +12 -4
  5. package/dist/core/chunk.mjs +58 -9
  6. package/dist/core/clients/ShelbyBlobClient.d.ts +155 -83
  7. package/dist/core/clients/ShelbyBlobClient.mjs +1286 -17
  8. package/dist/core/clients/ShelbyClient.d.ts +39 -6
  9. package/dist/core/clients/ShelbyClient.mjs +3233 -23
  10. package/dist/core/clients/ShelbyClientConfig.d.ts +9 -1
  11. package/dist/core/clients/ShelbyClientConfig.mjs +0 -1
  12. package/dist/core/clients/ShelbyMetadataClient.d.ts +20 -6
  13. package/dist/core/clients/ShelbyMetadataClient.mjs +388 -6
  14. package/dist/core/clients/ShelbyMicropaymentChannelClient.d.ts +7 -7
  15. package/dist/core/clients/ShelbyMicropaymentChannelClient.mjs +894 -13
  16. package/dist/core/clients/ShelbyPlacementGroupClient.mjs +310 -8
  17. package/dist/core/clients/ShelbyRPCClient.d.ts +117 -25
  18. package/dist/core/clients/ShelbyRPCClient.mjs +1245 -12
  19. package/dist/core/clients/index.d.ts +4 -2
  20. package/dist/core/clients/index.mjs +3802 -39
  21. package/dist/core/clients/utils.mjs +65 -6
  22. package/dist/core/commitments.d.ts +12 -1
  23. package/dist/core/commitments.mjs +277 -11
  24. package/dist/core/constants.d.ts +3 -3
  25. package/dist/core/constants.mjs +39 -16
  26. package/dist/core/erasure/clay-codes.mjs +129 -4
  27. package/dist/core/erasure/constants.d.ts +16 -1
  28. package/dist/core/erasure/constants.mjs +48 -11
  29. package/dist/core/erasure/default.mjs +191 -9
  30. package/dist/core/erasure/index.d.ts +1 -1
  31. package/dist/core/erasure/index.mjs +281 -24
  32. package/dist/core/erasure/reed-solomon.mjs +68 -4
  33. package/dist/core/erasure/utils.mjs +0 -2
  34. package/dist/core/errors.d.ts +20 -1
  35. package/dist/core/errors.mjs +58 -8
  36. package/dist/core/index.d.ts +9 -9
  37. package/dist/core/index.mjs +4194 -146
  38. package/dist/core/layout.mjs +37 -5
  39. package/dist/core/networks.mjs +10 -5
  40. package/dist/core/operations/generated/sdk.d.ts +807 -42
  41. package/dist/core/operations/generated/sdk.mjs +267 -18
  42. package/dist/core/operations/index.d.ts +1 -1
  43. package/dist/core/operations/index.mjs +368 -24
  44. package/dist/core/promises.mjs +4 -4
  45. package/dist/core/rpc-responses.d.ts +1 -57
  46. package/dist/core/rpc-responses.mjs +14 -15
  47. package/dist/core/shelby-explorer.mjs +31 -6
  48. package/dist/core/sp/chunk_proof.d.ts +23 -0
  49. package/dist/core/sp/chunk_proof.mjs +113 -0
  50. package/dist/core/sp/index.d.ts +3 -0
  51. package/dist/core/sp/index.mjs +402 -0
  52. package/dist/core/sp/sp_write_client.d.ts +53 -0
  53. package/dist/core/sp/sp_write_client.mjs +302 -0
  54. package/dist/core/strings.mjs +0 -2
  55. package/dist/core/types/blobs.d.ts +24 -5
  56. package/dist/core/types/blobs.mjs +24 -1
  57. package/dist/core/types/index.d.ts +2 -2
  58. package/dist/core/types/index.mjs +275 -10
  59. package/dist/core/types/payments.d.ts +6 -6
  60. package/dist/core/types/payments.mjs +230 -5
  61. package/dist/core/types/placement_groups.mjs +0 -1
  62. package/dist/core/types/storage_providers.d.ts +32 -6
  63. package/dist/core/types/storage_providers.mjs +22 -1
  64. package/dist/core/utils.mjs +94 -8
  65. package/dist/gen/rpc_server_pb.d.ts +295 -0
  66. package/dist/gen/rpc_server_pb.mjs +28 -0
  67. package/dist/node/clients/ShelbyNodeClient.d.ts +2 -0
  68. package/dist/node/clients/ShelbyNodeClient.mjs +3237 -24
  69. package/dist/node/clients/index.d.ts +2 -0
  70. package/dist/node/clients/index.mjs +3236 -26
  71. package/dist/node/index.d.ts +9 -9
  72. package/dist/node/index.mjs +4217 -153
  73. package/dist/node/parallel/commitment_worker.d.ts +9 -0
  74. package/dist/node/parallel/commitment_worker.mjs +85 -0
  75. package/dist/node/parallel/commitment_worker_pool.d.ts +35 -0
  76. package/dist/node/parallel/commitment_worker_pool.mjs +339 -0
  77. package/dist/node/parallel/coverage_flush.d.ts +16 -0
  78. package/dist/node/parallel/coverage_flush.mjs +43 -0
  79. package/dist/node/parallel/default_commitment_worker_pool.d.ts +24 -0
  80. package/dist/node/parallel/default_commitment_worker_pool.mjs +355 -0
  81. package/dist/node/parallel/index.d.ts +12 -0
  82. package/dist/node/parallel/index.mjs +631 -0
  83. package/dist/node/parallel/parallel_commitments.d.ts +32 -0
  84. package/dist/node/parallel/parallel_commitments.mjs +614 -0
  85. package/dist/node/parallel/worker_pool.d.ts +74 -0
  86. package/dist/node/parallel/worker_pool.mjs +301 -0
  87. package/dist/node/testUtil.mjs +8 -4
  88. package/package.json +16 -5
  89. package/dist/chunk-3NRBHSMQ.mjs +0 -12
  90. package/dist/chunk-5BVSWXQJ.mjs +0 -801
  91. package/dist/chunk-6FJNAUU5.mjs +0 -278
  92. package/dist/chunk-6XSZRDLJ.mjs +0 -375
  93. package/dist/chunk-7P6ASYW6.mjs +0 -9
  94. package/dist/chunk-A4IG6GSE.mjs +0 -21
  95. package/dist/chunk-AD2G3QYD.mjs +0 -0
  96. package/dist/chunk-AUQDI5BS.mjs +0 -43
  97. package/dist/chunk-AY3HBWXA.mjs +0 -51
  98. package/dist/chunk-BDSW5PHM.mjs +0 -37
  99. package/dist/chunk-C6RQ3AEU.mjs +0 -51
  100. package/dist/chunk-CQ6QPIZK.mjs +0 -37
  101. package/dist/chunk-D6GQHO6G.mjs +0 -15
  102. package/dist/chunk-EM67QTMR.mjs +0 -0
  103. package/dist/chunk-I6NG5GNL.mjs +0 -8
  104. package/dist/chunk-JTXYKO3U.mjs +0 -38
  105. package/dist/chunk-KG2TMLEV.mjs +0 -207
  106. package/dist/chunk-M3CP5TFP.mjs +0 -39
  107. package/dist/chunk-M7W43K2Q.mjs +0 -505
  108. package/dist/chunk-MB7C7VQF.mjs +0 -0
  109. package/dist/chunk-MNCQBEMS.mjs +0 -55
  110. package/dist/chunk-MQUVYMNQ.mjs +0 -0
  111. package/dist/chunk-NEO7Z3ZQ.mjs +0 -49
  112. package/dist/chunk-NHWWORCH.mjs +0 -72
  113. package/dist/chunk-OEK24ON7.mjs +0 -135
  114. package/dist/chunk-QQ57OGQ2.mjs +0 -0
  115. package/dist/chunk-RNNJXTX2.mjs +0 -154
  116. package/dist/chunk-S6Q4OG7H.mjs +0 -83
  117. package/dist/chunk-TET3DJEO.mjs +0 -199
  118. package/dist/chunk-UEZNZBJO.mjs +0 -104
  119. package/dist/chunk-V46D5NBW.mjs +0 -11
  120. package/dist/chunk-V4B3GAMQ.mjs +0 -42
  121. package/dist/chunk-WFWRBNTW.mjs +0 -412
  122. package/dist/chunk-WTICJPDB.mjs +0 -0
  123. package/dist/chunk-XNEIWM4O.mjs +0 -0
  124. package/dist/chunk-Z4FZ7W6L.mjs +0 -39
  125. package/dist/chunk-Z7RFCADT.mjs +0 -0
  126. package/dist/chunk-ZHXCVRZX.mjs +0 -0
@@ -0,0 +1,113 @@
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
+ export {
108
+ SP_WRITE_SUCCESS_ERROR_CODE,
109
+ buildChunkProof,
110
+ concatHashesSync,
111
+ generateChunksetInclusionProof,
112
+ serializeInclusionProofSiblings
113
+ };
@@ -0,0 +1,3 @@
1
+ export { SP_WRITE_SUCCESS_ERROR_CODE, buildChunkProof, concatHashesSync, generateChunksetInclusionProof, serializeInclusionProofSiblings } from './chunk_proof.js';
2
+ export { SpReadChunkParams, SpReadResult, SpWriteChunkParams, SpWriteClient, SpWriteResult } from './sp_write_client.js';
3
+ import '@aptos-labs/ts-sdk';
@@ -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 };