@ottochain/sdk 2.3.0 → 2.5.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/compute/index.js +33 -0
- package/dist/cjs/apps/compute/state-machines/compute-rule110.js +164 -0
- package/dist/cjs/apps/compute/state-machines/index.js +9 -0
- package/dist/cjs/apps/contracts/index.js +16 -9
- package/dist/cjs/apps/contracts/state-machines/contract-agreement.js +134 -161
- package/dist/cjs/apps/contracts/state-machines/contract-escrow.js +192 -170
- package/dist/cjs/apps/contracts/state-machines/contract-universal.js +46 -58
- package/dist/cjs/apps/corporate/index.js +2 -2
- package/dist/cjs/apps/corporate/state-machines/corp-board.js +500 -456
- package/dist/cjs/apps/corporate/state-machines/corp-entity.js +487 -361
- package/dist/cjs/apps/corporate/state-machines/corp-securities.js +769 -576
- package/dist/cjs/apps/corporate/state-machines/corp-shareholders.js +567 -557
- package/dist/cjs/apps/governance/index.js +21 -21
- package/dist/cjs/apps/governance/state-machines/dao-multisig.js +188 -209
- package/dist/cjs/apps/governance/state-machines/dao-reputation.js +181 -228
- package/dist/cjs/apps/governance/state-machines/dao-single.js +105 -88
- package/dist/cjs/apps/governance/state-machines/dao-token.js +199 -227
- package/dist/cjs/apps/governance/state-machines/governance-simple.js +231 -231
- package/dist/cjs/apps/governance/state-machines/governance-universal.js +64 -64
- package/dist/cjs/apps/identity/constants.js +55 -22
- package/dist/cjs/apps/identity/index.js +28 -12
- package/dist/cjs/apps/identity/state-machines/identity-agent.js +124 -158
- package/dist/cjs/apps/identity/state-machines/identity-oracle.js +158 -173
- package/dist/cjs/apps/identity/state-machines/identity-registry.js +262 -0
- package/dist/cjs/apps/identity/state-machines/identity-universal.js +51 -57
- package/dist/cjs/apps/identity/state-machines/index.js +3 -1
- package/dist/cjs/apps/index.js +22 -8
- package/dist/cjs/apps/lending/assets.js +247 -0
- package/dist/cjs/apps/lending/credit-scoring.js +87 -0
- package/dist/cjs/apps/lending/eligibility.js +105 -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 +433 -0
- package/dist/cjs/apps/markets/index.js +22 -12
- package/dist/cjs/apps/markets/state-machines/market-auction.js +116 -134
- package/dist/cjs/apps/markets/state-machines/market-crowdfund.js +131 -141
- package/dist/cjs/apps/markets/state-machines/market-group-buy.js +163 -184
- package/dist/cjs/apps/markets/state-machines/market-prediction.js +226 -233
- package/dist/cjs/apps/markets/state-machines/market-universal.js +65 -80
- package/dist/cjs/apps/oracles/index.js +3 -3
- package/dist/cjs/apps/privacy/index.js +67 -0
- package/dist/cjs/apps/privacy/state-machines/index.js +17 -0
- package/dist/cjs/apps/privacy/state-machines/mixer-ddhRing.js +286 -0
- package/dist/cjs/apps/privacy/state-machines/note-pool.js +476 -0
- package/dist/cjs/apps/staked-pool/assets.js +103 -0
- package/dist/cjs/apps/staked-pool/base.js +498 -0
- package/dist/cjs/apps/staked-pool/consumer.js +55 -0
- package/dist/cjs/apps/staked-pool/index.js +83 -0
- package/dist/cjs/apps/staked-pool/state-machines/index.js +12 -0
- package/dist/cjs/apps/staked-pool/state-machines/staked-pool-base.js +49 -0
- package/dist/cjs/apps/staked-pool/state-machines/staked-pool-oracle.js +131 -0
- package/dist/cjs/errors.js +3 -3
- package/dist/cjs/generated/google/protobuf/struct.js +5 -5
- package/dist/cjs/generated/google/protobuf/timestamp.js +1 -1
- 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 +51 -51
- package/dist/cjs/generated/ottochain/apps/corporate/v1/corporate.js +20 -21
- package/dist/cjs/generated/ottochain/apps/governance/v1/governance.js +14 -14
- package/dist/cjs/generated/ottochain/apps/identity/v1/attestation.js +5 -5
- package/dist/cjs/generated/ottochain/apps/identity/v1/identity.js +125 -131
- package/dist/cjs/generated/ottochain/apps/markets/v1/market.js +108 -108
- package/dist/cjs/generated/ottochain/v1/common.js +5 -5
- package/dist/cjs/generated/ottochain/v1/fiber.js +44 -6
- package/dist/cjs/generated/ottochain/v1/messages.js +1 -1
- package/dist/cjs/generated/ottochain/v1/records.js +1 -1
- package/dist/cjs/index.js +58 -7
- package/dist/cjs/openapi.js +2 -0
- package/dist/cjs/ottochain/drop-nulls.js +1 -2
- package/dist/cjs/ottochain/genesis-manifest.js +17 -11
- package/dist/cjs/ottochain/index.js +23 -13
- package/dist/cjs/ottochain/metagraph-client.js +81 -16
- package/dist/cjs/ottochain/snapshot.js +13 -16
- package/dist/cjs/ottochain/transaction.js +67 -14
- package/dist/cjs/ottochain/types.js +16 -2
- package/dist/cjs/privacy/index.js +14 -0
- package/dist/cjs/privacy/sealed-bid.js +213 -0
- package/dist/cjs/privacy/shield-app.js +131 -0
- package/dist/cjs/schema/effects.js +156 -0
- package/dist/cjs/schema/fiber-app.js +150 -15
- package/dist/cjs/schema/guard-lint.js +343 -0
- package/dist/cjs/schema/guards.js +246 -0
- package/dist/cjs/signing.js +80 -0
- package/dist/cjs/templates/asset-policy.js +190 -0
- package/dist/cjs/templates/index.js +86 -0
- package/dist/cjs/templates/machine.js +130 -0
- package/dist/cjs/templates/migration.js +39 -0
- package/dist/cjs/templates/state-shape.js +38 -0
- package/dist/cjs/types.js +2 -3
- package/dist/cjs/validation.js +9 -11
- 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/compute/index.js +29 -0
- package/dist/esm/apps/compute/state-machines/compute-rule110.js +161 -0
- package/dist/esm/apps/compute/state-machines/index.js +5 -0
- package/dist/esm/apps/contracts/index.js +9 -5
- package/dist/esm/apps/contracts/state-machines/contract-agreement.js +135 -162
- package/dist/esm/apps/contracts/state-machines/contract-escrow.js +193 -171
- package/dist/esm/apps/contracts/state-machines/contract-universal.js +47 -59
- package/dist/esm/apps/corporate/index.js +2 -2
- package/dist/esm/apps/corporate/state-machines/corp-board.js +501 -457
- package/dist/esm/apps/corporate/state-machines/corp-entity.js +488 -362
- package/dist/esm/apps/corporate/state-machines/corp-securities.js +770 -577
- package/dist/esm/apps/corporate/state-machines/corp-shareholders.js +568 -558
- package/dist/esm/apps/governance/index.js +10 -10
- package/dist/esm/apps/governance/state-machines/dao-multisig.js +189 -210
- package/dist/esm/apps/governance/state-machines/dao-reputation.js +182 -229
- package/dist/esm/apps/governance/state-machines/dao-single.js +106 -89
- package/dist/esm/apps/governance/state-machines/dao-token.js +200 -228
- package/dist/esm/apps/governance/state-machines/governance-simple.js +232 -232
- package/dist/esm/apps/governance/state-machines/governance-universal.js +65 -65
- package/dist/esm/apps/identity/constants.js +52 -21
- package/dist/esm/apps/identity/index.js +14 -9
- package/dist/esm/apps/identity/state-machines/identity-agent.js +125 -159
- package/dist/esm/apps/identity/state-machines/identity-oracle.js +159 -174
- package/dist/esm/apps/identity/state-machines/identity-registry.js +259 -0
- package/dist/esm/apps/identity/state-machines/identity-universal.js +52 -58
- package/dist/esm/apps/identity/state-machines/index.js +1 -0
- package/dist/esm/apps/index.js +9 -5
- package/dist/esm/apps/lending/assets.js +231 -0
- package/dist/esm/apps/lending/credit-scoring.js +81 -0
- package/dist/esm/apps/lending/eligibility.js +99 -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 +430 -0
- package/dist/esm/apps/markets/index.js +11 -7
- package/dist/esm/apps/markets/state-machines/market-auction.js +117 -135
- package/dist/esm/apps/markets/state-machines/market-crowdfund.js +132 -142
- package/dist/esm/apps/markets/state-machines/market-group-buy.js +164 -185
- package/dist/esm/apps/markets/state-machines/market-prediction.js +227 -234
- package/dist/esm/apps/markets/state-machines/market-universal.js +66 -81
- package/dist/esm/apps/oracles/index.js +3 -3
- package/dist/esm/apps/privacy/index.js +56 -0
- package/dist/esm/apps/privacy/state-machines/index.js +6 -0
- package/dist/esm/apps/privacy/state-machines/mixer-ddhRing.js +282 -0
- package/dist/esm/apps/privacy/state-machines/note-pool.js +470 -0
- package/dist/esm/apps/staked-pool/assets.js +91 -0
- package/dist/esm/apps/staked-pool/base.js +493 -0
- package/dist/esm/apps/staked-pool/consumer.js +49 -0
- package/dist/esm/apps/staked-pool/index.js +60 -0
- package/dist/esm/apps/staked-pool/state-machines/index.js +6 -0
- package/dist/esm/apps/staked-pool/state-machines/staked-pool-base.js +46 -0
- package/dist/esm/apps/staked-pool/state-machines/staked-pool-oracle.js +127 -0
- package/dist/esm/generated/google/protobuf/struct.js +1 -1
- package/dist/esm/generated/google/protobuf/timestamp.js +1 -1
- 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 +47 -47
- package/dist/esm/generated/ottochain/apps/corporate/v1/corporate.js +1 -1
- package/dist/esm/generated/ottochain/apps/governance/v1/governance.js +1 -1
- package/dist/esm/generated/ottochain/apps/identity/v1/attestation.js +1 -1
- package/dist/esm/generated/ottochain/apps/identity/v1/identity.js +115 -121
- package/dist/esm/generated/ottochain/apps/markets/v1/market.js +101 -101
- package/dist/esm/generated/ottochain/v1/common.js +1 -1
- package/dist/esm/generated/ottochain/v1/fiber.js +40 -2
- package/dist/esm/generated/ottochain/v1/messages.js +1 -1
- package/dist/esm/generated/ottochain/v1/records.js +1 -1
- package/dist/esm/index.js +28 -3
- package/dist/esm/openapi.js +1 -0
- package/dist/esm/ottochain/genesis-manifest.js +15 -9
- package/dist/esm/ottochain/index.js +2 -3
- package/dist/esm/ottochain/metagraph-client.js +81 -16
- package/dist/esm/ottochain/snapshot.js +6 -8
- package/dist/esm/ottochain/transaction.js +51 -3
- package/dist/esm/ottochain/types.js +15 -1
- package/dist/esm/privacy/index.js +6 -0
- package/dist/esm/privacy/sealed-bid.js +209 -0
- package/dist/esm/privacy/shield-app.js +127 -0
- package/dist/esm/schema/effects.js +145 -0
- package/dist/esm/schema/fiber-app.js +139 -8
- package/dist/esm/schema/guard-lint.js +337 -0
- package/dist/esm/schema/guards.js +228 -0
- package/dist/esm/signing.js +74 -0
- package/dist/esm/templates/asset-policy.js +182 -0
- package/dist/esm/templates/index.js +41 -0
- package/dist/esm/templates/machine.js +125 -0
- package/dist/esm/templates/migration.js +34 -0
- package/dist/esm/templates/state-shape.js +34 -0
- package/dist/esm/validation.js +1 -3
- 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/compute/index.d.ts +176 -0
- package/dist/types/apps/compute/state-machines/compute-rule110.d.ts +157 -0
- package/dist/types/apps/compute/state-machines/index.d.ts +5 -0
- package/dist/types/apps/contracts/index.d.ts +81 -221
- package/dist/types/apps/contracts/state-machines/contract-agreement.d.ts +23 -98
- package/dist/types/apps/contracts/state-machines/contract-escrow.d.ts +49 -115
- package/dist/types/apps/contracts/state-machines/contract-universal.d.ts +4 -4
- package/dist/types/apps/corporate/index.d.ts +1176 -440
- package/dist/types/apps/corporate/state-machines/corp-board.d.ts +271 -135
- package/dist/types/apps/corporate/state-machines/corp-entity.d.ts +279 -74
- package/dist/types/apps/corporate/state-machines/corp-securities.d.ts +373 -149
- package/dist/types/apps/corporate/state-machines/corp-shareholders.d.ts +252 -80
- package/dist/types/apps/governance/index.d.ts +368 -470
- package/dist/types/apps/governance/state-machines/dao-multisig.d.ts +92 -107
- package/dist/types/apps/governance/state-machines/dao-reputation.d.ts +61 -85
- package/dist/types/apps/governance/state-machines/dao-single.d.ts +25 -47
- package/dist/types/apps/governance/state-machines/dao-token.d.ts +79 -68
- package/dist/types/apps/governance/state-machines/governance-simple.d.ts +95 -125
- package/dist/types/apps/governance/state-machines/governance-universal.d.ts +6 -6
- package/dist/types/apps/identity/constants.d.ts +33 -5
- package/dist/types/apps/identity/index.d.ts +415 -131
- package/dist/types/apps/identity/state-machines/identity-agent.d.ts +23 -23
- package/dist/types/apps/identity/state-machines/identity-oracle.d.ts +27 -98
- package/dist/types/apps/identity/state-machines/identity-registry.d.ts +379 -0
- package/dist/types/apps/identity/state-machines/identity-universal.d.ts +4 -4
- package/dist/types/apps/identity/state-machines/index.d.ts +1 -0
- package/dist/types/apps/index.d.ts +9 -5
- 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 +556 -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 +536 -0
- package/dist/types/apps/markets/index.d.ts +181 -276
- package/dist/types/apps/markets/state-machines/market-auction.d.ts +25 -49
- package/dist/types/apps/markets/state-machines/market-crowdfund.d.ts +35 -53
- package/dist/types/apps/markets/state-machines/market-group-buy.d.ts +35 -61
- package/dist/types/apps/markets/state-machines/market-prediction.d.ts +65 -86
- package/dist/types/apps/markets/state-machines/market-universal.d.ts +12 -12
- package/dist/types/apps/oracles/index.d.ts +1 -1
- package/dist/types/apps/privacy/index.d.ts +328 -0
- package/dist/types/apps/privacy/state-machines/index.d.ts +6 -0
- package/dist/types/apps/privacy/state-machines/mixer-ddhRing.d.ts +337 -0
- package/dist/types/apps/privacy/state-machines/note-pool.d.ts +196 -0
- package/dist/types/apps/staked-pool/assets.d.ts +53 -0
- package/dist/types/apps/staked-pool/base.d.ts +80 -0
- package/dist/types/apps/staked-pool/consumer.d.ts +29 -0
- package/dist/types/apps/staked-pool/index.d.ts +51 -0
- package/dist/types/apps/staked-pool/state-machines/index.d.ts +6 -0
- package/dist/types/apps/staked-pool/state-machines/staked-pool-base.d.ts +8 -0
- package/dist/types/apps/staked-pool/state-machines/staked-pool-oracle.d.ts +34 -0
- package/dist/types/generated/index.d.ts +5 -5
- package/dist/types/generated/openapi.d.ts +1270 -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/fiber.d.ts +7 -0
- package/dist/types/index.d.ts +7 -3
- package/dist/types/openapi.d.ts +24 -0
- package/dist/types/ottochain/genesis-manifest.d.ts +11 -5
- package/dist/types/ottochain/index.d.ts +5 -6
- 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 +41 -1
- package/dist/types/ottochain/types.d.ts +336 -31
- 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 +141 -0
- package/dist/types/schema/fiber-app.d.ts +282 -2
- package/dist/types/schema/guard-lint.d.ts +111 -0
- package/dist/types/schema/guards.d.ts +146 -0
- package/dist/types/signing.d.ts +45 -0
- package/dist/types/templates/asset-policy.d.ts +119 -0
- package/dist/types/templates/index.d.ts +26 -0
- package/dist/types/templates/machine.d.ts +139 -0
- package/dist/types/templates/migration.d.ts +34 -0
- package/dist/types/templates/state-shape.d.ts +30 -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 +47 -15
- 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,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset-policy preset builders (§1.1 of the SDK std-lib/templates handoff).
|
|
3
|
+
*
|
|
4
|
+
* Each builder is a PURE function that emits the exact {@link CreateAssetPolicy} wire body the chain
|
|
5
|
+
* re-derives — no `Date.now()`/`Math.random()`/`crypto.randomUUID()`; fixed inputs → fixed output. The
|
|
6
|
+
* on-chain policy `schemaHash` and `logicHash` are both `RegistryShape.AssetPolicy.computeDigest` over
|
|
7
|
+
* `behavior`/`supply`/`morphisms`/`stateShape` (no JSON-Logic body), so these presets are pure data.
|
|
8
|
+
*
|
|
9
|
+
* Hard invariants (CLAUDE.md rule #1 — the signed canonical is frozen):
|
|
10
|
+
* - `behavior` is ALWAYS summed from {@link TOKEN_BEHAVIOR_BITS} (T=16, S=8, C=4, E=2, G=1), never a
|
|
11
|
+
* magic literal int.
|
|
12
|
+
* - `morphisms` is REQUIRED on the wire (presence required, emptiness meaningful) — emit `{}` when empty,
|
|
13
|
+
* NEVER omit it.
|
|
14
|
+
* - `supply` is REQUIRED — emit `{}` when there is no supply authority, never omit it.
|
|
15
|
+
* - Absent optionals are OMITTED, never emitted as `null`/`undefined` (the chain signs over
|
|
16
|
+
* `JCS(dropNulls(payload))`).
|
|
17
|
+
*
|
|
18
|
+
* @see src/ottochain/types.ts — CreateAssetPolicy, SupplyPolicy, MorphismSpec, MorphismKind, TOKEN_BEHAVIOR_BITS
|
|
19
|
+
*/
|
|
20
|
+
import { TOKEN_BEHAVIOR_BITS, } from '../ottochain/types.js';
|
|
21
|
+
/** The default mint/burn guard the riverdale presets use: an always-true predicate `{"==":[1,1]}`. */
|
|
22
|
+
const alwaysTrue = () => ({ '==': [1, 1] });
|
|
23
|
+
/** A `PUBLIC`-visibility morphism spec (the riverdale preset visibility). Fresh object per call. */
|
|
24
|
+
const publicMorphism = () => ({ visibility: 'PUBLIC' });
|
|
25
|
+
/** Sum behavior bits from their NAMES — never a magic literal int (CLAUDE.md #1). */
|
|
26
|
+
export function sumBehavior(bits) {
|
|
27
|
+
return bits.reduce((acc, name) => acc + TOKEN_BEHAVIOR_BITS[name], 0);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Derive the default `stateShape.typeName`: strip a trailing `.asset` label, PascalCase the remainder
|
|
31
|
+
* (splitting on `.`/`-`/`_`/space), then append `State`.
|
|
32
|
+
* `rvd.asset` → `RvdState`, `goods.asset` → `GoodsState`, `capped.asset` → `CappedState`.
|
|
33
|
+
*/
|
|
34
|
+
export function defaultStateTypeName(name) {
|
|
35
|
+
const ASSET_SUFFIX = '.asset';
|
|
36
|
+
const base = name.endsWith(ASSET_SUFFIX) ? name.slice(0, -ASSET_SUFFIX.length) : name;
|
|
37
|
+
const pascal = base
|
|
38
|
+
.split(/[.\-_\s]+/)
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1))
|
|
41
|
+
.join('');
|
|
42
|
+
return `${pascal}State`;
|
|
43
|
+
}
|
|
44
|
+
/** Build a {@link SupplyPolicy}, OMITTING every absent field (no `undefined`/`null` keys). */
|
|
45
|
+
function buildSupply(opts) {
|
|
46
|
+
const supply = {};
|
|
47
|
+
if (opts.maxSupply !== undefined)
|
|
48
|
+
supply.maxSupply = opts.maxSupply;
|
|
49
|
+
if (opts.mintPolicy !== undefined)
|
|
50
|
+
supply.mintPolicy = opts.mintPolicy;
|
|
51
|
+
if (opts.burnPolicy !== undefined)
|
|
52
|
+
supply.burnPolicy = opts.burnPolicy;
|
|
53
|
+
if (opts.decimals !== undefined)
|
|
54
|
+
supply.decimals = opts.decimals;
|
|
55
|
+
return supply;
|
|
56
|
+
}
|
|
57
|
+
/** Assemble the final {@link CreateAssetPolicy}, attaching `metadata` only when present. */
|
|
58
|
+
function assemble(args) {
|
|
59
|
+
const stateShape = {
|
|
60
|
+
typeName: args.stateTypeName ?? defaultStateTypeName(args.name),
|
|
61
|
+
fields: [],
|
|
62
|
+
};
|
|
63
|
+
const policy = {
|
|
64
|
+
name: args.name,
|
|
65
|
+
version: args.version,
|
|
66
|
+
behavior: args.behavior,
|
|
67
|
+
supply: args.supply,
|
|
68
|
+
morphisms: args.morphisms,
|
|
69
|
+
stateShape,
|
|
70
|
+
};
|
|
71
|
+
if (args.metadata !== undefined)
|
|
72
|
+
policy.metadata = args.metadata;
|
|
73
|
+
return policy;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fungible currency. Behavior `T|S|C = 28` (transferable + splittable + combinable). Standard morphism
|
|
77
|
+
* set TRANSFER + FRACTIONALIZE + STAKE (`stakeable`, default true) + BURN (when `burnable`), all PUBLIC.
|
|
78
|
+
*
|
|
79
|
+
* Reproduces `riverdale-economy/rvd-policy.json` exactly via
|
|
80
|
+
* `fungiblePolicy({ name: 'rvd.asset', version: '1.0.0', mintable: true, burnable: true })`.
|
|
81
|
+
*/
|
|
82
|
+
export function fungiblePolicy(p) {
|
|
83
|
+
const behavior = sumBehavior(['transferable', 'splittable', 'combinable']); // 28
|
|
84
|
+
const morphisms = {
|
|
85
|
+
TRANSFER: publicMorphism(),
|
|
86
|
+
FRACTIONALIZE: publicMorphism(),
|
|
87
|
+
};
|
|
88
|
+
if (p.stakeable !== false)
|
|
89
|
+
morphisms.STAKE = publicMorphism();
|
|
90
|
+
if (p.burnable)
|
|
91
|
+
morphisms.BURN = publicMorphism();
|
|
92
|
+
let mintPolicy;
|
|
93
|
+
if (p.mintGuard !== undefined)
|
|
94
|
+
mintPolicy = p.mintGuard;
|
|
95
|
+
else if (p.mintable)
|
|
96
|
+
mintPolicy = alwaysTrue();
|
|
97
|
+
const supply = buildSupply({
|
|
98
|
+
maxSupply: p.maxSupply,
|
|
99
|
+
mintPolicy,
|
|
100
|
+
burnPolicy: p.burnable ? alwaysTrue() : undefined,
|
|
101
|
+
decimals: p.decimals,
|
|
102
|
+
});
|
|
103
|
+
return assemble({
|
|
104
|
+
name: p.name,
|
|
105
|
+
version: p.version,
|
|
106
|
+
behavior,
|
|
107
|
+
supply,
|
|
108
|
+
morphisms,
|
|
109
|
+
stateTypeName: p.stateTypeName,
|
|
110
|
+
metadata: p.metadata,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Non-fungible token. Behavior `T = 16`, or `T|C = 20` with `combinable: true` (the riverdale
|
|
115
|
+
* `goods.asset`); `transferable: false` drops the T bit (a bound collectible). `morphisms` is `{}` by
|
|
116
|
+
* default (emitted explicitly, never omitted). Mintable by default ⇒ `supply.mintPolicy = {"==":[1,1]}`.
|
|
117
|
+
*
|
|
118
|
+
* Reproduces `riverdale-economy/goods-policy.json` exactly via
|
|
119
|
+
* `nftPolicy({ name: 'goods.asset', version: '1.0.0', combinable: true })`.
|
|
120
|
+
*/
|
|
121
|
+
export function nftPolicy(p) {
|
|
122
|
+
const bits = [];
|
|
123
|
+
if (p.transferable !== false)
|
|
124
|
+
bits.push('transferable');
|
|
125
|
+
if (p.combinable)
|
|
126
|
+
bits.push('combinable');
|
|
127
|
+
const behavior = sumBehavior(bits);
|
|
128
|
+
let mintPolicy;
|
|
129
|
+
if (p.mintGuard !== undefined)
|
|
130
|
+
mintPolicy = p.mintGuard;
|
|
131
|
+
else if (p.mintable !== false)
|
|
132
|
+
mintPolicy = alwaysTrue();
|
|
133
|
+
const supply = buildSupply({ maxSupply: p.maxSupply, mintPolicy });
|
|
134
|
+
return assemble({
|
|
135
|
+
name: p.name,
|
|
136
|
+
version: p.version,
|
|
137
|
+
behavior,
|
|
138
|
+
supply,
|
|
139
|
+
morphisms: {},
|
|
140
|
+
stateTypeName: p.stateTypeName,
|
|
141
|
+
metadata: p.metadata,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Soulbound token: non-transferable, governable only (`G = 1`; `+E = 3` when `expirable`). No TRANSFER
|
|
146
|
+
* morphism (`morphisms: {}`); minting is closed after issue (empty `supply`).
|
|
147
|
+
*/
|
|
148
|
+
export function soulboundPolicy(p) {
|
|
149
|
+
const bits = ['governable'];
|
|
150
|
+
if (p.expirable)
|
|
151
|
+
bits.push('expirable');
|
|
152
|
+
const behavior = sumBehavior(bits);
|
|
153
|
+
return assemble({
|
|
154
|
+
name: p.name,
|
|
155
|
+
version: p.version,
|
|
156
|
+
behavior,
|
|
157
|
+
supply: buildSupply({}), // mint closed after issue
|
|
158
|
+
morphisms: {}, // non-transferable: no TRANSFER morphism
|
|
159
|
+
stateTypeName: p.stateTypeName,
|
|
160
|
+
metadata: p.metadata,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Escape hatch: declare `behavior` by NAME (summed from {@link TOKEN_BEHAVIOR_BITS}, never a magic int)
|
|
165
|
+
* and pass `supply` + `morphisms` through raw. Use when no preset fits.
|
|
166
|
+
*
|
|
167
|
+
* Reproduces `riverdale-economy/capped-policy.json` exactly via
|
|
168
|
+
* `customPolicy({ name: 'capped.asset', version: '1.0.0',
|
|
169
|
+
* behavior: ['transferable', 'splittable', 'combinable'],
|
|
170
|
+
* supply: { maxSupply: 100, mintPolicy: { '==': [1, 1] } }, morphisms: {} })`.
|
|
171
|
+
*/
|
|
172
|
+
export function customPolicy(p) {
|
|
173
|
+
return assemble({
|
|
174
|
+
name: p.name,
|
|
175
|
+
version: p.version,
|
|
176
|
+
behavior: sumBehavior(p.behavior),
|
|
177
|
+
supply: p.supply,
|
|
178
|
+
morphisms: p.morphisms,
|
|
179
|
+
stateTypeName: p.stateTypeName,
|
|
180
|
+
metadata: p.metadata,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ottochain/sdk/templates` — the fiber & asset authoring template catalog.
|
|
3
|
+
*
|
|
4
|
+
* Typed builders that emit the EXACT canonical wire shapes app authors otherwise hand-roll as raw
|
|
5
|
+
* JSON-Logic (asset policies, versioned machines, effect directives, migrations, seeded state). Every
|
|
6
|
+
* builder is additive and pure — it returns the same wire shape the chain re-derives, so a template
|
|
7
|
+
* output signs to byte-identical `JCS(dropNulls(payload))` (CLAUDE.md rule #1). Hand-rolled JSON keeps
|
|
8
|
+
* working; these just make the recurring shapes typed, named, and mistake-resistant.
|
|
9
|
+
*
|
|
10
|
+
* Origin: the fiber-ergonomics program (ottochain `docs/proposals/fiber-ergonomics/`), Phase A — the
|
|
11
|
+
* `riverdale-economy` e2e is the migration proof (each preset reproduces its checked-in golden JSON).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* import {
|
|
15
|
+
* fungiblePolicy, machine, transition, effect, guard,
|
|
16
|
+
* transferAsset, triggers, spawn, seedState, seedFields,
|
|
17
|
+
* } from '@ottochain/sdk/templates';
|
|
18
|
+
*/
|
|
19
|
+
// ─── 1.1 Asset-policy presets ────────────────────────────────────────────────
|
|
20
|
+
// fungiblePolicy / nftPolicy / soulboundPolicy / customPolicy (+ sumBehavior, defaultStateTypeName,
|
|
21
|
+
// BehaviorBitName) — emit a `CreateAssetPolicy`; behavior is summed from TOKEN_BEHAVIOR_BITS, never a literal.
|
|
22
|
+
export * from './asset-policy.js';
|
|
23
|
+
// ─── 1.2 Versioned-machine skeleton + transition()/effect() ──────────────────
|
|
24
|
+
// machine / transition / effect (+ TransitionSpec, MachineSpec, Machine, …). The skeleton owns ONE
|
|
25
|
+
// canonical wire definition, so publish + create cannot drift (verified binding, F9).
|
|
26
|
+
export * from './machine.js';
|
|
27
|
+
// ─── 1.4 Migration helpers ───────────────────────────────────────────────────
|
|
28
|
+
// seedFields / migration — hides the bare-state-root ({"var":""}) foot-gun.
|
|
29
|
+
export * from './migration.js';
|
|
30
|
+
// ─── 1.5 State-shape-with-defaults ───────────────────────────────────────────
|
|
31
|
+
// seedState / declaredFields / StateShape — auto-seed accumulators so no field reads null (F5).
|
|
32
|
+
export * from './state-shape.js';
|
|
33
|
+
// ─── 1.3 Effect-directive builders ───────────────────────────────────────────
|
|
34
|
+
// transferAsset / triggers / spawn / emit / toFiber / toWallet / addDependency / setDependencyActive /
|
|
35
|
+
// RESERVED_EFFECT_KEYS — a typo'd directive is a TS error, not a silent state field (F4).
|
|
36
|
+
export * from '../schema/effects.js';
|
|
37
|
+
// ─── Authorization-guard builders (namespace) ────────────────────────────────
|
|
38
|
+
// `guard.signerIsParty('state.borrower')` reads as intent at the call site.
|
|
39
|
+
export * as guard from '../schema/guards.js';
|
|
40
|
+
// ─── Existing machine/policy primitives re-surfaced for one-import authoring ──
|
|
41
|
+
export { defineFiberApp, toProtoDefinition, constrained, unconstrained, immutable, projectFiberPolicy, } from '../schema/fiber-app.js';
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Versioned-machine skeleton + `transition()` / `effect()` composition (handoff §1.2).
|
|
3
|
+
*
|
|
4
|
+
* Two moves:
|
|
5
|
+
* 1. `transition()` / `effect()` build the wire `Transition` + its flat effect map from typed
|
|
6
|
+
* fragments instead of a hand-rolled JSON blob.
|
|
7
|
+
* 2. `machine()` is a versioned-package SKELETON that OWNS exactly ONE canonical wire definition
|
|
8
|
+
* (`toProtoDefinition(app)`, computed once) and hands the SAME object to BOTH `publishVersion()`
|
|
9
|
+
* and `create()`. That is the verified-binding invariant (F9): because publish and create cannot
|
|
10
|
+
* drift, the chain's `definition.computeDigest` equals the registered `logicHash` and the bind
|
|
11
|
+
* admits the fiber. The skeleton REUSES `toProtoDefinition` (the one canonicalization path) — it
|
|
12
|
+
* never re-derives the wire shape.
|
|
13
|
+
*
|
|
14
|
+
* Guardrail (CLAUDE.md #1): every builder here is a pure function emitting the exact wire shape the
|
|
15
|
+
* chain re-derives. Absent optionals (`metadata`/`participants`/`parentFiberId`/`migration`) are
|
|
16
|
+
* OMITTED, never `null`; required-no-default fields (`strict` on publish, `dependencies: []` per
|
|
17
|
+
* transition) are ALWAYS present.
|
|
18
|
+
*/
|
|
19
|
+
import { toProtoDefinition, } from '../schema/fiber-app.js';
|
|
20
|
+
/**
|
|
21
|
+
* Build a wire {@link Transition} from a typed spec.
|
|
22
|
+
*
|
|
23
|
+
* - `on` → `eventName` (the wire key);
|
|
24
|
+
* - `guard` defaults to `{ "==": [1, 1] }`;
|
|
25
|
+
* - `dependencies` defaults to `[]` and is ALWAYS present;
|
|
26
|
+
* - `effect` is passed through (build it with {@link effect}).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* transition({
|
|
31
|
+
* from: 'debt_current', to: 'debt_current', on: 'buy',
|
|
32
|
+
* effect: effect(
|
|
33
|
+
* { status: 'debt_current', purchaseCount: { '+': [{ var: 'state.purchaseCount' }, 1] } },
|
|
34
|
+
* triggers([...]), transferAsset([...]),
|
|
35
|
+
* ),
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function transition(t) {
|
|
40
|
+
return {
|
|
41
|
+
from: t.from,
|
|
42
|
+
to: t.to,
|
|
43
|
+
eventName: t.on,
|
|
44
|
+
guard: t.guard ?? { '==': [1, 1] },
|
|
45
|
+
effect: t.effect,
|
|
46
|
+
dependencies: t.dependencies ?? [],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// effect()
|
|
51
|
+
// =============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* Compose an effect into ONE FLAT map: the state-update fields PLUS any `_`-directive fragments,
|
|
54
|
+
* all in a single object (`Object.assign({}, stateUpdate, ...directives)`).
|
|
55
|
+
*
|
|
56
|
+
* Riverdale effects are flat maps that mix `_`-reserved directives (`_triggers`, `_transferAsset`,
|
|
57
|
+
* `_spawn`, …) with plain state-update fields — they are NOT `merge`-wrapped. Each directive fragment
|
|
58
|
+
* is a `Record` (e.g. the output of `transferAsset(...)` / `triggers(...)`), spread in here. Later
|
|
59
|
+
* fragments override earlier keys (`Object.assign` semantics).
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* effect(
|
|
64
|
+
* { status: 'debt_current', purchaseCount: { '+': [{ var: 'state.purchaseCount' }, 1] } },
|
|
65
|
+
* { _triggers: [...] },
|
|
66
|
+
* transferAsset([{ assetId: { var: 'event.payAssetId' }, recipient: toFiber({ var: 'event.retailerId' }) }]),
|
|
67
|
+
* );
|
|
68
|
+
* // => { status, purchaseCount, _triggers, _transferAsset }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function effect(stateUpdate, ...directives) {
|
|
72
|
+
return Object.assign({}, stateUpdate, ...directives);
|
|
73
|
+
}
|
|
74
|
+
/** A pinned-Exact `SchemaRef` for `name@version`. Wire: `{ name, version: { Exact: { version } } }`. */
|
|
75
|
+
function exactRef(name, version) {
|
|
76
|
+
return { name, version: { Exact: { version } } };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Build a versioned-machine skeleton that binds publish + create to ONE canonical definition.
|
|
80
|
+
*
|
|
81
|
+
* The definition is projected ONCE here (`toProtoDefinition(spec.app)`) and the SAME reference is handed
|
|
82
|
+
* to `wireDefinition()`, `publishVersion().definition`, `create().definition`, and
|
|
83
|
+
* `upgradeFrom().newDefinition` — so they cannot drift and the chain's verified binding admits the fiber
|
|
84
|
+
* (F9). REUSES `toProtoDefinition` (the single canonicalization path); never re-derives the wire shape.
|
|
85
|
+
*/
|
|
86
|
+
export function machine(spec) {
|
|
87
|
+
// Project the canonical wire definition ONCE; every consumer reuses this exact reference.
|
|
88
|
+
const wireDef = toProtoDefinition(spec.app);
|
|
89
|
+
return {
|
|
90
|
+
wireDefinition() {
|
|
91
|
+
return wireDef;
|
|
92
|
+
},
|
|
93
|
+
publishVersion(o) {
|
|
94
|
+
return {
|
|
95
|
+
name: spec.name,
|
|
96
|
+
version: spec.version,
|
|
97
|
+
// REQUIRED-no-default on the wire; '' is valid empty base64 when no descriptor set is bound.
|
|
98
|
+
schemaB64: o?.schemaB64 ?? '',
|
|
99
|
+
machineShape: spec.schemaShape,
|
|
100
|
+
definition: wireDef, // SAME reference as wireDefinition()/create()
|
|
101
|
+
strict: o?.strict ?? false, // REQUIRED — never omitted
|
|
102
|
+
...(o?.metadata ? { metadata: o.metadata } : {}), // OMIT when absent
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
create(o) {
|
|
106
|
+
return {
|
|
107
|
+
fiberId: o.fiberId,
|
|
108
|
+
definition: wireDef, // SAME reference as wireDefinition()/publishVersion()
|
|
109
|
+
initialData: o.initialData,
|
|
110
|
+
schemaRef: exactRef(spec.name, spec.version), // verified binding: name@version
|
|
111
|
+
...(o.participants ? { participants: o.participants } : {}), // OMIT when absent
|
|
112
|
+
...(o.parentFiberId ? { parentFiberId: o.parentFiberId } : {}), // OMIT when absent
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
upgradeFrom(o) {
|
|
116
|
+
return {
|
|
117
|
+
fiberId: o.fiberId,
|
|
118
|
+
targetRef: exactRef(spec.name, spec.version),
|
|
119
|
+
newDefinition: wireDef, // SAME reference — re-pin hashes to the same logicHash
|
|
120
|
+
targetSequenceNumber: o.targetSequenceNumber,
|
|
121
|
+
...(o.migration !== undefined ? { migration: o.migration } : {}), // OMIT (Option) when absent
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration builders — the `UpgradeFiber.migration` JSON-Logic transform applied on a version upgrade.
|
|
3
|
+
*
|
|
4
|
+
* THE FOOT-GUN (F9): a migration's evaluation root is the BARE prior state, not `state.x`.
|
|
5
|
+
* The chain evaluates the migration expression against `sm.stateData` directly
|
|
6
|
+
* (`MeteredEvaluator.eval(expr, sm.stateData, Migration)`, FiberEngine.scala:300), so:
|
|
7
|
+
* - `{ var: '' }` => the WHOLE prior state object
|
|
8
|
+
* - `{ var: 'loyaltyPoints' }` => the top-level `loyaltyPoints` field
|
|
9
|
+
* This is UNLIKE an effect, where the root is the transition context and you read `{ var: 'state.x' }`.
|
|
10
|
+
* Writing `state.loyaltyPoints` in a migration silently reads `undefined`. These helpers hide that
|
|
11
|
+
* asymmetry by pinning the bare-state root for you.
|
|
12
|
+
*
|
|
13
|
+
* `migration` is `Option` on `UpgradeFiber` — when there is no transform, OMIT the key (never send `null`).
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Seed / overwrite top-level state fields on a version upgrade.
|
|
17
|
+
* Emits `{ merge: [{ var: '' }, fields] }` — merges `fields` onto the bare prior state, so existing
|
|
18
|
+
* fields are preserved and the named fields are added or overwritten.
|
|
19
|
+
*
|
|
20
|
+
* The `{ var: '' }` root is the BARE prior state (NOT `state.x` like an effect) — that asymmetry is
|
|
21
|
+
* exactly the F9 foot-gun this helper hides.
|
|
22
|
+
*
|
|
23
|
+
* @example seedFields({ loyaltyPoints: 0 }) // => { merge: [{ var: '' }, { loyaltyPoints: 0 }] }
|
|
24
|
+
*/
|
|
25
|
+
export const seedFields = (fields) => ({
|
|
26
|
+
merge: [{ var: '' }, fields],
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* General migration transform with the bare-state root made explicit. `build` receives `{ var: '' }`
|
|
30
|
+
* (the whole prior state) and returns the migration JSON-Logic.
|
|
31
|
+
*
|
|
32
|
+
* @example migration((s) => ({ merge: [s, { x: 1 }] })) // => { merge: [{ var: '' }, { x: 1 }] }
|
|
33
|
+
*/
|
|
34
|
+
export const migration = (build) => build({ var: '' });
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State-shape-with-defaults — the SDK half of Proposal 01's "declared state shape" (removes F5).
|
|
3
|
+
*
|
|
4
|
+
* A declared shape carries a `default` (and optional `type`) per field. It seeds `initialData` so every
|
|
5
|
+
* accumulator starts at a concrete value — a counter read via `{ '+': [{ var: 'state.x' }, ...] }` never
|
|
6
|
+
* resolves `null` because `x` was never written. It also yields the set of declared field names for
|
|
7
|
+
* Proposal 01's var-path checks.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Build `initialData` by overlaying `overrides` onto each field's declared `default`.
|
|
11
|
+
* Every declared field gets a concrete value, so no field reads `null` at runtime. An override for a
|
|
12
|
+
* field replaces that field's default; overrides for undeclared fields are ignored (only declared fields
|
|
13
|
+
* are emitted). Determinism: pure function of its inputs, stable key order (declared order).
|
|
14
|
+
*
|
|
15
|
+
* @example seedState({ fields: { purchaseCount: { default: 0 }, status: { default: 'ACTIVE' } } })
|
|
16
|
+
* // => { purchaseCount: 0, status: 'ACTIVE' }
|
|
17
|
+
*/
|
|
18
|
+
export function seedState(shape, overrides) {
|
|
19
|
+
const seeded = {};
|
|
20
|
+
for (const [name, field] of Object.entries(shape.fields)) {
|
|
21
|
+
seeded[name] =
|
|
22
|
+
overrides !== undefined && Object.prototype.hasOwnProperty.call(overrides, name)
|
|
23
|
+
? overrides[name]
|
|
24
|
+
: field.default;
|
|
25
|
+
}
|
|
26
|
+
return seeded;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The set of declared state-field names — the fields the effects may read.
|
|
30
|
+
* Feeds Proposal 01's var-path checks (a `{ var: 'state.foo' }` whose `foo` is not declared is suspect).
|
|
31
|
+
*/
|
|
32
|
+
export function declaredFields(shape) {
|
|
33
|
+
return new Set(Object.keys(shape.fields));
|
|
34
|
+
}
|
package/dist/esm/validation.js
CHANGED
|
@@ -23,9 +23,7 @@ const hexString = (length) => {
|
|
|
23
23
|
/**
|
|
24
24
|
* Schema for a DAG address
|
|
25
25
|
*/
|
|
26
|
-
export const DagAddressSchema = z
|
|
27
|
-
.string()
|
|
28
|
-
.regex(/^DAG[0-9][a-zA-Z0-9]{36}$/, 'Must be a valid DAG address');
|
|
26
|
+
export const DagAddressSchema = z.string().regex(/^DAG[0-9][a-zA-Z0-9]{36}$/, 'Must be a valid DAG address');
|
|
29
27
|
// ============================================================================
|
|
30
28
|
// Core Type Schemas
|
|
31
29
|
// ============================================================================
|
package/dist/esm/verify.js
CHANGED
|
@@ -4,10 +4,16 @@
|
|
|
4
4
|
* The package embeds `mode: "standard"|"dataUpdate"` in signed objects and
|
|
5
5
|
* its verify() ignores the `isDataUpdate` parameter when `mode` is present.
|
|
6
6
|
* This wrapper strips `mode` so callers' explicit `isDataUpdate` always wins.
|
|
7
|
+
*
|
|
8
|
+
* In dataUpdate mode, verification happens over the null-dropped canonical
|
|
9
|
+
* bytes (drop null object fields, preserve array nulls, then RFC 8785) —
|
|
10
|
+
* mirroring the SDK's dataUpdate signers and metakit's content-hash rule.
|
|
7
11
|
*/
|
|
8
12
|
import { verify as _verify } from '@constellation-network/metagraph-sdk';
|
|
13
|
+
import { dropNulls } from './ottochain/drop-nulls.js';
|
|
9
14
|
export function verify(signed, isDataUpdate) {
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11
15
|
const { mode, ...rest } = signed;
|
|
12
|
-
|
|
16
|
+
const isDU = isDataUpdate ?? mode === 'dataUpdate';
|
|
17
|
+
const target = isDU ? { ...rest, value: dropNulls(signed.value) } : rest;
|
|
18
|
+
return _verify(target, isDU);
|
|
13
19
|
}
|
|
@@ -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);
|