Skip to content

Commit d814bfd

Browse files
author
sls_0x
committed
Harden canonical network config surface
1 parent 9d038ed commit d814bfd

19 files changed

Lines changed: 575 additions & 28 deletions

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Canonical Dark Null root defaults
2+
DARK_NULL_NETWORK=devnet
3+
DARK_NULL_RPC_URL=https://api.devnet.solana.com
4+
DARK_NULL_WALLET_PATH=~/.config/solana/id.json
5+
6+
# Optional proving overrides
7+
DARK_NULL_SNARKJS=snarkjs
8+
DARK_NULL_ZKEY_PATH=circuits/null_proof_final.zkey
9+
DARK_NULL_WASM_PATH=circuits/null_proof_js/null_proof.wasm
10+
DARK_NULL_VK_PATH=circuits/vk.json

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ vkey*.json
4545
.env
4646
.env.*
4747
*.env
48+
!.env.example
4849
deployer_wallet.json
4950
main_wallet.json
5051
relayer.json

MANIFEST.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@
2929
},
3030
{
3131
"path": "Cargo.toml",
32-
"sha256": "52c55cd3dbbae0bb9e199f382112e41c1b817998af1e286691a8ec115ab2234b",
33-
"size": 858
32+
"sha256": "692106d80328cc9c5835f7b7e4d3e26bc73f6f8bfbc4af65b6281899079f119c",
33+
"size": 1069
34+
},
35+
{
36+
"path": "NETWORKS.json",
37+
"sha256": "22828856a7d97f054ac0e2833a138400edeae1d7d84daa777eff69b320efd9e0",
38+
"size": 631
3439
},
3540
{
3641
"path": "src/lib.rs",

NETWORKS.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"canonicalProgramId": "2stas3cZYnBiWpndcTXQDGLXwfQ7kjEYYrW52DsUAcxF",
3+
"defaultNetwork": "devnet",
4+
"supportedNetworks": {
5+
"devnet": {
6+
"label": "Canonical devnet",
7+
"cluster": "devnet",
8+
"anchorCluster": "devnet",
9+
"rpcUrl": "https://api.devnet.solana.com",
10+
"wsUrl": "wss://api.devnet.solana.com/",
11+
"manifestKey": "canonicalDevnet"
12+
},
13+
"localnet": {
14+
"label": "Canonical localnet",
15+
"cluster": "localnet",
16+
"anchorCluster": "localnet",
17+
"rpcUrl": "http://127.0.0.1:8899",
18+
"wsUrl": "ws://127.0.0.1:8900",
19+
"manifestKey": "canonicalDevnet"
20+
}
21+
}
22+
}

README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This repo now has one canonical public root:
1212

1313
- one program ID: `2stas3cZYnBiWpndcTXQDGLXwfQ7kjEYYrW52DsUAcxF`
1414
- one root manifest: [`MANIFEST.json`](./MANIFEST.json)
15+
- one network map: [`NETWORKS.json`](./NETWORKS.json)
1516
- one root IDL: [`idl/paradox.json`](./idl/paradox.json)
1617
- one root circuit bundle: [`circuits/null_proof.circom`](./circuits/null_proof.circom), [`circuits/null_proof_final.zkey`](./circuits/null_proof_final.zkey), [`circuits/null_proof_js/null_proof.wasm`](./circuits/null_proof_js/null_proof.wasm), [`circuits/vk.json`](./circuits/vk.json)
1718
- one root verifier path: [`src/lib.rs`](./src/lib.rs) + [`src/verifying_key.rs`](./src/verifying_key.rs)
@@ -34,6 +35,21 @@ If you want the extended validation path:
3435
FULL_VALIDATION=1 sh scripts/bootstrap.sh
3536
```
3637

38+
## Canonical Network Selection
39+
40+
```bash
41+
npm run config:devnet
42+
npm run config:localnet
43+
```
44+
45+
For machine-readable output:
46+
47+
```bash
48+
npm run config:json:devnet
49+
```
50+
51+
Canonical defaults also live in [`.env.example`](./.env.example).
52+
3753
## npm SDK
3854

3955
```bash
@@ -51,6 +67,7 @@ npm install @dark-null/protocol @coral-xyz/anchor @solana/web3.js
5167
| Area | Root path |
5268
|---|---|
5369
| Program binding | [`MANIFEST.json`](./MANIFEST.json), [`Anchor.toml`](./Anchor.toml), [`src/lib.rs`](./src/lib.rs) |
70+
| Network config | [`NETWORKS.json`](./NETWORKS.json), [`.env.example`](./.env.example), [`scripts/network-config.mjs`](./scripts/network-config.mjs) |
5471
| Verifier | [`src/verifying_key.rs`](./src/verifying_key.rs), [`circuits/vk.json`](./circuits/vk.json) |
5572
| Circuit artifacts | [`circuits/null_proof.circom`](./circuits/null_proof.circom), [`circuits/null_proof_final.zkey`](./circuits/null_proof_final.zkey), [`circuits/null_proof_js/null_proof.wasm`](./circuits/null_proof_js/null_proof.wasm) |
5673
| Public IDL | [`idl/paradox.json`](./idl/paradox.json) |
@@ -71,6 +88,7 @@ npm install @dark-null/protocol @coral-xyz/anchor @solana/web3.js
7188
- a real Groth16 verifier path is published in the root
7289
- the root circuit, zkey, wasm, and vk are internally consistent
7390
- the root proof flow is reproducible locally with `npm test`
91+
- the root devnet/localnet selection now resolves through one published config surface
7492
- the repo has a canonical public devnet path instead of a placeholder root plus side branch
7593

7694
## What This Repo Does Not Prove
@@ -83,10 +101,11 @@ npm install @dark-null/protocol @coral-xyz/anchor @solana/web3.js
83101
## Verification Flow
84102

85103
1. Run `sh scripts/bootstrap.sh`.
86-
2. Read [`MANIFEST.json`](./MANIFEST.json) and [`docs/PROGRAM_IDS.md`](./docs/PROGRAM_IDS.md).
87-
3. Run `npm test`.
88-
4. Run `cargo test --offline`.
89-
5. If Python client dependencies are installed, run `npm run test:python:unit`.
104+
2. Run `npm run config:devnet` or `npm run config:localnet`.
105+
3. Read [`MANIFEST.json`](./MANIFEST.json), [`NETWORKS.json`](./NETWORKS.json), and [`docs/PROGRAM_IDS.md`](./docs/PROGRAM_IDS.md).
106+
4. Run `npm test`.
107+
5. Run `cargo test --offline`.
108+
6. If Python client dependencies are installed, run `npm run test:python:unit`.
90109

91110
## For Integrators and Agent Builders
92111

client/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This directory now carries a **canonical root helper client**, not a toy-root tr
1414
## What `dark_client.py` Matches
1515

1616
- program ID `2stas3cZYnBiWpndcTXQDGLXwfQ7kjEYYrW52DsUAcxF`
17+
- canonical network config from [`../NETWORKS.json`](../NETWORKS.json)
1718
- canonical root circuit artifacts under [`../circuits`](../circuits)
1819
- canonical root IDL semantics from [`../idl/paradox.json`](../idl/paradox.json)
1920

client/dark_client.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111

1212
REPO_ROOT = Path(__file__).resolve().parents[1]
1313
CIRCUITS_DIR = REPO_ROOT / "circuits"
14+
NETWORKS_PATH = REPO_ROOT / "NETWORKS.json"
1415
DEFAULT_ZKEY_PATH = CIRCUITS_DIR / "null_proof_final.zkey"
1516
DEFAULT_WASM_PATH = CIRCUITS_DIR / "null_proof_js" / "null_proof.wasm"
1617
DEFAULT_VK_PATH = CIRCUITS_DIR / "vk.json"
18+
NETWORKS = json.loads(NETWORKS_PATH.read_text(encoding="utf8"))
19+
SUPPORTED_NETWORKS = NETWORKS["supportedNetworks"]
1720

18-
PROGRAM_ID = Pubkey.from_string("2stas3cZYnBiWpndcTXQDGLXwfQ7kjEYYrW52DsUAcxF")
21+
PROGRAM_ID = Pubkey.from_string(NETWORKS["canonicalProgramId"])
1922
TOKEN_PROGRAM_ID = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
2023
SYSTEM_PROGRAM_ID = Pubkey.from_string("11111111111111111111111111111111")
2124
VAULT_SEED = b"merkle_vault"
@@ -47,14 +50,43 @@ def coerce_bytes32(value) -> bytes:
4750
return raw
4851

4952

53+
def list_supported_networks():
54+
return list(SUPPORTED_NETWORKS.keys())
55+
56+
57+
def resolve_network_config(network=None, rpc_url=None, wallet_path=None):
58+
selected_network = network or os.environ.get("DARK_NULL_NETWORK") or NETWORKS["defaultNetwork"]
59+
definition = SUPPORTED_NETWORKS.get(selected_network)
60+
if definition is None:
61+
known_networks = ", ".join(list_supported_networks())
62+
raise ValueError(f"Unknown network: {selected_network}. Expected one of {known_networks}")
63+
64+
return {
65+
"network": selected_network,
66+
"label": definition["label"],
67+
"cluster": definition["cluster"],
68+
"anchor_cluster": definition["anchorCluster"],
69+
"rpc_url": rpc_url or os.environ.get("DARK_NULL_RPC_URL") or definition["rpcUrl"],
70+
"ws_url": definition.get("wsUrl"),
71+
"wallet_path": wallet_path or os.environ.get("DARK_NULL_WALLET_PATH"),
72+
"program_id": NETWORKS["canonicalProgramId"],
73+
"manifest_key": definition["manifestKey"],
74+
}
75+
76+
5077
class DarkClient:
51-
def __init__(self, snarkjs_path=None, zkey_path=None, wasm_path=None, vk_path=None):
78+
def __init__(self, snarkjs_path=None, zkey_path=None, wasm_path=None, vk_path=None, network=None, rpc_url=None, wallet_path=None):
79+
self.network_config = resolve_network_config(network=network, rpc_url=rpc_url, wallet_path=wallet_path)
80+
self.program_id = Pubkey.from_string(self.network_config["program_id"])
81+
self.rpc_url = self.network_config["rpc_url"]
82+
self.wallet_path = self.network_config["wallet_path"]
5283
self.snarkjs_path = snarkjs_path or os.environ.get("DARK_NULL_SNARKJS")
5384
self.zkey_path = Path(zkey_path) if zkey_path else Path(os.environ.get("DARK_NULL_ZKEY_PATH", DEFAULT_ZKEY_PATH))
5485
self.wasm_path = Path(wasm_path) if wasm_path else Path(os.environ.get("DARK_NULL_WASM_PATH", DEFAULT_WASM_PATH))
5586
self.vk_path = Path(vk_path) if vk_path else Path(os.environ.get("DARK_NULL_VK_PATH", DEFAULT_VK_PATH))
5687

57-
def derive_vault_pda(self, program_id=PROGRAM_ID):
88+
def derive_vault_pda(self, program_id=None):
89+
program_id = program_id or self.program_id
5890
return Pubkey.find_program_address([VAULT_SEED], program_id)[0]
5991

6092
def build_proof_inputs(self, amount, blinding, nullifier_secret, root, path_elements, path_indices):
@@ -137,7 +169,8 @@ def verify_proof_locally(self, proof, public_signals):
137169
)
138170
return True
139171

140-
def build_initialize_instruction(self, user, vault=None, program_id=PROGRAM_ID):
172+
def build_initialize_instruction(self, user, vault=None, program_id=None):
173+
program_id = program_id or self.program_id
141174
vault = vault or self.derive_vault_pda(program_id)
142175
return Instruction(
143176
program_id,
@@ -149,7 +182,8 @@ def build_initialize_instruction(self, user, vault=None, program_id=PROGRAM_ID):
149182
],
150183
)
151184

152-
def build_update_root_instruction(self, signer, new_root, vault=None, program_id=PROGRAM_ID):
185+
def build_update_root_instruction(self, signer, new_root, vault=None, program_id=None):
186+
program_id = program_id or self.program_id
153187
vault = vault or self.derive_vault_pda(program_id)
154188
data = bytearray(anchor_discriminator("update_root"))
155189
data.extend(coerce_bytes32(new_root))
@@ -173,8 +207,9 @@ def build_deposit_wsol_and_whisper_instruction(
173207
ephemeral_pubkey,
174208
view_tag,
175209
vault=None,
176-
program_id=PROGRAM_ID,
210+
program_id=None,
177211
):
212+
program_id = program_id or self.program_id
178213
vault = vault or self.derive_vault_pda(program_id)
179214
data = bytearray(anchor_discriminator("deposit_wsol_and_whisper"))
180215
data.extend(int(amount).to_bytes(8, "little"))
@@ -203,8 +238,9 @@ def build_prepare_phantom_withdraw_instruction(
203238
amount,
204239
proof_bundle,
205240
vault=None,
206-
program_id=PROGRAM_ID,
241+
program_id=None,
207242
):
243+
program_id = program_id or self.program_id
208244
vault = vault or self.derive_vault_pda(program_id)
209245
proof_a = bytes(proof_bundle["proof_a"])
210246
proof_b = bytes(proof_bundle["proof_b"])
@@ -253,8 +289,9 @@ def build_burn_and_whisper_instruction(
253289
ephemeral_pubkey,
254290
view_tag,
255291
vault=None,
256-
program_id=PROGRAM_ID,
292+
program_id=None,
257293
):
294+
program_id = program_id or self.program_id
258295
vault = vault or self.derive_vault_pda(program_id)
259296
data = bytearray(anchor_discriminator("burn_and_whisper"))
260297
data.extend(int(amount).to_bytes(8, "little"))

client/test_dark_protocol.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
DEFAULT_WASM_PATH,
1212
DEFAULT_ZKEY_PATH,
1313
DarkClient,
14+
PROGRAM_ID,
1415
anchor_discriminator,
16+
list_supported_networks,
17+
resolve_network_config,
1518
)
1619
from nebula_core import NebulaCore
1720

@@ -58,6 +61,16 @@ class TestDarkClient:
5861
def setup_method(self):
5962
self.client = DarkClient()
6063

64+
def test_network_config_matches_canonical_root(self):
65+
devnet = resolve_network_config("devnet")
66+
localnet = resolve_network_config("localnet")
67+
68+
assert list_supported_networks() == ["devnet", "localnet"]
69+
assert devnet["program_id"] == str(PROGRAM_ID)
70+
assert devnet["rpc_url"] == "https://api.devnet.solana.com"
71+
assert localnet["rpc_url"] == "http://127.0.0.1:8899"
72+
assert self.client.program_id == PROGRAM_ID
73+
6174
def test_build_proof_inputs_requires_seven_level_path(self):
6275
inputs = self.client.build_proof_inputs(
6376
amount=1_000_000,

docs/getting-started.md

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,50 @@ That installs npm dependencies and runs the public checks.
1313
## 2. Read the Canonical Binding First
1414

1515
- [`../MANIFEST.json`](../MANIFEST.json)
16+
- [`../NETWORKS.json`](../NETWORKS.json)
1617
- [`PROGRAM_IDS.md`](./PROGRAM_IDS.md)
1718

18-
Those two files tell you which program ID and artifact set the root actually uses.
19+
Those three files tell you which program ID, network map, and artifact set the root actually uses.
1920

20-
## 3. Review the Root Program
21+
## 3. Resolve the Canonical Network Config
22+
23+
```bash
24+
npm run config:devnet
25+
```
26+
27+
If you are working against a local validator instead:
28+
29+
```bash
30+
npm run config:localnet
31+
```
32+
33+
## 4. Review the Root Program
2134

2235
- [`../Anchor.toml`](../Anchor.toml)
2336
- [`../Cargo.toml`](../Cargo.toml)
2437
- [`../src/lib.rs`](../src/lib.rs)
2538
- [`../src/verifying_key.rs`](../src/verifying_key.rs)
2639

27-
## 4. Review the Root Circuit Artifacts
40+
## 5. Review the Root Circuit Artifacts
2841

2942
- [`../circuits/null_proof.circom`](../circuits/null_proof.circom)
3043
- [`../circuits/null_proof_final.zkey`](../circuits/null_proof_final.zkey)
3144
- [`../circuits/null_proof_js/null_proof.wasm`](../circuits/null_proof_js/null_proof.wasm)
3245
- [`../circuits/vk.json`](../circuits/vk.json)
3346

34-
## 5. Use the npm SDK
47+
## 6. Use the npm SDK
3548

3649
```bash
3750
npm install @dark-null/protocol @coral-xyz/anchor @solana/web3.js
3851
```
3952

4053
```typescript
41-
import { createAnchorProgram, getInstructionDefinition, resolveProgramId } from "@dark-null/protocol";
54+
import {
55+
createAnchorProgram,
56+
getInstructionDefinition,
57+
resolveNetworkConfig,
58+
resolveProgramId,
59+
} from "@dark-null/protocol";
4260

4361
const program = await createAnchorProgram({
4462
anchor,
@@ -48,9 +66,10 @@ const program = await createAnchorProgram({
4866

4967
const withdraw = getInstructionDefinition("prepare_phantom_withdraw");
5068
const programId = resolveProgramId("canonicalDevnet");
69+
const network = resolveNetworkConfig("devnet");
5170
```
5271

53-
## 6. Or Use the Public IDL Directly
72+
## 7. Or Use the Public IDL Directly
5473

5574
```typescript
5675
import { AnchorProvider, Idl, Program } from "@coral-xyz/anchor";
@@ -66,15 +85,15 @@ const program = new Program(
6685
);
6786
```
6887

69-
## 7. Run the Canonical Checks
88+
## 8. Run the Canonical Checks
7089

7190
```bash
7291
npm test
7392
npm run test:python:unit
7493
cargo test --offline
7594
```
7695

77-
## 8. Keep the Boundary Straight
96+
## 9. Keep the Boundary Straight
7897

7998
- the root repo now has a real Groth16 verifier path and a canonical circuit bundle
8099
- the repo still does **not** prove a completed audit or a mainnet release

historical/null-mint/MANIFEST.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
},
2727
{
2828
"path": "programs/paradox/src/lib.rs",
29-
"sha256": "555362463b40d827a5d4de89b9e9e334c695b8865e7c9dbd8d0dcd82509041fc",
30-
"size": 11542
29+
"sha256": "c2c0fed68e54be04565517f3fbabe195b3b7a20acd339ff434af34aa1c257659",
30+
"size": 11485
3131
},
3232
{
3333
"path": "programs/paradox/Cargo.toml",
34-
"sha256": "a96355eae0ea46f83fcdd91c8593c2768508794983a14b6d54eb61daab563a71",
35-
"size": 811
34+
"sha256": "dc3d6172b15d4545590e5838702573fbfdc845cbdff5a1024a7fbf768eae4b05",
35+
"size": 1022
3636
},
3737
{
3838
"path": "idl/paradox.json",

0 commit comments

Comments
 (0)