@chainlink/cre-sdk 1.0.0 → 1.0.2
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/generated/capabilities/blockchain/evm/v1alpha/client_pb.d.ts +0 -192
- package/dist/generated/capabilities/blockchain/evm/v1alpha/client_pb.js +4 -27
- package/dist/generated/capabilities/networking/confidentialhttp/v1alpha/client_pb.d.ts +245 -0
- package/dist/generated/capabilities/networking/confidentialhttp/v1alpha/client_pb.js +47 -0
- package/dist/generated/capabilities/networking/http/v1alpha/client_pb.d.ts +21 -16
- package/dist/generated/capabilities/networking/http/v1alpha/client_pb.js +2 -1
- package/dist/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.d.ts +12 -13
- package/dist/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen.js +33 -103
- package/dist/generated-sdk/capabilities/networking/confidentialhttp/v1alpha/client_sdk_gen.d.ts +32 -0
- package/dist/generated-sdk/capabilities/networking/confidentialhttp/v1alpha/client_sdk_gen.js +71 -0
- package/dist/pb.d.ts +1 -0
- package/dist/pb.js +1 -0
- package/dist/sdk/cre/index.d.ts +3 -0
- package/dist/sdk/cre/index.js +4 -0
- package/dist/sdk/impl/runtime-impl.d.ts +70 -1
- package/dist/sdk/impl/runtime-impl.js +215 -110
- package/dist/sdk/runtime.d.ts +26 -8
- package/dist/sdk/utils/capabilities/blockchain/blockchain-helpers.d.ts +43 -0
- package/dist/sdk/utils/capabilities/blockchain/blockchain-helpers.js +51 -1
- package/dist/sdk/utils/capabilities/http/http-helpers.d.ts +7 -6
- package/dist/sdk/utils/chain-selectors/{getNetwork.d.ts → get-network.d.ts} +2 -8
- package/dist/sdk/utils/chain-selectors/get-network.js +18 -0
- package/dist/sdk/utils/chain-selectors/index.d.ts +2 -2
- package/dist/sdk/utils/chain-selectors/index.js +2 -2
- package/dist/sdk/utils/chain-selectors/network-lookup.d.ts +31 -0
- package/dist/sdk/utils/chain-selectors/network-lookup.js +82 -0
- package/dist/sdk/utils/config/index.js +2 -1
- package/dist/sdk/utils/hex-utils.d.ts +17 -0
- package/dist/sdk/utils/hex-utils.js +29 -0
- package/dist/sdk/utils/index.d.ts +1 -0
- package/dist/sdk/utils/index.js +1 -0
- package/dist/sdk/wasm/index.d.ts +1 -0
- package/dist/sdk/wasm/index.js +1 -0
- package/dist/sdk/wasm/send-error-response.d.ts +16 -0
- package/dist/sdk/wasm/send-error-response.js +43 -0
- package/dist/workflows/standard_tests/host_wasm_write_errors_are_respected/test.ts +32 -0
- package/package.json +8 -3
- package/scripts/src/compile-to-js.ts +38 -19
- package/scripts/src/generate-sdks.ts +3 -0
- package/scripts/src/workflow-wrapper.test.ts +321 -0
- package/scripts/src/workflow-wrapper.ts +163 -0
- package/dist/sdk/utils/chain-selectors/getNetwork.js +0 -77
- /package/dist/sdk/utils/chain-selectors/{getAllNetworks.d.ts → get-all-networks.d.ts} +0 -0
- /package/dist/sdk/utils/chain-selectors/{getAllNetworks.js → get-all-networks.js} +0 -0
|
@@ -3,44 +3,113 @@ import { type AwaitCapabilitiesRequest, type AwaitCapabilitiesResponse, type Awa
|
|
|
3
3
|
import type { BaseRuntime, CallCapabilityParams, NodeRuntime, ReportRequest, ReportRequestJson, Runtime } from '..';
|
|
4
4
|
import type { Report } from '../report';
|
|
5
5
|
import { type ConsensusAggregation, type PrimitiveTypes, type UnwrapOptions } from '../utils';
|
|
6
|
+
/**
|
|
7
|
+
* Base implementation shared by DON and Node runtimes.
|
|
8
|
+
*
|
|
9
|
+
* Call ID Management:
|
|
10
|
+
* - DON mode: IDs increment (1, 2, 3...)
|
|
11
|
+
* - Node mode: IDs decrement (-1, -2, -3...)
|
|
12
|
+
* This prevents collisions when both modes are active.
|
|
13
|
+
*/
|
|
6
14
|
export declare class BaseRuntimeImpl<C> implements BaseRuntime<C> {
|
|
7
15
|
config: C;
|
|
8
16
|
nextCallId: number;
|
|
9
17
|
protected helpers: RuntimeHelpers;
|
|
10
18
|
protected maxResponseSize: bigint;
|
|
11
19
|
private mode;
|
|
20
|
+
/**
|
|
21
|
+
* When set, prevents operations that aren't allowed in current mode.
|
|
22
|
+
* - Set in DON mode when code tries to use NodeRuntime
|
|
23
|
+
* - Set in Node mode when code tries to use Runtime
|
|
24
|
+
*/
|
|
12
25
|
modeError?: Error;
|
|
13
26
|
constructor(config: C, nextCallId: number, helpers: RuntimeHelpers, maxResponseSize: bigint, mode: Mode);
|
|
27
|
+
/**
|
|
28
|
+
* Calls a capability and returns a lazy result.
|
|
29
|
+
* The actual call happens immediately, but result retrieval is deferred.
|
|
30
|
+
*/
|
|
14
31
|
callCapability<I extends Message, O extends Message>({ capabilityId, method, payload, inputSchema, outputSchema, }: CallCapabilityParams<I, O>): {
|
|
15
32
|
result: () => O;
|
|
16
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Allocates a unique callback ID for a capability request.
|
|
36
|
+
* DON mode increments, Node mode decrements (prevents collisions).
|
|
37
|
+
*/
|
|
38
|
+
private allocateCallbackId;
|
|
39
|
+
/**
|
|
40
|
+
* Awaits capability response and unwraps the result or throws error.
|
|
41
|
+
*/
|
|
42
|
+
private awaitAndUnwrapCapabilityResponse;
|
|
17
43
|
getNextCallId(): number;
|
|
18
44
|
now(): Date;
|
|
19
45
|
log(message: string): void;
|
|
20
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* It is used when a BFT guarantee cannot be provided automatically (e.g. calling a standard API).
|
|
49
|
+
* You tell each node to perform a task on its own.
|
|
50
|
+
* Each node returns its own individual answer, and you are responsible for telling the SDK how to combine them into a single, trusted result by providing an aggregation algorithm.
|
|
51
|
+
*
|
|
52
|
+
* Useful in situation where you already expect non-determinism (e.g., inherently variable HTTP responses).
|
|
53
|
+
* Switching from Node Mode back to DON mode requires workflow authors to handle consensus themselves.
|
|
54
|
+
*/
|
|
21
55
|
export declare class NodeRuntimeImpl<C> extends BaseRuntimeImpl<C> implements NodeRuntime<C> {
|
|
22
56
|
_isNodeRuntime: true;
|
|
23
57
|
constructor(config: C, nextCallId: number, helpers: RuntimeHelpers, maxResponseSize: bigint);
|
|
24
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* It is used for operations that are guaranteed to be Byzantine Fault Tolerant (BFT).
|
|
61
|
+
* You ask the network to execute something, and CRE handles the underlying complexity to ensure you get back one final, secure, and trustworthy result.
|
|
62
|
+
*/
|
|
25
63
|
export declare class RuntimeImpl<C> extends BaseRuntimeImpl<C> implements Runtime<C> {
|
|
26
64
|
private nextNodeCallId;
|
|
27
65
|
constructor(config: C, nextCallId: number, helpers: RuntimeHelpers, maxResponseSize: bigint);
|
|
28
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Executes a function in Node mode on each node, then aggregates via consensus.
|
|
68
|
+
*
|
|
69
|
+
* Flow:
|
|
70
|
+
* 1. Switches to Node mode, preventing DON operations
|
|
71
|
+
* 2. Executes fn() on each node independently
|
|
72
|
+
* 3. Captures result or error as "observation"
|
|
73
|
+
* 4. Switches back to DON mode
|
|
74
|
+
* 5. Runs consensus to aggregate observations
|
|
75
|
+
* 6. Returns aggregated result
|
|
76
|
+
*/
|
|
77
|
+
runInNodeMode<TArgs extends unknown[], TOutput>(fn: (nodeRuntime: NodeRuntime<C>, ...args: TArgs) => TOutput, consensusAggregation: ConsensusAggregation<TOutput, true>, unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions<TOutput>): (...args: TArgs) => {
|
|
29
78
|
result: () => TOutput;
|
|
30
79
|
};
|
|
80
|
+
private prepareConsensusInput;
|
|
81
|
+
private captureObservation;
|
|
82
|
+
private captureError;
|
|
83
|
+
private restoreDonMode;
|
|
84
|
+
private runConsensusAndWrap;
|
|
31
85
|
getSecret(request: SecretRequest | SecretRequestJson): {
|
|
32
86
|
result: () => Secret;
|
|
33
87
|
};
|
|
88
|
+
private awaitAndUnwrapSecret;
|
|
89
|
+
/**
|
|
90
|
+
* Generates a report via consensus mechanism.
|
|
91
|
+
*/
|
|
34
92
|
report(input: ReportRequest | ReportRequestJson): {
|
|
35
93
|
result: () => Report;
|
|
36
94
|
};
|
|
37
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Interface to the WASM host environment.
|
|
98
|
+
* Provides low-level access to capabilities, secrets, and utilities.
|
|
99
|
+
*/
|
|
38
100
|
export interface RuntimeHelpers {
|
|
101
|
+
/** Initiates a capability call. Returns false if capability not found. */
|
|
39
102
|
call(request: CapabilityRequest): boolean;
|
|
103
|
+
/** Awaits capability responses. Blocks until responses are ready. */
|
|
40
104
|
await(request: AwaitCapabilitiesRequest, maxResponseSize: bigint): AwaitCapabilitiesResponse;
|
|
105
|
+
/** Requests secrets from host. Returns false if host rejects request. */
|
|
41
106
|
getSecrets(request: GetSecretsRequest, maxResponseSize: bigint): boolean;
|
|
107
|
+
/** Awaits secret responses. Blocks until secrets are ready. */
|
|
42
108
|
awaitSecrets(request: AwaitSecretsRequest, maxResponseSize: bigint): AwaitSecretsResponse;
|
|
109
|
+
/** Switches execution mode (DON vs Node). Affects available operations. */
|
|
43
110
|
switchModes(mode: Mode): void;
|
|
111
|
+
/** Returns current time in nanoseconds. */
|
|
44
112
|
now(): number;
|
|
113
|
+
/** Logs a message to the host environment. */
|
|
45
114
|
log(message: string): void;
|
|
46
115
|
}
|
|
@@ -5,13 +5,25 @@ import { ConsensusCapability } from '../../generated-sdk/capabilities/internal/c
|
|
|
5
5
|
import { Value, } from '../utils';
|
|
6
6
|
import { CapabilityError } from '../utils/capabilities/capability-error';
|
|
7
7
|
import { DonModeError, NodeModeError, SecretsError } from '../errors';
|
|
8
|
+
/**
|
|
9
|
+
* Base implementation shared by DON and Node runtimes.
|
|
10
|
+
*
|
|
11
|
+
* Call ID Management:
|
|
12
|
+
* - DON mode: IDs increment (1, 2, 3...)
|
|
13
|
+
* - Node mode: IDs decrement (-1, -2, -3...)
|
|
14
|
+
* This prevents collisions when both modes are active.
|
|
15
|
+
*/
|
|
8
16
|
export class BaseRuntimeImpl {
|
|
9
17
|
config;
|
|
10
18
|
nextCallId;
|
|
11
19
|
helpers;
|
|
12
20
|
maxResponseSize;
|
|
13
21
|
mode;
|
|
14
|
-
|
|
22
|
+
/**
|
|
23
|
+
* When set, prevents operations that aren't allowed in current mode.
|
|
24
|
+
* - Set in DON mode when code tries to use NodeRuntime
|
|
25
|
+
* - Set in Node mode when code tries to use Runtime
|
|
26
|
+
*/
|
|
15
27
|
modeError;
|
|
16
28
|
constructor(config, nextCallId, helpers, maxResponseSize, mode) {
|
|
17
29
|
this.config = config;
|
|
@@ -20,7 +32,12 @@ export class BaseRuntimeImpl {
|
|
|
20
32
|
this.maxResponseSize = maxResponseSize;
|
|
21
33
|
this.mode = mode;
|
|
22
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Calls a capability and returns a lazy result.
|
|
37
|
+
* The actual call happens immediately, but result retrieval is deferred.
|
|
38
|
+
*/
|
|
23
39
|
callCapability({ capabilityId, method, payload, inputSchema, outputSchema, }) {
|
|
40
|
+
// Enforce mode restrictions
|
|
24
41
|
if (this.modeError) {
|
|
25
42
|
return {
|
|
26
43
|
result: () => {
|
|
@@ -28,22 +45,10 @@ export class BaseRuntimeImpl {
|
|
|
28
45
|
},
|
|
29
46
|
};
|
|
30
47
|
}
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
// eg. - first call don mode: nextCallId = 1
|
|
35
|
-
// - second call: nextCallId = 2
|
|
36
|
-
// - first call node mode: nextCallId = -1
|
|
37
|
-
// - second call node mode: nextCallId = -2
|
|
38
|
-
// - etc...
|
|
48
|
+
// Allocate unique callback ID for this request
|
|
49
|
+
const callbackId = this.allocateCallbackId();
|
|
50
|
+
// Send request to WASM host
|
|
39
51
|
const anyPayload = anyPack(inputSchema, payload);
|
|
40
|
-
const callbackId = this.nextCallId;
|
|
41
|
-
if (this.mode === Mode.DON) {
|
|
42
|
-
this.nextCallId++;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
this.nextCallId--;
|
|
46
|
-
}
|
|
47
52
|
const req = create(CapabilityRequestSchema, {
|
|
48
53
|
id: capabilityId,
|
|
49
54
|
method,
|
|
@@ -61,49 +66,68 @@ export class BaseRuntimeImpl {
|
|
|
61
66
|
},
|
|
62
67
|
};
|
|
63
68
|
}
|
|
69
|
+
// Return lazy result - await and unwrap when .result() is called
|
|
64
70
|
return {
|
|
65
|
-
result: () =>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
result: () => this.awaitAndUnwrapCapabilityResponse(callbackId, capabilityId, method, outputSchema),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Allocates a unique callback ID for a capability request.
|
|
76
|
+
* DON mode increments, Node mode decrements (prevents collisions).
|
|
77
|
+
*/
|
|
78
|
+
allocateCallbackId() {
|
|
79
|
+
const callbackId = this.nextCallId;
|
|
80
|
+
if (this.mode === Mode.DON) {
|
|
81
|
+
this.nextCallId++;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.nextCallId--;
|
|
85
|
+
}
|
|
86
|
+
return callbackId;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Awaits capability response and unwraps the result or throws error.
|
|
90
|
+
*/
|
|
91
|
+
awaitAndUnwrapCapabilityResponse(callbackId, capabilityId, method, outputSchema) {
|
|
92
|
+
const awaitRequest = create(AwaitCapabilitiesRequestSchema, {
|
|
93
|
+
ids: [callbackId],
|
|
94
|
+
});
|
|
95
|
+
const awaitResponse = this.helpers.await(awaitRequest, this.maxResponseSize);
|
|
96
|
+
const capabilityResponse = awaitResponse.responses[callbackId];
|
|
97
|
+
if (!capabilityResponse) {
|
|
98
|
+
throw new CapabilityError(`No response found for callback ID ${callbackId}`, {
|
|
99
|
+
capabilityId,
|
|
100
|
+
method,
|
|
101
|
+
callbackId,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const response = capabilityResponse.response;
|
|
105
|
+
switch (response.case) {
|
|
106
|
+
case 'payload': {
|
|
107
|
+
try {
|
|
108
|
+
return anyUnpack(response.value, outputSchema);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new CapabilityError(`Error cannot unwrap payload`, {
|
|
73
112
|
capabilityId,
|
|
74
113
|
method,
|
|
75
114
|
callbackId,
|
|
76
115
|
});
|
|
77
116
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
case 'error':
|
|
93
|
-
throw new CapabilityError(`Error ${response.value}`, {
|
|
94
|
-
capabilityId,
|
|
95
|
-
method,
|
|
96
|
-
callbackId,
|
|
97
|
-
});
|
|
98
|
-
default:
|
|
99
|
-
throw new CapabilityError(`Error cannot unwrap ${response.case}`, {
|
|
100
|
-
capabilityId,
|
|
101
|
-
method,
|
|
102
|
-
callbackId,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
};
|
|
117
|
+
}
|
|
118
|
+
case 'error':
|
|
119
|
+
throw new CapabilityError(`Error ${response.value}`, {
|
|
120
|
+
capabilityId,
|
|
121
|
+
method,
|
|
122
|
+
callbackId,
|
|
123
|
+
});
|
|
124
|
+
default:
|
|
125
|
+
throw new CapabilityError(`Error cannot unwrap ${response.case}`, {
|
|
126
|
+
capabilityId,
|
|
127
|
+
method,
|
|
128
|
+
callbackId,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
107
131
|
}
|
|
108
132
|
getNextCallId() {
|
|
109
133
|
return this.nextCallId;
|
|
@@ -116,6 +140,14 @@ export class BaseRuntimeImpl {
|
|
|
116
140
|
this.helpers.log(message);
|
|
117
141
|
}
|
|
118
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* It is used when a BFT guarantee cannot be provided automatically (e.g. calling a standard API).
|
|
145
|
+
* You tell each node to perform a task on its own.
|
|
146
|
+
* Each node returns its own individual answer, and you are responsible for telling the SDK how to combine them into a single, trusted result by providing an aggregation algorithm.
|
|
147
|
+
*
|
|
148
|
+
* Useful in situation where you already expect non-determinism (e.g., inherently variable HTTP responses).
|
|
149
|
+
* Switching from Node Mode back to DON mode requires workflow authors to handle consensus themselves.
|
|
150
|
+
*/
|
|
119
151
|
export class NodeRuntimeImpl extends BaseRuntimeImpl {
|
|
120
152
|
_isNodeRuntime = true;
|
|
121
153
|
constructor(config, nextCallId, helpers, maxResponseSize) {
|
|
@@ -123,58 +155,98 @@ export class NodeRuntimeImpl extends BaseRuntimeImpl {
|
|
|
123
155
|
super(config, nextCallId, helpers, maxResponseSize, Mode.NODE);
|
|
124
156
|
}
|
|
125
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* It is used for operations that are guaranteed to be Byzantine Fault Tolerant (BFT).
|
|
160
|
+
* You ask the network to execute something, and CRE handles the underlying complexity to ensure you get back one final, secure, and trustworthy result.
|
|
161
|
+
*/
|
|
126
162
|
export class RuntimeImpl extends BaseRuntimeImpl {
|
|
127
163
|
nextNodeCallId = -1;
|
|
128
164
|
constructor(config, nextCallId, helpers, maxResponseSize) {
|
|
129
165
|
helpers.switchModes(Mode.DON);
|
|
130
166
|
super(config, nextCallId, helpers, maxResponseSize, Mode.DON);
|
|
131
167
|
}
|
|
132
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Executes a function in Node mode on each node, then aggregates via consensus.
|
|
170
|
+
*
|
|
171
|
+
* Flow:
|
|
172
|
+
* 1. Switches to Node mode, preventing DON operations
|
|
173
|
+
* 2. Executes fn() on each node independently
|
|
174
|
+
* 3. Captures result or error as "observation"
|
|
175
|
+
* 4. Switches back to DON mode
|
|
176
|
+
* 5. Runs consensus to aggregate observations
|
|
177
|
+
* 6. Returns aggregated result
|
|
178
|
+
*/
|
|
179
|
+
runInNodeMode(fn, consensusAggregation, unwrapOptions) {
|
|
133
180
|
return (...args) => {
|
|
181
|
+
// Step 1: Create node runtime and prevent DON operations
|
|
134
182
|
this.modeError = new DonModeError();
|
|
135
183
|
const nodeRuntime = new NodeRuntimeImpl(this.config, this.nextNodeCallId, this.helpers, this.maxResponseSize);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (consensusAggretation.defaultValue) {
|
|
140
|
-
// This cast is safe, since ConsensusAggregation can only have true its second argument if T extends CreSerializable<TOutput>
|
|
141
|
-
consensusInput.default = Value.from(consensusAggretation.defaultValue).proto();
|
|
142
|
-
}
|
|
184
|
+
// Step 2: Prepare consensus input with config
|
|
185
|
+
const consensusInput = this.prepareConsensusInput(consensusAggregation);
|
|
186
|
+
// Step 3: Execute node function and capture result/error
|
|
143
187
|
try {
|
|
144
188
|
const observation = fn(nodeRuntime, ...args);
|
|
145
|
-
|
|
146
|
-
consensusInput.observation = {
|
|
147
|
-
case: 'value',
|
|
148
|
-
value: Value.from(observation).proto(),
|
|
149
|
-
};
|
|
189
|
+
this.captureObservation(consensusInput, observation, consensusAggregation.descriptor);
|
|
150
190
|
}
|
|
151
191
|
catch (e) {
|
|
152
|
-
consensusInput
|
|
153
|
-
case: 'error',
|
|
154
|
-
value: (e instanceof Error && e.message) || String(e),
|
|
155
|
-
};
|
|
192
|
+
this.captureError(consensusInput, e);
|
|
156
193
|
}
|
|
157
194
|
finally {
|
|
158
|
-
// Always restore DON mode
|
|
159
|
-
this.
|
|
160
|
-
this.nextNodeCallId = nodeRuntime.nextCallId;
|
|
161
|
-
nodeRuntime.modeError = new NodeModeError();
|
|
162
|
-
this.helpers.switchModes(Mode.DON);
|
|
195
|
+
// Step 4: Always restore DON mode
|
|
196
|
+
this.restoreDonMode(nodeRuntime);
|
|
163
197
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
198
|
+
// Step 5: Run consensus and return lazy result
|
|
199
|
+
return this.runConsensusAndWrap(consensusInput, unwrapOptions);
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
prepareConsensusInput(consensusAggregation) {
|
|
203
|
+
const consensusInput = create(SimpleConsensusInputsSchema, {
|
|
204
|
+
descriptors: consensusAggregation.descriptor,
|
|
205
|
+
});
|
|
206
|
+
if (consensusAggregation.defaultValue) {
|
|
207
|
+
// Safe cast: ConsensusAggregation<T, true> implies T extends CreSerializable
|
|
208
|
+
const defaultValue = Value.from(consensusAggregation.defaultValue).proto();
|
|
209
|
+
clearIgnoredFields(defaultValue, consensusAggregation.descriptor);
|
|
210
|
+
consensusInput.default = defaultValue;
|
|
211
|
+
}
|
|
212
|
+
return consensusInput;
|
|
213
|
+
}
|
|
214
|
+
captureObservation(consensusInput, observation, descriptor) {
|
|
215
|
+
// Safe cast: ConsensusAggregation<T, true> implies T extends CreSerializable
|
|
216
|
+
const observationValue = Value.from(observation).proto();
|
|
217
|
+
clearIgnoredFields(observationValue, descriptor);
|
|
218
|
+
consensusInput.observation = {
|
|
219
|
+
case: 'value',
|
|
220
|
+
value: observationValue,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
captureError(consensusInput, e) {
|
|
224
|
+
consensusInput.observation = {
|
|
225
|
+
case: 'error',
|
|
226
|
+
value: (e instanceof Error && e.message) || String(e),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
restoreDonMode(nodeRuntime) {
|
|
230
|
+
this.modeError = undefined;
|
|
231
|
+
this.nextNodeCallId = nodeRuntime.nextCallId;
|
|
232
|
+
nodeRuntime.modeError = new NodeModeError();
|
|
233
|
+
this.helpers.switchModes(Mode.DON);
|
|
234
|
+
}
|
|
235
|
+
runConsensusAndWrap(consensusInput, unwrapOptions) {
|
|
236
|
+
const consensus = new ConsensusCapability();
|
|
237
|
+
const call = consensus.simple(this, consensusInput);
|
|
238
|
+
return {
|
|
239
|
+
result: () => {
|
|
240
|
+
const result = call.result();
|
|
241
|
+
const wrappedValue = Value.wrap(result);
|
|
242
|
+
return unwrapOptions
|
|
243
|
+
? wrappedValue.unwrapToType(unwrapOptions)
|
|
244
|
+
: wrappedValue.unwrap();
|
|
245
|
+
},
|
|
175
246
|
};
|
|
176
247
|
}
|
|
177
248
|
getSecret(request) {
|
|
249
|
+
// Enforce mode restrictions
|
|
178
250
|
if (this.modeError) {
|
|
179
251
|
return {
|
|
180
252
|
result: () => {
|
|
@@ -182,9 +254,11 @@ export class RuntimeImpl extends BaseRuntimeImpl {
|
|
|
182
254
|
},
|
|
183
255
|
};
|
|
184
256
|
}
|
|
257
|
+
// Normalize request (accept both protobuf and JSON formats)
|
|
185
258
|
const secretRequest = request.$typeName
|
|
186
259
|
? request
|
|
187
260
|
: create(SecretRequestSchema, request);
|
|
261
|
+
// Allocate callback ID and send request
|
|
188
262
|
const id = this.nextCallId;
|
|
189
263
|
this.nextCallId++;
|
|
190
264
|
const secretsReq = create(GetSecretsRequestSchema, {
|
|
@@ -198,37 +272,68 @@ export class RuntimeImpl extends BaseRuntimeImpl {
|
|
|
198
272
|
},
|
|
199
273
|
};
|
|
200
274
|
}
|
|
275
|
+
// Return lazy result
|
|
201
276
|
return {
|
|
202
|
-
result: () =>
|
|
203
|
-
const awaitRequest = create(AwaitSecretsRequestSchema, { ids: [id] });
|
|
204
|
-
const awaitResponse = this.helpers.awaitSecrets(awaitRequest, this.maxResponseSize);
|
|
205
|
-
const secretsResponse = awaitResponse.responses[id];
|
|
206
|
-
if (!secretsResponse) {
|
|
207
|
-
throw new SecretsError(secretRequest, 'no response');
|
|
208
|
-
}
|
|
209
|
-
const responses = secretsResponse.responses;
|
|
210
|
-
if (responses.length !== 1) {
|
|
211
|
-
throw new SecretsError(secretRequest, 'invalid value returned from host');
|
|
212
|
-
}
|
|
213
|
-
const response = responses[0].response;
|
|
214
|
-
switch (response.case) {
|
|
215
|
-
case 'secret':
|
|
216
|
-
return response.value;
|
|
217
|
-
case 'error':
|
|
218
|
-
throw new SecretsError(secretRequest, response.value.error);
|
|
219
|
-
default:
|
|
220
|
-
throw new SecretsError(secretRequest, 'cannot unmarshal returned value from host');
|
|
221
|
-
}
|
|
222
|
-
},
|
|
277
|
+
result: () => this.awaitAndUnwrapSecret(id, secretRequest),
|
|
223
278
|
};
|
|
224
279
|
}
|
|
280
|
+
awaitAndUnwrapSecret(id, secretRequest) {
|
|
281
|
+
const awaitRequest = create(AwaitSecretsRequestSchema, { ids: [id] });
|
|
282
|
+
const awaitResponse = this.helpers.awaitSecrets(awaitRequest, this.maxResponseSize);
|
|
283
|
+
const secretsResponse = awaitResponse.responses[id];
|
|
284
|
+
if (!secretsResponse) {
|
|
285
|
+
throw new SecretsError(secretRequest, 'no response');
|
|
286
|
+
}
|
|
287
|
+
const responses = secretsResponse.responses;
|
|
288
|
+
if (responses.length !== 1) {
|
|
289
|
+
throw new SecretsError(secretRequest, 'invalid value returned from host');
|
|
290
|
+
}
|
|
291
|
+
const response = responses[0].response;
|
|
292
|
+
switch (response.case) {
|
|
293
|
+
case 'secret':
|
|
294
|
+
return response.value;
|
|
295
|
+
case 'error':
|
|
296
|
+
throw new SecretsError(secretRequest, response.value.error);
|
|
297
|
+
default:
|
|
298
|
+
throw new SecretsError(secretRequest, 'cannot unmarshal returned value from host');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Generates a report via consensus mechanism.
|
|
303
|
+
*/
|
|
225
304
|
report(input) {
|
|
226
305
|
const consensus = new ConsensusCapability();
|
|
227
306
|
const call = consensus.report(this, input);
|
|
228
307
|
return {
|
|
229
|
-
result: () =>
|
|
230
|
-
return call.result();
|
|
231
|
-
},
|
|
308
|
+
result: () => call.result(),
|
|
232
309
|
};
|
|
233
310
|
}
|
|
234
311
|
}
|
|
312
|
+
function clearIgnoredFields(value, descriptor) {
|
|
313
|
+
if (!descriptor || !value) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const fieldsMap = descriptor.descriptor?.case === 'fieldsMap' ? descriptor.descriptor.value : undefined;
|
|
317
|
+
if (!fieldsMap) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (value.value?.case === 'mapValue') {
|
|
321
|
+
const mapValue = value.value.value;
|
|
322
|
+
if (!mapValue || !mapValue.fields) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
for (const [key, val] of Object.entries(mapValue.fields)) {
|
|
326
|
+
const nestedDescriptor = fieldsMap.fields[key];
|
|
327
|
+
if (!nestedDescriptor) {
|
|
328
|
+
delete mapValue.fields[key];
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const nestedFieldsMap = nestedDescriptor.descriptor?.case === 'fieldsMap'
|
|
332
|
+
? nestedDescriptor.descriptor.value
|
|
333
|
+
: undefined;
|
|
334
|
+
if (nestedFieldsMap && val.value?.case === 'mapValue') {
|
|
335
|
+
clearIgnoredFields(val, nestedDescriptor);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
package/dist/sdk/runtime.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Message } from '@bufbuild/protobuf';
|
|
2
2
|
import type { GenMessage } from '@bufbuild/protobuf/codegenv2';
|
|
3
3
|
import type { ReportRequest, ReportRequestJson } from '../generated/sdk/v1alpha/sdk_pb';
|
|
4
|
+
import type { Report } from './report';
|
|
4
5
|
import type { ConsensusAggregation, PrimitiveTypes, UnwrapOptions } from './utils';
|
|
5
6
|
import type { SecretsProvider } from '.';
|
|
6
7
|
export type { ReportRequest, ReportRequestJson };
|
|
@@ -11,23 +12,40 @@ export type CallCapabilityParams<I extends Message, O extends Message> = {
|
|
|
11
12
|
inputSchema: GenMessage<I>;
|
|
12
13
|
outputSchema: GenMessage<O>;
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Base runtime available in both DON and Node execution modes.
|
|
17
|
+
* Provides core functionality for calling capabilities and logging.
|
|
18
|
+
*/
|
|
19
|
+
export interface BaseRuntime<C> {
|
|
16
20
|
config: C;
|
|
17
21
|
callCapability<I extends Message, O extends Message>(params: CallCapabilityParams<I, O>): {
|
|
18
22
|
result: () => O;
|
|
19
23
|
};
|
|
20
24
|
now(): Date;
|
|
21
25
|
log(message: string): void;
|
|
22
|
-
}
|
|
23
|
-
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Runtime for Node mode execution.
|
|
29
|
+
*/
|
|
30
|
+
export interface NodeRuntime<C> extends BaseRuntime<C> {
|
|
31
|
+
readonly _isNodeRuntime: true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Runtime for DON mode execution.
|
|
35
|
+
*/
|
|
36
|
+
export interface Runtime<C> extends BaseRuntime<C>, SecretsProvider {
|
|
37
|
+
/**
|
|
38
|
+
* Executes a function in Node mode and aggregates results via consensus.
|
|
39
|
+
*
|
|
40
|
+
* @param fn - Function to execute in each node (receives NodeRuntime)
|
|
41
|
+
* @param consensusAggregation - How to aggregate results across nodes
|
|
42
|
+
* @param unwrapOptions - Optional unwrapping config for complex return types
|
|
43
|
+
* @returns Wrapped function that returns aggregated result
|
|
44
|
+
*/
|
|
24
45
|
runInNodeMode<TArgs extends unknown[], TOutput>(fn: (nodeRuntime: NodeRuntime<C>, ...args: TArgs) => TOutput, consensusAggregation: ConsensusAggregation<TOutput, true>, unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions<TOutput>): (...args: TArgs) => {
|
|
25
46
|
result: () => TOutput;
|
|
26
47
|
};
|
|
27
48
|
report(input: ReportRequest | ReportRequestJson): {
|
|
28
49
|
result: () => Report;
|
|
29
50
|
};
|
|
30
|
-
}
|
|
31
|
-
export type NodeRuntime<C> = BaseRuntime<C> & {
|
|
32
|
-
readonly _isNodeRuntime: true;
|
|
33
|
-
};
|
|
51
|
+
}
|
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
import type { CallMsgJson } from '../../../../generated/capabilities/blockchain/evm/v1alpha/client_pb';
|
|
2
2
|
import type { ReportRequestJson } from '../../../../generated/sdk/v1alpha/sdk_pb';
|
|
3
|
+
import { type BigInt as GeneratedBigInt } from '../../../../generated/values/v1/values_pb';
|
|
3
4
|
import type { Address, Hex } from 'viem';
|
|
5
|
+
/**
|
|
6
|
+
* Protobuf BigInt structure returned by SDK methods (e.g., headerByNumber).
|
|
7
|
+
* Uses Pick to extract just the data fields from the generated type.
|
|
8
|
+
*/
|
|
9
|
+
export type ProtoBigInt = Pick<GeneratedBigInt, 'absVal' | 'sign'>;
|
|
10
|
+
/**
|
|
11
|
+
* Converts a native JS bigint to a protobuf BigInt JSON representation.
|
|
12
|
+
* Use this when passing bigint values to SDK methods.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const response = evmClient.callContract(runtime, {
|
|
16
|
+
* call: encodeCallMsg({...}),
|
|
17
|
+
* blockNumber: bigintToProtoBigInt(9768438n)
|
|
18
|
+
* }).result()
|
|
19
|
+
*
|
|
20
|
+
* @param n - The native bigint, number, or string value.
|
|
21
|
+
* @returns The protobuf BigInt JSON representation.
|
|
22
|
+
*/
|
|
23
|
+
export declare const bigintToProtoBigInt: (n: number | bigint | string) => import("../../../../generated/values/v1/values_pb").BigIntJson;
|
|
24
|
+
/**
|
|
25
|
+
* Converts a protobuf BigInt to a native JS bigint.
|
|
26
|
+
* Use this when extracting bigint values from SDK responses.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const latestHeader = evmClient.headerByNumber(runtime, {}).result()
|
|
30
|
+
* const latestBlockNum = protoBigIntToBigint(latestHeader.header.blockNumber!)
|
|
31
|
+
* const customBlock = latestBlockNum - 500n
|
|
32
|
+
*
|
|
33
|
+
* @param pb - The protobuf BigInt object with absVal and sign fields.
|
|
34
|
+
* @returns The native JS bigint value.
|
|
35
|
+
*/
|
|
36
|
+
export declare const protoBigIntToBigint: (pb: ProtoBigInt) => bigint;
|
|
37
|
+
/**
|
|
38
|
+
* Convenience alias for `bigintToProtoBigInt`.
|
|
39
|
+
* Creates a block number object for EVM capability requests.
|
|
40
|
+
*
|
|
41
|
+
* @param n - The block number.
|
|
42
|
+
* @returns The protobuf BigInt JSON representation.
|
|
43
|
+
*/
|
|
44
|
+
export declare const blockNumber: (n: number | bigint | string) => import("../../../../generated/values/v1/values_pb").BigIntJson;
|
|
4
45
|
/**
|
|
5
46
|
* EVM Capability Helper.
|
|
6
47
|
*
|
|
@@ -8,6 +49,7 @@ import type { Address, Hex } from 'viem';
|
|
|
8
49
|
* That blockNumber can be:
|
|
9
50
|
* - the latest mined block (`LATEST_BLOCK_NUMBER`) (default)
|
|
10
51
|
* - the last finalized block (`LAST_FINALIZED_BLOCK_NUMBER`)
|
|
52
|
+
* - a specific block number (use `blockNumber(n)` or `bigintToProtoBigInt(n)`)
|
|
11
53
|
*
|
|
12
54
|
* Using this constant will indicate that the call should be executed at the last finalized block.
|
|
13
55
|
*/
|
|
@@ -65,3 +107,4 @@ export declare const EVM_DEFAULT_REPORT_ENCODER: {
|
|
|
65
107
|
* @returns The prepared report request.
|
|
66
108
|
*/
|
|
67
109
|
export declare const prepareReportRequest: (hexEncodedPayload: Hex, reportEncoder?: Exclude<ReportRequestJson, "encodedPayload">) => ReportRequestJson;
|
|
110
|
+
export declare const isChainSelectorSupported: (chainSelectorName: string) => boolean;
|