@ottochain/sdk 2.5.0 → 2.7.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/lending/assets.js +11 -1
- package/dist/cjs/index.js +9 -2
- package/dist/cjs/ottochain/index.js +11 -1
- package/dist/cjs/ottochain/metagraph-client.js +47 -3
- package/dist/cjs/ottochain/morphism-lint.js +99 -0
- package/dist/cjs/ottochain/state-proof.js +118 -0
- package/dist/cjs/ottochain/transaction.js +7 -1
- package/dist/cjs/ottochain/webhook-notifications.js +33 -0
- package/dist/cjs/schema/effects.js +43 -6
- package/dist/cjs/schema/fiber-app.js +7 -2
- package/dist/cjs/schema/guard-lint.js +102 -0
- package/dist/esm/apps/lending/assets.js +11 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/ottochain/index.js +4 -0
- package/dist/esm/ottochain/metagraph-client.js +47 -3
- package/dist/esm/ottochain/morphism-lint.js +95 -0
- package/dist/esm/ottochain/state-proof.js +113 -0
- package/dist/esm/ottochain/transaction.js +7 -1
- package/dist/esm/ottochain/webhook-notifications.js +30 -0
- package/dist/esm/schema/effects.js +41 -5
- package/dist/esm/schema/fiber-app.js +7 -2
- package/dist/esm/schema/guard-lint.js +102 -0
- package/dist/types/apps/lending/assets.d.ts +11 -1
- package/dist/types/index.d.ts +5 -1
- package/dist/types/ottochain/index.d.ts +7 -1
- package/dist/types/ottochain/metagraph-client.d.ts +69 -15
- package/dist/types/ottochain/morphism-lint.d.ts +56 -0
- package/dist/types/ottochain/state-proof.d.ts +51 -0
- package/dist/types/ottochain/transaction.d.ts +7 -1
- package/dist/types/ottochain/types.d.ts +22 -0
- package/dist/types/ottochain/webhook-notifications.d.ts +91 -0
- package/dist/types/schema/effects.d.ts +39 -5
- package/dist/types/schema/guard-lint.d.ts +6 -0
- package/package.json +6 -6
|
@@ -112,6 +112,11 @@ function createMintAssetPayload(params) {
|
|
|
112
112
|
/**
|
|
113
113
|
* Apply a typed morphism (Transfer / Burn / Compose / ...). asset-model.md §7.
|
|
114
114
|
* Transfer = `{ kind: "Transfer", recipient }`; Burn (repay) = `{ kind: "Burn" }`.
|
|
115
|
+
*
|
|
116
|
+
* C2 — a `Compose`/`Pool` that folds in a counter-party the signer does NOT own is now rejected on-chain
|
|
117
|
+
* unless a live `AuthorizeCompose` nonce authorizes it: pass that nonce here (`nonce`) and build the commit
|
|
118
|
+
* half with {@link createAuthorizeComposePayload}. A same-holder compose (all parts signer-owned) needs no
|
|
119
|
+
* nonce. `otherAssets` must be duplicate-free and must not include `assetId`.
|
|
115
120
|
*/
|
|
116
121
|
function createApplyMorphismPayload(params) {
|
|
117
122
|
return {
|
|
@@ -128,7 +133,12 @@ function createApplyMorphismPayload(params) {
|
|
|
128
133
|
},
|
|
129
134
|
};
|
|
130
135
|
}
|
|
131
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* Commit half of a two-party (cross-holder) compose. asset-model.md §7/§8. As of chain finding C2 this
|
|
138
|
+
* handshake is MANDATORY: a cross-holder `Compose`/`Pool` is rejected unless a matching, unexpired nonce
|
|
139
|
+
* from this call is live for the counter-party. The composing party then echoes `nonce` in its
|
|
140
|
+
* `ApplyMorphism`. A same-holder compose (all parts owned by the signer) does not need this.
|
|
141
|
+
*/
|
|
132
142
|
function createAuthorizeComposePayload(params) {
|
|
133
143
|
return {
|
|
134
144
|
AuthorizeCompose: {
|
package/dist/cjs/index.js
CHANGED
|
@@ -31,8 +31,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
31
31
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
32
32
|
};
|
|
33
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
-
exports.
|
|
35
|
-
exports.SHIELDED_POOL_STATE = exports.shieldApp = exports.RESERVED_EFFECT_KEYS = exports.toWallet = exports.toFiber = exports.emit = exports.spawn = exports.triggers = exports.transferAsset = exports.setDependencyActive = exports.addDependency = exports.actorNotInArray = exports.depInState = exports.signerHasRoleVia = exports.signerHasReputationVia = exports.signerHasRole = exports.signerHasReputation = exports.actorHasEntry = exports.actorInSet = exports.actorIsSigner = exports.assetSignerIs = exports.signerHasEntry = exports.signerIsNotParty = exports.signerInSet = exports.signerIsAnyParty = exports.signerIsParty = exports.projectFiberPolicy = void 0;
|
|
34
|
+
exports.constrained = exports.defineFiberApp = exports.toProtoDefinition = exports.SNAPSHOT_FINALIZED_EVENT = exports.OttoMetagraphClient = exports.assert = exports.safeParse = exports.validateKeyPair = exports.validateAddress = exports.validatePublicKey = exports.validatePrivateKey = exports.validate = exports.CompleteContractRequestSchema = exports.AcceptContractRequestSchema = exports.ProposeContractRequestSchema = exports.ContractTermsSchema = exports.PlatformLinkSchema = exports.AgentIdentityRegistrationSchema = exports.TransferParamsSchema = exports.CurrencyTransactionSchema = exports.CurrencyTransactionValueSchema = exports.TransactionReferenceSchema = exports.SignedSchema = exports.SignatureProofSchema = exports.KeyPairSchema = exports.PublicKeySchema = exports.PrivateKeySchema = exports.DagAddressSchema = exports.wrapError = exports.isErrorCode = exports.ErrorCode = exports.TransactionError = exports.SigningError = exports.ValidationError = exports.NetworkError = exports.OttoChainError = exports.GENESIS_MANIFEST_VERSION = exports.buildGenesisManifest = exports.MORPHISM_LINT_CODES = exports.lintApplyMorphism = exports.dropNulls = exports.MetagraphNetworkError = exports.HttpClient = exports.createMetagraphClient = exports.MetagraphClient = exports.batchSign = exports.addSignature = exports.createSignedObject = exports.signDataUpdate = exports.verify = void 0;
|
|
35
|
+
exports.SHIELDED_POOL_STATE = exports.shieldApp = exports.RESERVED_EFFECT_KEYS = exports.toWallet = exports.toFiber = exports.scriptCall = exports.emit = exports.spawn = exports.triggers = exports.transferAsset = exports.setDependencyActive = exports.addDependency = exports.actorNotInArray = exports.depInState = exports.signerHasRoleVia = exports.signerHasReputationVia = exports.signerHasRole = exports.signerHasReputation = exports.actorHasEntry = exports.actorInSet = exports.actorIsSigner = exports.assetSignerIs = exports.signerHasEntry = exports.signerIsNotParty = exports.signerInSet = exports.signerIsAnyParty = exports.signerIsParty = exports.projectFiberPolicy = exports.IMMUTABLE_POLICY = exports.immutable = exports.unconstrained = void 0;
|
|
36
36
|
// ─── Core metagraph SDK ───────────────────────────────────────────────────────
|
|
37
37
|
// Consumers can also import directly from '@constellation-network/metagraph-sdk'
|
|
38
38
|
__exportStar(require("@constellation-network/metagraph-sdk"), exports);
|
|
@@ -62,6 +62,10 @@ __exportStar(require("./types.js"), exports);
|
|
|
62
62
|
__exportStar(require("./ottochain/transaction.js"), exports);
|
|
63
63
|
var drop_nulls_js_1 = require("./ottochain/drop-nulls.js");
|
|
64
64
|
Object.defineProperty(exports, "dropNulls", { enumerable: true, get: function () { return drop_nulls_js_1.dropNulls; } });
|
|
65
|
+
// Message-layer advisory lint for ApplyMorphism (Compose/Pool consent + inflation checks).
|
|
66
|
+
var morphism_lint_js_1 = require("./ottochain/morphism-lint.js");
|
|
67
|
+
Object.defineProperty(exports, "lintApplyMorphism", { enumerable: true, get: function () { return morphism_lint_js_1.lintApplyMorphism; } });
|
|
68
|
+
Object.defineProperty(exports, "MORPHISM_LINT_CODES", { enumerable: true, get: function () { return morphism_lint_js_1.MORPHISM_LINT_CODES; } });
|
|
65
69
|
// ─── Genesis manifest exporter (std-app pre-registration content) ─────────────
|
|
66
70
|
var genesis_manifest_js_1 = require("./ottochain/genesis-manifest.js");
|
|
67
71
|
Object.defineProperty(exports, "buildGenesisManifest", { enumerable: true, get: function () { return genesis_manifest_js_1.buildGenesisManifest; } });
|
|
@@ -108,6 +112,8 @@ Object.defineProperty(exports, "assert", { enumerable: true, get: function () {
|
|
|
108
112
|
// ─── OttoChain metagraph client ───────────────────────────────────────────────
|
|
109
113
|
var metagraph_client_js_1 = require("./ottochain/metagraph-client.js");
|
|
110
114
|
Object.defineProperty(exports, "OttoMetagraphClient", { enumerable: true, get: function () { return metagraph_client_js_1.MetagraphClient; } });
|
|
115
|
+
var webhook_notifications_js_1 = require("./ottochain/webhook-notifications.js");
|
|
116
|
+
Object.defineProperty(exports, "SNAPSHOT_FINALIZED_EVENT", { enumerable: true, get: function () { return webhook_notifications_js_1.SNAPSHOT_FINALIZED_EVENT; } });
|
|
111
117
|
// Proto definition helper
|
|
112
118
|
var fiber_app_js_1 = require("./schema/fiber-app.js");
|
|
113
119
|
Object.defineProperty(exports, "toProtoDefinition", { enumerable: true, get: function () { return fiber_app_js_1.toProtoDefinition; } });
|
|
@@ -150,6 +156,7 @@ Object.defineProperty(exports, "transferAsset", { enumerable: true, get: functio
|
|
|
150
156
|
Object.defineProperty(exports, "triggers", { enumerable: true, get: function () { return effects_js_1.triggers; } });
|
|
151
157
|
Object.defineProperty(exports, "spawn", { enumerable: true, get: function () { return effects_js_1.spawn; } });
|
|
152
158
|
Object.defineProperty(exports, "emit", { enumerable: true, get: function () { return effects_js_1.emit; } });
|
|
159
|
+
Object.defineProperty(exports, "scriptCall", { enumerable: true, get: function () { return effects_js_1.scriptCall; } });
|
|
153
160
|
Object.defineProperty(exports, "toFiber", { enumerable: true, get: function () { return effects_js_1.toFiber; } });
|
|
154
161
|
Object.defineProperty(exports, "toWallet", { enumerable: true, get: function () { return effects_js_1.toWallet; } });
|
|
155
162
|
Object.defineProperty(exports, "RESERVED_EFFECT_KEYS", { enumerable: true, get: function () { return effects_js_1.RESERVED_EFFECT_KEYS; } });
|
|
@@ -40,7 +40,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
40
40
|
};
|
|
41
41
|
})();
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.GENESIS_MANIFEST_VERSION = exports.buildGenesisManifest = exports.dropNulls = exports.createAuthorizeComposePayload = exports.createApplyMorphismPayload = exports.createMintAssetPayload = exports.createAssetPolicyPayload = exports.createDataTransactionRequest = exports.createScriptPayload = exports.createStateMachinePayload = exports.getPublicKeyForRegistration = exports.addTransactionSignature = exports.signTransaction = exports.createInvokeScriptPayload = exports.createArchivePayload = exports.createTransitionPayload = exports.MetagraphClient = exports.extractOnChainState = exports.getScriptInvocations = exports.getEventReceipts = exports.getLogsForFiber = exports.getLatestOnChainState = exports.getSnapshotOnChainState = exports.decodeOnChainState = exports.TOKEN_BEHAVIOR_BITS = exports.OTTOCHAIN_MESSAGE_TYPES = exports.proto = void 0;
|
|
43
|
+
exports.GENESIS_MANIFEST_VERSION = exports.buildGenesisManifest = exports.dropNulls = exports.MORPHISM_LINT_CODES = exports.lintApplyMorphism = exports.createAuthorizeComposePayload = exports.createApplyMorphismPayload = exports.createMintAssetPayload = exports.createAssetPolicyPayload = exports.createDataTransactionRequest = exports.createScriptPayload = exports.createStateMachinePayload = exports.getPublicKeyForRegistration = exports.addTransactionSignature = exports.signTransaction = exports.createInvokeScriptPayload = exports.createArchivePayload = exports.createTransitionPayload = exports.SNAPSHOT_FINALIZED_EVENT = exports.commitKeyPath = exports.verifyMptInclusion = exports.verifyStateProof = exports.MetagraphClient = exports.extractOnChainState = exports.getScriptInvocations = exports.getEventReceipts = exports.getLogsForFiber = exports.getLatestOnChainState = exports.getSnapshotOnChainState = exports.decodeOnChainState = exports.TOKEN_BEHAVIOR_BITS = exports.OTTOCHAIN_MESSAGE_TYPES = exports.proto = void 0;
|
|
44
44
|
// Re-export generated protobuf types (for binary encoding)
|
|
45
45
|
exports.proto = __importStar(require("../generated/index.js"));
|
|
46
46
|
// Runtime message type validation
|
|
@@ -57,6 +57,12 @@ Object.defineProperty(exports, "getScriptInvocations", { enumerable: true, get:
|
|
|
57
57
|
Object.defineProperty(exports, "extractOnChainState", { enumerable: true, get: function () { return snapshot_js_1.extractOnChainState; } });
|
|
58
58
|
var metagraph_client_js_1 = require("./metagraph-client.js");
|
|
59
59
|
Object.defineProperty(exports, "MetagraphClient", { enumerable: true, get: function () { return metagraph_client_js_1.MetagraphClient; } });
|
|
60
|
+
var state_proof_js_1 = require("./state-proof.js");
|
|
61
|
+
Object.defineProperty(exports, "verifyStateProof", { enumerable: true, get: function () { return state_proof_js_1.verifyStateProof; } });
|
|
62
|
+
Object.defineProperty(exports, "verifyMptInclusion", { enumerable: true, get: function () { return state_proof_js_1.verifyMptInclusion; } });
|
|
63
|
+
Object.defineProperty(exports, "commitKeyPath", { enumerable: true, get: function () { return state_proof_js_1.commitKeyPath; } });
|
|
64
|
+
var webhook_notifications_js_1 = require("./webhook-notifications.js");
|
|
65
|
+
Object.defineProperty(exports, "SNAPSHOT_FINALIZED_EVENT", { enumerable: true, get: function () { return webhook_notifications_js_1.SNAPSHOT_FINALIZED_EVENT; } });
|
|
60
66
|
// Transaction helpers (state machine payloads, signing)
|
|
61
67
|
var transaction_js_1 = require("./transaction.js");
|
|
62
68
|
Object.defineProperty(exports, "createTransitionPayload", { enumerable: true, get: function () { return transaction_js_1.createTransitionPayload; } });
|
|
@@ -72,6 +78,10 @@ Object.defineProperty(exports, "createAssetPolicyPayload", { enumerable: true, g
|
|
|
72
78
|
Object.defineProperty(exports, "createMintAssetPayload", { enumerable: true, get: function () { return transaction_js_1.createMintAssetPayload; } });
|
|
73
79
|
Object.defineProperty(exports, "createApplyMorphismPayload", { enumerable: true, get: function () { return transaction_js_1.createApplyMorphismPayload; } });
|
|
74
80
|
Object.defineProperty(exports, "createAuthorizeComposePayload", { enumerable: true, get: function () { return transaction_js_1.createAuthorizeComposePayload; } });
|
|
81
|
+
// Message-layer advisory lint for ApplyMorphism (Compose/Pool C2 mistakes the chain rejects).
|
|
82
|
+
var morphism_lint_js_1 = require("./morphism-lint.js");
|
|
83
|
+
Object.defineProperty(exports, "lintApplyMorphism", { enumerable: true, get: function () { return morphism_lint_js_1.lintApplyMorphism; } });
|
|
84
|
+
Object.defineProperty(exports, "MORPHISM_LINT_CODES", { enumerable: true, get: function () { return morphism_lint_js_1.MORPHISM_LINT_CODES; } });
|
|
75
85
|
// Data utilities
|
|
76
86
|
var drop_nulls_js_1 = require("./drop-nulls.js");
|
|
77
87
|
Object.defineProperty(exports, "dropNulls", { enumerable: true, get: function () { return drop_nulls_js_1.dropNulls; } });
|
|
@@ -119,7 +119,11 @@ class MetagraphClient {
|
|
|
119
119
|
// -------------------------------------------------------------------------
|
|
120
120
|
// Registry, audit, state-proofs, fee estimates (ML0 /data-application/v1/*)
|
|
121
121
|
// -------------------------------------------------------------------------
|
|
122
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* Get the node's service identity + build metadata. The chain's `GET …/version` returns a
|
|
124
|
+
* `VersionInfo` OBJECT (`{ service, version, name, scalaVersion, sbtVersion, gitCommit, buildTime,
|
|
125
|
+
* tessellationVersion }`) — chain `ServiceMeta.scala` — NOT a bare string.
|
|
126
|
+
*/
|
|
123
127
|
async getVersion() {
|
|
124
128
|
return this.ml0.get('/data-application/v1/version');
|
|
125
129
|
}
|
|
@@ -165,15 +169,55 @@ class MetagraphClient {
|
|
|
165
169
|
async getAssetStateProof(assetId, field) {
|
|
166
170
|
return this.ml0.get(`/data-application/v1/assets/${assetId}/state-proof?field=${encodeURIComponent(field)}`);
|
|
167
171
|
}
|
|
168
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Estimate the fee/gas for a transition event on a state-machine fiber. Returns a
|
|
174
|
+
* {@link TransitionFeeEstimate} (`{ fiberId, currentState, event, gasEstimate, opCount, maxDepth,
|
|
175
|
+
* candidateTransitions, note }`) — chain `FeeEstimates.scala`.
|
|
176
|
+
*/
|
|
169
177
|
async estimateTransitionFee(fiberId, eventName) {
|
|
170
178
|
return this.ml0.get(`/data-application/v1/state-machines/${fiberId}/estimate-fee?event=${encodeURIComponent(eventName)}`);
|
|
171
179
|
}
|
|
172
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Estimate the fee/gas for invoking a script fiber. Returns a {@link ScriptFeeEstimate}
|
|
182
|
+
* (`{ scriptId, gasEstimate, opCount, maxDepth, note }`) — chain `FeeEstimates.scala`.
|
|
183
|
+
*/
|
|
173
184
|
async estimateScriptFee(fiberId) {
|
|
174
185
|
return this.ml0.get(`/data-application/v1/scripts/${fiberId}/estimate-fee`);
|
|
175
186
|
}
|
|
176
187
|
// -------------------------------------------------------------------------
|
|
188
|
+
// Webhook subscriptions (ML0 /data-application/v1/webhooks/*)
|
|
189
|
+
// -------------------------------------------------------------------------
|
|
190
|
+
/**
|
|
191
|
+
* Subscribe a callback URL to snapshot-notification webhooks.
|
|
192
|
+
*
|
|
193
|
+
* `POST …/webhooks/subscribe` with `SubscribeRequest { callbackUrl, secret? }`; the chain replies
|
|
194
|
+
* `201 Created` with `SubscribeResponse { id, callbackUrl, createdAt }`. Chain: `ML0Routes.scala`
|
|
195
|
+
* (`webhook.subscribe`) + `webhooks/Subscriber.scala`.
|
|
196
|
+
*/
|
|
197
|
+
async subscribeWebhook(request) {
|
|
198
|
+
return this.ml0.post('/data-application/v1/webhooks/subscribe', request);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Unsubscribe a webhook by its subscriber id.
|
|
202
|
+
*
|
|
203
|
+
* `DELETE …/webhooks/subscribe/{id}` → `204 No Content` (empty body) on success, or `404` if the id is
|
|
204
|
+
* unknown. Chain: `ML0Routes.scala` (`webhook.unsubscribe`). The vendored `HttpClient` exposes only
|
|
205
|
+
* `get`/`post`, so this reuses its shared private `request` helper — same `NetworkError`/timeout
|
|
206
|
+
* handling as every other call — to issue the DELETE.
|
|
207
|
+
*/
|
|
208
|
+
async unsubscribeWebhook(id) {
|
|
209
|
+
await this.ml0.request('DELETE', `/data-application/v1/webhooks/subscribe/${encodeURIComponent(id)}`);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* List the current webhook subscribers (secrets are redacted server-side).
|
|
213
|
+
*
|
|
214
|
+
* `GET …/webhooks/subscribers` → `SubscriberList { subscribers: Subscriber[] }`. Chain:
|
|
215
|
+
* `ML0Routes.scala` (`webhook.list`) + `webhooks/Subscriber.scala`.
|
|
216
|
+
*/
|
|
217
|
+
async listWebhookSubscribers() {
|
|
218
|
+
return this.ml0.get('/data-application/v1/webhooks/subscribers');
|
|
219
|
+
}
|
|
220
|
+
// -------------------------------------------------------------------------
|
|
177
221
|
// Framework snapshot endpoints (ML0 /snapshots/*)
|
|
178
222
|
// -------------------------------------------------------------------------
|
|
179
223
|
/**
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Message-layer advisory lint for an {@link ApplyMorphism} transaction (asset-model.md §7; chain
|
|
4
|
+
* audit finding C2, enforced in `AssetCombiner`).
|
|
5
|
+
*
|
|
6
|
+
* Purpose
|
|
7
|
+
* -------
|
|
8
|
+
* The definition-scoped {@link ../schema/guard-lint.ts | guard-lint} walks fiber-app DEFINITIONS; it
|
|
9
|
+
* never sees an `ApplyMorphism` MESSAGE, so a malformed / no-consent `Compose` slips past it. This
|
|
10
|
+
* linter closes that gap at the transaction-builder layer: hand it the `ApplyMorphism` you are about
|
|
11
|
+
* to sign and it flags the two classes of C2 mistake the chain rejects.
|
|
12
|
+
*
|
|
13
|
+
* The chain rule being mirrored (C2, `Compose`/`Pool` only):
|
|
14
|
+
* - `otherAssetIds` must be free of duplicates and must NOT include the source `assetId`
|
|
15
|
+
* (the old self/duplicate-inflation path — rejected unconditionally, holder-independent).
|
|
16
|
+
* - every counter-party the signer does NOT hold requires a live {@link AuthorizeCompose} `nonce`;
|
|
17
|
+
* a same-holder / all-signer-owned compose needs none.
|
|
18
|
+
*
|
|
19
|
+
* ERRORS are statically certain (the chain rejects regardless of who holds what). WARNINGS are
|
|
20
|
+
* heuristic — the signer's holdings/ownership are unknown at build time, so a missing consent nonce
|
|
21
|
+
* cannot be proven fatal (a same-holder compose is legitimately nonce-less).
|
|
22
|
+
*
|
|
23
|
+
* This is a PURE, non-throwing advisory. It is deliberately NOT wired into
|
|
24
|
+
* {@link createApplyMorphismPayload} (which must stay non-breaking and side-effect-free); call it
|
|
25
|
+
* explicitly before signing, or in your app's own pre-flight checks.
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.MORPHISM_LINT_CODES = void 0;
|
|
29
|
+
exports.lintApplyMorphism = lintApplyMorphism;
|
|
30
|
+
/** Stable rule codes for {@link lintApplyMorphism} — referenced by tests and report tooling. */
|
|
31
|
+
exports.MORPHISM_LINT_CODES = {
|
|
32
|
+
/** `otherAssetIds` contains a duplicate id (chain rejects — inflation path). */
|
|
33
|
+
DUPLICATE_OTHER_ID: 'morphism-duplicate-other-id',
|
|
34
|
+
/** `otherAssetIds` includes the source `assetId` (self-composition — chain rejects). */
|
|
35
|
+
SELF_COMPOSE: 'morphism-self-compose',
|
|
36
|
+
/** `otherAssetIds` is non-empty but `kind` ignores it (not Compose/Pool — likely a mistake). */
|
|
37
|
+
OTHER_IDS_IGNORED: 'morphism-other-ids-ignored',
|
|
38
|
+
/** Compose/Pool with counter-parties and no consent `nonce` (rejected IF any part is not signer-owned). */
|
|
39
|
+
COMPOSE_NO_NONCE: 'morphism-compose-no-nonce',
|
|
40
|
+
};
|
|
41
|
+
/** The morphism kinds whose semantics READ `otherAssetIds` (fold counter-party parts into a composite). */
|
|
42
|
+
const COMPOSING_KINDS = new Set(['COMPOSE', 'POOL']);
|
|
43
|
+
function finding(severity, code, message, path) {
|
|
44
|
+
return { severity, code, message, path };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Lint a single {@link ApplyMorphism} message. Pure and non-throwing. Returns every finding
|
|
48
|
+
* (`error` + `warn`); callers decide the policy (e.g. block on any `error`, surface `warn`s).
|
|
49
|
+
*
|
|
50
|
+
* ERRORS (statically certain — the chain rejects unconditionally, holder-independent):
|
|
51
|
+
* - {@link MORPHISM_LINT_CODES.DUPLICATE_OTHER_ID} — `otherAssetIds` contains a duplicate id.
|
|
52
|
+
* - {@link MORPHISM_LINT_CODES.SELF_COMPOSE} — `otherAssetIds` includes the source `assetId`.
|
|
53
|
+
* - {@link MORPHISM_LINT_CODES.OTHER_IDS_IGNORED} — `otherAssetIds` is non-empty but `kind` is not
|
|
54
|
+
* `COMPOSE`/`POOL` (the field is ignored for other kinds — a likely authoring mistake).
|
|
55
|
+
*
|
|
56
|
+
* WARNINGS (heuristic — holder/ownership unknown at build time):
|
|
57
|
+
* - {@link MORPHISM_LINT_CODES.COMPOSE_NO_NONCE} — a `COMPOSE`/`POOL` with a non-empty
|
|
58
|
+
* `otherAssetIds` and NO `nonce`. IF any counter-party is not signer-owned the chain rejects it
|
|
59
|
+
* (C2); attach the reveal half of an {@link AuthorizeCompose} handshake as `nonce`. A same-holder
|
|
60
|
+
* compose (all parts signer-owned) is legitimately nonce-less — hence a warning, not an error.
|
|
61
|
+
*/
|
|
62
|
+
function lintApplyMorphism(msg) {
|
|
63
|
+
const out = [];
|
|
64
|
+
const ids = msg.otherAssetIds ?? [];
|
|
65
|
+
const isComposing = COMPOSING_KINDS.has(msg.kind);
|
|
66
|
+
// ---- error: duplicate ids in otherAssetIds ---------------------------------
|
|
67
|
+
// One finding per DISTINCT duplicated id (path points at its first repeat occurrence).
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const dupReported = new Set();
|
|
70
|
+
ids.forEach((id, i) => {
|
|
71
|
+
if (seen.has(id) && !dupReported.has(id)) {
|
|
72
|
+
dupReported.add(id);
|
|
73
|
+
out.push(finding('error', exports.MORPHISM_LINT_CODES.DUPLICATE_OTHER_ID, `otherAssetIds contains duplicate id "${id}". The chain rejects a Compose/Pool whose ` +
|
|
74
|
+
`otherAssetIds has duplicates (the old inflation path). List each counter-party asset once.`, `otherAssetIds[${i}]`));
|
|
75
|
+
}
|
|
76
|
+
seen.add(id);
|
|
77
|
+
});
|
|
78
|
+
// ---- error: otherAssetIds includes the source assetId (self-composition) ----
|
|
79
|
+
const selfIdx = ids.indexOf(msg.assetId);
|
|
80
|
+
if (selfIdx !== -1) {
|
|
81
|
+
out.push(finding('error', exports.MORPHISM_LINT_CODES.SELF_COMPOSE, `otherAssetIds includes the source assetId "${msg.assetId}" (self-composition). The chain ` +
|
|
82
|
+
`rejects this unconditionally. otherAssetIds must list only the OTHER parts folded into the composite.`, `otherAssetIds[${selfIdx}]`));
|
|
83
|
+
}
|
|
84
|
+
// ---- error: otherAssetIds set but kind ignores it ---------------------------
|
|
85
|
+
if (ids.length > 0 && !isComposing) {
|
|
86
|
+
out.push(finding('error', exports.MORPHISM_LINT_CODES.OTHER_IDS_IGNORED, `otherAssetIds is non-empty but kind is "${msg.kind}", which ignores otherAssetIds entirely ` +
|
|
87
|
+
`(only COMPOSE/POOL fold in counter-party parts). This is almost certainly a mistake — set ` +
|
|
88
|
+
`kind to COMPOSE/POOL, or drop otherAssetIds.`, 'otherAssetIds'));
|
|
89
|
+
}
|
|
90
|
+
// ---- warn: composing with counter-parties but no consent nonce --------------
|
|
91
|
+
// nonce === 0 is a valid live nonce; only an OMITTED nonce (undefined/null) triggers the warning.
|
|
92
|
+
if (isComposing && ids.length > 0 && msg.nonce == null) {
|
|
93
|
+
out.push(finding('warn', exports.MORPHISM_LINT_CODES.COMPOSE_NO_NONCE, `${msg.kind} folds in otherAssetIds but carries no consent nonce. If ANY counter-party asset ` +
|
|
94
|
+
`is not held by the signer, the chain REJECTS this (C2): attach the reveal half of an ` +
|
|
95
|
+
`AuthorizeCompose handshake as \`nonce\`. A same-holder compose (all parts signer-owned) ` +
|
|
96
|
+
`needs none, so this is advisory.`, 'nonce'));
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.commitKeyPath = commitKeyPath;
|
|
4
|
+
exports.verifyMptInclusion = verifyMptInclusion;
|
|
5
|
+
exports.verifyStateProof = verifyStateProof;
|
|
6
|
+
/**
|
|
7
|
+
* Light-client verification of committed state proofs.
|
|
8
|
+
*
|
|
9
|
+
* The chain's `/state-machines|scripts|assets/{id}/state-proof` endpoints return a
|
|
10
|
+
* {@link StateProof}: the record plus a Merkle-Patricia inclusion proof against `mptRoot`,
|
|
11
|
+
* whose combined hash IS the snapshot's consensus-signed `calculatedStateProof`. Verifying
|
|
12
|
+
* the proof locally means trusting NO serving node — only snapshot consensus.
|
|
13
|
+
*
|
|
14
|
+
* The fold mirrors metakit's `MerklePatriciaVerifier` byte-for-byte (validated against live
|
|
15
|
+
* cluster proofs):
|
|
16
|
+
*
|
|
17
|
+
* - the response `witness` is LEAF-FIRST; verification walks it reversed (root-first);
|
|
18
|
+
* - every node's digest = `sha256(typePrefix ++ utf8(JCS(dropNulls(contents))))` over the
|
|
19
|
+
* commitment's SUBTYPE (contents-only) encoding — prefixes: leaf `0x00`, branch `0x01`,
|
|
20
|
+
* extension `0x02`;
|
|
21
|
+
* - a Branch consumes one path nibble via `pathsDigest`; an Extension consumes its `shared`
|
|
22
|
+
* nibbles and continues at `childDigest`;
|
|
23
|
+
* - the terminal Leaf must match the remaining path (`remaining`) and bind
|
|
24
|
+
* `dataDigest == sha256(JCS(dropNulls(record)))` (no prefix);
|
|
25
|
+
* - the trie path of a committed key is `hex(utf8(key))` (metakit `CommitKey.toHex`), e.g.
|
|
26
|
+
* `fiber/<uuid>` → `66696265722f…`.
|
|
27
|
+
*
|
|
28
|
+
* NOTE: this intentionally does NOT route through the JLVM `mpt_verify` opcode — the
|
|
29
|
+
* TypeScript port of that opcode mis-canonicalizes non-scalar leaf values (fiber records)
|
|
30
|
+
* and returns false; its conformance vectors only cover scalar leaves. The Scala (chain)
|
|
31
|
+
* side is correct. Once the port is fixed upstream this helper stays valid either way: it
|
|
32
|
+
* is the same algorithm without the JsonLogic value-domain round-trip.
|
|
33
|
+
*/
|
|
34
|
+
const metagraph_sdk_1 = require("@constellation-network/metagraph-sdk");
|
|
35
|
+
const sha256_js_1 = require("@noble/hashes/sha256.js");
|
|
36
|
+
const utf8 = (s) => new TextEncoder().encode(s);
|
|
37
|
+
const toHexStr = (b) => Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
|
|
38
|
+
/** `sha256(prefixByte? ++ utf8(JCS(dropNulls(value))))` — the chain's content-digest rule. */
|
|
39
|
+
function jcsSha256(value, prefix) {
|
|
40
|
+
const canonical = utf8((0, metagraph_sdk_1.canonicalize)(value));
|
|
41
|
+
if (prefix === undefined)
|
|
42
|
+
return toHexStr((0, sha256_js_1.sha256)(canonical));
|
|
43
|
+
const framed = new Uint8Array(1 + canonical.length);
|
|
44
|
+
framed[0] = prefix;
|
|
45
|
+
framed.set(canonical, 1);
|
|
46
|
+
return toHexStr((0, sha256_js_1.sha256)(framed));
|
|
47
|
+
}
|
|
48
|
+
/** The MPT trie path of a committed key — `hex(utf8(key))` (metakit `CommitKey.toHex`). */
|
|
49
|
+
function commitKeyPath(key) {
|
|
50
|
+
return toHexStr(utf8(key));
|
|
51
|
+
}
|
|
52
|
+
const NODE_PREFIX = { Leaf: 0, Branch: 1, Extension: 2 };
|
|
53
|
+
/**
|
|
54
|
+
* Verify a Merkle-Patricia inclusion proof: `record` is committed at `key` under `mptRoot`.
|
|
55
|
+
*
|
|
56
|
+
* Pure and side-effect free; never throws on malformed input (returns `{ ok: false }`).
|
|
57
|
+
*/
|
|
58
|
+
function verifyMptInclusion(mptRoot, key, record, proof) {
|
|
59
|
+
const fail = (reason) => ({ ok: false, reason });
|
|
60
|
+
if (!proof || typeof proof.path !== 'string' || !Array.isArray(proof.witness) || proof.witness.length === 0)
|
|
61
|
+
return fail('malformed proof (missing path/witness)');
|
|
62
|
+
const expectedPath = commitKeyPath(key);
|
|
63
|
+
if (proof.path.toLowerCase() !== expectedPath)
|
|
64
|
+
return fail(`trie path != hex(utf8("${key}"))`);
|
|
65
|
+
let digest = String(mptRoot).replace(/^0x/, '').toLowerCase();
|
|
66
|
+
let path = proof.path.toLowerCase();
|
|
67
|
+
const nodes = [...proof.witness].reverse(); // response is leaf-first; fold root-first
|
|
68
|
+
for (const [depth, node] of nodes.entries()) {
|
|
69
|
+
const isLast = depth === nodes.length - 1;
|
|
70
|
+
const prefix = NODE_PREFIX[node?.type];
|
|
71
|
+
if (prefix === undefined)
|
|
72
|
+
return fail(`unknown witness node type '${String(node?.type)}'`);
|
|
73
|
+
if (jcsSha256(node.contents, prefix) !== digest)
|
|
74
|
+
return fail(`${node.type.toLowerCase()} commitment mismatch at depth ${depth}`);
|
|
75
|
+
if (node.type === 'Branch') {
|
|
76
|
+
const child = node.contents.pathsDigest?.[path[0]];
|
|
77
|
+
if (!child)
|
|
78
|
+
return fail(`branch has no child at nibble '${path[0]}' (depth ${depth})`);
|
|
79
|
+
digest = child.toLowerCase();
|
|
80
|
+
path = path.slice(1);
|
|
81
|
+
}
|
|
82
|
+
else if (node.type === 'Extension') {
|
|
83
|
+
const shared = String(node.contents.shared ?? '');
|
|
84
|
+
if (!path.startsWith(shared.toLowerCase()))
|
|
85
|
+
return fail(`extension shared-nibble mismatch at depth ${depth}`);
|
|
86
|
+
path = path.slice(shared.length);
|
|
87
|
+
digest = String(node.contents.childDigest ?? '').toLowerCase();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Leaf
|
|
91
|
+
if (!isLast)
|
|
92
|
+
return fail('leaf before end of witness');
|
|
93
|
+
if (String(node.contents.remaining ?? '').toLowerCase() !== path)
|
|
94
|
+
return fail('leaf remaining-path mismatch');
|
|
95
|
+
if (String(node.contents.dataDigest ?? '').toLowerCase() !== jcsSha256(record))
|
|
96
|
+
return fail('leaf dataDigest != sha256(JCS(record))');
|
|
97
|
+
return { ok: true };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return fail('witness does not terminate in a leaf');
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Verify a whole {@link StateProof} response for a committed key.
|
|
104
|
+
*
|
|
105
|
+
* Checks the trie inclusion of `record` at `key` under the response's `mptRoot` (see
|
|
106
|
+
* {@link verifyMptInclusion}). The caller supplies the key it EXPECTS (`fiber/<id>`,
|
|
107
|
+
* `script/<id>`, `asset/<id>`, …) so a proof for some other record cannot be substituted;
|
|
108
|
+
* the response's own `key` field must agree.
|
|
109
|
+
*
|
|
110
|
+
* Trust anchor: `committedRoot` (= `sha256(mptRoot ++ catalogRoot)`) rides in the
|
|
111
|
+
* consensus-signed snapshot as `calculatedStateProof`; binding `mptRoot` to a snapshot is
|
|
112
|
+
* the caller's (or a snapshot-following light client's) responsibility.
|
|
113
|
+
*/
|
|
114
|
+
function verifyStateProof(resp, expectedKey) {
|
|
115
|
+
if (resp.key !== expectedKey)
|
|
116
|
+
return { ok: false, reason: `response key '${resp.key}' != expected '${expectedKey}'` };
|
|
117
|
+
return verifyMptInclusion(String(resp.mptRoot), expectedKey, resp.record, resp.proof);
|
|
118
|
+
}
|
|
@@ -166,7 +166,13 @@ function createAssetPolicyPayload(params) {
|
|
|
166
166
|
function createMintAssetPayload(params) {
|
|
167
167
|
return { MintAsset: params };
|
|
168
168
|
}
|
|
169
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* Wrap an {@link ApplyMorphism} (apply a typed morphism to an asset instance).
|
|
171
|
+
*
|
|
172
|
+
* @see lintApplyMorphism (`./morphism-lint.ts`) — pure, non-throwing advisory that flags the
|
|
173
|
+
* Compose/Pool mistakes the chain rejects (duplicate / self-referential `otherAssetIds`; a
|
|
174
|
+
* cross-holder compose with no consent `nonce`). Run it on `params` before signing.
|
|
175
|
+
*/
|
|
170
176
|
function createApplyMorphismPayload(params) {
|
|
171
177
|
return { ApplyMorphism: params };
|
|
172
178
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Webhook notification PAYLOAD types — the server-initiated push the chain POSTs to a subscribed
|
|
4
|
+
* callback, NOT part of the client's request surface.
|
|
5
|
+
*
|
|
6
|
+
* These are hand-authored (not generated from the OpenAPI contract) because the push is an OUTBOUND
|
|
7
|
+
* POST from the metagraph to the subscriber's callback URL — it is not an endpoint the chain's tapir
|
|
8
|
+
* `ApiEndpoints` serves, so it never appears in `openapi/ottochain-openapi-ml0.json`. They mirror the
|
|
9
|
+
* chain's shared network DTOs byte-for-byte:
|
|
10
|
+
*
|
|
11
|
+
* chain: modules/models/src/main/scala/xyz/kd5ujc/schema/api/webhooks/Subscriber.scala
|
|
12
|
+
* (SnapshotNotification / NotificationStats / SnapshotRejection)
|
|
13
|
+
* emit: modules/l0/.../webhooks/WebhookDispatcher.scala — `notification.asJson.noSpaces`
|
|
14
|
+
*
|
|
15
|
+
* Field-shape notes tied to the chain source:
|
|
16
|
+
* - The dispatcher serializes with a plain derived encoder (`.asJson`, NO `dropNullValues`), so an
|
|
17
|
+
* `Option[Long]` that is `None` is emitted as an explicit `null` — the KEY is always present, the
|
|
18
|
+
* VALUE is nullable. Hence `number | null` (a required key), not `number | undefined` (an optional
|
|
19
|
+
* key). Consumers must handle `null`.
|
|
20
|
+
* - `Long` → JSON number; `Instant` → ISO-8601 string; `UUID` → string.
|
|
21
|
+
* - The dispatcher only ever emits `"snapshot.finalized"` today (there is a dead, never-dispatched
|
|
22
|
+
* `RejectionNotification`/`transaction.rejected` type in the chain models — deliberately NOT mirrored
|
|
23
|
+
* here, because the SDK should model only what is actually sent). Rejections now ride the finalized
|
|
24
|
+
* snapshot's `rejections[]`, drained from its committed `RejectionReceipt`s.
|
|
25
|
+
*
|
|
26
|
+
* @see modules/models/.../schema/api/webhooks/Subscriber.scala
|
|
27
|
+
* @see modules/l0/.../webhooks/WebhookDispatcher.scala
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.SNAPSHOT_FINALIZED_EVENT = void 0;
|
|
32
|
+
/** The only event the chain's `WebhookDispatcher` currently emits. */
|
|
33
|
+
exports.SNAPSHOT_FINALIZED_EVENT = 'snapshot.finalized';
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.RESERVED_EFFECT_KEYS = exports.emit = exports.spawn = exports.triggers = exports.toWallet = exports.toFiber = exports.transferAsset = exports.setDependencyActive = exports.addDependency = void 0;
|
|
19
|
+
exports.RESERVED_EFFECT_KEYS = exports.scriptCall = exports.emit = exports.spawn = exports.triggers = exports.toWallet = exports.toFiber = exports.transferAsset = exports.setDependencyActive = exports.addDependency = void 0;
|
|
20
20
|
/**
|
|
21
21
|
* `_addDependency` (#24): add — or re-activate — a runtime DYNAMIC dependency on `fiberId`, so that
|
|
22
22
|
* fiber's state appears in `machines.<fiberId>` for SUBSEQUENT transitions of this fiber. `fiberId` may
|
|
@@ -121,11 +121,20 @@ exports.triggers = triggers;
|
|
|
121
121
|
* MUST be a literal {@link ProtoStateMachineDefinition} (e.g. a nested `machine().wireDefinition()` /
|
|
122
122
|
* `toProtoDefinition(child)` output) — NOT an expression the engine would evaluate at runtime.
|
|
123
123
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
124
|
+
* H1 constraint — child `owners` MUST be a SUBSET of the SPAWNING PARENT fiber's `owners`. As of the
|
|
125
|
+
* fiber-engine permissionless-hardening (chain branch `fix/fiber-engine-permissionless-hardening`, audit
|
|
126
|
+
* finding H1) the chain FAILS-CLOSED — it aborts the whole transition under EVERY `spawnOwnerPolicy` — if
|
|
127
|
+
* a `_spawn`'s resolved child `owners` are not ⊆ the parent's owners. Assigning arbitrary, unsigned owners
|
|
128
|
+
* (the old "auction owned by all its bidders" pattern, where the bidders are NOT parent owners) is no
|
|
129
|
+
* longer valid: those events are not silently rejected, the spawn itself is rejected.
|
|
130
|
+
*
|
|
131
|
+
* So DO NOT try to enroll later participants by listing them here. A spawned child's transitions are gated
|
|
132
|
+
* by `owners ∪ authorizedSigners` for the DIRECT (wallet-signed) path; admit additional drivers through
|
|
133
|
+
* the child's OWN transitions (`authorizedSigners`), or drive it via the cascade (`_triggers`) subject to
|
|
134
|
+
* its `acceptedCallers` — never by widening `owners` beyond the parent's set. `owners` may be a literal id
|
|
135
|
+
* array or an expression (e.g. `{ var: "state.owners" }` — resolve it WITHIN the parent's owners), but a
|
|
136
|
+
* value the parent does not own (e.g. `{ var: "event.auctionOwners" }` supplied by the caller) will be
|
|
137
|
+
* rejected on-chain. `childId` / `initialData` likewise accept literals or expressions.
|
|
129
138
|
*/
|
|
130
139
|
const spawn = (ds) => ({ _spawn: ds });
|
|
131
140
|
exports.spawn = spawn;
|
|
@@ -139,6 +148,34 @@ const emit = (es) => ({
|
|
|
139
148
|
_emit: es,
|
|
140
149
|
});
|
|
141
150
|
exports.emit = emit;
|
|
151
|
+
/**
|
|
152
|
+
* `_scriptCall`: invoke a SCRIPT fiber's `method` from an effect. Unlike the array-valued directives,
|
|
153
|
+
* `_scriptCall` is a SINGLE object `{ fiberId, method, args }` (chain `ReservedKeys.scala` `SCRIPT_CALL` /
|
|
154
|
+
* `EffectExtractor.extractScriptCall`). `fiberId` is the target script's UUID (a literal or an expression,
|
|
155
|
+
* e.g. `{ var: "state.resolverId" }`); `method` is the script method NAME; `args` is the argument body —
|
|
156
|
+
* a JSON-Logic value the chain EVALUATES against the transition context before dispatch.
|
|
157
|
+
*
|
|
158
|
+
* The chain's extractor requires ALL THREE fields — if `args` is absent the whole call is silently DROPPED
|
|
159
|
+
* (fail-silent, like the other extractors). So the builder ALWAYS emits `args`, defaulting an omitted one
|
|
160
|
+
* to `{}` (no-args), mirroring how {@link triggers} defaults an absent `payload`. Authoring this as a
|
|
161
|
+
* builder makes a typo'd `_scriptcall` / wrong field a TypeScript error rather than a silently-merged
|
|
162
|
+
* state field. Place the fragment INSIDE the effect's state-update map so it rides in the evaluated result.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* effect: { merge: [ { var: "state" }, {
|
|
166
|
+
* status: "resolving",
|
|
167
|
+
* ...scriptCall({ fiberId: { var: "state.resolverId" }, method: "resolve",
|
|
168
|
+
* args: { marketId: { var: "machineId" } } }),
|
|
169
|
+
* } ] }
|
|
170
|
+
*/
|
|
171
|
+
const scriptCall = (call) => ({
|
|
172
|
+
_scriptCall: {
|
|
173
|
+
fiberId: call.fiberId,
|
|
174
|
+
method: call.method,
|
|
175
|
+
args: call.args ?? {},
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
exports.scriptCall = scriptCall;
|
|
142
179
|
/**
|
|
143
180
|
* The complete set of `_`-prefixed RESERVED effect keys the chain's `EffectExtractor` consumes and
|
|
144
181
|
* `StateMerger` strips from state (`ReservedKeys.scala:12-49`). Exported so a validator (Proposal 01)
|
|
@@ -194,8 +194,13 @@ function toProtoDefinition(def) {
|
|
|
194
194
|
from: t.from,
|
|
195
195
|
to: t.to,
|
|
196
196
|
eventName: t.eventName,
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
// guard/effect are REQUIRED by the Scala `Transition` case class (no defaults). The authoring
|
|
198
|
+
// `Transition` types them OPTIONAL for ergonomics, so ALWAYS project a concrete value here — an
|
|
199
|
+
// omitted key would serialize to `undefined`, get dropped, and fail the chain's decode (HTTP 400).
|
|
200
|
+
// The defaults are the exact shapes the chain's `PublishVersionSigningCanonicalSuite` accepts:
|
|
201
|
+
// an always-true guard `{"==":[1,1]}` and an empty (no-op) effect `{}`.
|
|
202
|
+
guard: t.guard ?? { '==': [1, 1] },
|
|
203
|
+
effect: t.effect ?? {},
|
|
199
204
|
// Chain `Transition.dependencies: Set[UUID]` is REQUIRED (no Scala default), and each
|
|
200
205
|
// element must be a fiber UUID string. A `DependencySpec` object is a build-time-only
|
|
201
206
|
// authoring affordance with no wire representation (concrete dependency UUIDs are per
|