@orbinum/sdk 0.11.0 → 0.12.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/index.d.mts +178 -52
- package/dist/index.d.ts +178 -52
- package/dist/index.js +187 -59
- package/dist/index.mjs +191 -62
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -26,8 +26,10 @@ __export(index_exports, {
|
|
|
26
26
|
BABYJUB_SUBORDER: () => BABYJUB_SUBORDER,
|
|
27
27
|
BN254_R: () => BN254_R,
|
|
28
28
|
Blake2256: () => import_substrate_bindings3.Blake2256,
|
|
29
|
+
CURRENT_CIRCUIT_VERSION: () => CURRENT_CIRCUIT_VERSION,
|
|
29
30
|
CircuitId: () => CircuitId,
|
|
30
|
-
CircuitType: () =>
|
|
31
|
+
CircuitType: () => import_proof_generator2.CircuitType,
|
|
32
|
+
CircuitVersionResolver: () => CircuitVersionResolver,
|
|
31
33
|
CryptoPrecompiles: () => CryptoPrecompiles,
|
|
32
34
|
ENCRYPTED_MEMO_SIZE: () => ENCRYPTED_MEMO_SIZE,
|
|
33
35
|
EncryptedMemo: () => EncryptedMemo,
|
|
@@ -50,7 +52,7 @@ __export(index_exports, {
|
|
|
50
52
|
Storage: () => import_substrate_bindings3.Storage,
|
|
51
53
|
SubstrateClient: () => SubstrateClient,
|
|
52
54
|
VaultLockedError: () => VaultLockedError,
|
|
53
|
-
WebArtifactProvider: () =>
|
|
55
|
+
WebArtifactProvider: () => import_proof_generator2.WebArtifactProvider,
|
|
54
56
|
ZkVerifierModule: () => ZkVerifierModule,
|
|
55
57
|
accountIdHexToSs58: () => accountIdHexToSs58,
|
|
56
58
|
addressToAccountIdHex: () => addressToAccountIdHex,
|
|
@@ -1228,7 +1230,7 @@ var IndexerClient = class _IndexerClient {
|
|
|
1228
1230
|
/**
|
|
1229
1231
|
* Returns a paginated list of stealth scan hints ordered ascending by leafIndex.
|
|
1230
1232
|
* Each hint contains only the fields required for ECDH triage and decryption:
|
|
1231
|
-
* leafIndex, commitmentHex, assetId, ephPkHex, encryptedMemo.
|
|
1233
|
+
* leafIndex, commitmentHex, assetId, ephPkHex, encryptedMemo, circuitVersion.
|
|
1232
1234
|
*
|
|
1233
1235
|
* Use `sinceLeafIndex` for incremental scans (cursor = last seen leafIndex + 1).
|
|
1234
1236
|
*/
|
|
@@ -1648,8 +1650,8 @@ var BABYJUB_SUBORDER = 273603035897990940278080071815715938607681397215856725920
|
|
|
1648
1650
|
// src/shielded-pool/protocol/memo.ts
|
|
1649
1651
|
var import_sha2 = require("@noble/hashes/sha2.js");
|
|
1650
1652
|
var KEY_DOMAIN = new TextEncoder().encode("orbinum-note-encryption-v1");
|
|
1651
|
-
var MEMO_PLAINTEXT_SIZE =
|
|
1652
|
-
function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk) {
|
|
1653
|
+
var MEMO_PLAINTEXT_SIZE = 120;
|
|
1654
|
+
function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk, circuitVersion) {
|
|
1653
1655
|
const buf = new Uint8Array(MEMO_PLAINTEXT_SIZE);
|
|
1654
1656
|
const view = new DataView(buf.buffer);
|
|
1655
1657
|
view.setBigUint64(0, value & 0xffffffffffffffffn, true);
|
|
@@ -1658,6 +1660,7 @@ function serializeMemo(value, ownerPk, blinding, assetId, counterpartyPk) {
|
|
|
1658
1660
|
buf.set(blinding.slice(0, 32), 48);
|
|
1659
1661
|
view.setUint32(80, assetId >>> 0, true);
|
|
1660
1662
|
buf.set(counterpartyPk.slice(0, 32), 84);
|
|
1663
|
+
view.setUint32(116, circuitVersion >>> 0, true);
|
|
1661
1664
|
return buf;
|
|
1662
1665
|
}
|
|
1663
1666
|
function deriveEncryptionKey(sharedSecret, commitment) {
|
|
@@ -1670,7 +1673,7 @@ function deriveEncryptionKey(sharedSecret, commitment) {
|
|
|
1670
1673
|
|
|
1671
1674
|
// src/shielded-pool/protocol/EncryptedMemo.ts
|
|
1672
1675
|
var NONCE_SIZE = 12;
|
|
1673
|
-
var CIPHERTEXT_SIZE =
|
|
1676
|
+
var CIPHERTEXT_SIZE = 136;
|
|
1674
1677
|
var EPH_PK_SIZE = 32;
|
|
1675
1678
|
var ENCRYPTED_MEMO_SIZE = NONCE_SIZE + CIPHERTEXT_SIZE + EPH_PK_SIZE;
|
|
1676
1679
|
function bytesToBjjScalar(bytes) {
|
|
@@ -1689,14 +1692,15 @@ function parsePlaintext(nonce, ciphertextWithMac, encKey) {
|
|
|
1689
1692
|
const blinding = bytesToBigintLE(plaintext.slice(48, 80));
|
|
1690
1693
|
const assetId = BigInt(view.getUint32(80, true));
|
|
1691
1694
|
const counterpartyPk = bytesToBigintLE(plaintext.slice(84, 116));
|
|
1692
|
-
|
|
1695
|
+
const circuitVersion = view.getUint32(116, true);
|
|
1696
|
+
return { value, ownerPk, blinding, assetId, counterpartyPk, circuitVersion };
|
|
1693
1697
|
} catch {
|
|
1694
1698
|
return null;
|
|
1695
1699
|
}
|
|
1696
1700
|
}
|
|
1697
1701
|
var EncryptedMemo = {
|
|
1698
1702
|
/**
|
|
1699
|
-
* Build and encrypt a memo for a note using ECDH (v2,
|
|
1703
|
+
* Build and encrypt a memo for a note using ECDH (v2, 180 bytes).
|
|
1700
1704
|
*
|
|
1701
1705
|
* @param value Note value in planck.
|
|
1702
1706
|
* @param ownerPk 32-byte owner public key (LE).
|
|
@@ -1708,11 +1712,20 @@ var EncryptedMemo = {
|
|
|
1708
1712
|
* decoded from a privacy address).
|
|
1709
1713
|
* Pass `new Uint8Array(32)` (all zeros) for a publicly-readable memo.
|
|
1710
1714
|
* @param counterpartyPk 32-byte counterparty BJJ Ax. Default: all zeros.
|
|
1711
|
-
* @
|
|
1715
|
+
* @param circuitVersion ZK circuit version the note is spent under. Default: 0.
|
|
1716
|
+
* @param ephSkOverride 32-byte ephemeral secret key (stealth coordination). Optional.
|
|
1717
|
+
* @returns 180-byte encrypted memo: nonce(12) || ciphertext+MAC(136) || ephPk(32).
|
|
1712
1718
|
*/
|
|
1713
|
-
encrypt(value, ownerPk, blinding, assetId, commitment, recipientIvkPacked, counterpartyPk = new Uint8Array(32), ephSkOverride) {
|
|
1719
|
+
encrypt(value, ownerPk, blinding, assetId, commitment, recipientIvkPacked, counterpartyPk = new Uint8Array(32), circuitVersion = 0, ephSkOverride) {
|
|
1714
1720
|
const nonce = (0, import_utils.randomBytes)(NONCE_SIZE);
|
|
1715
|
-
const plaintext = serializeMemo(
|
|
1721
|
+
const plaintext = serializeMemo(
|
|
1722
|
+
value,
|
|
1723
|
+
ownerPk,
|
|
1724
|
+
blinding,
|
|
1725
|
+
assetId,
|
|
1726
|
+
counterpartyPk,
|
|
1727
|
+
circuitVersion
|
|
1728
|
+
);
|
|
1716
1729
|
const isZeroKey = recipientIvkPacked.every((b) => b === 0);
|
|
1717
1730
|
let sharedSecret;
|
|
1718
1731
|
let ephPkPackedBytes;
|
|
@@ -1743,29 +1756,31 @@ var EncryptedMemo = {
|
|
|
1743
1756
|
return result;
|
|
1744
1757
|
},
|
|
1745
1758
|
/**
|
|
1746
|
-
* Returns a
|
|
1759
|
+
* Returns a 180-byte public memo encrypted with a zero viewing key.
|
|
1747
1760
|
* Decryptable by anyone with `decrypt(memo, commitment, new Uint8Array(32))`.
|
|
1748
1761
|
* Convenience alias for `encrypt(..., new Uint8Array(32))`.
|
|
1749
1762
|
*/
|
|
1750
|
-
encryptPublic(value, ownerPk, blinding, assetId, commitment) {
|
|
1763
|
+
encryptPublic(value, ownerPk, blinding, assetId, commitment, circuitVersion = 0) {
|
|
1751
1764
|
return EncryptedMemo.encrypt(
|
|
1752
1765
|
value,
|
|
1753
1766
|
ownerPk,
|
|
1754
1767
|
blinding,
|
|
1755
1768
|
assetId,
|
|
1756
1769
|
commitment,
|
|
1757
|
-
new Uint8Array(32)
|
|
1770
|
+
new Uint8Array(32),
|
|
1771
|
+
new Uint8Array(32),
|
|
1772
|
+
circuitVersion
|
|
1758
1773
|
);
|
|
1759
1774
|
},
|
|
1760
1775
|
/**
|
|
1761
|
-
* Returns a
|
|
1776
|
+
* Returns a 180-byte zeroed dummy memo (no information, always valid on-chain).
|
|
1762
1777
|
*/
|
|
1763
1778
|
dummy() {
|
|
1764
1779
|
return new Uint8Array(ENCRYPTED_MEMO_SIZE);
|
|
1765
1780
|
},
|
|
1766
1781
|
/**
|
|
1767
1782
|
* Validates that `bytes` is a properly-sized encrypted memo.
|
|
1768
|
-
* Throws an Error if the length is not ENCRYPTED_MEMO_SIZE (
|
|
1783
|
+
* Throws an Error if the length is not ENCRYPTED_MEMO_SIZE (180 bytes).
|
|
1769
1784
|
*
|
|
1770
1785
|
* Call this at system boundaries (extrinsic builders, precompile encoders)
|
|
1771
1786
|
* to catch malformed memos before they reach the chain and fail on-chain.
|
|
@@ -1786,7 +1801,7 @@ var EncryptedMemo = {
|
|
|
1786
1801
|
* Returns null if decryption fails — wrong key, bad MAC, or malformed memo.
|
|
1787
1802
|
* Never throws; safe for scan loops.
|
|
1788
1803
|
*
|
|
1789
|
-
* @param memoBytes
|
|
1804
|
+
* @param memoBytes 180-byte encrypted memo.
|
|
1790
1805
|
* @param commitment 32-byte note commitment (LE).
|
|
1791
1806
|
* @param viewingSecretKey 32-byte HKDF viewing secret key from deriveViewingSecretKey().
|
|
1792
1807
|
*/
|
|
@@ -1798,13 +1813,13 @@ var EncryptedMemo = {
|
|
|
1798
1813
|
* Extract the ECDH shared secret from an encrypted memo using the recipient's viewing secret key.
|
|
1799
1814
|
*
|
|
1800
1815
|
* Used by NoteDecryptor to obtain the shared secret needed for stealth address derivation
|
|
1801
|
-
* without re-running the full decrypt path. Safe to call on any
|
|
1816
|
+
* without re-running the full decrypt path. Safe to call on any 180-byte memo.
|
|
1802
1817
|
*
|
|
1803
1818
|
* Returns `new Uint8Array(32)` (all zeros) for public/dummy memos (zero ephPk).
|
|
1804
1819
|
* Returns `null` if the memo is malformed or the ephPk is not a valid BJJ point.
|
|
1805
1820
|
* Never throws; safe for scan loops.
|
|
1806
1821
|
*
|
|
1807
|
-
* @param memoBytes
|
|
1822
|
+
* @param memoBytes 180-byte encrypted memo.
|
|
1808
1823
|
* @param viewingSecretKey 32-byte HKDF viewing secret key from deriveViewingSecretKey().
|
|
1809
1824
|
*/
|
|
1810
1825
|
extractSharedSecret(memoBytes, viewingSecretKey) {
|
|
@@ -2008,7 +2023,7 @@ var ShieldedPoolModule = class {
|
|
|
2008
2023
|
* Withdraws tokens from the shielded pool to a public address.
|
|
2009
2024
|
* Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
|
|
2010
2025
|
* Pass a `signer` to fall back to signed submission (e.g. for testing).
|
|
2011
|
-
* Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee)
|
|
2026
|
+
* Extrinsic: shieldedPool.unshield(proof, merkleRoot, nullifier, assetId, amount, recipient, fee, changeCommitment, changeEncryptedMemo, relayer, circuitVersion)
|
|
2012
2027
|
*/
|
|
2013
2028
|
async unshield(params, signer, txOptions) {
|
|
2014
2029
|
const entry = resolveTx(this.substrate.unsafe, "ShieldedPool", "unshield");
|
|
@@ -2032,8 +2047,9 @@ var ShieldedPoolModule = class {
|
|
|
2032
2047
|
fee: params.fee ?? 0n,
|
|
2033
2048
|
change_commitment: changeCommitment,
|
|
2034
2049
|
change_encrypted_memo: changeEncryptedMemo,
|
|
2035
|
-
relayer: void 0
|
|
2050
|
+
relayer: void 0,
|
|
2036
2051
|
// Option<H160> — None for direct Substrate submissions
|
|
2052
|
+
circuit_version: params.circuitVersion
|
|
2037
2053
|
});
|
|
2038
2054
|
if (signer) {
|
|
2039
2055
|
return toTxResult(
|
|
@@ -2046,7 +2062,7 @@ var ShieldedPoolModule = class {
|
|
|
2046
2062
|
* Performs a private (shielded) transfer between two notes.
|
|
2047
2063
|
* Submits as an UNSIGNED (gasless) transaction — fee is embedded in the ZK proof.
|
|
2048
2064
|
* Pass a `signer` to fall back to signed submission (e.g. for testing).
|
|
2049
|
-
* Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee)
|
|
2065
|
+
* Extrinsic: shieldedPool.privateTransfer(proof, merkleRoot, nullifiers, commitments, memos, assetId, fee, relayer, circuitVersion)
|
|
2050
2066
|
*/
|
|
2051
2067
|
async privateTransfer(params, signer, txOptions) {
|
|
2052
2068
|
const nullifiers = params.inputs.map((inp) => inp.nullifier);
|
|
@@ -2067,8 +2083,9 @@ var ShieldedPoolModule = class {
|
|
|
2067
2083
|
encrypted_memos: memos,
|
|
2068
2084
|
asset_id: params.assetId,
|
|
2069
2085
|
fee: params.fee ?? 0n,
|
|
2070
|
-
relayer: void 0
|
|
2086
|
+
relayer: void 0,
|
|
2071
2087
|
// Option<H160> — None for direct Substrate submissions
|
|
2088
|
+
circuit_version: params.circuitVersion
|
|
2072
2089
|
});
|
|
2073
2090
|
if (signer) {
|
|
2074
2091
|
return toTxResult(
|
|
@@ -2102,7 +2119,7 @@ var ShieldedPoolModule = class {
|
|
|
2102
2119
|
* This is a SIGNED transaction — the relayer must sign it with their wallet.
|
|
2103
2120
|
* Before calling this, generate a ZK value proof with generateFeeClaimProof() (not yet implemented).
|
|
2104
2121
|
*
|
|
2105
|
-
* Extrinsic: shieldedPool.claim_shielded_fees(commitment, amount, asset_id, memo, proof, public_signals)
|
|
2122
|
+
* Extrinsic: shieldedPool.claim_shielded_fees(commitment, amount, asset_id, memo, proof, public_signals, circuit_version)
|
|
2106
2123
|
*/
|
|
2107
2124
|
async claimShieldedFees(params, signer, txOptions) {
|
|
2108
2125
|
EncryptedMemo.validate(params.encryptedMemo, "claimShieldedFees.encryptedMemo");
|
|
@@ -2113,7 +2130,8 @@ var ShieldedPoolModule = class {
|
|
|
2113
2130
|
asset_id: params.assetId,
|
|
2114
2131
|
encrypted_memo: params.encryptedMemo,
|
|
2115
2132
|
proof: params.proof,
|
|
2116
|
-
public_signals: params.publicSignals
|
|
2133
|
+
public_signals: params.publicSignals,
|
|
2134
|
+
circuit_version: params.circuitVersion
|
|
2117
2135
|
});
|
|
2118
2136
|
return toTxResult(
|
|
2119
2137
|
await (txOptions !== void 0 ? tx.signAndSubmit(signer, txOptions) : tx.signAndSubmit(signer))
|
|
@@ -2631,6 +2649,74 @@ var ZkVerifierModule = class {
|
|
|
2631
2649
|
}
|
|
2632
2650
|
};
|
|
2633
2651
|
|
|
2652
|
+
// src/shielded-pool/CircuitVersionResolver.ts
|
|
2653
|
+
var import_proof_generator = require("@orbinum/proof-generator");
|
|
2654
|
+
var CircuitVersionResolver = class {
|
|
2655
|
+
constructor(zkVerifier, baseUrl, providerFactory) {
|
|
2656
|
+
this.zkVerifier = zkVerifier;
|
|
2657
|
+
this.makeProvider = providerFactory ?? ((circuit, noteVersion) => new import_proof_generator.WebArtifactProvider({
|
|
2658
|
+
...baseUrl ? { baseUrl } : {},
|
|
2659
|
+
circuitVersions: { [circuit]: noteVersion }
|
|
2660
|
+
}));
|
|
2661
|
+
}
|
|
2662
|
+
zkVerifier;
|
|
2663
|
+
makeProvider;
|
|
2664
|
+
/**
|
|
2665
|
+
* Resolves the prover + on-chain version for spending a note of `circuit`
|
|
2666
|
+
* created under `noteVersion`. Fail-closed: throws on unsupported version or
|
|
2667
|
+
* VK-hash mismatch (CDN vs chain), before generating any proof.
|
|
2668
|
+
*/
|
|
2669
|
+
async resolve(circuit, noteVersion) {
|
|
2670
|
+
if (!Number.isInteger(noteVersion) || noteVersion <= 0) {
|
|
2671
|
+
throw new Error(
|
|
2672
|
+
`CircuitVersionResolver: invalid note circuitVersion ${noteVersion} for ${circuit}`
|
|
2673
|
+
);
|
|
2674
|
+
}
|
|
2675
|
+
const provider = this.makeProvider(circuit, noteVersion);
|
|
2676
|
+
const resolved = await provider.getResolvedVersion(circuit);
|
|
2677
|
+
if (resolved.version !== noteVersion) {
|
|
2678
|
+
throw new Error(
|
|
2679
|
+
`CircuitVersionResolver: prover resolved ${circuit} to v${resolved.version}, expected v${noteVersion}`
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
const circuitId = (0, import_proof_generator.circuitTypeToId)(circuit);
|
|
2683
|
+
const info = await this.zkVerifier.getCircuitVersionInfo(circuitId);
|
|
2684
|
+
if (!info) {
|
|
2685
|
+
throw new Error(
|
|
2686
|
+
`CircuitVersionResolver: chain reports no version info for ${circuit} (id ${circuitId})`
|
|
2687
|
+
);
|
|
2688
|
+
}
|
|
2689
|
+
if (!info.supportedVersions.includes(noteVersion)) {
|
|
2690
|
+
throw new Error(
|
|
2691
|
+
`CircuitVersionResolver: chain does not support ${circuit} v${noteVersion} (supported: ${info.supportedVersions.join(", ")})`
|
|
2692
|
+
);
|
|
2693
|
+
}
|
|
2694
|
+
const chainVk = info.vkHashes.find((h) => h.version === noteVersion);
|
|
2695
|
+
if (!chainVk) {
|
|
2696
|
+
throw new Error(
|
|
2697
|
+
`CircuitVersionResolver: chain has no VK hash for ${circuit} v${noteVersion}`
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
if (!vkHashEquals(chainVk.vkHash, resolved.vkHash)) {
|
|
2701
|
+
throw new Error(
|
|
2702
|
+
`CircuitVersionResolver: VK hash mismatch for ${circuit} v${noteVersion} \u2014 prover ${resolved.vkHash}, chain ${chainVk.vkHash}`
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
2705
|
+
return { provider, version: noteVersion };
|
|
2706
|
+
}
|
|
2707
|
+
};
|
|
2708
|
+
function vkHashEquals(a, b) {
|
|
2709
|
+
const na = normalizeHash(a);
|
|
2710
|
+
const nb = normalizeHash(b);
|
|
2711
|
+
if (na === null || nb === null) return false;
|
|
2712
|
+
return na === nb;
|
|
2713
|
+
}
|
|
2714
|
+
function normalizeHash(h) {
|
|
2715
|
+
if (typeof h !== "string") return null;
|
|
2716
|
+
const s = h.toLowerCase().startsWith("0x") ? h.toLowerCase().slice(2) : h.toLowerCase();
|
|
2717
|
+
return /^[0-9a-f]{64}$/.test(s) ? s : null;
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2634
2720
|
// src/relayer/RelayerStatusModule.ts
|
|
2635
2721
|
var RelayerStatusModule = class {
|
|
2636
2722
|
constructor(substrate) {
|
|
@@ -2876,12 +2962,12 @@ var AM_SEL = {
|
|
|
2876
2962
|
var SP_SEL = {
|
|
2877
2963
|
// shield(uint32,bytes32,bytes) → 0x9feb22ea (payable, amount = msg.value)
|
|
2878
2964
|
SHIELD: new Uint8Array([159, 235, 34, 234]),
|
|
2879
|
-
// privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256)
|
|
2880
|
-
PRIVATE_TRANSFER: new Uint8Array([
|
|
2881
|
-
// unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32)
|
|
2882
|
-
UNSHIELD: new Uint8Array([
|
|
2883
|
-
// claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes)
|
|
2884
|
-
CLAIM_SHIELDED_FEES: new Uint8Array([
|
|
2965
|
+
// privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256,uint32) → 0x66ed2cd4
|
|
2966
|
+
PRIVATE_TRANSFER: new Uint8Array([102, 237, 44, 212]),
|
|
2967
|
+
// unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32,bytes,uint32) → 0x4e505348
|
|
2968
|
+
UNSHIELD: new Uint8Array([78, 80, 83, 72]),
|
|
2969
|
+
// claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32) → 0x88d9deba
|
|
2970
|
+
CLAIM_SHIELDED_FEES: new Uint8Array([136, 217, 222, 186])
|
|
2885
2971
|
};
|
|
2886
2972
|
var KNOWN_PRECOMPILES = {
|
|
2887
2973
|
// ── Ethereum standard (EIP) ─────────────────────────────────────────────
|
|
@@ -2925,9 +3011,9 @@ var KNOWN_PRECOMPILES = {
|
|
|
2925
3011
|
name: "ShieldedPool",
|
|
2926
3012
|
functions: {
|
|
2927
3013
|
"9feb22ea": "shield(uint32,bytes32,bytes)",
|
|
2928
|
-
"
|
|
2929
|
-
|
|
2930
|
-
"
|
|
3014
|
+
"66ed2cd4": "privateTransfer(bytes,bytes32,bytes32[],bytes32[],bytes[],uint32,uint256,uint32)",
|
|
3015
|
+
"4e505348": "unshield(bytes,bytes32,bytes32,uint32,uint256,bytes32,uint256,bytes32,bytes,uint32)",
|
|
3016
|
+
"88d9deba": "claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)"
|
|
2931
3017
|
}
|
|
2932
3018
|
}
|
|
2933
3019
|
};
|
|
@@ -2981,7 +3067,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
2981
3067
|
// ─── privateTransfer ───────────────────────────────────────────────────────
|
|
2982
3068
|
/**
|
|
2983
3069
|
* Returns the ABI-encoded calldata for
|
|
2984
|
-
* `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256)`.
|
|
3070
|
+
* `privateTransfer(bytes, bytes32, bytes32[], bytes32[], bytes[], uint32, uint256, uint32)`.
|
|
3071
|
+
* The trailing `uint32` is the circuit version the input notes were created under.
|
|
2985
3072
|
*/
|
|
2986
3073
|
buildPrivateTransferCalldata(params) {
|
|
2987
3074
|
const nullifiers = params.inputs.map((i) => fromHex(i.nullifier));
|
|
@@ -3002,7 +3089,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
3002
3089
|
{ type: "bytes32[]", value: commitments },
|
|
3003
3090
|
{ type: "bytes[]", value: memos },
|
|
3004
3091
|
{ type: "uint", value: BigInt(params.assetId) },
|
|
3005
|
-
{ type: "uint", value: params.fee ?? 0n }
|
|
3092
|
+
{ type: "uint", value: params.fee ?? 0n },
|
|
3093
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
3006
3094
|
);
|
|
3007
3095
|
}
|
|
3008
3096
|
/**
|
|
@@ -3043,7 +3131,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
3043
3131
|
{ type: "bytes32", value: recipientBytes },
|
|
3044
3132
|
{ type: "uint", value: params.fee ?? 0n },
|
|
3045
3133
|
{ type: "bytes32", value: changeCommitment },
|
|
3046
|
-
{ type: "bytes", value: changeEncryptedMemo }
|
|
3134
|
+
{ type: "bytes", value: changeEncryptedMemo },
|
|
3135
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
3047
3136
|
);
|
|
3048
3137
|
}
|
|
3049
3138
|
/**
|
|
@@ -3093,7 +3182,7 @@ var ShieldedPoolPrecompile = class {
|
|
|
3093
3182
|
// ─── claimShieldedFees ───────────────────────────────────────────────────────────────────
|
|
3094
3183
|
/**
|
|
3095
3184
|
* Returns the ABI-encoded calldata for
|
|
3096
|
-
* `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes)`.
|
|
3185
|
+
* `claimShieldedFees(bytes32,uint256,uint32,bytes,bytes,bytes,uint32)`.
|
|
3097
3186
|
*
|
|
3098
3187
|
* ABI layout (params after selector):
|
|
3099
3188
|
* - `commitment` — bytes32 (fixed)
|
|
@@ -3102,6 +3191,7 @@ var ShieldedPoolPrecompile = class {
|
|
|
3102
3191
|
* - `memo` — bytes (dynamic)
|
|
3103
3192
|
* - `proof` — bytes (dynamic, 128 bytes Groth16)
|
|
3104
3193
|
* - `publicSignals` — bytes (dynamic, 76 bytes)
|
|
3194
|
+
* - `circuitVersion` — uint32 (fixed, right-aligned)
|
|
3105
3195
|
*
|
|
3106
3196
|
* The validator identity is derived from `msg.sender` in the precompile —
|
|
3107
3197
|
* do NOT include it in the calldata.
|
|
@@ -3127,7 +3217,8 @@ var ShieldedPoolPrecompile = class {
|
|
|
3127
3217
|
{ type: "uint", value: BigInt(params.assetId) },
|
|
3128
3218
|
{ type: "bytes", value: params.encryptedMemo },
|
|
3129
3219
|
{ type: "bytes", value: params.proof },
|
|
3130
|
-
{ type: "bytes", value: params.publicSignals }
|
|
3220
|
+
{ type: "bytes", value: params.publicSignals },
|
|
3221
|
+
{ type: "uint", value: BigInt(params.circuitVersion) }
|
|
3131
3222
|
);
|
|
3132
3223
|
}
|
|
3133
3224
|
/**
|
|
@@ -3565,6 +3656,11 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3565
3656
|
chain;
|
|
3566
3657
|
/** Typed access to `zkVerifier_*` custom RPC endpoints. */
|
|
3567
3658
|
zkVerifier;
|
|
3659
|
+
/**
|
|
3660
|
+
* Resolves a note's circuit version to a pinned prover + on-chain version
|
|
3661
|
+
* before spending it (fail-closed: throws on unsupported version / VK mismatch).
|
|
3662
|
+
*/
|
|
3663
|
+
circuitVersionResolver;
|
|
3568
3664
|
/** Typed access to `relayer_*` RPC endpoints (registry lookup and pending fee queries). */
|
|
3569
3665
|
relayerStatus;
|
|
3570
3666
|
/**
|
|
@@ -3573,7 +3669,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3573
3669
|
*/
|
|
3574
3670
|
precompiles;
|
|
3575
3671
|
/** @internal Use `OrbinumClient.connect()` to obtain an instance. */
|
|
3576
|
-
constructor(substrate, evm, indexer) {
|
|
3672
|
+
constructor(substrate, evm, indexer, circuitsBaseUrl) {
|
|
3577
3673
|
this.substrate = substrate;
|
|
3578
3674
|
this.evm = evm;
|
|
3579
3675
|
this.evmExplorer = evm ? new EvmExplorer(evm) : null;
|
|
@@ -3583,6 +3679,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3583
3679
|
this.privacy = new PrivacyModule(substrate);
|
|
3584
3680
|
this.chain = new ChainModule(substrate);
|
|
3585
3681
|
this.zkVerifier = new ZkVerifierModule(substrate);
|
|
3682
|
+
this.circuitVersionResolver = new CircuitVersionResolver(this.zkVerifier, circuitsBaseUrl);
|
|
3586
3683
|
this.relayerStatus = new RelayerStatusModule(substrate);
|
|
3587
3684
|
this.precompiles = evm ? {
|
|
3588
3685
|
shieldedPool: new ShieldedPoolPrecompile(evm),
|
|
@@ -3603,7 +3700,7 @@ var OrbinumClient = class _OrbinumClient {
|
|
|
3603
3700
|
);
|
|
3604
3701
|
const evm = config.evmRpc ? new EvmClient(config.evmRpc) : null;
|
|
3605
3702
|
const indexer = config.indexerUrl ? new IndexerClient({ baseUrl: config.indexerUrl }) : null;
|
|
3606
|
-
return new _OrbinumClient(substrate, evm, indexer);
|
|
3703
|
+
return new _OrbinumClient(substrate, evm, indexer, config.circuitsBaseUrl);
|
|
3607
3704
|
}
|
|
3608
3705
|
/** Closes the underlying Substrate WebSocket connection and releases all resources. */
|
|
3609
3706
|
destroy() {
|
|
@@ -3725,6 +3822,8 @@ var OrbinumClientProvider = class {
|
|
|
3725
3822
|
};
|
|
3726
3823
|
if (this.config.evmRpc) connectConfig.evmRpc = this.config.evmRpc;
|
|
3727
3824
|
if (this.config.indexerUrl) connectConfig.indexerUrl = this.config.indexerUrl;
|
|
3825
|
+
if (this.config.circuitsBaseUrl)
|
|
3826
|
+
connectConfig.circuitsBaseUrl = this.config.circuitsBaseUrl;
|
|
3728
3827
|
const clientPromise = OrbinumClient.connect(connectConfig);
|
|
3729
3828
|
const client = await Promise.race([clientPromise, timeoutPromise]);
|
|
3730
3829
|
orphanClient = client;
|
|
@@ -3911,6 +4010,9 @@ var OrbinumClientProvider = class {
|
|
|
3911
4010
|
}
|
|
3912
4011
|
};
|
|
3913
4012
|
|
|
4013
|
+
// src/shielded-pool/protocol/types.ts
|
|
4014
|
+
var CURRENT_CIRCUIT_VERSION = 1;
|
|
4015
|
+
|
|
3914
4016
|
// src/utils/stealth.ts
|
|
3915
4017
|
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
3916
4018
|
var import_hkdf = require("@noble/hashes/hkdf.js");
|
|
@@ -4019,6 +4121,7 @@ var NoteBuilder = class {
|
|
|
4019
4121
|
const blinding = input.blinding ?? BigInt(Date.now());
|
|
4020
4122
|
const spendingKey = input.spendingKey ?? 0n;
|
|
4021
4123
|
const counterpartyPk = input.counterpartyPk ?? 0n;
|
|
4124
|
+
const circuitVersion = input.circuitVersion ?? CURRENT_CIRCUIT_VERSION;
|
|
4022
4125
|
const useStealth = input.viewingPublicKey !== void 0 && input.recipientOwnerPk !== void 0;
|
|
4023
4126
|
let memo;
|
|
4024
4127
|
if (useStealth) {
|
|
@@ -4053,6 +4156,7 @@ var NoteBuilder = class {
|
|
|
4053
4156
|
stealthCommitmentBytes,
|
|
4054
4157
|
recipientIvkPacked,
|
|
4055
4158
|
bigintTo32Le(counterpartyPk),
|
|
4159
|
+
circuitVersion,
|
|
4056
4160
|
ephSk
|
|
4057
4161
|
)
|
|
4058
4162
|
);
|
|
@@ -4070,6 +4174,7 @@ var NoteBuilder = class {
|
|
|
4070
4174
|
ownerPk: effectiveOwnerPk,
|
|
4071
4175
|
blinding,
|
|
4072
4176
|
spendingKey,
|
|
4177
|
+
circuitVersion,
|
|
4073
4178
|
spent: false,
|
|
4074
4179
|
spentAt: null,
|
|
4075
4180
|
commitment: commitment2,
|
|
@@ -4092,7 +4197,8 @@ var NoteBuilder = class {
|
|
|
4092
4197
|
Number(assetId),
|
|
4093
4198
|
commitmentBytes,
|
|
4094
4199
|
input.viewingPublicKey,
|
|
4095
|
-
bigintTo32Le(counterpartyPk)
|
|
4200
|
+
bigintTo32Le(counterpartyPk),
|
|
4201
|
+
circuitVersion
|
|
4096
4202
|
)
|
|
4097
4203
|
) : Array.from(EncryptedMemo.dummy());
|
|
4098
4204
|
if (memo.length !== ENCRYPTED_MEMO_SIZE)
|
|
@@ -4105,6 +4211,7 @@ var NoteBuilder = class {
|
|
|
4105
4211
|
ownerPk,
|
|
4106
4212
|
blinding,
|
|
4107
4213
|
spendingKey,
|
|
4214
|
+
circuitVersion,
|
|
4108
4215
|
spent: false,
|
|
4109
4216
|
spentAt: null,
|
|
4110
4217
|
commitment,
|
|
@@ -4116,7 +4223,7 @@ var NoteBuilder = class {
|
|
|
4116
4223
|
};
|
|
4117
4224
|
}
|
|
4118
4225
|
/**
|
|
4119
|
-
* Build the
|
|
4226
|
+
* Build the 180-byte ECDH-encrypted memo for a note.
|
|
4120
4227
|
*
|
|
4121
4228
|
* Pure TypeScript implementation — no WASM dependency.
|
|
4122
4229
|
* Uses ChaCha20-Poly1305 with ECDH key agreement (BabyJubJub ephemeral keypair).
|
|
@@ -4135,7 +4242,8 @@ var NoteBuilder = class {
|
|
|
4135
4242
|
Number(note.assetId),
|
|
4136
4243
|
bigintTo32Le(note.commitment),
|
|
4137
4244
|
recipientIvkPacked ?? new Uint8Array(32),
|
|
4138
|
-
counterpartyPk ?? bigintTo32Le(note.counterpartyPk ?? 0n)
|
|
4245
|
+
counterpartyPk ?? bigintTo32Le(note.counterpartyPk ?? 0n),
|
|
4246
|
+
note.circuitVersion
|
|
4139
4247
|
);
|
|
4140
4248
|
}
|
|
4141
4249
|
};
|
|
@@ -4197,6 +4305,7 @@ function tryDecryptNoteVerbose(commitment, viewingSecretKey, spendingKey, ownOwn
|
|
|
4197
4305
|
ownerPk: effectiveOwnerPk,
|
|
4198
4306
|
blinding: plaintext.blinding,
|
|
4199
4307
|
spendingKey: effectiveSpendingKey,
|
|
4308
|
+
circuitVersion: plaintext.circuitVersion,
|
|
4200
4309
|
spent: false,
|
|
4201
4310
|
spentAt: null,
|
|
4202
4311
|
commitment: recomputed,
|
|
@@ -4270,7 +4379,7 @@ function selectNotes(notes, needed) {
|
|
|
4270
4379
|
for (let j = i + 1; j < sorted.length; j++) {
|
|
4271
4380
|
const a = sorted[i];
|
|
4272
4381
|
const b = sorted[j];
|
|
4273
|
-
if (a !== void 0 && b !== void 0 && a.value + b.value >= needed) {
|
|
4382
|
+
if (a !== void 0 && b !== void 0 && a.circuitVersion === b.circuitVersion && a.value + b.value >= needed) {
|
|
4274
4383
|
return [a, b];
|
|
4275
4384
|
}
|
|
4276
4385
|
}
|
|
@@ -4627,6 +4736,11 @@ async function encryptNote(key, blindKey, note) {
|
|
|
4627
4736
|
}
|
|
4628
4737
|
async function decryptNoteRecord(key, rec) {
|
|
4629
4738
|
const note = await decryptJson(key, rec.iv, rec.ciphertext);
|
|
4739
|
+
if (typeof note.circuitVersion !== "number") {
|
|
4740
|
+
throw new Error(
|
|
4741
|
+
"decryptNoteRecord: note is missing circuitVersion (invalid or corrupt record)"
|
|
4742
|
+
);
|
|
4743
|
+
}
|
|
4630
4744
|
return applyNoteStatus(note, {
|
|
4631
4745
|
...rec.spent !== void 0 && { spent: rec.spent },
|
|
4632
4746
|
spentAt: rec.spentAt ?? null
|
|
@@ -4637,7 +4751,7 @@ async function noteBlindTag(blindKey, hex) {
|
|
|
4637
4751
|
}
|
|
4638
4752
|
|
|
4639
4753
|
// src/proof-generator/unshield.ts
|
|
4640
|
-
var
|
|
4754
|
+
var import_proof_generator2 = require("@orbinum/proof-generator");
|
|
4641
4755
|
var import_utils3 = require("@noble/ciphers/utils.js");
|
|
4642
4756
|
var import_baby_jubjub6 = require("@zk-kit/baby-jubjub");
|
|
4643
4757
|
var import_poseidon_lite4 = require("poseidon-lite");
|
|
@@ -4683,15 +4797,15 @@ async function generateUnshieldProof(inputs, options = {}) {
|
|
|
4683
4797
|
change_blinding: changeBlinding.toString(),
|
|
4684
4798
|
change_owner_pubkey: changeOwnerPubkey.toString()
|
|
4685
4799
|
};
|
|
4686
|
-
const provider = options.provider ?? new
|
|
4800
|
+
const provider = options.provider ?? new import_proof_generator2.WebArtifactProvider();
|
|
4687
4801
|
const opts = { provider };
|
|
4688
4802
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4689
|
-
const proofResult = await (0,
|
|
4803
|
+
const proofResult = await (0, import_proof_generator2.generateProof)(import_proof_generator2.CircuitType.Unshield, circuitInputs, opts);
|
|
4690
4804
|
return { ...proofResult, changeCommitment, changeValue, changeBlinding, changeOwnerPubkey };
|
|
4691
4805
|
}
|
|
4692
4806
|
|
|
4693
4807
|
// src/proof-generator/transfer.ts
|
|
4694
|
-
var
|
|
4808
|
+
var import_proof_generator3 = require("@orbinum/proof-generator");
|
|
4695
4809
|
async function generateTransferProof(params, options = {}) {
|
|
4696
4810
|
const root = leHexToBigint(params.merkleRoot).toString();
|
|
4697
4811
|
const [i0, i1] = params.inputs;
|
|
@@ -4716,14 +4830,14 @@ async function generateTransferProof(params, options = {}) {
|
|
|
4716
4830
|
output_owner_pubkeys: [o0.ownerPk.toString(), o1.ownerPk.toString()],
|
|
4717
4831
|
output_blindings: [o0.blinding.toString(), o1.blinding.toString()]
|
|
4718
4832
|
};
|
|
4719
|
-
const provider = options.provider ?? new
|
|
4833
|
+
const provider = options.provider ?? new import_proof_generator3.WebArtifactProvider();
|
|
4720
4834
|
const opts = { provider };
|
|
4721
4835
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4722
|
-
return (0,
|
|
4836
|
+
return (0, import_proof_generator3.generateProof)(import_proof_generator3.CircuitType.Transfer, circuitInputs, opts);
|
|
4723
4837
|
}
|
|
4724
4838
|
|
|
4725
4839
|
// src/proof-generator/fee-claim.ts
|
|
4726
|
-
var
|
|
4840
|
+
var import_proof_generator4 = require("@orbinum/proof-generator");
|
|
4727
4841
|
async function generateFeeClaimProof(inputs, options = {}) {
|
|
4728
4842
|
if (inputs.amount <= 0n) {
|
|
4729
4843
|
throw new Error("Fee claim amount must be greater than zero.");
|
|
@@ -4735,10 +4849,10 @@ async function generateFeeClaimProof(inputs, options = {}) {
|
|
|
4735
4849
|
owner_pubkey: inputs.ownerPubkey.toString(),
|
|
4736
4850
|
blinding: inputs.blinding.toString()
|
|
4737
4851
|
};
|
|
4738
|
-
const provider = options.provider ?? new
|
|
4852
|
+
const provider = options.provider ?? new import_proof_generator4.WebArtifactProvider();
|
|
4739
4853
|
const opts = { provider };
|
|
4740
4854
|
if (options.verbose !== void 0) opts.verbose = options.verbose;
|
|
4741
|
-
const proofResult = await (0,
|
|
4855
|
+
const proofResult = await (0, import_proof_generator4.generateProof)(import_proof_generator4.CircuitType.ValueProof, circuitInputs, opts);
|
|
4742
4856
|
const [sigCommitment, sigValue, sigAssetId, sigOwnerHash] = proofResult.publicSignals.map(BigInt);
|
|
4743
4857
|
const buf = new Uint8Array(76);
|
|
4744
4858
|
buf.set(bigintTo32Le(sigCommitment), 0);
|
|
@@ -4794,9 +4908,19 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4794
4908
|
const recipient = toHex(data.slice(160, 192));
|
|
4795
4909
|
const fee = decodeUint(data, 192);
|
|
4796
4910
|
const changeCommitment = toHex(data.slice(224, 256));
|
|
4911
|
+
const circuitVersion = decodeUint(data, 288);
|
|
4797
4912
|
return {
|
|
4798
4913
|
fnSig,
|
|
4799
|
-
args: {
|
|
4914
|
+
args: {
|
|
4915
|
+
root,
|
|
4916
|
+
nullifier,
|
|
4917
|
+
assetId,
|
|
4918
|
+
amount,
|
|
4919
|
+
recipient,
|
|
4920
|
+
fee,
|
|
4921
|
+
changeCommitment,
|
|
4922
|
+
circuitVersion
|
|
4923
|
+
}
|
|
4800
4924
|
};
|
|
4801
4925
|
} catch {
|
|
4802
4926
|
return { fnSig, args: {} };
|
|
@@ -4812,7 +4936,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4812
4936
|
const commitments = Number(decodeUint(data, commOffset));
|
|
4813
4937
|
const assetId = decodeUint(data, 160);
|
|
4814
4938
|
const fee = decodeUint(data, 192);
|
|
4815
|
-
|
|
4939
|
+
const circuitVersion = decodeUint(data, 224);
|
|
4940
|
+
return { fnSig, args: { root, nullifiers, commitments, assetId, fee, circuitVersion } };
|
|
4816
4941
|
} catch {
|
|
4817
4942
|
return { fnSig, args: {} };
|
|
4818
4943
|
}
|
|
@@ -4823,7 +4948,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4823
4948
|
const commitment = toHex(data.slice(0, 32));
|
|
4824
4949
|
const amount = decodeUint(data, 32);
|
|
4825
4950
|
const assetId = decodeUint(data, 64);
|
|
4826
|
-
|
|
4951
|
+
const circuitVersion = decodeUint(data, 192);
|
|
4952
|
+
return { fnSig, args: { commitment, amount, assetId, circuitVersion } };
|
|
4827
4953
|
} catch {
|
|
4828
4954
|
return { fnSig, args: {} };
|
|
4829
4955
|
}
|
|
@@ -4835,8 +4961,8 @@ function decodePrecompileCalldata(address, input) {
|
|
|
4835
4961
|
var CircuitId = {
|
|
4836
4962
|
Transfer: 1,
|
|
4837
4963
|
Unshield: 2,
|
|
4838
|
-
|
|
4839
|
-
|
|
4964
|
+
PrivateLink: 5,
|
|
4965
|
+
ValueProof: 6
|
|
4840
4966
|
};
|
|
4841
4967
|
|
|
4842
4968
|
// src/utils/string.ts
|
|
@@ -5508,8 +5634,10 @@ var import_polkadot_api4 = require("polkadot-api");
|
|
|
5508
5634
|
BABYJUB_SUBORDER,
|
|
5509
5635
|
BN254_R,
|
|
5510
5636
|
Blake2256,
|
|
5637
|
+
CURRENT_CIRCUIT_VERSION,
|
|
5511
5638
|
CircuitId,
|
|
5512
5639
|
CircuitType,
|
|
5640
|
+
CircuitVersionResolver,
|
|
5513
5641
|
CryptoPrecompiles,
|
|
5514
5642
|
ENCRYPTED_MEMO_SIZE,
|
|
5515
5643
|
EncryptedMemo,
|