@crediolabs/policy-synth 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/install/authority-overlap.d.ts +101 -0
- package/dist/install/authority-overlap.js +227 -0
- package/dist/install/index.d.ts +1 -0
- package/dist/install/index.js +4 -0
- package/dist/record/freshness.d.ts +17 -11
- package/dist/record/freshness.js +28 -18
- package/dist/record/index.d.ts +10 -0
- package/dist/record/index.js +32 -1
- package/dist/record/rpc.d.ts +4 -0
- package/dist/record/rpc.js +4 -1
- package/dist/registry/identify.d.ts +10 -1
- package/dist/registry/identify.js +4 -1
- package/dist/registry/on-chain-spec.d.ts +37 -0
- package/dist/registry/on-chain-spec.js +152 -0
- package/dist/run/index.d.ts +25 -4
- package/dist/run/index.js +65 -4
- package/dist/run/schemas.d.ts +313 -0
- package/dist/run/schemas.js +52 -0
- package/dist/synth/declare.d.ts +30 -0
- package/dist/synth/declare.js +98 -0
- package/dist/synth/index.d.ts +1 -0
- package/dist/synth/index.js +1 -0
- package/dist-cjs/install/authority-overlap.d.ts +101 -0
- package/dist-cjs/install/authority-overlap.js +236 -0
- package/dist-cjs/install/index.d.ts +1 -0
- package/dist-cjs/install/index.js +13 -2
- package/dist-cjs/record/freshness.d.ts +17 -11
- package/dist-cjs/record/freshness.js +28 -18
- package/dist-cjs/record/index.d.ts +10 -0
- package/dist-cjs/record/index.js +31 -0
- package/dist-cjs/record/rpc.d.ts +4 -0
- package/dist-cjs/record/rpc.js +7 -3
- package/dist-cjs/registry/identify.d.ts +10 -1
- package/dist-cjs/registry/identify.js +4 -0
- package/dist-cjs/registry/on-chain-spec.d.ts +37 -0
- package/dist-cjs/registry/on-chain-spec.js +159 -0
- package/dist-cjs/run/index.d.ts +25 -4
- package/dist-cjs/run/index.js +65 -2
- package/dist-cjs/run/schemas.d.ts +313 -0
- package/dist-cjs/run/schemas.js +53 -1
- package/dist-cjs/synth/declare.d.ts +30 -0
- package/dist-cjs/synth/declare.js +101 -0
- package/dist-cjs/synth/index.d.ts +1 -0
- package/dist-cjs/synth/index.js +3 -1
- package/package.json +2 -2
- package/src/install/authority-overlap.ts +312 -0
- package/src/install/index.ts +20 -0
- package/src/record/freshness.ts +29 -18
- package/src/record/index.ts +59 -2
- package/src/record/rpc.ts +4 -1
- package/src/registry/identify.ts +4 -1
- package/src/registry/on-chain-spec.ts +168 -0
- package/src/run/index.ts +79 -2
- package/src/run/schemas.ts +57 -0
- package/src/synth/declare.ts +157 -0
- package/src/synth/index.ts +5 -0
- package/dist/adapters/interpreter/index.d.ts +0 -1
- package/dist/adapters/interpreter/index.js +0 -2
- package/dist/adapters/oz/adapter.d.ts +0 -20
- package/dist/adapters/oz/adapter.js +0 -281
- package/dist/adapters/oz/index.d.ts +0 -1
- package/dist/adapters/oz/index.js +0 -2
- package/dist/codegen/compile-gate.d.ts +0 -33
- package/dist/codegen/compile-gate.js +0 -124
- package/dist/codegen/index.d.ts +0 -2
- package/dist/codegen/index.js +0 -8
- package/dist/codegen/template.d.ts +0 -18
- package/dist/codegen/template.js +0 -148
- package/dist/ir/index.d.ts +0 -1
- package/dist/ir/index.js +0 -2
- package/dist/ir/types.d.ts +0 -71
- package/dist/ir/types.js +0 -9
- package/dist/mandate/index.d.ts +0 -2
- package/dist/mandate/index.js +0 -2
- package/dist/mandate/to-ir.d.ts +0 -3
- package/dist/mandate/to-ir.js +0 -60
- package/dist/mandate/types.d.ts +0 -20
- package/dist/mandate/types.js +0 -8
- package/dist/seams/index.d.ts +0 -1
- package/dist/seams/index.js +0 -2
- package/dist/seams/types.d.ts +0 -64
- package/dist/seams/types.js +0 -11
- package/dist/synth/deny-cases.d.ts +0 -28
- package/dist/synth/deny-cases.js +0 -212
- package/dist/synth/evaluate.d.ts +0 -18
- package/dist/synth/evaluate.js +0 -271
- package/dist/synth/harness.d.ts +0 -28
- package/dist/synth/harness.js +0 -47
- package/dist/synth/minimize.d.ts +0 -4
- package/dist/synth/minimize.js +0 -38
- package/dist/synth/permit-context.d.ts +0 -8
- package/dist/synth/permit-context.js +0 -35
- package/dist/synth/predicate-literals.d.ts +0 -5
- package/dist/synth/predicate-literals.js +0 -25
- package/dist/synth/synthesize-from-mandate.d.ts +0 -20
- package/dist/synth/synthesize-from-mandate.js +0 -59
- package/dist/verify/envelope.d.ts +0 -15
- package/dist/verify/envelope.js +0 -22
- package/dist/verify/index.d.ts +0 -3
- package/dist/verify/index.js +0 -3
- package/dist/verify/simulate.d.ts +0 -18
- package/dist/verify/simulate.js +0 -172
- package/dist/verify/verify.d.ts +0 -15
- package/dist/verify/verify.js +0 -181
- package/dist-cjs/adapters/interpreter/index.d.ts +0 -1
- package/dist-cjs/adapters/interpreter/index.js +0 -7
- package/dist-cjs/adapters/oz/adapter.d.ts +0 -20
- package/dist-cjs/adapters/oz/adapter.js +0 -286
- package/dist-cjs/adapters/oz/index.d.ts +0 -1
- package/dist-cjs/adapters/oz/index.js +0 -8
- package/dist-cjs/codegen/compile-gate.d.ts +0 -33
- package/dist-cjs/codegen/compile-gate.js +0 -128
- package/dist-cjs/codegen/index.d.ts +0 -2
- package/dist-cjs/codegen/index.js +0 -14
- package/dist-cjs/codegen/template.d.ts +0 -18
- package/dist-cjs/codegen/template.js +0 -151
- package/dist-cjs/ir/index.d.ts +0 -1
- package/dist-cjs/ir/index.js +0 -3
- package/dist-cjs/ir/types.d.ts +0 -71
- package/dist-cjs/ir/types.js +0 -10
- package/dist-cjs/mandate/index.d.ts +0 -2
- package/dist-cjs/mandate/index.js +0 -6
- package/dist-cjs/mandate/to-ir.d.ts +0 -3
- package/dist-cjs/mandate/to-ir.js +0 -63
- package/dist-cjs/mandate/types.d.ts +0 -20
- package/dist-cjs/mandate/types.js +0 -9
- package/dist-cjs/seams/index.d.ts +0 -1
- package/dist-cjs/seams/index.js +0 -3
- package/dist-cjs/seams/types.d.ts +0 -64
- package/dist-cjs/seams/types.js +0 -12
- package/dist-cjs/synth/deny-cases.d.ts +0 -28
- package/dist-cjs/synth/deny-cases.js +0 -217
- package/dist-cjs/synth/evaluate.d.ts +0 -18
- package/dist-cjs/synth/evaluate.js +0 -274
- package/dist-cjs/synth/harness.d.ts +0 -28
- package/dist-cjs/synth/harness.js +0 -50
- package/dist-cjs/synth/minimize.d.ts +0 -4
- package/dist-cjs/synth/minimize.js +0 -41
- package/dist-cjs/synth/permit-context.d.ts +0 -8
- package/dist-cjs/synth/permit-context.js +0 -38
- package/dist-cjs/synth/predicate-literals.d.ts +0 -5
- package/dist-cjs/synth/predicate-literals.js +0 -28
- package/dist-cjs/synth/synthesize-from-mandate.d.ts +0 -20
- package/dist-cjs/synth/synthesize-from-mandate.js +0 -62
- package/dist-cjs/verify/envelope.d.ts +0 -15
- package/dist-cjs/verify/envelope.js +0 -23
- package/dist-cjs/verify/index.d.ts +0 -3
- package/dist-cjs/verify/index.js +0 -8
- package/dist-cjs/verify/simulate.d.ts +0 -18
- package/dist-cjs/verify/simulate.js +0 -175
- package/dist-cjs/verify/verify.d.ts +0 -15
- package/dist-cjs/verify/verify.js +0 -184
|
@@ -44,17 +44,23 @@ function isBelowThreshold(c) {
|
|
|
44
44
|
* under-confidence recording.
|
|
45
45
|
*
|
|
46
46
|
* The remediation text branches on which diagnostic bucket is non-empty:
|
|
47
|
-
* - unknownContracts only ->
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* - neither (the "denom === 0" path) ->
|
|
57
|
-
*
|
|
47
|
+
* - unknownContracts only -> the contract is not in the compiled-in
|
|
48
|
+
* registry. NOT "the contract has no interface": most Soroban
|
|
49
|
+
* contracts publish a typed spec on chain, this package just never
|
|
50
|
+
* reads one. The reason code stays `no-abi` because it is a published
|
|
51
|
+
* enum value, so the TEXT has to carry the correction.
|
|
52
|
+
* - opaqueScVals only -> the recorder met a value shape it should
|
|
53
|
+
* support but did not decode. That is a decoder bug, and the guidance
|
|
54
|
+
* says so rather than implying the caller can fix it.
|
|
55
|
+
* - both -> list both diagnostics and chain the right remediation.
|
|
56
|
+
* - neither (the "denom === 0" path) -> the caller did not actually hit
|
|
57
|
+
* a code-level barrier.
|
|
58
|
+
*
|
|
59
|
+
* Do NOT tell the caller to supply an ABI. There is no input for one:
|
|
60
|
+
* recognition runs off the compiled-in registry plus a known-contract set
|
|
61
|
+
* that no public tool boundary exposes. The old text named an action
|
|
62
|
+
* nobody could take and sent users looking for a file they do not have and
|
|
63
|
+
* would not need. */
|
|
58
64
|
function buildLowConfidenceQuestion(c) {
|
|
59
65
|
const hasUnknown = c.unknownContracts.length > 0;
|
|
60
66
|
const hasOpaque = c.opaqueScVals.length > 0;
|
|
@@ -67,16 +73,20 @@ function buildLowConfidenceQuestion(c) {
|
|
|
67
73
|
}
|
|
68
74
|
const why = reasons.length === 0 ? 'no diagnostic reason available' : reasons.join('; ');
|
|
69
75
|
const header = `Recording refused: parseConfidence ${c.overall.toFixed(3)} is below the threshold ${c.thresholdUsed.toFixed(3)}. Diagnostic: ${why}.`;
|
|
70
|
-
// The
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
76
|
+
// The remediation branches are explicit so the user (or the agent) can
|
|
77
|
+
// dispatch on the verb. Neither branch asks for an ABI: there is no input
|
|
78
|
+
// that accepts one, and for the unknown-contract branch the contract's
|
|
79
|
+
// own spec is usually on chain already - it is this package that does not
|
|
80
|
+
// read it.
|
|
81
|
+
const unknownAdvice = `The contract is not in this package's built-in registry - "no-abi" means unrecognised here, not that the contract lacks an interface. ` +
|
|
82
|
+
`You cannot supply one: the registry is compiled in. Either build the policy without deriving it from this transaction, or re-capture against a protocol the registry covers. ` +
|
|
83
|
+
`Passing confidenceOverride accepts the recording as it stands, including the arguments this tool could not attribute - do that only if you have checked them yourself.`;
|
|
74
84
|
if (hasUnknown && hasOpaque) {
|
|
75
|
-
return (`${header}
|
|
76
|
-
`
|
|
85
|
+
return (`${header} ${unknownAdvice} Separately, file the opaque ScVal path against the recorder (a decoder limitation) ` +
|
|
86
|
+
`and re-run record_transaction after a tool upgrade.`);
|
|
77
87
|
}
|
|
78
88
|
if (hasUnknown) {
|
|
79
|
-
return `${header}
|
|
89
|
+
return `${header} ${unknownAdvice}`;
|
|
80
90
|
}
|
|
81
91
|
if (hasOpaque) {
|
|
82
92
|
return `${header} This is a recorder decoder limitation (the value shape is supported in principle but not yet decoded) - the agent cannot fix it by supplying an ABI. Report the opaque ScVal path above against the recorder and re-run record_transaction after a tool upgrade.`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolResponse } from '../errors.ts';
|
|
2
|
+
import { type SpecFetcher } from '../registry/on-chain-spec.ts';
|
|
2
3
|
import type { Network, RecordedTransaction } from '../types.ts';
|
|
3
4
|
import { type RpcFetcher } from './rpc.ts';
|
|
4
5
|
/** Public input shape. The brief pins:
|
|
@@ -27,6 +28,15 @@ export interface RecordInput {
|
|
|
27
28
|
* automatically; tests can pass a deterministic stub. */
|
|
28
29
|
crossNetworkFetcher?: RpcFetcher;
|
|
29
30
|
confidenceOverride?: number;
|
|
31
|
+
/** Read a contract's own interface off chain when the compiled-in registry
|
|
32
|
+
* does not recognise it. Default ON: the registry covers the protocols we
|
|
33
|
+
* pinned by hand, and refusing everything else reported `no-abi` for
|
|
34
|
+
* contracts that publish a full typed spec. Set false to record against
|
|
35
|
+
* the registry alone (no extra RPC). */
|
|
36
|
+
resolveContractSpecs?: boolean;
|
|
37
|
+
/** Test seam for the spec lookup. Unset in production, where it is built
|
|
38
|
+
* from the network's pinned RPC URL. */
|
|
39
|
+
specFetcher?: SpecFetcher;
|
|
30
40
|
}
|
|
31
41
|
export type RecordResult = ToolResponse<RecordedTransaction>;
|
|
32
42
|
export declare function recordTransaction(input: RecordInput): Promise<RecordResult>;
|
package/dist-cjs/record/index.js
CHANGED
|
@@ -19,11 +19,40 @@
|
|
|
19
19
|
// defined in src/errors.ts.
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.recordTransaction = recordTransaction;
|
|
22
|
+
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
23
|
+
const on_chain_spec_ts_1 = require("../registry/on-chain-spec.js");
|
|
22
24
|
const decode_ts_1 = require("./decode.js");
|
|
23
25
|
const freshness_ts_1 = require("./freshness.js");
|
|
24
26
|
const movements_ts_1 = require("./movements.js");
|
|
25
27
|
const rpc_ts_1 = require("./rpc.js");
|
|
26
28
|
const validate_ts_1 = require("./validate.js");
|
|
29
|
+
/** Second pass over the contracts the compiled-in registry did not recognise.
|
|
30
|
+
*
|
|
31
|
+
* Each candidate's own interface is read off chain and every call it received
|
|
32
|
+
* is checked against it; the ones that verify are fed back through the decoder
|
|
33
|
+
* as known. Re-decoding rather than patching the first result keeps ONE code
|
|
34
|
+
* path computing parseConfidence - a hand-adjusted count here would be a
|
|
35
|
+
* second implementation of the gate, free to drift from the real one.
|
|
36
|
+
*
|
|
37
|
+
* Only ever ADDS recognition. A missing spec, an unreachable RPC or a call the
|
|
38
|
+
* interface does not describe all leave the recording exactly as it was. */
|
|
39
|
+
async function resolveByOnChainSpec(input, decoded, redecode) {
|
|
40
|
+
if (input.resolveContractSpecs === false)
|
|
41
|
+
return decoded;
|
|
42
|
+
if (decoded.unknownContracts.length === 0)
|
|
43
|
+
return decoded;
|
|
44
|
+
const fetcher = input.specFetcher ??
|
|
45
|
+
(0, on_chain_spec_ts_1.specFetcherFromRpc)(rpc_ts_1.PUBLIC_RPC_URLS[input.network], input.network === 'mainnet' ? stellar_sdk_1.Networks.PUBLIC : stellar_sdk_1.Networks.TESTNET);
|
|
46
|
+
let resolved;
|
|
47
|
+
try {
|
|
48
|
+
resolved = await (0, on_chain_spec_ts_1.resolveContractsByOnChainSpec)(decoded.invocations, decoded.unknownContracts.map((u) => u.contract), fetcher);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// A lookup failure must not fail the recording that already succeeded.
|
|
52
|
+
return decoded;
|
|
53
|
+
}
|
|
54
|
+
return resolved.size === 0 ? decoded : redecode(resolved);
|
|
55
|
+
}
|
|
27
56
|
async function recordTransaction(input) {
|
|
28
57
|
if (!input.network) {
|
|
29
58
|
return err('RECORDING_FAILED', 'network required', false);
|
|
@@ -79,6 +108,7 @@ async function recordTransaction(input) {
|
|
|
79
108
|
return err('RECORDING_FAILED', e.message, false);
|
|
80
109
|
throw e;
|
|
81
110
|
}
|
|
111
|
+
decoded = await resolveByOnChainSpec(input, decoded, (known) => (0, decode_ts_1.decodeEnvelope)(fetched.envelopeXdr, events, [], fetched.ledger, known, input.network));
|
|
82
112
|
return finish(input.network, decoded, input.confidenceOverride);
|
|
83
113
|
}
|
|
84
114
|
// XDR mode has no raw on-chain events, so the events-based cross-check is
|
|
@@ -96,6 +126,7 @@ async function recordTransaction(input) {
|
|
|
96
126
|
return err('RECORDING_FAILED', e.message, false);
|
|
97
127
|
return err('RECORDING_FAILED', `failed to decode base64 envelope XDR: ${e.message}`, false);
|
|
98
128
|
}
|
|
129
|
+
decoded = await resolveByOnChainSpec(input, decoded, (known) => (0, decode_ts_1.decodeEnvelopeXdr)(xdrStr, [], [], 0, known, input.network));
|
|
99
130
|
return finish(input.network, decoded, input.confidenceOverride);
|
|
100
131
|
}
|
|
101
132
|
function combineEvents(raw) {
|
package/dist-cjs/record/rpc.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export interface SorobanTxResponse {
|
|
|
14
14
|
events: rpc.Api.TransactionEvents;
|
|
15
15
|
}
|
|
16
16
|
export type RpcFetcher = (hash: string) => Promise<SorobanTxResponse | null>;
|
|
17
|
+
/** Exported so the on-chain spec lookup reads from the SAME endpoint the
|
|
18
|
+
* recorder fetched the transaction from. Two different endpoints could
|
|
19
|
+
* disagree about what a contract is. */
|
|
20
|
+
export declare const PUBLIC_RPC_URLS: Record<Network, string>;
|
|
17
21
|
/** Build a fetcher backed by the public Soroban RPC for the given network.
|
|
18
22
|
* Injectable by tests via the `fetcher` parameter to `recordTransaction`. */
|
|
19
23
|
export declare function createRpcServer(network: Network): RpcFetcher;
|
package/dist-cjs/record/rpc.js
CHANGED
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
// Unit tests inject a fetcher so they never hit the network; the integration test
|
|
6
6
|
// uses the real Soroban RPC through `createRpcServer`.
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.PUBLIC_RPC_URLS = void 0;
|
|
8
9
|
exports.createRpcServer = createRpcServer;
|
|
9
10
|
exports.probeNetwork = probeNetwork;
|
|
10
11
|
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
11
|
-
|
|
12
|
+
/** Exported so the on-chain spec lookup reads from the SAME endpoint the
|
|
13
|
+
* recorder fetched the transaction from. Two different endpoints could
|
|
14
|
+
* disagree about what a contract is. */
|
|
15
|
+
exports.PUBLIC_RPC_URLS = {
|
|
12
16
|
testnet: 'https://soroban-testnet.stellar.org',
|
|
13
17
|
// The brief pins testnet; mainnet is left to the caller via injection. We keep
|
|
14
18
|
// a public default that matches the brief's note ("e.g. https://mainnet.sorobanrpc.com").
|
|
@@ -17,7 +21,7 @@ const PUBLIC_RPC_URLS = {
|
|
|
17
21
|
/** Build a fetcher backed by the public Soroban RPC for the given network.
|
|
18
22
|
* Injectable by tests via the `fetcher` parameter to `recordTransaction`. */
|
|
19
23
|
function createRpcServer(network) {
|
|
20
|
-
const server = new stellar_sdk_1.rpc.Server(PUBLIC_RPC_URLS[network], { allowHttp: false });
|
|
24
|
+
const server = new stellar_sdk_1.rpc.Server(exports.PUBLIC_RPC_URLS[network], { allowHttp: false });
|
|
21
25
|
return async (hash) => {
|
|
22
26
|
const resp = await server.getTransaction(hash);
|
|
23
27
|
if (resp.status === stellar_sdk_1.rpc.Api.GetTransactionStatus.NOT_FOUND) {
|
|
@@ -50,7 +54,7 @@ function createRpcServer(network) {
|
|
|
50
54
|
/** Lightweight reachability probe used by the integration test to decide whether
|
|
51
55
|
* to run or self-skip. Sends a JSON-RPC `getHealth` to the public endpoint. */
|
|
52
56
|
async function probeNetwork(network, timeoutMs = 3000) {
|
|
53
|
-
const url = PUBLIC_RPC_URLS[network];
|
|
57
|
+
const url = exports.PUBLIC_RPC_URLS[network];
|
|
54
58
|
const controller = new AbortController();
|
|
55
59
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
56
60
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScVal } from '../types.ts';
|
|
2
|
-
import type { ProtocolId } from './protocols.ts';
|
|
2
|
+
import type { AbiArg, ProtocolId } from './protocols.ts';
|
|
3
3
|
export interface IdentifiedProtocol {
|
|
4
4
|
protocol: ProtocolId;
|
|
5
5
|
fn: string;
|
|
@@ -9,3 +9,12 @@ export interface IdentifiedProtocol {
|
|
|
9
9
|
* off, or unknown method on a pinned address). The caller MUST keep the
|
|
10
10
|
* null path fail-closed. */
|
|
11
11
|
export declare function identifyProtocol(contract: string, method: string, args: ScVal[], network?: 'mainnet' | 'testnet'): IdentifiedProtocol | null;
|
|
12
|
+
/** Compare the decoded args against the ABI signature. Returns true only when
|
|
13
|
+
* the arg count matches AND every arg's ScVal subset type matches the ABI's
|
|
14
|
+
* declared type. `other` is intentionally NOT a valid ABI match - it means
|
|
15
|
+
* the decoder couldn't classify the value, which is exactly the signal
|
|
16
|
+
* fail-closed should refuse. */
|
|
17
|
+
/** Exported so the on-chain-spec path checks a call the SAME way a pinned
|
|
18
|
+
* protocol does. Reimplementing it there would let the two drift, and a
|
|
19
|
+
* looser copy would be the fail-OPEN direction. */
|
|
20
|
+
export declare function argsMatchAbi(expected: AbiArg[], actual: ScVal[]): boolean;
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
// ScVal subset type against the ABI signature; mismatch -> null.
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
33
|
exports.identifyProtocol = identifyProtocol;
|
|
34
|
+
exports.argsMatchAbi = argsMatchAbi;
|
|
34
35
|
const known_addresses_ts_1 = require("./known-addresses.js");
|
|
35
36
|
const protocols_ts_1 = require("./protocols.js");
|
|
36
37
|
/** Identify the protocol for a single (contract, method, args) invocation.
|
|
@@ -91,6 +92,9 @@ function identifyProtocol(contract, method, args, network) {
|
|
|
91
92
|
* declared type. `other` is intentionally NOT a valid ABI match - it means
|
|
92
93
|
* the decoder couldn't classify the value, which is exactly the signal
|
|
93
94
|
* fail-closed should refuse. */
|
|
95
|
+
/** Exported so the on-chain-spec path checks a call the SAME way a pinned
|
|
96
|
+
* protocol does. Reimplementing it there would let the two drift, and a
|
|
97
|
+
* looser copy would be the fail-OPEN direction. */
|
|
94
98
|
function argsMatchAbi(expected, actual) {
|
|
95
99
|
if (expected.length !== actual.length)
|
|
96
100
|
return false;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { contract as sdkContract } from '@stellar/stellar-sdk';
|
|
2
|
+
import type { ContractInvocation } from '../types.ts';
|
|
3
|
+
import type { AbiArgType, ProtocolAbi } from './protocols.ts';
|
|
4
|
+
/** Map an XDR spec type to the ScVal subset vocabulary the matcher uses.
|
|
5
|
+
*
|
|
6
|
+
* Returns null for a type the recorder's ScVal subset cannot represent. That
|
|
7
|
+
* is deliberate: an argument we cannot type is an argument we cannot check,
|
|
8
|
+
* and claiming a match on it would be the fail-OPEN direction. A function
|
|
9
|
+
* with any such argument is dropped from the derived ABI, so a call to it
|
|
10
|
+
* stays unrecognised rather than being waved through. */
|
|
11
|
+
export declare function abiTypeFromSpecType(specTypeName: string): AbiArgType | null;
|
|
12
|
+
/** The RPC surface this module needs. Narrowed to one method so a test can
|
|
13
|
+
* supply a stub without standing up a server, and so the recorder's existing
|
|
14
|
+
* RPC client can be passed straight in. */
|
|
15
|
+
export interface SpecFetcher {
|
|
16
|
+
contractSpec(contractId: string): Promise<sdkContract.Spec | null>;
|
|
17
|
+
}
|
|
18
|
+
/** Build a `SpecFetcher` over the SDK's contract client. */
|
|
19
|
+
export declare function specFetcherFromRpc(rpcUrl: string, networkPassphrase: string): SpecFetcher;
|
|
20
|
+
/** Convert a fetched spec into the ABI shape `identifyProtocol` already
|
|
21
|
+
* matches against. Functions with an argument outside the ScVal subset are
|
|
22
|
+
* OMITTED rather than partially typed. */
|
|
23
|
+
export declare function abiFromSpec(spec: sdkContract.Spec): ProtocolAbi;
|
|
24
|
+
/** Fetch and convert in one step. Returns null when no usable interface was
|
|
25
|
+
* obtained, which the caller must treat as "unrecognised". */
|
|
26
|
+
export declare function fetchContractAbi(contractId: string, fetcher: SpecFetcher): Promise<ProtocolAbi | null>;
|
|
27
|
+
/** Contracts whose EVERY recorded call matches their own published interface.
|
|
28
|
+
*
|
|
29
|
+
* All-or-nothing per contract, deliberately. A contract where one call
|
|
30
|
+
* verifies and another does not is a contract we do not understand, and
|
|
31
|
+
* marking it recognised would raise confidence on the strength of the call
|
|
32
|
+
* we happened to check. The unverified call is the one that matters.
|
|
33
|
+
*
|
|
34
|
+
* A contract with no fetchable spec, or a call naming a function absent from
|
|
35
|
+
* it, simply stays unknown - this only ever ADDS recognition, so a failure
|
|
36
|
+
* here degrades to today's behaviour rather than to a wrong answer. */
|
|
37
|
+
export declare function resolveContractsByOnChainSpec(invocations: ReadonlyArray<ContractInvocation>, candidates: ReadonlyArray<string>, fetcher: SpecFetcher): Promise<Set<string>>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/registry/on-chain-spec.ts - read a contract's own interface off chain.
|
|
3
|
+
//
|
|
4
|
+
// The compiled-in registry covers the protocols we pinned by hand. Everything
|
|
5
|
+
// else was reported as `no-abi`, which read as "this contract does not
|
|
6
|
+
// describe itself" when it almost always does: a Soroban contract embeds a
|
|
7
|
+
// typed spec in its wasm, and the network will hand it over. The recorder was
|
|
8
|
+
// refusing calls whose interface was one RPC round-trip away.
|
|
9
|
+
//
|
|
10
|
+
// What this buys is NARROW and deliberately so. A fetched spec says what the
|
|
11
|
+
// contract's arguments ARE, not what they MEAN. So a call verified against it
|
|
12
|
+
// is recognised - the decode is trustworthy, the confidence gate stops
|
|
13
|
+
// refusing it - but the arguments carry the contract's own parameter NAMES
|
|
14
|
+
// rather than a curated meaning, and nothing here infers which argument is a
|
|
15
|
+
// spend or a recipient. A pinned protocol still outranks a fetched spec for
|
|
16
|
+
// exactly that reason, and `identifyProtocol` is consulted first.
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.abiTypeFromSpecType = abiTypeFromSpecType;
|
|
19
|
+
exports.specFetcherFromRpc = specFetcherFromRpc;
|
|
20
|
+
exports.abiFromSpec = abiFromSpec;
|
|
21
|
+
exports.fetchContractAbi = fetchContractAbi;
|
|
22
|
+
exports.resolveContractsByOnChainSpec = resolveContractsByOnChainSpec;
|
|
23
|
+
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
24
|
+
const identify_ts_1 = require("./identify.js");
|
|
25
|
+
/** Map an XDR spec type to the ScVal subset vocabulary the matcher uses.
|
|
26
|
+
*
|
|
27
|
+
* Returns null for a type the recorder's ScVal subset cannot represent. That
|
|
28
|
+
* is deliberate: an argument we cannot type is an argument we cannot check,
|
|
29
|
+
* and claiming a match on it would be the fail-OPEN direction. A function
|
|
30
|
+
* with any such argument is dropped from the derived ABI, so a call to it
|
|
31
|
+
* stays unrecognised rather than being waved through. */
|
|
32
|
+
function abiTypeFromSpecType(specTypeName) {
|
|
33
|
+
switch (specTypeName) {
|
|
34
|
+
case 'scSpecTypeAddress':
|
|
35
|
+
return 'address';
|
|
36
|
+
case 'scSpecTypeI128':
|
|
37
|
+
return 'i128';
|
|
38
|
+
case 'scSpecTypeU64':
|
|
39
|
+
case 'scSpecTypeI64':
|
|
40
|
+
return 'u64';
|
|
41
|
+
case 'scSpecTypeU32':
|
|
42
|
+
case 'scSpecTypeI32':
|
|
43
|
+
return 'u32';
|
|
44
|
+
case 'scSpecTypeSymbol':
|
|
45
|
+
return 'symbol';
|
|
46
|
+
case 'scSpecTypeVec':
|
|
47
|
+
return 'vec';
|
|
48
|
+
case 'scSpecTypeBytes':
|
|
49
|
+
case 'scSpecTypeBytesN':
|
|
50
|
+
return 'bytes';
|
|
51
|
+
case 'scSpecTypeMap':
|
|
52
|
+
return 'map';
|
|
53
|
+
default:
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Build a `SpecFetcher` over the SDK's contract client. */
|
|
58
|
+
function specFetcherFromRpc(rpcUrl, networkPassphrase) {
|
|
59
|
+
return {
|
|
60
|
+
async contractSpec(contractId) {
|
|
61
|
+
try {
|
|
62
|
+
const client = await stellar_sdk_1.contract.Client.from({ contractId, networkPassphrase, rpcUrl });
|
|
63
|
+
return client.spec ?? null;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// A contract with no spec, an unreachable RPC and a bad address all
|
|
67
|
+
// land here and all mean the same thing to the caller: no interface
|
|
68
|
+
// was obtained, so the contract stays unrecognised. Fail closed.
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Convert a fetched spec into the ABI shape `identifyProtocol` already
|
|
75
|
+
* matches against. Functions with an argument outside the ScVal subset are
|
|
76
|
+
* OMITTED rather than partially typed. */
|
|
77
|
+
function abiFromSpec(spec) {
|
|
78
|
+
const abi = {};
|
|
79
|
+
for (const fn of spec.funcs()) {
|
|
80
|
+
const name = fn.name().toString();
|
|
81
|
+
// The constructor is not callable after deployment, so a recorded
|
|
82
|
+
// invocation can never be one. Keeping it would only widen the surface.
|
|
83
|
+
if (name === '__constructor')
|
|
84
|
+
continue;
|
|
85
|
+
const args = [];
|
|
86
|
+
let usable = true;
|
|
87
|
+
for (const input of fn.inputs()) {
|
|
88
|
+
const type = abiTypeFromSpecType(input.type().switch().name);
|
|
89
|
+
if (type === null) {
|
|
90
|
+
usable = false;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
const argName = input.name().toString();
|
|
94
|
+
args.push({
|
|
95
|
+
name: argName,
|
|
96
|
+
type,
|
|
97
|
+
// The contract's own parameter name is the honest description. It is
|
|
98
|
+
// NOT a curated meaning - nothing here knows whether `value` is a
|
|
99
|
+
// spend - so downstream must not treat it as one.
|
|
100
|
+
meaning: `${argName} (from the contract's on-chain interface)`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (usable)
|
|
104
|
+
abi[name] = { args };
|
|
105
|
+
}
|
|
106
|
+
return abi;
|
|
107
|
+
}
|
|
108
|
+
/** Fetch and convert in one step. Returns null when no usable interface was
|
|
109
|
+
* obtained, which the caller must treat as "unrecognised". */
|
|
110
|
+
async function fetchContractAbi(contractId, fetcher) {
|
|
111
|
+
const spec = await fetcher.contractSpec(contractId);
|
|
112
|
+
if (spec === null)
|
|
113
|
+
return null;
|
|
114
|
+
const abi = abiFromSpec(spec);
|
|
115
|
+
return Object.keys(abi).length === 0 ? null : abi;
|
|
116
|
+
}
|
|
117
|
+
/** Every contract invoked anywhere in the tree, with the calls made on it. */
|
|
118
|
+
function callsByContract(invocations) {
|
|
119
|
+
const out = new Map();
|
|
120
|
+
const walk = (inv) => {
|
|
121
|
+
const list = out.get(inv.contract) ?? [];
|
|
122
|
+
list.push({ fn: inv.fn, args: inv.args });
|
|
123
|
+
out.set(inv.contract, list);
|
|
124
|
+
for (const sub of inv.subInvocations)
|
|
125
|
+
walk(sub);
|
|
126
|
+
};
|
|
127
|
+
for (const inv of invocations)
|
|
128
|
+
walk(inv);
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
/** Contracts whose EVERY recorded call matches their own published interface.
|
|
132
|
+
*
|
|
133
|
+
* All-or-nothing per contract, deliberately. A contract where one call
|
|
134
|
+
* verifies and another does not is a contract we do not understand, and
|
|
135
|
+
* marking it recognised would raise confidence on the strength of the call
|
|
136
|
+
* we happened to check. The unverified call is the one that matters.
|
|
137
|
+
*
|
|
138
|
+
* A contract with no fetchable spec, or a call naming a function absent from
|
|
139
|
+
* it, simply stays unknown - this only ever ADDS recognition, so a failure
|
|
140
|
+
* here degrades to today's behaviour rather than to a wrong answer. */
|
|
141
|
+
async function resolveContractsByOnChainSpec(invocations, candidates, fetcher) {
|
|
142
|
+
const resolved = new Set();
|
|
143
|
+
const calls = callsByContract(invocations);
|
|
144
|
+
for (const contract of new Set(candidates)) {
|
|
145
|
+
const made = calls.get(contract);
|
|
146
|
+
if (!made || made.length === 0)
|
|
147
|
+
continue;
|
|
148
|
+
const abi = await fetchContractAbi(contract, fetcher);
|
|
149
|
+
if (abi === null)
|
|
150
|
+
continue;
|
|
151
|
+
const everyCallVerifies = made.every((c) => {
|
|
152
|
+
const entry = abi[c.fn];
|
|
153
|
+
return entry !== undefined && (0, identify_ts_1.argsMatchAbi)(entry.args, c.args);
|
|
154
|
+
});
|
|
155
|
+
if (everyCallVerifies)
|
|
156
|
+
resolved.add(contract);
|
|
157
|
+
}
|
|
158
|
+
return resolved;
|
|
159
|
+
}
|
package/dist-cjs/run/index.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { type ErrorCode, type PredicateNode, type ProposedPolicy, type RecordedTransaction, type ToolError, type ToolResponse } from '../index.ts';
|
|
2
|
+
import { type AuthorityOverlap } from '../install/authority-overlap.ts';
|
|
2
3
|
import { type BuildInstallPolicyResult, type BuildRevokePolicyResult } from '../install/build-install-policy.ts';
|
|
3
4
|
import { getInterpreterInfo } from '../install/get-interpreter-info.ts';
|
|
4
5
|
import { type RecordTransactionInput, type SimulatePolicyInput, type SynthesizePolicyInput, type VerifyPolicyInput } from './schemas.ts';
|
|
5
|
-
export type { GetInterpreterInfoInput, InstallPolicyInput, RecordTransactionInput, RevokePolicyInput, SimulatePolicyInput, SynthesizePolicyInput, VerifyPolicyInput, } from './schemas.ts';
|
|
6
|
-
export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, NetworkSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, } from './schemas.ts';
|
|
6
|
+
export type { DeclarePolicyInput, GetInterpreterInfoInput, InstallPolicyInput, RecordTransactionInput, RevokePolicyInput, SimulatePolicyInput, SynthesizePolicyInput, VerifyPolicyInput, } from './schemas.ts';
|
|
7
|
+
export { ComposeUserResponsesSchema, DeclarePolicyInputSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, NetworkSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, } from './schemas.ts';
|
|
7
8
|
export type RunRecordTransactionInput = RecordTransactionInput;
|
|
8
9
|
export type RunSynthesizePolicyInput = SynthesizePolicyInput;
|
|
9
10
|
export type RunSimulatePolicyInput = SimulatePolicyInput;
|
|
10
11
|
export type RunVerifyPolicyInput = VerifyPolicyInput;
|
|
11
|
-
type RunToolName = 'record_transaction' | 'synthesize_policy' | 'simulate_policy' | 'verify_policy' | 'install_policy' | 'revoke_policy' | 'get_interpreter_info';
|
|
12
|
+
type RunToolName = 'record_transaction' | 'synthesize_policy' | 'declare_policy' | 'simulate_policy' | 'verify_policy' | 'install_policy' | 'revoke_policy' | 'get_interpreter_info';
|
|
12
13
|
/** `record_transaction` body - wraps `recordTransaction`. The tool input
|
|
13
14
|
* matches the core RecordInput minus the injected `fetcher` (the transport
|
|
14
15
|
* layer does not own the RPC). Returns the core ToolResponse unchanged.
|
|
@@ -37,7 +38,9 @@ export declare function runSynthesizePolicy(raw: unknown): Promise<ToolResponse<
|
|
|
37
38
|
predicateTree: PredicateNode | null;
|
|
38
39
|
};
|
|
39
40
|
}>;
|
|
40
|
-
export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<BuildInstallPolicyResult
|
|
41
|
+
export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<BuildInstallPolicyResult & {
|
|
42
|
+
authorityScan: AuthorityOverlap[] | null;
|
|
43
|
+
}>>;
|
|
41
44
|
/** `revoke_policy` body - thin wrapper over `buildRevokePolicyXdr`.
|
|
42
45
|
* Emits an unsigned XDR for `account.remove_context_rule(ruleId)`; the
|
|
43
46
|
* smart account itself handles uninstalling each attached policy. Auth
|
|
@@ -63,6 +66,24 @@ export declare function runSimulatePolicy(raw: unknown): ToolResponse<{
|
|
|
63
66
|
argCount: number;
|
|
64
67
|
};
|
|
65
68
|
}>;
|
|
69
|
+
/** `declare_policy` body - the DECLARATIVE front-end.
|
|
70
|
+
*
|
|
71
|
+
* `synthesize_policy` infers a predicate from a transaction that happened;
|
|
72
|
+
* this takes the constraint stated outright. No RPC, no decoding and no
|
|
73
|
+
* parseConfidence, so nothing here can be refused for a contract the registry
|
|
74
|
+
* does not recognise - which is most of the point of having it.
|
|
75
|
+
*
|
|
76
|
+
* The returned `warnings` are load-bearing, not decoration. An argument index
|
|
77
|
+
* the caller did not supply is DEFAULTED to the SEP-41 position, and a bound
|
|
78
|
+
* on the wrong argument constrains something the caller did not mean without
|
|
79
|
+
* ever announcing itself, so a caller that ignores warnings can install a
|
|
80
|
+
* predicate that reads correctly and binds nothing. */
|
|
81
|
+
export declare function runDeclarePolicy(raw: unknown): ToolResponse<{
|
|
82
|
+
predicate: PredicateNode;
|
|
83
|
+
encodedPredicate: string;
|
|
84
|
+
predicateHash: string;
|
|
85
|
+
warnings: string[];
|
|
86
|
+
}>;
|
|
66
87
|
/** `verify_policy` body - the permit case plus a generated deny case per
|
|
67
88
|
* dimension.
|
|
68
89
|
*
|
package/dist-cjs/run/index.js
CHANGED
|
@@ -18,20 +18,23 @@
|
|
|
18
18
|
// No business logic. No retries. No session state. The same call shape can
|
|
19
19
|
// drive the CLI (which calls into the same core directly without MCP).
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.ToolErrorSchema = exports.TESTNET_RPC_URL = exports.SynthesizePolicyInputSchema = exports.RPC_URL_BY_NETWORK = exports.RevokePolicyInputSchema = exports.RecordTransactionInputSchema = exports.RecordedTransactionSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.NetworkSchema = exports.MAINNET_RPC_URL = exports.InterpreterOptionsSchema = exports.InstallPolicyInputSchema = exports.GetInterpreterInfoInputSchema = exports.ComposeUserResponsesSchema = void 0;
|
|
21
|
+
exports.ToolErrorSchema = exports.TESTNET_RPC_URL = exports.SynthesizePolicyInputSchema = exports.RPC_URL_BY_NETWORK = exports.RevokePolicyInputSchema = exports.RecordTransactionInputSchema = exports.RecordedTransactionSchema = exports.PredicateNodeSchema = exports.PredicateLeafSchema = exports.PINNED_INTERPRETER_WASM_SHA256 = exports.PINNED_INTERPRETER_TESTNET_ADDRESS = exports.PINNED_INTERPRETER_MAINNET_ADDRESS = exports.PINNED_INTERPRETER_GRAMMAR_VERSION = exports.PINNED_INTERPRETER_ADDRESS_BY_NETWORK = exports.NetworkSchema = exports.MAINNET_RPC_URL = exports.InterpreterOptionsSchema = exports.InstallPolicyInputSchema = exports.GetInterpreterInfoInputSchema = exports.DeclarePolicyInputSchema = exports.ComposeUserResponsesSchema = void 0;
|
|
22
22
|
exports.runRecordTransaction = runRecordTransaction;
|
|
23
23
|
exports.runSynthesizePolicy = runSynthesizePolicy;
|
|
24
24
|
exports.runInstallPolicy = runInstallPolicy;
|
|
25
25
|
exports.runRevokePolicy = runRevokePolicy;
|
|
26
26
|
exports.runSimulatePolicy = runSimulatePolicy;
|
|
27
|
+
exports.runDeclarePolicy = runDeclarePolicy;
|
|
27
28
|
exports.runVerifyPolicy = runVerifyPolicy;
|
|
28
29
|
exports.runGetInterpreterInfo = runGetInterpreterInfo;
|
|
29
30
|
exports.caughtError = caughtError;
|
|
30
31
|
const node_crypto_1 = require("node:crypto");
|
|
31
32
|
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
32
33
|
const index_ts_1 = require("../index.js");
|
|
34
|
+
const authority_overlap_ts_1 = require("../install/authority-overlap.js");
|
|
33
35
|
const build_install_policy_ts_1 = require("../install/build-install-policy.js");
|
|
34
36
|
const get_interpreter_info_ts_1 = require("../install/get-interpreter-info.js");
|
|
37
|
+
const decode_ts_1 = require("../predicate/decode.js");
|
|
35
38
|
const index_ts_2 = require("../simulate/index.js");
|
|
36
39
|
const schemas_ts_1 = require("./schemas.js");
|
|
37
40
|
// Re-export the underlying Zod schemas so the MCP package (and any other
|
|
@@ -40,6 +43,7 @@ const schemas_ts_1 = require("./schemas.js");
|
|
|
40
43
|
// truth - MCP tool shapes are derived from them.
|
|
41
44
|
var schemas_ts_2 = require("./schemas.js");
|
|
42
45
|
Object.defineProperty(exports, "ComposeUserResponsesSchema", { enumerable: true, get: function () { return schemas_ts_2.ComposeUserResponsesSchema; } });
|
|
46
|
+
Object.defineProperty(exports, "DeclarePolicyInputSchema", { enumerable: true, get: function () { return schemas_ts_2.DeclarePolicyInputSchema; } });
|
|
43
47
|
Object.defineProperty(exports, "GetInterpreterInfoInputSchema", { enumerable: true, get: function () { return schemas_ts_2.GetInterpreterInfoInputSchema; } });
|
|
44
48
|
Object.defineProperty(exports, "InstallPolicyInputSchema", { enumerable: true, get: function () { return schemas_ts_2.InstallPolicyInputSchema; } });
|
|
45
49
|
Object.defineProperty(exports, "InterpreterOptionsSchema", { enumerable: true, get: function () { return schemas_ts_2.InterpreterOptionsSchema; } });
|
|
@@ -65,6 +69,7 @@ Object.defineProperty(exports, "ToolErrorSchema", { enumerable: true, get: funct
|
|
|
65
69
|
const TOOL_ERROR_CODE = {
|
|
66
70
|
record_transaction: 'RECORDING_FAILED',
|
|
67
71
|
synthesize_policy: 'SYNTHESIS_ERROR',
|
|
72
|
+
declare_policy: 'SYNTHESIS_ERROR',
|
|
68
73
|
simulate_policy: 'SIMULATION_ERROR',
|
|
69
74
|
verify_policy: 'VERIFICATION_FAILED',
|
|
70
75
|
install_policy: 'INSTALL_BUILD_FAILED',
|
|
@@ -179,7 +184,27 @@ async function runInstallPolicy(raw) {
|
|
|
179
184
|
rpc: rpcClient,
|
|
180
185
|
...(input.baseFee !== undefined ? { baseFee: input.baseFee } : {}),
|
|
181
186
|
});
|
|
182
|
-
|
|
187
|
+
// Cross-rule scan, when the caller supplied what else is on the account.
|
|
188
|
+
// ABSENT is reported as `null` rather than an empty list: "we did not
|
|
189
|
+
// look" and "we looked and found nothing" are different answers, and
|
|
190
|
+
// collapsing them would let a caller read silence as safety.
|
|
191
|
+
const authorityScan = input.existingRules === undefined
|
|
192
|
+
? null
|
|
193
|
+
: (0, authority_overlap_ts_1.findAuthorityOverlaps)({
|
|
194
|
+
intended: {
|
|
195
|
+
// `add_context_rule` gets its id FROM the account, so there is
|
|
196
|
+
// no existing rule this install replaces. A sentinel no real id
|
|
197
|
+
// can equal keeps every observed rule in scope.
|
|
198
|
+
ruleId: -1,
|
|
199
|
+
contextType: input.rule.contextRuleType,
|
|
200
|
+
signers: input.rule.signers,
|
|
201
|
+
predicate: (0, decode_ts_1.decodePredicate)(encodedPredicate),
|
|
202
|
+
},
|
|
203
|
+
// The schema types `predicate` loosely (it is the shared
|
|
204
|
+
// PredicateNodeSchema); the shape is already validated.
|
|
205
|
+
existing: input.existingRules,
|
|
206
|
+
});
|
|
207
|
+
return { ok: true, data: { ...result, authorityScan } };
|
|
183
208
|
}
|
|
184
209
|
catch (e) {
|
|
185
210
|
return toolFailure('install_policy', e);
|
|
@@ -298,6 +323,44 @@ function runSimulatePolicy(raw) {
|
|
|
298
323
|
return toolFailure('simulate_policy', e);
|
|
299
324
|
}
|
|
300
325
|
}
|
|
326
|
+
/** `declare_policy` body - the DECLARATIVE front-end.
|
|
327
|
+
*
|
|
328
|
+
* `synthesize_policy` infers a predicate from a transaction that happened;
|
|
329
|
+
* this takes the constraint stated outright. No RPC, no decoding and no
|
|
330
|
+
* parseConfidence, so nothing here can be refused for a contract the registry
|
|
331
|
+
* does not recognise - which is most of the point of having it.
|
|
332
|
+
*
|
|
333
|
+
* The returned `warnings` are load-bearing, not decoration. An argument index
|
|
334
|
+
* the caller did not supply is DEFAULTED to the SEP-41 position, and a bound
|
|
335
|
+
* on the wrong argument constrains something the caller did not mean without
|
|
336
|
+
* ever announcing itself, so a caller that ignores warnings can install a
|
|
337
|
+
* predicate that reads correctly and binds nothing. */
|
|
338
|
+
function runDeclarePolicy(raw) {
|
|
339
|
+
const parsed = schemas_ts_1.DeclarePolicyInputSchema.safeParse(raw);
|
|
340
|
+
if (!parsed.success) {
|
|
341
|
+
return { ok: false, error: validationError('declare_policy', parsed.error.issues) };
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
// Rebuilt field-by-field rather than passed through: the schema's
|
|
345
|
+
// optionals are `T | undefined` and `PolicyDeclaration`'s are absent-or-T,
|
|
346
|
+
// which `exactOptionalPropertyTypes` treats as different.
|
|
347
|
+
const d = parsed.data;
|
|
348
|
+
const { predicate, warnings } = (0, index_ts_1.declarePredicate)({
|
|
349
|
+
fn: d.fn,
|
|
350
|
+
...(d.contract !== undefined ? { contract: d.contract } : {}),
|
|
351
|
+
...(d.maxAmount !== undefined ? { maxAmount: d.maxAmount } : {}),
|
|
352
|
+
...(d.amountArgIndex !== undefined ? { amountArgIndex: d.amountArgIndex } : {}),
|
|
353
|
+
...(d.recipients !== undefined ? { recipients: d.recipients } : {}),
|
|
354
|
+
...(d.recipientArgIndex !== undefined ? { recipientArgIndex: d.recipientArgIndex } : {}),
|
|
355
|
+
...(d.allowZeroCap !== undefined ? { allowZeroCap: d.allowZeroCap } : {}),
|
|
356
|
+
});
|
|
357
|
+
const { encodedPredicate, predicateHash } = (0, index_ts_1.encodePredicate)(predicate);
|
|
358
|
+
return { ok: true, data: { predicate, encodedPredicate, predicateHash, warnings } };
|
|
359
|
+
}
|
|
360
|
+
catch (e) {
|
|
361
|
+
return toolFailure('declare_policy', e);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
301
364
|
/** `verify_policy` body - the permit case plus a generated deny case per
|
|
302
365
|
* dimension.
|
|
303
366
|
*
|