@ottochain/sdk 2.2.5 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/apps/contracts/index.js +15 -8
- package/dist/cjs/apps/contracts/state-machines/contract-agreement.js +89 -46
- package/dist/cjs/apps/contracts/state-machines/contract-escrow.js +152 -45
- package/dist/cjs/apps/contracts/state-machines/contract-universal.js +40 -8
- package/dist/cjs/apps/corporate/index.js +2 -2
- package/dist/cjs/apps/corporate/state-machines/corp-board.js +169 -59
- package/dist/cjs/apps/corporate/state-machines/corp-entity.js +245 -47
- package/dist/cjs/apps/corporate/state-machines/corp-securities.js +407 -99
- package/dist/cjs/apps/corporate/state-machines/corp-shareholders.js +151 -37
- package/dist/cjs/apps/governance/index.js +14 -14
- package/dist/cjs/apps/governance/state-machines/dao-multisig.js +85 -43
- package/dist/cjs/apps/governance/state-machines/dao-reputation.js +73 -43
- package/dist/cjs/apps/governance/state-machines/dao-single.js +55 -14
- package/dist/cjs/apps/governance/state-machines/dao-token.js +103 -45
- package/dist/cjs/apps/governance/state-machines/governance-simple.js +129 -65
- package/dist/cjs/apps/governance/state-machines/governance-universal.js +29 -5
- package/dist/cjs/apps/identity/constants.js +55 -14
- package/dist/cjs/apps/identity/index.js +27 -11
- package/dist/cjs/apps/identity/state-machines/identity-agent.js +57 -9
- package/dist/cjs/apps/identity/state-machines/identity-oracle.js +70 -27
- package/dist/cjs/apps/identity/state-machines/identity-registry.js +288 -0
- package/dist/cjs/apps/identity/state-machines/identity-universal.js +30 -6
- package/dist/cjs/apps/identity/state-machines/index.js +3 -1
- package/dist/cjs/apps/index.js +19 -8
- package/dist/cjs/apps/lending/assets.js +223 -0
- package/dist/cjs/apps/lending/credit-scoring.js +87 -0
- package/dist/cjs/apps/lending/eligibility.js +121 -0
- package/dist/cjs/apps/lending/index.js +91 -0
- package/dist/cjs/apps/lending/state-machines/index.js +9 -0
- package/dist/cjs/apps/lending/state-machines/lending-zk-loan.js +412 -0
- package/dist/cjs/apps/markets/index.js +21 -11
- package/dist/cjs/apps/markets/state-machines/market-auction.js +44 -19
- package/dist/cjs/apps/markets/state-machines/market-crowdfund.js +54 -29
- package/dist/cjs/apps/markets/state-machines/market-group-buy.js +53 -31
- package/dist/cjs/apps/markets/state-machines/market-prediction.js +120 -61
- package/dist/cjs/apps/markets/state-machines/market-universal.js +50 -10
- package/dist/cjs/apps/oracles/index.js +2 -2
- package/dist/cjs/errors.js +3 -3
- package/dist/cjs/generated/google/protobuf/struct.js +4 -4
- package/dist/cjs/generated/index.js +11 -7
- package/dist/cjs/generated/openapi.js +6 -0
- package/dist/cjs/generated/ottochain/apps/contracts/v1/contract.js +50 -50
- package/dist/cjs/generated/ottochain/apps/corporate/v1/corporate.js +19 -20
- package/dist/cjs/generated/ottochain/apps/governance/v1/governance.js +13 -13
- package/dist/cjs/generated/ottochain/apps/identity/v1/attestation.js +4 -4
- package/dist/cjs/generated/ottochain/apps/identity/v1/identity.js +124 -130
- package/dist/cjs/generated/ottochain/apps/markets/v1/market.js +107 -107
- package/dist/cjs/generated/ottochain/v1/common.js +1722 -1
- package/dist/cjs/generated/ottochain/v1/fiber.js +355 -5
- package/dist/cjs/generated/ottochain/v1/messages.js +775 -3
- package/dist/cjs/generated/ottochain/v1/records.js +350 -3
- package/dist/cjs/index.js +45 -7
- package/dist/cjs/openapi.js +2 -0
- package/dist/cjs/ottochain/drop-nulls.js +1 -2
- package/dist/cjs/ottochain/genesis-manifest.js +193 -0
- package/dist/cjs/ottochain/index.js +27 -13
- package/dist/cjs/ottochain/metagraph-client.js +65 -8
- package/dist/cjs/ottochain/snapshot.js +11 -12
- package/dist/cjs/ottochain/transaction.js +57 -14
- package/dist/cjs/ottochain/types.js +19 -1
- package/dist/cjs/privacy/index.js +14 -0
- package/dist/cjs/privacy/sealed-bid.js +210 -0
- package/dist/cjs/privacy/shield-app.js +131 -0
- package/dist/cjs/schema/effects.js +40 -0
- package/dist/cjs/schema/fiber-app.js +19 -13
- package/dist/cjs/schema/guard-lint.js +352 -0
- package/dist/cjs/schema/guards.js +246 -0
- package/dist/cjs/signing.js +80 -0
- package/dist/cjs/types.js +2 -3
- package/dist/cjs/validation.js +8 -8
- package/dist/cjs/verify.js +9 -4
- package/dist/cjs/zk/commit.js +53 -0
- package/dist/cjs/zk/guard.js +107 -0
- package/dist/cjs/zk/index.js +48 -0
- package/dist/cjs/zk/preimage.js +37 -0
- package/dist/cjs/zk/prover.js +87 -0
- package/dist/cjs/zk/registry.js +62 -0
- package/dist/cjs/zk/types.js +28 -0
- package/dist/cjs/zk/witness.js +19 -0
- package/dist/esm/apps/contracts/index.js +7 -3
- package/dist/esm/apps/contracts/state-machines/contract-agreement.js +89 -46
- package/dist/esm/apps/contracts/state-machines/contract-escrow.js +152 -45
- package/dist/esm/apps/contracts/state-machines/contract-universal.js +40 -8
- package/dist/esm/apps/corporate/state-machines/corp-board.js +169 -59
- package/dist/esm/apps/corporate/state-machines/corp-entity.js +245 -47
- package/dist/esm/apps/corporate/state-machines/corp-securities.js +407 -99
- package/dist/esm/apps/corporate/state-machines/corp-shareholders.js +151 -37
- package/dist/esm/apps/governance/state-machines/dao-multisig.js +85 -43
- package/dist/esm/apps/governance/state-machines/dao-reputation.js +73 -43
- package/dist/esm/apps/governance/state-machines/dao-single.js +55 -14
- package/dist/esm/apps/governance/state-machines/dao-token.js +103 -45
- package/dist/esm/apps/governance/state-machines/governance-simple.js +129 -65
- package/dist/esm/apps/governance/state-machines/governance-universal.js +29 -5
- package/dist/esm/apps/identity/constants.js +51 -12
- package/dist/esm/apps/identity/index.js +12 -7
- package/dist/esm/apps/identity/state-machines/identity-agent.js +57 -9
- package/dist/esm/apps/identity/state-machines/identity-oracle.js +70 -27
- package/dist/esm/apps/identity/state-machines/identity-registry.js +285 -0
- package/dist/esm/apps/identity/state-machines/identity-universal.js +30 -6
- package/dist/esm/apps/identity/state-machines/index.js +1 -0
- package/dist/esm/apps/index.js +1 -0
- package/dist/esm/apps/lending/assets.js +207 -0
- package/dist/esm/apps/lending/credit-scoring.js +81 -0
- package/dist/esm/apps/lending/eligibility.js +115 -0
- package/dist/esm/apps/lending/index.js +69 -0
- package/dist/esm/apps/lending/state-machines/index.js +5 -0
- package/dist/esm/apps/lending/state-machines/lending-zk-loan.js +409 -0
- package/dist/esm/apps/markets/index.js +8 -4
- package/dist/esm/apps/markets/state-machines/market-auction.js +44 -19
- package/dist/esm/apps/markets/state-machines/market-crowdfund.js +54 -29
- package/dist/esm/apps/markets/state-machines/market-group-buy.js +53 -31
- package/dist/esm/apps/markets/state-machines/market-prediction.js +120 -61
- package/dist/esm/apps/markets/state-machines/market-universal.js +50 -10
- package/dist/esm/generated/index.js +9 -5
- package/dist/esm/generated/openapi.js +5 -0
- package/dist/esm/generated/ottochain/apps/contracts/v1/contract.js +46 -46
- package/dist/esm/generated/ottochain/apps/identity/v1/identity.js +114 -120
- package/dist/esm/generated/ottochain/apps/markets/v1/market.js +100 -100
- package/dist/esm/generated/ottochain/v1/common.js +1718 -0
- package/dist/esm/generated/ottochain/v1/fiber.js +351 -1
- package/dist/esm/generated/ottochain/v1/messages.js +774 -2
- package/dist/esm/generated/ottochain/v1/records.js +349 -2
- package/dist/esm/index.js +23 -2
- package/dist/esm/openapi.js +1 -0
- package/dist/esm/ottochain/genesis-manifest.js +189 -0
- package/dist/esm/ottochain/index.js +4 -3
- package/dist/esm/ottochain/metagraph-client.js +65 -8
- package/dist/esm/ottochain/snapshot.js +4 -4
- package/dist/esm/ottochain/transaction.js +43 -3
- package/dist/esm/ottochain/types.js +18 -0
- package/dist/esm/privacy/index.js +6 -0
- package/dist/esm/privacy/sealed-bid.js +206 -0
- package/dist/esm/privacy/shield-app.js +127 -0
- package/dist/esm/schema/effects.js +35 -0
- package/dist/esm/schema/fiber-app.js +13 -6
- package/dist/esm/schema/guard-lint.js +346 -0
- package/dist/esm/schema/guards.js +229 -0
- package/dist/esm/signing.js +74 -0
- package/dist/esm/verify.js +8 -2
- package/dist/esm/zk/commit.js +44 -0
- package/dist/esm/zk/guard.js +99 -0
- package/dist/esm/zk/index.js +19 -0
- package/dist/esm/zk/preimage.js +30 -0
- package/dist/esm/zk/prover.js +82 -0
- package/dist/esm/zk/registry.js +55 -0
- package/dist/esm/zk/types.js +25 -0
- package/dist/esm/zk/witness.js +15 -0
- package/dist/types/apps/contracts/index.d.ts +159 -227
- package/dist/types/apps/contracts/state-machines/contract-agreement.d.ts +50 -101
- package/dist/types/apps/contracts/state-machines/contract-escrow.d.ts +86 -120
- package/dist/types/apps/contracts/state-machines/contract-universal.d.ts +20 -4
- package/dist/types/apps/corporate/index.d.ts +1040 -304
- package/dist/types/apps/corporate/state-machines/corp-board.d.ts +188 -52
- package/dist/types/apps/corporate/state-machines/corp-entity.d.ts +267 -62
- package/dist/types/apps/corporate/state-machines/corp-securities.d.ts +355 -131
- package/dist/types/apps/corporate/state-machines/corp-shareholders.d.ts +225 -53
- package/dist/types/apps/governance/index.d.ts +396 -417
- package/dist/types/apps/governance/state-machines/dao-multisig.d.ts +92 -95
- package/dist/types/apps/governance/state-machines/dao-reputation.d.ts +57 -69
- package/dist/types/apps/governance/state-machines/dao-single.d.ts +40 -50
- package/dist/types/apps/governance/state-machines/dao-token.d.ts +80 -53
- package/dist/types/apps/governance/state-machines/governance-simple.d.ts +107 -121
- package/dist/types/apps/governance/state-machines/governance-universal.d.ts +17 -5
- package/dist/types/apps/identity/constants.d.ts +32 -4
- package/dist/types/apps/identity/index.d.ts +449 -100
- package/dist/types/apps/identity/state-machines/identity-agent.d.ts +30 -0
- package/dist/types/apps/identity/state-machines/identity-oracle.d.ts +41 -87
- package/dist/types/apps/identity/state-machines/identity-registry.d.ts +379 -0
- package/dist/types/apps/identity/state-machines/identity-universal.d.ts +15 -0
- package/dist/types/apps/identity/state-machines/index.d.ts +1 -0
- package/dist/types/apps/index.d.ts +1 -0
- package/dist/types/apps/lending/assets.d.ts +258 -0
- package/dist/types/apps/lending/credit-scoring.d.ts +80 -0
- package/dist/types/apps/lending/eligibility.d.ts +83 -0
- package/dist/types/apps/lending/index.d.ts +548 -0
- package/dist/types/apps/lending/state-machines/index.d.ts +5 -0
- package/dist/types/apps/lending/state-machines/lending-zk-loan.d.ts +528 -0
- package/dist/types/apps/markets/index.d.ts +184 -204
- package/dist/types/apps/markets/state-machines/market-auction.d.ts +25 -37
- package/dist/types/apps/markets/state-machines/market-crowdfund.d.ts +26 -34
- package/dist/types/apps/markets/state-machines/market-group-buy.d.ts +31 -43
- package/dist/types/apps/markets/state-machines/market-prediction.d.ts +67 -72
- package/dist/types/apps/markets/state-machines/market-universal.d.ts +30 -10
- package/dist/types/generated/index.d.ts +5 -5
- package/dist/types/generated/openapi.d.ts +1257 -0
- package/dist/types/generated/ottochain/apps/contracts/v1/contract.d.ts +5 -5
- package/dist/types/generated/ottochain/apps/identity/v1/identity.d.ts +12 -12
- package/dist/types/generated/ottochain/apps/markets/v1/market.d.ts +11 -11
- package/dist/types/generated/ottochain/v1/common.d.ts +228 -0
- package/dist/types/generated/ottochain/v1/fiber.d.ts +32 -1
- package/dist/types/generated/ottochain/v1/messages.d.ts +84 -0
- package/dist/types/generated/ottochain/v1/records.d.ts +34 -0
- package/dist/types/index.d.ts +8 -2
- package/dist/types/openapi.d.ts +22 -0
- package/dist/types/ottochain/genesis-manifest.d.ts +130 -0
- package/dist/types/ottochain/index.d.ts +6 -5
- package/dist/types/ottochain/metagraph-client.d.ts +49 -7
- package/dist/types/ottochain/snapshot.d.ts +6 -6
- package/dist/types/ottochain/transaction.d.ts +33 -1
- package/dist/types/ottochain/types.d.ts +602 -11
- package/dist/types/privacy/index.d.ts +6 -0
- package/dist/types/privacy/sealed-bid.d.ts +348 -0
- package/dist/types/privacy/shield-app.d.ts +48 -0
- package/dist/types/schema/effects.d.ts +34 -0
- package/dist/types/schema/fiber-app.d.ts +41 -3
- package/dist/types/schema/guard-lint.d.ts +111 -0
- package/dist/types/schema/guards.d.ts +134 -0
- package/dist/types/signing.d.ts +45 -0
- package/dist/types/validation.d.ts +22 -217
- package/dist/types/verify.d.ts +4 -0
- package/dist/types/zk/commit.d.ts +28 -0
- package/dist/types/zk/guard.d.ts +46 -0
- package/dist/types/zk/index.d.ts +19 -0
- package/dist/types/zk/preimage.d.ts +10 -0
- package/dist/types/zk/prover.d.ts +34 -0
- package/dist/types/zk/registry.d.ts +34 -0
- package/dist/types/zk/types.d.ts +33 -0
- package/dist/types/zk/witness.d.ts +40 -0
- package/package.json +52 -35
- package/dist/cjs/ottochain/normalize.js +0 -186
- package/dist/esm/ottochain/normalize.js +0 -179
- package/dist/types/ottochain/normalize.d.ts +0 -79
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Poseidon commitments over BN254 Fr, computed through the JLVM `poseidon` opcode — the SAME
|
|
3
|
+
* byte-for-byte implementation the chain and the Rust/Scala circuits use. We deliberately route
|
|
4
|
+
* through `jsonLogic.apply` rather than reimplementing Poseidon: the opcode locks the field order,
|
|
5
|
+
* the circomlib round constants, and the `0x` 32-byte big-endian Fr encoding every crypto opcode
|
|
6
|
+
* expects (hard acceptance vector: `poseidon([1,2]) == 0x115cc0f5…189a`).
|
|
7
|
+
*
|
|
8
|
+
* (The standalone `poseidonHash`/`encodeFr` functions exist inside `-jlvm` but are NOT part of its
|
|
9
|
+
* public export surface — only the VM opcodes are — so the opcode path is also the only stable one.)
|
|
10
|
+
*/
|
|
11
|
+
import { jsonLogic } from '@constellation-network/metagraph-sdk-jlvm';
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
/**
|
|
14
|
+
* BN254 (alt_bn128) scalar field modulus R. A public field constant (not a secret / not Poseidon
|
|
15
|
+
* itself); the `poseidon` opcode re-validates canonicity, so this only drives reduction + sampling.
|
|
16
|
+
*/
|
|
17
|
+
export const R = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
18
|
+
/** Reduce an arbitrary bigint into a canonical BN254 Fr element `[0, R)`. */
|
|
19
|
+
export const toFr = (x) => ((x % R) + R) % R;
|
|
20
|
+
/** Encode a field element as the lowercase `0x` 32-byte big-endian hex the crypto opcodes expect. */
|
|
21
|
+
export const frToHex = (x) => `0x${toFr(x).toString(16).padStart(64, '0')}`;
|
|
22
|
+
const bytesToBig = (b) => b.reduce((a, x) => (a << 8n) | BigInt(x), 0n);
|
|
23
|
+
/** A fresh random salt as a canonical Fr (rejection-sampled to avoid modulo bias near R). */
|
|
24
|
+
export function randomSalt() {
|
|
25
|
+
for (;;) {
|
|
26
|
+
const v = bytesToBig(randomBytes(32));
|
|
27
|
+
if (v < R)
|
|
28
|
+
return v;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** `cm = Poseidon([...fields, salt])` via the VM's own opcode. Returns the `0x` 32-byte commitment. */
|
|
32
|
+
export function poseidonCommitN(fields, saltFr) {
|
|
33
|
+
const inputs = [...fields.map(frToHex), frToHex(saltFr)];
|
|
34
|
+
// The bundled poseidon supports width t ≤ 5 (≤ 4 inputs incl. the salt); the opcode throws past it.
|
|
35
|
+
return jsonLogic.apply({ poseidon: inputs }, {});
|
|
36
|
+
}
|
|
37
|
+
/** `cm = Poseidon([fieldFr, saltFr])` — the commitment for one value field (a bid, a score). */
|
|
38
|
+
export function poseidonCommit(fieldFr, saltFr) {
|
|
39
|
+
return poseidonCommitN([fieldFr], saltFr);
|
|
40
|
+
}
|
|
41
|
+
/** Commit `fields` under a fresh (or supplied) salt; returns the commitment + opening. */
|
|
42
|
+
export function openCommitment(fields, salt = randomSalt()) {
|
|
43
|
+
return { cm: poseidonCommitN(fields, salt), fields, salt };
|
|
44
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The semi-private guard: the JLVM predicate a policy embeds, and its client-side mirror.
|
|
3
|
+
*
|
|
4
|
+
* The guard pins THREE facts about the witness the holder carries, using only opcodes metakit
|
|
5
|
+
* already gas-meters — no new `jlvm_pv_decode`, the public-values blob is parsed with native
|
|
6
|
+
* `substr`/`cat`:
|
|
7
|
+
* 1. `groth16_verify[vkey, witness.publicValues, witness.proof]` — the SP1 proof is valid.
|
|
8
|
+
* 2. the `exprHash` word of the public values == the policy's `logicHash` — the proof ran the
|
|
9
|
+
* INTENDED rule (the pinned, legible predicate).
|
|
10
|
+
* 3. the `outputHash` word == `keccak256("true")` — that rule evaluated to TRUE on the hidden data.
|
|
11
|
+
*
|
|
12
|
+
* `publicValues` rides as a `0x` + 256-hex-char string (`0x` | exprHash | dataHash | outputHash | ok),
|
|
13
|
+
* so word w begins at char `2 + 64*w` and is 64 chars wide. `cat("0x", substr(pv, off, 64))` lifts a
|
|
14
|
+
* word back to a `0x`-hex value comparable with a `bytes32` literal. These offsets are the on-chain
|
|
15
|
+
* guard's contract; the Scala policy mirrors this exact shape.
|
|
16
|
+
*/
|
|
17
|
+
import { jsonLogic } from '@constellation-network/metagraph-sdk-jlvm';
|
|
18
|
+
import { KECCAK_TRUE } from './preimage';
|
|
19
|
+
import { decodeJlvmPublicValues } from './types';
|
|
20
|
+
const HEX_PREFIX = 2; // "0x"
|
|
21
|
+
const WORD_HEX = 64; // 32-byte ABI word as hex chars
|
|
22
|
+
/** Char offset of public-values word `w` (0=exprHash, 1=dataHash, 2=outputHash, 3=ok) in the `0x` string. */
|
|
23
|
+
export const wordOffset = (w) => HEX_PREFIX + WORD_HEX * w;
|
|
24
|
+
/** Lift public-values word `w` back to a `0x`-hex value (`cat("0x", substr(pv, off, 64))`). */
|
|
25
|
+
const pvWord = (w) => ({
|
|
26
|
+
cat: ['0x', { substr: [{ var: 'witness.publicValues' }, wordOffset(w), WORD_HEX] }],
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Build the JLVM guard a `Governed` morphism / mint policy embeds. `vkey` is the zk-jlvm program
|
|
30
|
+
* verification key (a `bytes32` `0x`-hex literal); `logicHash` is the pinned rule's hash (from
|
|
31
|
+
* {@link ExprRegistry.logicHashOf}). Returns plain JSON — feed it into an ASSET policy's guard/mintPolicy
|
|
32
|
+
* (the asset combiner injects the reserved `witness`). In a fiber TRANSITION there is no `witness` key —
|
|
33
|
+
* the proof rides in the event payload, so read `event.witness.*` instead.
|
|
34
|
+
*
|
|
35
|
+
* REPLAY (audit zk-guards): the proven public values commit only {exprHash, dataHash, outputHash, ok} —
|
|
36
|
+
* nothing situational (no fiber / asset / `$ordinal` / nonce). A valid `{publicValues, proof}` is therefore
|
|
37
|
+
* a reusable bearer token, replayable across any action pinning the same vkey + logicHash. Bind the action
|
|
38
|
+
* INSIDE the proven rule (pin a spender/asset/subject, as `reputationCreditRule` pins `subject`) and/or gate
|
|
39
|
+
* with a one-time nonce ledger until the public values carry action context.
|
|
40
|
+
*/
|
|
41
|
+
export function semiPrivateGuard(vkey, logicHash, opts = {}) {
|
|
42
|
+
const { requireTrue = true } = opts;
|
|
43
|
+
const clauses = [
|
|
44
|
+
{ groth16_verify: [vkey, { var: 'witness.publicValues' }, { var: 'witness.proof' }] },
|
|
45
|
+
{ '==': [pvWord(0), logicHash] },
|
|
46
|
+
];
|
|
47
|
+
if (requireTrue) {
|
|
48
|
+
clauses.push({ '==': [pvWord(2), KECCAK_TRUE] });
|
|
49
|
+
// ok bit: the final hex pair of word 3 (offset 256) must be "01" — the JLVM run did not error.
|
|
50
|
+
clauses.push({ '==': [{ substr: [{ var: 'witness.publicValues' }, wordOffset(3) + 62, 2] }, '01'] });
|
|
51
|
+
}
|
|
52
|
+
return { and: clauses };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Locally run the SAME `groth16_verify` pairing the chain runs (the verifier opcode, not the prover).
|
|
56
|
+
* Returns `false` — never throws — on an invalid/garbage bundle, mirroring the guard's graceful deny.
|
|
57
|
+
*/
|
|
58
|
+
export function verifyGroth16Bundle(bundle) {
|
|
59
|
+
try {
|
|
60
|
+
return jsonLogic.apply({ groth16_verify: [bundle.vkey, bundle.publicValues, bundle.proof] }, {}) === true;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check the binding words WITHOUT the pairing: `exprHash == expectedLogicHash`, `ok` set, and
|
|
68
|
+
* (when `requireTrue`) `outputHash == keccak256("true")`. Pair with {@link verifyGroth16Bundle} for
|
|
69
|
+
* the full client-side mirror of the on-chain guard (see {@link verifySemiPrivate}).
|
|
70
|
+
*/
|
|
71
|
+
export function checkSemiPrivateBinding(bundle, expectedLogicHash, opts = {}) {
|
|
72
|
+
const { requireTrue = true } = opts;
|
|
73
|
+
const decoded = decodeJlvmPublicValues(bundle.publicValues);
|
|
74
|
+
const eq = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
75
|
+
const reasons = [];
|
|
76
|
+
if (!eq(decoded.exprHash, expectedLogicHash))
|
|
77
|
+
reasons.push(`exprHash ${decoded.exprHash} != expected logicHash ${expectedLogicHash}`);
|
|
78
|
+
if (!decoded.ok)
|
|
79
|
+
reasons.push('ok bit is false (guest evaluation errored)');
|
|
80
|
+
if (requireTrue && !eq(decoded.outputHash, KECCAK_TRUE))
|
|
81
|
+
reasons.push(`outputHash ${decoded.outputHash} != keccak256("true") ${KECCAK_TRUE}`);
|
|
82
|
+
return { ok: reasons.length === 0, reasons, decoded };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The full client-side mirror of the on-chain guard: the proof verifies AND its words bind to the
|
|
86
|
+
* pinned rule (and, by default, to a `true` result). Run this before submitting to fail fast instead
|
|
87
|
+
* of paying for an on-chain rejection.
|
|
88
|
+
*/
|
|
89
|
+
export function verifySemiPrivate(bundle, expectedLogicHash, opts = {}) {
|
|
90
|
+
const binding = checkSemiPrivateBinding(bundle, expectedLogicHash, opts);
|
|
91
|
+
if (!verifyGroth16Bundle(bundle)) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
reasons: ['groth16_verify failed (invalid proof for this vkey/publicValues)', ...binding.reasons],
|
|
95
|
+
decoded: binding.decoded,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return binding;
|
|
99
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ottochain/sdk/zk` — the client-side semi-private tier.
|
|
3
|
+
*
|
|
4
|
+
* Public-by-default, private-by-opt-in: a holder proves (off-chain, via SP1 zk-jlvm) that some
|
|
5
|
+
* PRIVATE data satisfies a PUBLIC, pinned JLVM rule, and carries only `{publicValues, proof}` on a
|
|
6
|
+
* signed op. A policy's {@link semiPrivateGuard} re-verifies the proof and binds it to the pinned
|
|
7
|
+
* rule on-chain — the value stays hidden, the predicate stays legible.
|
|
8
|
+
*
|
|
9
|
+
* The ONE canonical invariant tying it together: the prover is fed `canonicalForSigning(expr)` /
|
|
10
|
+
* `canonicalForSigning(data)`, so a proof's `exprHash` word equals {@link exprHash}`(rule)` equals a
|
|
11
|
+
* policy's `logicHash` — see {@link proverPreimage} and docs/design/client-side-private-data.md §3.0.
|
|
12
|
+
*/
|
|
13
|
+
export { canonicalForSigning, proverPreimage, exprHash, dataHash, KECCAK_TRUE, } from './preimage';
|
|
14
|
+
export { R, toFr, frToHex, randomSalt, poseidonCommit, poseidonCommitN, openCommitment, } from './commit';
|
|
15
|
+
export { decodeJlvmPublicValues, } from './types';
|
|
16
|
+
export { groth16Witness, pmtWitness, } from './witness';
|
|
17
|
+
export { ExprRegistry, boundRule, atLeast, atMost, } from './registry';
|
|
18
|
+
export { wordOffset, semiPrivateGuard, verifyGroth16Bundle, checkSemiPrivateBinding, verifySemiPrivate, } from './guard';
|
|
19
|
+
export { SubprocessProver, parseGroth16Stdout, } from './prover';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE canonical path for the semi-private tier.
|
|
3
|
+
*
|
|
4
|
+
* metakit-sdk 1.8.x `canonicalize` = `serializeJcs(dropNullFields)` — it drops null object-fields
|
|
5
|
+
* before RFC 8785 (server-aligned), so it is the EXACT byte string the chain signs over. Feeding
|
|
6
|
+
* the zk-jlvm prover this same string makes the proof's keccak preimage equal the signature
|
|
7
|
+
* preimage — the determinism binding (see docs/design/client-side-private-data.md §3.0).
|
|
8
|
+
*
|
|
9
|
+
* Note the two hashes differ by design: signing is sha256 over the canonical bytes (+ Constellation
|
|
10
|
+
* prefix), the zk-jlvm guest is keccak256 over the canonical bytes — they bind the SAME canonical
|
|
11
|
+
* string, which is the load-bearing invariant.
|
|
12
|
+
*/
|
|
13
|
+
import { canonicalize } from '@constellation-network/metagraph-sdk';
|
|
14
|
+
import { keccak_256 } from '@noble/hashes/sha3.js';
|
|
15
|
+
const utf8 = (s) => new TextEncoder().encode(s);
|
|
16
|
+
const toHex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
|
|
17
|
+
/** The single canonical string both the signer and the prover-feed share (JCS ∘ dropNullFields). */
|
|
18
|
+
export function canonicalForSigning(x) {
|
|
19
|
+
return canonicalize(x);
|
|
20
|
+
}
|
|
21
|
+
/** keccak256 of the canonical bytes — the value the zk-jlvm guest commits as exprHash / dataHash. */
|
|
22
|
+
export function proverPreimage(x) {
|
|
23
|
+
return `0x${toHex(keccak_256(utf8(canonicalForSigning(x))))}`;
|
|
24
|
+
}
|
|
25
|
+
/** exprHash of a published JLVM rule (canonicalized + keccak'd). Equals the chain's `logicHash`. */
|
|
26
|
+
export const exprHash = (rule) => proverPreimage(rule);
|
|
27
|
+
/** dataHash of a private data context — kept private; only its hash is public. */
|
|
28
|
+
export const dataHash = (data) => proverPreimage(data);
|
|
29
|
+
/** `keccak256(canonicalize(true))` — the `outputHash` a guard binds for a "rule returned true" proof. */
|
|
30
|
+
export const KECCAK_TRUE = proverPreimage(true);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driving the SP1 zk-jlvm host (`rust/zk-jlvm/script`, `--mode groth16`) to produce a
|
|
3
|
+
* {@link Groth16Bundle} from a JLVM `expr` + `data`.
|
|
4
|
+
*
|
|
5
|
+
* THE load-bearing step: the host's guest hashes the RAW `--expr` / `--data` strings
|
|
6
|
+
* (`exprHash = keccak256(expr_bytes)`), so this driver feeds it `canonicalForSigning(expr)` /
|
|
7
|
+
* `canonicalForSigning(data)`. That makes a proof's `exprHash` word equal {@link exprHash}`(rule)`
|
|
8
|
+
* equal a policy's `logicHash` — without it the binding silently never matches.
|
|
9
|
+
*
|
|
10
|
+
* Proving is heavy (SP1; minutes, optionally `SP1_PROVER=cuda`) and needs the Rust toolchain, so this
|
|
11
|
+
* is an out-of-process driver, not an in-SDK prover. Point it at a prebuilt `binaryPath`, or at the
|
|
12
|
+
* crate via `cargoManifestPath`; configure either explicitly or through `ZK_JLVM_BIN` /
|
|
13
|
+
* `ZK_JLVM_MANIFEST` env vars.
|
|
14
|
+
*/
|
|
15
|
+
import { canonicalForSigning } from './preimage';
|
|
16
|
+
const reLine = (label) => new RegExp(`^${label}:\\s*(0x[0-9a-fA-F]+)\\s*$`, 'm');
|
|
17
|
+
/** Parse the host's `vkey:` / `public values:` / `proof bytes:` stdout lines into a bundle. */
|
|
18
|
+
export function parseGroth16Stdout(stdout) {
|
|
19
|
+
const grab = (label, key) => {
|
|
20
|
+
const m = stdout.match(reLine(label));
|
|
21
|
+
if (!m)
|
|
22
|
+
throw new Error(`zk-jlvm host produced no "${label}:" line (could not read ${key})`);
|
|
23
|
+
return m[1].toLowerCase();
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
vkey: grab('vkey', 'vkey'),
|
|
27
|
+
publicValues: grab('public values', 'publicValues'),
|
|
28
|
+
proof: grab('proof bytes', 'proof'),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Drives the zk-jlvm host out-of-process. */
|
|
32
|
+
export class SubprocessProver {
|
|
33
|
+
constructor(opts = {}) {
|
|
34
|
+
this.opts = opts;
|
|
35
|
+
}
|
|
36
|
+
async proveGroth16(req) {
|
|
37
|
+
// Canonicalize BEFORE proving so exprHash/dataHash bind the same bytes the chain signs over.
|
|
38
|
+
const expr = canonicalForSigning(req.expr);
|
|
39
|
+
const data = canonicalForSigning(req.data);
|
|
40
|
+
const modeArgs = ['--mode', 'groth16', '--expr', expr, '--data', data];
|
|
41
|
+
const binaryPath = this.opts.binaryPath ?? process.env.ZK_JLVM_BIN;
|
|
42
|
+
const manifest = this.opts.cargoManifestPath ?? process.env.ZK_JLVM_MANIFEST;
|
|
43
|
+
let command;
|
|
44
|
+
let args;
|
|
45
|
+
if (binaryPath) {
|
|
46
|
+
command = binaryPath;
|
|
47
|
+
args = modeArgs;
|
|
48
|
+
}
|
|
49
|
+
else if (manifest) {
|
|
50
|
+
command = 'cargo';
|
|
51
|
+
args = ['run', '--release', '--manifest-path', manifest, '--', ...modeArgs];
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
throw new Error('SubprocessProver: set `binaryPath` (or ZK_JLVM_BIN), or `cargoManifestPath` (or ZK_JLVM_MANIFEST)');
|
|
55
|
+
}
|
|
56
|
+
const stdout = await this.run(command, args);
|
|
57
|
+
return parseGroth16Stdout(stdout);
|
|
58
|
+
}
|
|
59
|
+
run(command, args) {
|
|
60
|
+
const { env, cwd, timeoutMs = 1800000 } = this.opts;
|
|
61
|
+
// Lazy-load child_process so importing the zk slice stays light for non-proving consumers.
|
|
62
|
+
return import('node:child_process').then(({ spawn }) => new Promise((resolve, reject) => {
|
|
63
|
+
// argv (no shell) — `expr`/`data` are JSON with quotes/braces; never interpolate into a shell.
|
|
64
|
+
const child = spawn(command, args, {
|
|
65
|
+
cwd,
|
|
66
|
+
env: { ...process.env, ...env },
|
|
67
|
+
timeout: timeoutMs,
|
|
68
|
+
});
|
|
69
|
+
let out = '';
|
|
70
|
+
let err = '';
|
|
71
|
+
child.stdout.on('data', (d) => (out += d.toString()));
|
|
72
|
+
child.stderr.on('data', (d) => (err += d.toString()));
|
|
73
|
+
child.on('error', reject);
|
|
74
|
+
child.on('close', (code) => {
|
|
75
|
+
if (code === 0)
|
|
76
|
+
resolve(out);
|
|
77
|
+
else
|
|
78
|
+
reject(new Error(`zk-jlvm host exited ${code}: ${err.trim() || out.trim()}`));
|
|
79
|
+
});
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The expression registry binds a human name → a published JLVM rule → its `logicHash`
|
|
3
|
+
* (`keccak256(canonicalize(rule))`). The `logicHash` is the load-bearing public value: it equals the
|
|
4
|
+
* `exprHash` word a zk-jlvm proof commits AND the literal a semi-private guard pins. Registering a
|
|
5
|
+
* rule with a LITERAL bound ("pin the bound into the rule", e.g. `{">=":[{"var":"score"},700]}`)
|
|
6
|
+
* makes "the proof's exprHash == this logicHash" mean "the hidden value satisfies THIS public,
|
|
7
|
+
* legible rule" — privacy of the value, transparency of the predicate.
|
|
8
|
+
*/
|
|
9
|
+
import { exprHash } from './preimage';
|
|
10
|
+
/** A client-side registry mapping rule names to their canonical `logicHash`. */
|
|
11
|
+
export class ExprRegistry {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.byName = new Map();
|
|
14
|
+
}
|
|
15
|
+
/** Register (or replace) a named rule; returns the entry with its computed `logicHash`. */
|
|
16
|
+
register(name, rule) {
|
|
17
|
+
const entry = { rule, logicHash: exprHash(rule) };
|
|
18
|
+
this.byName.set(name, entry);
|
|
19
|
+
return entry;
|
|
20
|
+
}
|
|
21
|
+
/** Whether a rule is registered under `name`. */
|
|
22
|
+
has(name) {
|
|
23
|
+
return this.byName.has(name);
|
|
24
|
+
}
|
|
25
|
+
/** The registered rule, or throw if unknown. */
|
|
26
|
+
ruleOf(name) {
|
|
27
|
+
return this.require(name).rule;
|
|
28
|
+
}
|
|
29
|
+
/** The registered rule's `logicHash`, or throw if unknown. */
|
|
30
|
+
logicHashOf(name) {
|
|
31
|
+
return this.require(name).logicHash;
|
|
32
|
+
}
|
|
33
|
+
/** All registered names, in insertion order. */
|
|
34
|
+
names() {
|
|
35
|
+
return [...this.byName.keys()];
|
|
36
|
+
}
|
|
37
|
+
require(name) {
|
|
38
|
+
const entry = this.byName.get(name);
|
|
39
|
+
if (!entry)
|
|
40
|
+
throw new Error(`ExprRegistry: no rule registered under "${name}"`);
|
|
41
|
+
return entry;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A single-bound predicate `{[op]:[{var:varName}, bound]}` — the "pinned bound" a proof binds to.
|
|
46
|
+
* `bound` stays a JSON primitive (number/string) so the canonical bytes the prover hashes match the
|
|
47
|
+
* canonical bytes the rule registers under (bigint is not JSON-serializable — pre-encode if needed).
|
|
48
|
+
*/
|
|
49
|
+
export function boundRule(varName, op, bound) {
|
|
50
|
+
return { [op]: [{ var: varName }, bound] };
|
|
51
|
+
}
|
|
52
|
+
/** `{">=":[{"var":varName}, bound]}` — "the hidden `varName` is at least `bound`". */
|
|
53
|
+
export const atLeast = (varName, bound) => boundRule(varName, '>=', bound);
|
|
54
|
+
/** `{"<=":[{"var":varName}, bound]}` — "the hidden `varName` is at most `bound`". */
|
|
55
|
+
export const atMost = (varName, bound) => boundRule(varName, '<=', bound);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SP1 zk-jlvm public-values types + decode.
|
|
3
|
+
*
|
|
4
|
+
* The guest commits `abi_encode(JlvmPublicValues{ bytes32 exprHash, bytes32 dataHash,
|
|
5
|
+
* bytes32 outputHash, bool ok })` — four 32-byte words, the bool right-aligned in its word.
|
|
6
|
+
* There is no on-chain `jlvm_pv_decode` opcode: the chain guard parses the same blob with the
|
|
7
|
+
* native JLVM `cat`/`substr` ops. This decode mirrors that for client-side binding/inspection.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Decode `abi_encode(JlvmPublicValues)`: four 32-byte words `[exprHash | dataHash | outputHash | ok]`.
|
|
11
|
+
* The bool is right-aligned in its word (final byte `0x00`/`0x01`).
|
|
12
|
+
*/
|
|
13
|
+
export function decodeJlvmPublicValues(pv) {
|
|
14
|
+
const b = pv.startsWith('0x') ? pv.slice(2) : pv;
|
|
15
|
+
if (b.length < 4 * 64) {
|
|
16
|
+
throw new Error(`publicValues too short: ${b.length} hex chars (need >= 256)`);
|
|
17
|
+
}
|
|
18
|
+
const word = (i) => `0x${b.slice(i * 64, i * 64 + 64)}`;
|
|
19
|
+
return {
|
|
20
|
+
exprHash: word(0),
|
|
21
|
+
dataHash: word(1),
|
|
22
|
+
outputHash: word(2),
|
|
23
|
+
ok: b.slice(3 * 64 + 62, 3 * 64 + 64) !== '00',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { decodeJlvmPublicValues } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Shape a produced {@link Groth16Bundle} into the on-op witness map (`{publicValues, proof}`) and
|
|
4
|
+
* surface its decoded public values. The vkey rides in the guard, not the witness, so it is dropped.
|
|
5
|
+
*/
|
|
6
|
+
export function groth16Witness(bundle) {
|
|
7
|
+
return {
|
|
8
|
+
witness: { publicValues: bundle.publicValues, proof: bundle.proof },
|
|
9
|
+
decoded: decodeJlvmPublicValues(bundle.publicValues),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Shape a Poseidon-Merkle inclusion path into the on-op `pmt_verify` witness map. */
|
|
13
|
+
export function pmtWitness(leaf, index, siblings) {
|
|
14
|
+
return { leaf, index, siblings };
|
|
15
|
+
}
|