@arcium-hq/client 0.10.4 → 0.11.1

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/src/pda.ts CHANGED
@@ -1,22 +1,30 @@
1
1
  import { PublicKey, AddressLookupTableProgram } from '@solana/web3.js';
2
2
  import * as anchor from '@anchor-lang/core';
3
3
  import {
4
+ ARCIUM_SIGNER_ACC_SEED,
4
5
  ARX_NODE_ACC_SEED,
5
6
  CLOCK_ACC_SEED,
6
7
  CLUSTER_ACC_SEED,
7
8
  COMP_DEF_ACC_SEED,
8
9
  COMPUTATION_ACC_SEED,
9
10
  EXEC_POOL_ACC_SEED,
11
+ FAILURE_CLAIM_ACC_SEED,
10
12
  MEMPOOL_ACC_SEED,
11
13
  MXE_ACCOUNT_SEED,
12
14
  MXE_RECOVERY_ACC_SEED,
15
+ OPERATOR_ACC_SEED,
13
16
  OFFSET_BUFFER_SIZE,
14
17
  POOL_ACC_SEED,
15
18
  RAW_CIRCUIT_ACC_SEED,
16
19
  RECOVERY_CLUSTER_ACC_SEED,
20
+ RECOVERY_PEER_ACC_SEED,
17
21
  } from './constants.js';
18
22
  import { ARCIUM_ADDR } from './idl/index.js';
19
23
 
24
+ const U64_SEED_SIZE = 8;
25
+ const MAX_U8 = 0xff;
26
+ const MAX_U32 = 0xffffffff;
27
+
20
28
  /**
21
29
  * Return the public key of the deployed Arcium program on Solana.
22
30
  * @returns Arcium program's public key.
@@ -32,11 +40,7 @@ export function getArciumProgramId(): PublicKey {
32
40
  * @returns Derived computation account public key.
33
41
  */
34
42
  export function getComputationAccAddress(clusterOffset: number, computationOffset: anchor.BN): PublicKey {
35
- const clOffsetBuffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
36
- clOffsetBuffer.writeUInt32LE(clusterOffset, 0);
37
-
38
- const seeds = [Buffer.from(COMPUTATION_ACC_SEED), clOffsetBuffer, computationOffset.toArrayLike(Buffer, 'le', 8)];
39
- return generateArciumPDAFrom(seeds)[0];
43
+ return findArciumPda([stringSeed(COMPUTATION_ACC_SEED), u32Seed(clusterOffset), u64Seed(computationOffset)]);
40
44
  }
41
45
 
42
46
  /**
@@ -45,11 +49,7 @@ export function getComputationAccAddress(clusterOffset: number, computationOffse
45
49
  * @returns Derived mempool account public key.
46
50
  */
47
51
  export function getMempoolAccAddress(clusterOffset: number): PublicKey {
48
- const clOffsetBuffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
49
- clOffsetBuffer.writeUInt32LE(clusterOffset, 0);
50
-
51
- const seeds = [Buffer.from(MEMPOOL_ACC_SEED), clOffsetBuffer];
52
- return generateArciumPDAFrom(seeds)[0];
52
+ return findArciumPda([stringSeed(MEMPOOL_ACC_SEED), u32Seed(clusterOffset)]);
53
53
  }
54
54
 
55
55
  /**
@@ -58,12 +58,7 @@ export function getMempoolAccAddress(clusterOffset: number): PublicKey {
58
58
  * @returns Derived executing pool account public key.
59
59
  */
60
60
  export function getExecutingPoolAccAddress(clusterOffset: number): PublicKey {
61
- const clOffsetBuffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
62
- clOffsetBuffer.writeUInt32LE(clusterOffset, 0);
63
-
64
- const seeds = [Buffer.from(EXEC_POOL_ACC_SEED), clOffsetBuffer];
65
-
66
- return generateArciumPDAFrom(seeds)[0];
61
+ return findArciumPda([stringSeed(EXEC_POOL_ACC_SEED), u32Seed(clusterOffset)]);
67
62
  }
68
63
 
69
64
  /**
@@ -71,9 +66,7 @@ export function getExecutingPoolAccAddress(clusterOffset: number): PublicKey {
71
66
  * @returns Derived fee pool account public key.
72
67
  */
73
68
  export function getFeePoolAccAddress(): PublicKey {
74
- const seeds = [Buffer.from(POOL_ACC_SEED)];
75
-
76
- return generateArciumPDAFrom(seeds)[0];
69
+ return findArciumPda([stringSeed(POOL_ACC_SEED)]);
77
70
  }
78
71
 
79
72
  /**
@@ -81,9 +74,7 @@ export function getFeePoolAccAddress(): PublicKey {
81
74
  * @returns Derived clock account public key.
82
75
  */
83
76
  export function getClockAccAddress(): PublicKey {
84
- const seeds = [Buffer.from(CLOCK_ACC_SEED)];
85
-
86
- return generateArciumPDAFrom(seeds)[0];
77
+ return findArciumPda([stringSeed(CLOCK_ACC_SEED)]);
87
78
  }
88
79
 
89
80
  /**
@@ -92,11 +83,7 @@ export function getClockAccAddress(): PublicKey {
92
83
  * @returns Derived cluster account public key.
93
84
  */
94
85
  export function getClusterAccAddress(clusterOffset: number): PublicKey {
95
- const offsetBuffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
96
- offsetBuffer.writeUInt32LE(clusterOffset, 0);
97
- const seeds = [Buffer.from(CLUSTER_ACC_SEED), offsetBuffer];
98
-
99
- return generateArciumPDAFrom(seeds)[0];
86
+ return findArciumPda([stringSeed(CLUSTER_ACC_SEED), u32Seed(clusterOffset)]);
100
87
  }
101
88
 
102
89
  /**
@@ -105,11 +92,7 @@ export function getClusterAccAddress(clusterOffset: number): PublicKey {
105
92
  * @returns Derived ArxNode account public key.
106
93
  */
107
94
  export function getArxNodeAccAddress(nodeOffset: number): PublicKey {
108
- const offsetBuffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
109
- offsetBuffer.writeUInt32LE(nodeOffset, 0);
110
- const seeds = [Buffer.from(ARX_NODE_ACC_SEED), offsetBuffer];
111
-
112
- return generateArciumPDAFrom(seeds)[0];
95
+ return findArciumPda([stringSeed(ARX_NODE_ACC_SEED), u32Seed(nodeOffset)]);
113
96
  }
114
97
 
115
98
  /**
@@ -118,9 +101,7 @@ export function getArxNodeAccAddress(nodeOffset: number): PublicKey {
118
101
  * @returns Derived MXE account public key.
119
102
  */
120
103
  export function getMXEAccAddress(mxeProgramId: PublicKey): PublicKey {
121
- const seeds = [Buffer.from(MXE_ACCOUNT_SEED), mxeProgramId.toBuffer()];
122
-
123
- return generateArciumPDAFrom(seeds)[0];
104
+ return findArciumPda([stringSeed(MXE_ACCOUNT_SEED), pubkeySeed(mxeProgramId)]);
124
105
  }
125
106
 
126
107
  /**
@@ -130,11 +111,7 @@ export function getMXEAccAddress(mxeProgramId: PublicKey): PublicKey {
130
111
  * @returns Derived computation definition account public key.
131
112
  */
132
113
  export function getCompDefAccAddress(mxeProgramId: PublicKey, compDefOffset: number): PublicKey {
133
- const offsetBuffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
134
- offsetBuffer.writeUInt32LE(compDefOffset, 0);
135
- const seeds = [Buffer.from(COMP_DEF_ACC_SEED), mxeProgramId.toBuffer(), offsetBuffer];
136
-
137
- return generateArciumPDAFrom(seeds)[0];
114
+ return findArciumPda([stringSeed(COMP_DEF_ACC_SEED), pubkeySeed(mxeProgramId), u32Seed(compDefOffset)]);
138
115
  }
139
116
 
140
117
  /**
@@ -143,8 +120,7 @@ export function getCompDefAccAddress(mxeProgramId: PublicKey, compDefOffset: num
143
120
  * @returns Derived recovery cluster account public key.
144
121
  */
145
122
  export function getRecoveryClusterAccAddress(mxeProgramId: PublicKey): PublicKey {
146
- const seeds = [Buffer.from(RECOVERY_CLUSTER_ACC_SEED), mxeProgramId.toBuffer()];
147
- return generateArciumPDAFrom(seeds)[0];
123
+ return findArciumPda([stringSeed(RECOVERY_CLUSTER_ACC_SEED), pubkeySeed(mxeProgramId)]);
148
124
  }
149
125
 
150
126
  /**
@@ -153,23 +129,57 @@ export function getRecoveryClusterAccAddress(mxeProgramId: PublicKey): PublicKey
153
129
  * @returns Derived MXE recovery account public key.
154
130
  */
155
131
  export function getMxeRecoveryAccAddress(originalMxeProgramId: PublicKey): PublicKey {
156
- const seeds = [Buffer.from(MXE_RECOVERY_ACC_SEED), originalMxeProgramId.toBuffer()];
157
- return generateArciumPDAFrom(seeds)[0];
132
+ return findArciumPda([stringSeed(MXE_RECOVERY_ACC_SEED), pubkeySeed(originalMxeProgramId)]);
133
+ }
134
+
135
+ /**
136
+ * Derive the recovery peer account address for a given recovery peer offset.
137
+ * @param peerOffset - Recovery peer offset as a u32 in `[0, 2^32 - 1]`.
138
+ * @returns Derived recovery peer account public key.
139
+ */
140
+ export function getRecoveryPeerAccAddress(peerOffset: number): PublicKey {
141
+ return findArciumPda([stringSeed(RECOVERY_PEER_ACC_SEED), u32Seed(peerOffset)]);
142
+ }
143
+
144
+ /**
145
+ * Derive the operator account address for a given operator signer.
146
+ * @param operatorSigner - Public key of the operator signer.
147
+ * @returns Derived operator account public key.
148
+ */
149
+ export function getOperatorAccAddress(operatorSigner: PublicKey): PublicKey {
150
+ return findArciumPda([stringSeed(OPERATOR_ACC_SEED), pubkeySeed(operatorSigner)]);
151
+ }
152
+
153
+ /**
154
+ * Derive the failure claim account address for a given MXE program and computation offset.
155
+ * @param mxeProgramId - Public key of the MXE program.
156
+ * @param computationOffset - Computation offset as an anchor.BN in `[0, 2^64 - 1]`.
157
+ * @returns Derived failure claim account public key.
158
+ */
159
+ export function getFailureClaimAccAddress(mxeProgramId: PublicKey, computationOffset: anchor.BN): PublicKey {
160
+ return findArciumPda([stringSeed(FAILURE_CLAIM_ACC_SEED), pubkeySeed(mxeProgramId), u64Seed(computationOffset)]);
161
+ }
162
+
163
+ /**
164
+ * Derive the Arcium signer account address used by an MXE program when invoking the Arcium
165
+ * program. Unlike most other helpers in this module, this PDA is derived under the MXE
166
+ * program ID (the caller), not the Arcium program ID - Arcium verifies the seed via
167
+ * `seeds::program = _mxe_program`.
168
+ * @param mxeProgramId - Public key of the MXE program that owns the signer PDA.
169
+ * @returns Derived Arcium signer account public key.
170
+ */
171
+ export function getArciumSignerAccAddress(mxeProgramId: PublicKey): PublicKey {
172
+ return findPda(mxeProgramId, [stringSeed(ARCIUM_SIGNER_ACC_SEED)]);
158
173
  }
159
174
 
160
175
  /**
161
176
  * Derive the raw circuit account address for a given computation definition and index.
162
177
  * @param compDefPubkey - Public key of the computation definition account.
163
- * @param rawCircuitIndex - Index of the raw circuit account (0-based).
178
+ * @param rawCircuitIndex - Index of the raw circuit account as a u8 in `[0, 255]`.
164
179
  * @returns Derived raw circuit account public key.
165
180
  */
166
181
  export function getRawCircuitAccAddress(compDefPubkey: PublicKey, rawCircuitIndex: number): PublicKey {
167
- const seeds = [
168
- Buffer.from(RAW_CIRCUIT_ACC_SEED),
169
- compDefPubkey.toBuffer(),
170
- Buffer.from([rawCircuitIndex]),
171
- ];
172
- return generateArciumPDAFrom(seeds)[0];
182
+ return findArciumPda([stringSeed(RAW_CIRCUIT_ACC_SEED), pubkeySeed(compDefPubkey), u8Seed(rawCircuitIndex)]);
173
183
  }
174
184
 
175
185
  /**
@@ -180,19 +190,48 @@ export function getRawCircuitAccAddress(compDefPubkey: PublicKey, rawCircuitInde
180
190
  */
181
191
  export function getLookupTableAddress(mxeProgramId: PublicKey, lutOffset: anchor.BN): PublicKey {
182
192
  const mxeAccount = getMXEAccAddress(mxeProgramId);
183
- const lutIndexBuffer = lutOffset.toArrayLike(Buffer, 'le', 8);
184
- const seeds = [mxeAccount.toBuffer(), lutIndexBuffer];
185
- return PublicKey.findProgramAddressSync(seeds, AddressLookupTableProgram.programId)[0];
193
+ return findPda(AddressLookupTableProgram.programId, [pubkeySeed(mxeAccount), u64Seed(lutOffset)]);
186
194
  }
187
195
 
188
- /**
189
- * Generate a program-derived address (PDA) from the provided seeds and the Arcium program ID.
190
- * @param seeds - Array of Buffer seeds used for PDA derivation.
191
- * @returns Tuple containing the derived public key and the bump seed.
192
- */
193
- function generateArciumPDAFrom(
194
- seeds: Buffer[],
195
- ): [PublicKey, number] {
196
- const programId = getArciumProgramId();
197
- return PublicKey.findProgramAddressSync(seeds, programId);
196
+ function stringSeed(value: string): Buffer {
197
+ return Buffer.from(value);
198
+ }
199
+
200
+ function u8Seed(value: number): Buffer {
201
+ assertIntegerInRange(value, 0, MAX_U8, 'u8 seed');
202
+ return Buffer.from([value]);
203
+ }
204
+
205
+ function u32Seed(value: number): Buffer {
206
+ assertIntegerInRange(value, 0, MAX_U32, 'u32 seed');
207
+ const buffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
208
+ buffer.writeUInt32LE(value, 0);
209
+ return buffer;
210
+ }
211
+
212
+ function u64Seed(value: anchor.BN): Buffer {
213
+ if (value.isNeg() || value.byteLength() > U64_SEED_SIZE) {
214
+ throw new RangeError(
215
+ `u64 seed must be between 0 and 18446744073709551615 (got ${value.toString()})`,
216
+ );
217
+ }
218
+ return value.toArrayLike(Buffer, 'le', U64_SEED_SIZE);
219
+ }
220
+
221
+ function pubkeySeed(value: PublicKey): Buffer {
222
+ return value.toBuffer();
223
+ }
224
+
225
+ function findArciumPda(seeds: Buffer[]): PublicKey {
226
+ return findPda(getArciumProgramId(), seeds);
227
+ }
228
+
229
+ function findPda(programId: PublicKey, seeds: Buffer[]): PublicKey {
230
+ return PublicKey.findProgramAddressSync(seeds, programId)[0];
231
+ }
232
+
233
+ function assertIntegerInRange(value: number, min: number, max: number, label: string): void {
234
+ if (!Number.isInteger(value) || value < min || value > max) {
235
+ throw new RangeError(`${label} must be an integer between ${min} and ${max} (got ${value})`);
236
+ }
198
237
  }
@@ -0,0 +1,41 @@
1
+ import {
2
+ MAX_RAW_CIRCUIT_ACCOUNTS,
3
+ MAX_RAW_CIRCUIT_BYTES_PER_ACC,
4
+ } from './constants.js';
5
+
6
+ /** @internal */
7
+ export function getRawCircuitAccountCount(rawCircuitByteLength: number): number {
8
+ if (!Number.isInteger(rawCircuitByteLength) || rawCircuitByteLength < 0) {
9
+ throw new RangeError(`rawCircuit byte length must be a non-negative integer (got ${rawCircuitByteLength})`);
10
+ }
11
+
12
+ const accountCount = Math.ceil(rawCircuitByteLength / MAX_RAW_CIRCUIT_BYTES_PER_ACC);
13
+ if (accountCount > MAX_RAW_CIRCUIT_ACCOUNTS) {
14
+ throw new RangeError(
15
+ `Raw circuit is too large for on-chain upload: requires ${accountCount} raw circuit accounts, maximum is ${MAX_RAW_CIRCUIT_ACCOUNTS}`,
16
+ );
17
+ }
18
+
19
+ return accountCount;
20
+ }
21
+
22
+ /** @internal */
23
+ export function assertRawCircuitUploadLength(
24
+ rawCircuitByteLength: number,
25
+ declaredCircuitByteLength: number,
26
+ circuitName: string,
27
+ ): void {
28
+ if (!Number.isInteger(rawCircuitByteLength) || rawCircuitByteLength < 0) {
29
+ throw new RangeError(`rawCircuit byte length must be a non-negative integer (got ${rawCircuitByteLength})`);
30
+ }
31
+ if (!Number.isInteger(declaredCircuitByteLength) || declaredCircuitByteLength <= 0) {
32
+ throw new RangeError(
33
+ `Declared circuit byte length for ${circuitName} must be a positive integer (got ${declaredCircuitByteLength})`,
34
+ );
35
+ }
36
+ if (rawCircuitByteLength !== declaredCircuitByteLength) {
37
+ throw new RangeError(
38
+ `Raw circuit byte length for ${circuitName} must match the computation definition: got ${rawCircuitByteLength}, expected ${declaredCircuitByteLength}`,
39
+ );
40
+ }
41
+ }
@@ -0,0 +1,46 @@
1
+ import { MAX_RECOVERY_PEERS, MIN_RECOVERY_PEERS } from './constants.js';
2
+
3
+ const MAX_U32 = 0xffffffff;
4
+
5
+ /**
6
+ * Mirrors the on-chain `init_mxe_part2` recovery-peer bounds before building the instruction.
7
+ * @internal
8
+ */
9
+ export function assertValidRecoveryPeers(recoveryPeers: readonly number[]): void {
10
+ if (recoveryPeers.length > MAX_RECOVERY_PEERS) {
11
+ throw new Error(
12
+ `recoveryPeers must have at most ${MAX_RECOVERY_PEERS} elements (got ${recoveryPeers.length})`,
13
+ );
14
+ }
15
+
16
+ let nonZeroPeers = 0;
17
+ for (let index = 0; index < recoveryPeers.length; index += 1) {
18
+ const peer = recoveryPeers[index];
19
+ if (!Number.isInteger(peer) || peer < 0 || peer > MAX_U32) {
20
+ throw new RangeError(
21
+ `recoveryPeers[${index}] must be an integer in [0, ${MAX_U32}] (got ${peer})`,
22
+ );
23
+ }
24
+ if (peer !== 0) {
25
+ nonZeroPeers += 1;
26
+ }
27
+ }
28
+
29
+ if (nonZeroPeers > 0 && nonZeroPeers < MIN_RECOVERY_PEERS) {
30
+ throw new Error(
31
+ `recoveryPeers must have 0 or at least ${MIN_RECOVERY_PEERS} non-zero offsets (found ${nonZeroPeers})`,
32
+ );
33
+ }
34
+ }
35
+
36
+ /** @internal */
37
+ export function normalizeRecoveryPeers(recoveryPeers: readonly number[]): number[] {
38
+ assertValidRecoveryPeers(recoveryPeers);
39
+
40
+ const paddedRecoveryPeers = [...recoveryPeers];
41
+ while (paddedRecoveryPeers.length < MAX_RECOVERY_PEERS) {
42
+ paddedRecoveryPeers.push(0);
43
+ }
44
+
45
+ return paddedRecoveryPeers;
46
+ }