@frontiercompute/zcash-ika 0.1.0 → 0.3.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/README.md +126 -92
- package/dist/hybrid.d.ts +119 -0
- package/dist/hybrid.js +148 -0
- package/dist/index.d.ts +117 -65
- package/dist/index.js +671 -88
- package/dist/tx-builder.d.ts +67 -0
- package/dist/tx-builder.js +534 -0
- package/package.json +32 -4
- package/dist/test-dkg.d.ts +0 -17
- package/dist/test-dkg.js +0 -150
- package/src/index.ts +0 -338
- package/src/test-dkg.ts +0 -199
- package/tsconfig.json +0 -13
package/dist/test-dkg.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/**
|
|
3
|
-
* Ika DKG test - create dWallets on testnet.
|
|
4
|
-
*
|
|
5
|
-
* Creates:
|
|
6
|
-
* 1. Ed25519 dWallet (Zcash Orchard shielded)
|
|
7
|
-
* 2. secp256k1 dWallet (Bitcoin + USDC + USDT + any EVM)
|
|
8
|
-
*
|
|
9
|
-
* One operator, split-key custody across all chains.
|
|
10
|
-
* Swiss bank in your pocket. Jailbroken but legal tender.
|
|
11
|
-
*
|
|
12
|
-
* Requires: SUI_PRIVATE_KEY env var (base64 Sui keypair)
|
|
13
|
-
* Get testnet SUI: https://faucet.sui.io
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* SUI_PRIVATE_KEY=... node dist/test-dkg.js
|
|
17
|
-
*/
|
|
18
|
-
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
19
|
-
import { Transaction } from "@mysten/sui/transactions";
|
|
20
|
-
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
|
|
21
|
-
import { IkaClient, IkaTransaction, UserShareEncryptionKeys, getNetworkConfig, Curve, prepareDKGAsync, createRandomSessionIdentifier, CHAIN_PARAMS, } from "./index.js";
|
|
22
|
-
const NETWORK = "testnet";
|
|
23
|
-
async function createDWallet(ikaClient, suiClient, keypair, encKeys, chain, address) {
|
|
24
|
-
const params = CHAIN_PARAMS[chain];
|
|
25
|
-
console.log(`\n--- ${params.description} ---`);
|
|
26
|
-
console.log(`Params: ${params.curve}/${params.algorithm}/${params.hash}`);
|
|
27
|
-
// Prepare DKG (async fetches protocol public params from network)
|
|
28
|
-
const bytesToHash = createRandomSessionIdentifier();
|
|
29
|
-
const dkgInput = await prepareDKGAsync(ikaClient, Curve[params.curve], encKeys, bytesToHash, address);
|
|
30
|
-
console.log("DKG prepared locally");
|
|
31
|
-
// Build transaction
|
|
32
|
-
const tx = new Transaction();
|
|
33
|
-
const ikaTx = new IkaTransaction({
|
|
34
|
-
ikaClient,
|
|
35
|
-
transaction: tx,
|
|
36
|
-
userShareEncryptionKeys: encKeys,
|
|
37
|
-
});
|
|
38
|
-
const sessionId = ikaTx.registerSessionIdentifier(bytesToHash);
|
|
39
|
-
// Get network encryption key
|
|
40
|
-
const networkEncKey = await ikaClient.getLatestNetworkEncryptionKey?.()
|
|
41
|
-
|| await ikaClient.getConfiguredNetworkEncryptionKey?.();
|
|
42
|
-
if (!networkEncKey) {
|
|
43
|
-
// Try without - some SDK versions auto-detect
|
|
44
|
-
console.log("No explicit encryption key method found, trying auto-detect...");
|
|
45
|
-
}
|
|
46
|
-
// Submit DKG request
|
|
47
|
-
const dkgResult = await ikaTx.requestDWalletDKG({
|
|
48
|
-
dkgRequestInput: dkgInput,
|
|
49
|
-
sessionIdentifier: sessionId,
|
|
50
|
-
dwalletNetworkEncryptionKeyId: networkEncKey?.id,
|
|
51
|
-
curve: Curve[params.curve],
|
|
52
|
-
ikaCoin: tx.splitCoins(tx.gas, [50_000_000]),
|
|
53
|
-
suiCoin: tx.splitCoins(tx.gas, [50_000_000]),
|
|
54
|
-
});
|
|
55
|
-
console.log("Submitting DKG to Ika network...");
|
|
56
|
-
const result = await suiClient.signAndExecuteTransaction({
|
|
57
|
-
transaction: tx,
|
|
58
|
-
signer: keypair,
|
|
59
|
-
options: { showEffects: true, showEvents: true },
|
|
60
|
-
});
|
|
61
|
-
console.log("TX digest:", result.digest);
|
|
62
|
-
if (result.effects?.status?.status !== "success") {
|
|
63
|
-
console.error("TX failed:", result.effects?.status?.error);
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
console.log("DKG submitted. Poll for completion...");
|
|
67
|
-
// Extract dWallet ID from events/created objects
|
|
68
|
-
const created = result.effects?.created || [];
|
|
69
|
-
console.log(`Created ${created.length} objects`);
|
|
70
|
-
for (const obj of created) {
|
|
71
|
-
console.log(` ${obj.reference?.objectId || obj}`);
|
|
72
|
-
}
|
|
73
|
-
return result;
|
|
74
|
-
}
|
|
75
|
-
async function main() {
|
|
76
|
-
const privKeyRaw = process.env.SUI_PRIVATE_KEY;
|
|
77
|
-
if (!privKeyRaw) {
|
|
78
|
-
console.log("zcash-ika DKG test");
|
|
79
|
-
console.log("==================");
|
|
80
|
-
console.log("");
|
|
81
|
-
console.log("Chain params (what Ika signs for each chain):");
|
|
82
|
-
for (const [chain, params] of Object.entries(CHAIN_PARAMS)) {
|
|
83
|
-
console.log(` ${chain}: ${params.curve}/${params.algorithm}/${params.hash}`);
|
|
84
|
-
}
|
|
85
|
-
console.log("");
|
|
86
|
-
console.log("secp256k1/ECDSA/DoubleSHA256 signs for:");
|
|
87
|
-
console.log(" - Bitcoin (BTC)");
|
|
88
|
-
console.log(" - Zcash transparent (t-addr)");
|
|
89
|
-
console.log(" - Ethereum/Base (USDC, USDT) via KECCAK256 variant");
|
|
90
|
-
console.log("");
|
|
91
|
-
console.log("Ed25519/EdDSA/SHA512 signs for:");
|
|
92
|
-
console.log(" - Zcash Orchard (shielded ZEC)");
|
|
93
|
-
console.log("");
|
|
94
|
-
console.log("One operator. Split-key custody. Every chain.");
|
|
95
|
-
console.log("");
|
|
96
|
-
console.log("To run DKG:");
|
|
97
|
-
console.log(" SUI_PRIVATE_KEY=suiprivkey1... node dist/test-dkg.js");
|
|
98
|
-
console.log(" Get testnet SUI: https://faucet.sui.io");
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
// Decode Sui keypair
|
|
102
|
-
const decoded = decodeSuiPrivateKey(privKeyRaw);
|
|
103
|
-
const keypair = Ed25519Keypair.fromSecretKey(decoded.secretKey);
|
|
104
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
105
|
-
console.log(`Sui address: ${address}`);
|
|
106
|
-
// Init clients
|
|
107
|
-
// PublicNode doesn't rate limit like Mysten's public RPC
|
|
108
|
-
const { SuiJsonRpcClient } = await import("@mysten/sui/jsonRpc");
|
|
109
|
-
const suiClient = new SuiJsonRpcClient({ url: "https://sui-testnet-rpc.publicnode.com" });
|
|
110
|
-
const ikaConfig = getNetworkConfig(NETWORK);
|
|
111
|
-
if (!ikaConfig)
|
|
112
|
-
throw new Error("No Ika testnet config");
|
|
113
|
-
const ikaClient = new IkaClient({ suiClient, config: ikaConfig });
|
|
114
|
-
await ikaClient.initialize();
|
|
115
|
-
console.log("Ika client initialized on testnet");
|
|
116
|
-
// Check balance
|
|
117
|
-
const balance = await suiClient.getBalance({ owner: address });
|
|
118
|
-
const suiBalance = Number(balance.totalBalance) / 1e9;
|
|
119
|
-
console.log(`SUI balance: ${suiBalance} SUI`);
|
|
120
|
-
if (suiBalance < 0.2) {
|
|
121
|
-
console.log("Need at least 0.2 SUI for gas (two DKG operations).");
|
|
122
|
-
console.log("Get testnet SUI: https://faucet.sui.io");
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
// Generate encryption keys - one per curve
|
|
126
|
-
const seed = new Uint8Array(32);
|
|
127
|
-
crypto.getRandomValues(seed);
|
|
128
|
-
// Ed25519 encryption keys for shielded ZEC wallet
|
|
129
|
-
const edEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.ED25519);
|
|
130
|
-
console.log("Ed25519 encryption keys generated");
|
|
131
|
-
// secp256k1 encryption keys for BTC/stablecoin wallet
|
|
132
|
-
const secpEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256K1);
|
|
133
|
-
console.log("secp256k1 encryption keys generated");
|
|
134
|
-
// Create Ed25519 dWallet (Zcash Orchard)
|
|
135
|
-
try {
|
|
136
|
-
await createDWallet(ikaClient, suiClient, keypair, edEncKeys, "zcash-shielded", address);
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
console.error("Ed25519 DKG error:", err.message);
|
|
140
|
-
}
|
|
141
|
-
// Create secp256k1 dWallet (Bitcoin + stablecoins)
|
|
142
|
-
try {
|
|
143
|
-
await createDWallet(ikaClient, suiClient, keypair, secpEncKeys, "bitcoin", address);
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
console.error("secp256k1 DKG error:", err.message);
|
|
147
|
-
}
|
|
148
|
-
console.log("\nDone.");
|
|
149
|
-
}
|
|
150
|
-
main().catch(console.error);
|
package/src/index.ts
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @frontiercompute/zcash-ika
|
|
3
|
-
*
|
|
4
|
-
* Zero-trust custody for Zcash and Bitcoin. Born shielded, stay shielded.
|
|
5
|
-
*
|
|
6
|
-
* Two dWallets, one operator:
|
|
7
|
-
* - Ed25519 dWallet -> Zcash Orchard (shielded ZEC)
|
|
8
|
-
* - secp256k1 dWallet -> Bitcoin (BTC) + Zcash transparent (t-addr)
|
|
9
|
-
*
|
|
10
|
-
* Neither key ever exists whole. Both chains signed through Ika 2PC-MPC.
|
|
11
|
-
* Every operation attested to Zcash via ZAP1.
|
|
12
|
-
*
|
|
13
|
-
* Built on Ika's 2PC-MPC network (Sui).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
Curve,
|
|
18
|
-
Hash,
|
|
19
|
-
SignatureAlgorithm,
|
|
20
|
-
IkaClient,
|
|
21
|
-
IkaTransaction,
|
|
22
|
-
UserShareEncryptionKeys,
|
|
23
|
-
getNetworkConfig,
|
|
24
|
-
createClassGroupsKeypair,
|
|
25
|
-
createRandomSessionIdentifier,
|
|
26
|
-
prepareDKG,
|
|
27
|
-
prepareDKGAsync,
|
|
28
|
-
prepareDKGSecondRound,
|
|
29
|
-
prepareDKGSecondRoundAsync,
|
|
30
|
-
createDKGUserOutput,
|
|
31
|
-
publicKeyFromDWalletOutput,
|
|
32
|
-
parseSignatureFromSignOutput,
|
|
33
|
-
} from "@ika.xyz/sdk";
|
|
34
|
-
|
|
35
|
-
// Chain identifiers for wallet creation
|
|
36
|
-
export type Chain = "zcash-shielded" | "zcash-transparent" | "bitcoin";
|
|
37
|
-
|
|
38
|
-
export interface ZcashIkaConfig {
|
|
39
|
-
/** Ika network: mainnet or testnet */
|
|
40
|
-
network: "mainnet" | "testnet";
|
|
41
|
-
/** Sui RPC URL (defaults to Ika's network config) */
|
|
42
|
-
suiRpcUrl?: string;
|
|
43
|
-
/** Zebra node RPC for broadcasting Zcash txs */
|
|
44
|
-
zebraRpcUrl: string;
|
|
45
|
-
/** ZAP1 API for attestation */
|
|
46
|
-
zap1ApiUrl: string;
|
|
47
|
-
/** ZAP1 API key for write operations */
|
|
48
|
-
zap1ApiKey?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Parameters for dWallet creation per chain */
|
|
52
|
-
export const CHAIN_PARAMS = {
|
|
53
|
-
"zcash-shielded": {
|
|
54
|
-
curve: "ED25519" as const,
|
|
55
|
-
algorithm: "EdDSA" as const,
|
|
56
|
-
hash: "SHA512" as const,
|
|
57
|
-
description: "Zcash Orchard shielded pool (Ed25519/EdDSA)",
|
|
58
|
-
},
|
|
59
|
-
"zcash-transparent": {
|
|
60
|
-
curve: "SECP256K1" as const,
|
|
61
|
-
algorithm: "ECDSASecp256k1" as const,
|
|
62
|
-
hash: "DoubleSHA256" as const,
|
|
63
|
-
description: "Zcash transparent t-address (secp256k1/ECDSA)",
|
|
64
|
-
},
|
|
65
|
-
bitcoin: {
|
|
66
|
-
curve: "SECP256K1" as const,
|
|
67
|
-
algorithm: "ECDSASecp256k1" as const,
|
|
68
|
-
hash: "DoubleSHA256" as const,
|
|
69
|
-
description: "Bitcoin (secp256k1/ECDSA, DoubleSHA256)",
|
|
70
|
-
},
|
|
71
|
-
} as const;
|
|
72
|
-
|
|
73
|
-
export interface DWalletHandle {
|
|
74
|
-
/** dWallet object ID on Sui */
|
|
75
|
-
id: string;
|
|
76
|
-
/** Raw public key bytes */
|
|
77
|
-
publicKey: Uint8Array;
|
|
78
|
-
/** Which chain this wallet targets */
|
|
79
|
-
chain: Chain;
|
|
80
|
-
/** Derived address for the target chain */
|
|
81
|
-
address: string;
|
|
82
|
-
/** Ika network (mainnet/testnet) */
|
|
83
|
-
network: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface DualCustody {
|
|
87
|
-
/** Shielded ZEC wallet (Ed25519 dWallet -> Orchard address) */
|
|
88
|
-
shielded: DWalletHandle;
|
|
89
|
-
/** Bitcoin wallet (secp256k1 dWallet -> BTC address) */
|
|
90
|
-
bitcoin: DWalletHandle;
|
|
91
|
-
/** Operator ID (shared across both wallets) */
|
|
92
|
-
operatorId: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface SpendPolicy {
|
|
96
|
-
/** Max zatoshis (or satoshis) per single transaction */
|
|
97
|
-
maxPerTx: number;
|
|
98
|
-
/** Max per 24h window */
|
|
99
|
-
maxDaily: number;
|
|
100
|
-
/** Allowed recipient addresses (empty = any) */
|
|
101
|
-
allowedRecipients: string[];
|
|
102
|
-
/** Require operator approval above this amount */
|
|
103
|
-
approvalThreshold: number;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface SpendRequest {
|
|
107
|
-
/** Recipient address (Orchard UA, t-addr, or BTC address) */
|
|
108
|
-
to: string;
|
|
109
|
-
/** Amount in smallest unit (zatoshis or satoshis) */
|
|
110
|
-
amount: number;
|
|
111
|
-
/** Memo (ZAP1 structured or plain text, Zcash only) */
|
|
112
|
-
memo?: string;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface SpendResult {
|
|
116
|
-
/** Transaction ID on target chain */
|
|
117
|
-
txid: string;
|
|
118
|
-
/** ZAP1 attestation leaf hash */
|
|
119
|
-
leafHash: string;
|
|
120
|
-
/** Chain the spend was on */
|
|
121
|
-
chain: Chain;
|
|
122
|
-
/** Whether policy was checked */
|
|
123
|
-
policyChecked: boolean;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export interface SignRequest {
|
|
127
|
-
/** Raw message hash to sign (sighash) */
|
|
128
|
-
messageHash: Uint8Array;
|
|
129
|
-
/** Which dWallet to sign with */
|
|
130
|
-
walletId: string;
|
|
131
|
-
/** Chain determines signing params */
|
|
132
|
-
chain: Chain;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export interface SignResult {
|
|
136
|
-
/** DER-encoded signature (ECDSA) or raw Ed25519 signature */
|
|
137
|
-
signature: Uint8Array;
|
|
138
|
-
/** Public key used */
|
|
139
|
-
publicKey: Uint8Array;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Create a dual-custody setup: one shielded ZEC wallet + one BTC wallet.
|
|
144
|
-
* Same operator controls both via Ika split-key.
|
|
145
|
-
*
|
|
146
|
-
* Flow per wallet:
|
|
147
|
-
* 1. Generate UserShareEncryptionKeys from operator seed
|
|
148
|
-
* 2. Run DKG on Ika (2PC-MPC key generation)
|
|
149
|
-
* 3. Extract public key, derive chain-specific address
|
|
150
|
-
* 4. Attest wallet creation via ZAP1
|
|
151
|
-
*/
|
|
152
|
-
export async function createDualCustody(
|
|
153
|
-
config: ZcashIkaConfig,
|
|
154
|
-
operatorSeed: Uint8Array
|
|
155
|
-
): Promise<DualCustody> {
|
|
156
|
-
// Both wallets share one operator seed.
|
|
157
|
-
// Each gets its own dWallet with different curve params.
|
|
158
|
-
//
|
|
159
|
-
// Phase 1 (current): throw with setup instructions
|
|
160
|
-
// Phase 2: actual DKG on Ika testnet
|
|
161
|
-
|
|
162
|
-
throw new Error(
|
|
163
|
-
"createDualCustody requires Ika network access. " +
|
|
164
|
-
"Shielded: Ed25519/EdDSA/SHA512 dWallet -> Orchard address. " +
|
|
165
|
-
"Bitcoin: secp256k1/ECDSA/DoubleSHA256 dWallet -> BTC address. " +
|
|
166
|
-
"npm install @ika.xyz/sdk @mysten/sui && configure Sui wallet. " +
|
|
167
|
-
"See https://docs.ika.xyz for DKG walkthrough."
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Create a single dWallet for a specific chain.
|
|
173
|
-
*/
|
|
174
|
-
export async function createWallet(
|
|
175
|
-
config: ZcashIkaConfig,
|
|
176
|
-
chain: Chain,
|
|
177
|
-
operatorSeed: Uint8Array
|
|
178
|
-
): Promise<DWalletHandle> {
|
|
179
|
-
const params = CHAIN_PARAMS[chain];
|
|
180
|
-
|
|
181
|
-
// The DKG flow:
|
|
182
|
-
// 1. IkaClient.init({ network: config.network })
|
|
183
|
-
// 2. UserShareEncryptionKeys from operatorSeed
|
|
184
|
-
// 3. prepareDKG(curve, signatureAlgorithm, hash)
|
|
185
|
-
// 4. Submit DKG round 1 to Ika via IkaTransaction
|
|
186
|
-
// 5. prepareDKGSecondRound with network output
|
|
187
|
-
// 6. Submit round 2 -> get dWallet object ID + public key
|
|
188
|
-
// 7. Derive chain address from public key
|
|
189
|
-
|
|
190
|
-
throw new Error(
|
|
191
|
-
`createWallet(${chain}) requires Ika ${config.network} access. ` +
|
|
192
|
-
`Params: ${params.curve}/${params.algorithm}/${params.hash}. ` +
|
|
193
|
-
`${params.description}.`
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Sign a message hash through Ika 2PC-MPC.
|
|
199
|
-
*
|
|
200
|
-
* The operator provides their seed, Ika provides the network share.
|
|
201
|
-
* Neither party ever sees the full private key.
|
|
202
|
-
*
|
|
203
|
-
* Flow:
|
|
204
|
-
* 1. Create presign session on Ika
|
|
205
|
-
* 2. Compute partial user signature locally
|
|
206
|
-
* 3. Submit to Ika coordinator
|
|
207
|
-
* 4. Poll for completion
|
|
208
|
-
* 5. Extract full signature from sign output
|
|
209
|
-
*/
|
|
210
|
-
export async function sign(
|
|
211
|
-
config: ZcashIkaConfig,
|
|
212
|
-
operatorSeed: Uint8Array,
|
|
213
|
-
request: SignRequest
|
|
214
|
-
): Promise<SignResult> {
|
|
215
|
-
const params = CHAIN_PARAMS[request.chain];
|
|
216
|
-
|
|
217
|
-
// The sign flow:
|
|
218
|
-
// 1. IkaClient.init({ network: config.network })
|
|
219
|
-
// 2. RequestGlobalPresign for the dWallet
|
|
220
|
-
// 3. createUserSignMessageWithCentralizedOutput(messageHash, userShare, ...)
|
|
221
|
-
// 4. ApproveMessage on Sui (this is where Move policy gates)
|
|
222
|
-
// 5. RequestSign -> poll SessionsManager for Completed status
|
|
223
|
-
// 6. parseSignatureFromSignOutput(signOutput)
|
|
224
|
-
|
|
225
|
-
throw new Error(
|
|
226
|
-
`sign requires active dWallet + Ika ${config.network}. ` +
|
|
227
|
-
`Chain: ${request.chain}, params: ${params.curve}/${params.algorithm}/${params.hash}.`
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Set spending policy on the dWallet.
|
|
233
|
-
* Policy enforced at Sui Move contract level.
|
|
234
|
-
* The agent cannot bypass it - the contract holds the DWalletCap.
|
|
235
|
-
*/
|
|
236
|
-
export async function setPolicy(
|
|
237
|
-
config: ZcashIkaConfig,
|
|
238
|
-
walletId: string,
|
|
239
|
-
policy: SpendPolicy
|
|
240
|
-
): Promise<string> {
|
|
241
|
-
throw new Error(
|
|
242
|
-
"setPolicy requires a deployed Move module on Sui. " +
|
|
243
|
-
"The module gates approve_message() with spending constraints. " +
|
|
244
|
-
"See docs/move-policy-template.move for the template."
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Spend from a shielded ZEC wallet.
|
|
250
|
-
*
|
|
251
|
-
* 1. Build Zcash Orchard transaction (zcash_primitives)
|
|
252
|
-
* 2. Extract sighash
|
|
253
|
-
* 3. Sign via Ika 2PC-MPC (Ed25519/EdDSA)
|
|
254
|
-
* 4. Attach signature to transaction
|
|
255
|
-
* 5. Broadcast via Zebra sendrawtransaction
|
|
256
|
-
* 6. Attest via ZAP1 as AGENT_ACTION
|
|
257
|
-
*/
|
|
258
|
-
export async function spendShielded(
|
|
259
|
-
config: ZcashIkaConfig,
|
|
260
|
-
walletId: string,
|
|
261
|
-
operatorSeed: Uint8Array,
|
|
262
|
-
request: SpendRequest
|
|
263
|
-
): Promise<SpendResult> {
|
|
264
|
-
throw new Error(
|
|
265
|
-
"spendShielded requires active Ed25519 dWallet + Zebra node. " +
|
|
266
|
-
"Integration in progress - need Ed25519 -> Orchard spending key derivation bridge."
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Spend from a Bitcoin wallet.
|
|
272
|
-
*
|
|
273
|
-
* 1. Build Bitcoin transaction
|
|
274
|
-
* 2. Compute sighash (DoubleSHA256)
|
|
275
|
-
* 3. Sign via Ika 2PC-MPC (secp256k1/ECDSA)
|
|
276
|
-
* 4. Attach signature
|
|
277
|
-
* 5. Broadcast to Bitcoin network
|
|
278
|
-
* 6. Attest via ZAP1 as AGENT_ACTION
|
|
279
|
-
*/
|
|
280
|
-
export async function spendBitcoin(
|
|
281
|
-
config: ZcashIkaConfig,
|
|
282
|
-
walletId: string,
|
|
283
|
-
operatorSeed: Uint8Array,
|
|
284
|
-
request: SpendRequest
|
|
285
|
-
): Promise<SpendResult> {
|
|
286
|
-
throw new Error(
|
|
287
|
-
"spendBitcoin requires active secp256k1 dWallet + Bitcoin node. " +
|
|
288
|
-
"Same MPC flow as Zcash transparent - DoubleSHA256 sighash, ECDSA signature."
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Verify the wallet's attestation history via ZAP1.
|
|
294
|
-
* Works today against the live API.
|
|
295
|
-
*/
|
|
296
|
-
export async function getHistory(
|
|
297
|
-
config: ZcashIkaConfig,
|
|
298
|
-
walletId: string
|
|
299
|
-
): Promise<{ leafHash: string; eventType: string; timestamp: string }[]> {
|
|
300
|
-
const resp = await fetch(`${config.zap1ApiUrl}/lifecycle/${walletId}`);
|
|
301
|
-
if (!resp.ok) return [];
|
|
302
|
-
const data = (await resp.json()) as {
|
|
303
|
-
leaves?: { leaf_hash: string; event_type: string; created_at: string }[];
|
|
304
|
-
};
|
|
305
|
-
return (data.leaves || []).map((l) => ({
|
|
306
|
-
leafHash: l.leaf_hash,
|
|
307
|
-
eventType: l.event_type,
|
|
308
|
-
timestamp: l.created_at,
|
|
309
|
-
}));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Check wallet's policy compliance status via ZAP1.
|
|
314
|
-
* Works today against the live API.
|
|
315
|
-
*/
|
|
316
|
-
export async function checkCompliance(
|
|
317
|
-
config: ZcashIkaConfig,
|
|
318
|
-
walletId: string
|
|
319
|
-
): Promise<{
|
|
320
|
-
compliant: boolean;
|
|
321
|
-
violations: number;
|
|
322
|
-
bondDeposits: number;
|
|
323
|
-
}> {
|
|
324
|
-
const resp = await fetch(
|
|
325
|
-
`${config.zap1ApiUrl}/agent/${walletId}/policy/verify`
|
|
326
|
-
);
|
|
327
|
-
if (!resp.ok) return { compliant: false, violations: -1, bondDeposits: 0 };
|
|
328
|
-
const data = (await resp.json()) as {
|
|
329
|
-
compliant: boolean;
|
|
330
|
-
violations: number;
|
|
331
|
-
bond_deposits?: number;
|
|
332
|
-
};
|
|
333
|
-
return {
|
|
334
|
-
compliant: data.compliant,
|
|
335
|
-
violations: data.violations,
|
|
336
|
-
bondDeposits: data.bond_deposits || 0,
|
|
337
|
-
};
|
|
338
|
-
}
|
package/src/test-dkg.ts
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/**
|
|
3
|
-
* Ika DKG test - create dWallets on testnet.
|
|
4
|
-
*
|
|
5
|
-
* Creates:
|
|
6
|
-
* 1. Ed25519 dWallet (Zcash Orchard shielded)
|
|
7
|
-
* 2. secp256k1 dWallet (Bitcoin + USDC + USDT + any EVM)
|
|
8
|
-
*
|
|
9
|
-
* One operator, split-key custody across all chains.
|
|
10
|
-
* Swiss bank in your pocket. Jailbroken but legal tender.
|
|
11
|
-
*
|
|
12
|
-
* Requires: SUI_PRIVATE_KEY env var (base64 Sui keypair)
|
|
13
|
-
* Get testnet SUI: https://faucet.sui.io
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* SUI_PRIVATE_KEY=... node dist/test-dkg.js
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { CoreClient } from "@mysten/sui/client";
|
|
20
|
-
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
21
|
-
import { Transaction } from "@mysten/sui/transactions";
|
|
22
|
-
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
|
|
23
|
-
import {
|
|
24
|
-
IkaClient,
|
|
25
|
-
IkaTransaction,
|
|
26
|
-
UserShareEncryptionKeys,
|
|
27
|
-
getNetworkConfig,
|
|
28
|
-
Curve,
|
|
29
|
-
prepareDKGAsync,
|
|
30
|
-
publicKeyFromDWalletOutput,
|
|
31
|
-
createRandomSessionIdentifier,
|
|
32
|
-
CHAIN_PARAMS,
|
|
33
|
-
type Chain,
|
|
34
|
-
} from "./index.js";
|
|
35
|
-
|
|
36
|
-
const NETWORK = "testnet";
|
|
37
|
-
|
|
38
|
-
async function createDWallet(
|
|
39
|
-
ikaClient: IkaClient,
|
|
40
|
-
suiClient: CoreClient,
|
|
41
|
-
keypair: Ed25519Keypair,
|
|
42
|
-
encKeys: UserShareEncryptionKeys,
|
|
43
|
-
chain: Chain,
|
|
44
|
-
address: string,
|
|
45
|
-
) {
|
|
46
|
-
const params = CHAIN_PARAMS[chain];
|
|
47
|
-
console.log(`\n--- ${params.description} ---`);
|
|
48
|
-
console.log(`Params: ${params.curve}/${params.algorithm}/${params.hash}`);
|
|
49
|
-
|
|
50
|
-
// Prepare DKG (async fetches protocol public params from network)
|
|
51
|
-
const bytesToHash = createRandomSessionIdentifier();
|
|
52
|
-
const dkgInput = await prepareDKGAsync(
|
|
53
|
-
ikaClient,
|
|
54
|
-
Curve[params.curve],
|
|
55
|
-
encKeys,
|
|
56
|
-
bytesToHash,
|
|
57
|
-
address,
|
|
58
|
-
);
|
|
59
|
-
console.log("DKG prepared locally");
|
|
60
|
-
|
|
61
|
-
// Build transaction
|
|
62
|
-
const tx = new Transaction();
|
|
63
|
-
const ikaTx = new IkaTransaction({
|
|
64
|
-
ikaClient,
|
|
65
|
-
transaction: tx,
|
|
66
|
-
userShareEncryptionKeys: encKeys,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const sessionId = ikaTx.registerSessionIdentifier(bytesToHash);
|
|
70
|
-
|
|
71
|
-
// Get network encryption key
|
|
72
|
-
const networkEncKey = await (ikaClient as any).getLatestNetworkEncryptionKey?.()
|
|
73
|
-
|| await (ikaClient as any).getConfiguredNetworkEncryptionKey?.();
|
|
74
|
-
|
|
75
|
-
if (!networkEncKey) {
|
|
76
|
-
// Try without - some SDK versions auto-detect
|
|
77
|
-
console.log("No explicit encryption key method found, trying auto-detect...");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Submit DKG request
|
|
81
|
-
const dkgResult = await (ikaTx as any).requestDWalletDKG({
|
|
82
|
-
dkgRequestInput: dkgInput,
|
|
83
|
-
sessionIdentifier: sessionId,
|
|
84
|
-
dwalletNetworkEncryptionKeyId: networkEncKey?.id,
|
|
85
|
-
curve: Curve[params.curve],
|
|
86
|
-
ikaCoin: tx.splitCoins(tx.gas, [50_000_000]),
|
|
87
|
-
suiCoin: tx.splitCoins(tx.gas, [50_000_000]),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
console.log("Submitting DKG to Ika network...");
|
|
91
|
-
const result = await suiClient.signAndExecuteTransaction({
|
|
92
|
-
transaction: tx,
|
|
93
|
-
signer: keypair,
|
|
94
|
-
options: { showEffects: true, showEvents: true },
|
|
95
|
-
});
|
|
96
|
-
console.log("TX digest:", result.digest);
|
|
97
|
-
|
|
98
|
-
if (result.effects?.status?.status !== "success") {
|
|
99
|
-
console.error("TX failed:", result.effects?.status?.error);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
console.log("DKG submitted. Poll for completion...");
|
|
104
|
-
|
|
105
|
-
// Extract dWallet ID from events/created objects
|
|
106
|
-
const created = result.effects?.created || [];
|
|
107
|
-
console.log(`Created ${created.length} objects`);
|
|
108
|
-
for (const obj of created) {
|
|
109
|
-
console.log(` ${(obj.reference as any)?.objectId || obj}`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function main() {
|
|
116
|
-
const privKeyRaw = process.env.SUI_PRIVATE_KEY;
|
|
117
|
-
if (!privKeyRaw) {
|
|
118
|
-
console.log("zcash-ika DKG test");
|
|
119
|
-
console.log("==================");
|
|
120
|
-
console.log("");
|
|
121
|
-
console.log("Chain params (what Ika signs for each chain):");
|
|
122
|
-
for (const [chain, params] of Object.entries(CHAIN_PARAMS)) {
|
|
123
|
-
console.log(` ${chain}: ${params.curve}/${params.algorithm}/${params.hash}`);
|
|
124
|
-
}
|
|
125
|
-
console.log("");
|
|
126
|
-
console.log("secp256k1/ECDSA/DoubleSHA256 signs for:");
|
|
127
|
-
console.log(" - Bitcoin (BTC)");
|
|
128
|
-
console.log(" - Zcash transparent (t-addr)");
|
|
129
|
-
console.log(" - Ethereum/Base (USDC, USDT) via KECCAK256 variant");
|
|
130
|
-
console.log("");
|
|
131
|
-
console.log("Ed25519/EdDSA/SHA512 signs for:");
|
|
132
|
-
console.log(" - Zcash Orchard (shielded ZEC)");
|
|
133
|
-
console.log("");
|
|
134
|
-
console.log("One operator. Split-key custody. Every chain.");
|
|
135
|
-
console.log("");
|
|
136
|
-
console.log("To run DKG:");
|
|
137
|
-
console.log(" SUI_PRIVATE_KEY=suiprivkey1... node dist/test-dkg.js");
|
|
138
|
-
console.log(" Get testnet SUI: https://faucet.sui.io");
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Decode Sui keypair
|
|
143
|
-
const decoded = decodeSuiPrivateKey(privKeyRaw);
|
|
144
|
-
const keypair = Ed25519Keypair.fromSecretKey(decoded.secretKey);
|
|
145
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
146
|
-
console.log(`Sui address: ${address}`);
|
|
147
|
-
|
|
148
|
-
// Init clients
|
|
149
|
-
// PublicNode doesn't rate limit like Mysten's public RPC
|
|
150
|
-
const { SuiJsonRpcClient } = await import("@mysten/sui/jsonRpc");
|
|
151
|
-
const suiClient = new SuiJsonRpcClient({ url: "https://sui-testnet-rpc.publicnode.com" });
|
|
152
|
-
const ikaConfig = getNetworkConfig(NETWORK);
|
|
153
|
-
if (!ikaConfig) throw new Error("No Ika testnet config");
|
|
154
|
-
|
|
155
|
-
const ikaClient = new IkaClient({ suiClient, config: ikaConfig });
|
|
156
|
-
await ikaClient.initialize();
|
|
157
|
-
console.log("Ika client initialized on testnet");
|
|
158
|
-
|
|
159
|
-
// Check balance
|
|
160
|
-
const balance = await suiClient.getBalance({ owner: address });
|
|
161
|
-
const suiBalance = Number(balance.totalBalance) / 1e9;
|
|
162
|
-
console.log(`SUI balance: ${suiBalance} SUI`);
|
|
163
|
-
|
|
164
|
-
if (suiBalance < 0.2) {
|
|
165
|
-
console.log("Need at least 0.2 SUI for gas (two DKG operations).");
|
|
166
|
-
console.log("Get testnet SUI: https://faucet.sui.io");
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Generate encryption keys - one per curve
|
|
171
|
-
const seed = new Uint8Array(32);
|
|
172
|
-
crypto.getRandomValues(seed);
|
|
173
|
-
|
|
174
|
-
// Ed25519 encryption keys for shielded ZEC wallet
|
|
175
|
-
const edEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.ED25519);
|
|
176
|
-
console.log("Ed25519 encryption keys generated");
|
|
177
|
-
|
|
178
|
-
// secp256k1 encryption keys for BTC/stablecoin wallet
|
|
179
|
-
const secpEncKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256K1);
|
|
180
|
-
console.log("secp256k1 encryption keys generated");
|
|
181
|
-
|
|
182
|
-
// Create Ed25519 dWallet (Zcash Orchard)
|
|
183
|
-
try {
|
|
184
|
-
await createDWallet(ikaClient, suiClient, keypair, edEncKeys, "zcash-shielded", address);
|
|
185
|
-
} catch (err) {
|
|
186
|
-
console.error("Ed25519 DKG error:", (err as Error).message);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Create secp256k1 dWallet (Bitcoin + stablecoins)
|
|
190
|
-
try {
|
|
191
|
-
await createDWallet(ikaClient, suiClient, keypair, secpEncKeys, "bitcoin", address);
|
|
192
|
-
} catch (err) {
|
|
193
|
-
console.error("secp256k1 DKG error:", (err as Error).message);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
console.log("\nDone.");
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
main().catch(console.error);
|
package/tsconfig.json
DELETED