@midnight-ntwrk/wallet-sdk-capabilities 4.0.0-beta.2 → 4.0.0-beta.3

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.
Files changed (68) hide show
  1. package/README.md +27 -3
  2. package/dist/chainVersion/chainVersionProbe.d.ts +95 -0
  3. package/dist/chainVersion/chainVersionProbe.js +83 -0
  4. package/dist/chainVersion/index.d.ts +1 -0
  5. package/dist/chainVersion/index.js +13 -0
  6. package/dist/codecs/index.d.ts +1 -0
  7. package/dist/codecs/index.js +13 -0
  8. package/dist/codecs/ledgerParameters.d.ts +81 -0
  9. package/dist/codecs/ledgerParameters.js +63 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.js +3 -0
  12. package/dist/pendingTransactions/pendingTransactions.d.ts +113 -9
  13. package/dist/pendingTransactions/pendingTransactions.js +147 -30
  14. package/dist/pendingTransactions/pendingTransactionsService.d.ts +25 -9
  15. package/dist/pendingTransactions/pendingTransactionsService.js +33 -21
  16. package/dist/proving/index.d.ts +2 -0
  17. package/dist/proving/index.js +2 -0
  18. package/dist/proving/provingService.d.ts +175 -15
  19. package/dist/proving/provingService.js +104 -11
  20. package/dist/proving/v8ProvingService.d.ts +53 -0
  21. package/dist/proving/v8ProvingService.js +71 -0
  22. package/dist/proving/versionedProving.d.ts +42 -0
  23. package/dist/proving/versionedProving.js +103 -0
  24. package/dist/signatures/index.d.ts +2 -0
  25. package/dist/signatures/index.js +14 -0
  26. package/dist/signatures/signing.d.ts +37 -0
  27. package/dist/signatures/signing.js +13 -0
  28. package/dist/signatures/v8Signatures.d.ts +54 -0
  29. package/dist/signatures/v8Signatures.js +62 -0
  30. package/dist/simulation/ForkSimulator.d.ts +114 -0
  31. package/dist/simulation/ForkSimulator.js +209 -0
  32. package/dist/simulation/LedgerTranslation.d.ts +53 -0
  33. package/dist/simulation/LedgerTranslation.js +56 -0
  34. package/dist/simulation/core/VersionTimeline.d.ts +54 -0
  35. package/dist/simulation/core/VersionTimeline.js +56 -0
  36. package/dist/simulation/core/blocks.d.ts +38 -0
  37. package/dist/simulation/core/blocks.js +52 -0
  38. package/dist/simulation/core/index.d.ts +13 -0
  39. package/dist/simulation/core/index.js +25 -0
  40. package/dist/simulation/core/strictness.d.ts +29 -0
  41. package/dist/simulation/core/strictness.js +39 -0
  42. package/dist/simulation/index.d.ts +22 -2
  43. package/dist/simulation/index.js +25 -14
  44. package/dist/simulation/v8/Simulator.d.ts +231 -0
  45. package/dist/simulation/v8/Simulator.js +503 -0
  46. package/dist/simulation/{SimulatorState.d.ts → v8/SimulatorState.d.ts} +31 -44
  47. package/dist/simulation/v8/SimulatorState.js +290 -0
  48. package/dist/simulation/v8/index.d.ts +2 -0
  49. package/dist/simulation/v8/index.js +26 -0
  50. package/dist/simulation/{Simulator.d.ts → v9/Simulator.d.ts} +57 -6
  51. package/dist/simulation/{Simulator.js → v9/Simulator.js} +68 -18
  52. package/dist/simulation/v9/SimulatorState.d.ts +336 -0
  53. package/dist/simulation/{SimulatorState.js → v9/SimulatorState.js} +34 -67
  54. package/dist/simulation/v9/index.d.ts +2 -0
  55. package/dist/simulation/v9/index.js +26 -0
  56. package/dist/submission/submissionService.d.ts +2 -1
  57. package/dist/submission/submissionService.js +1 -1
  58. package/dist/validation/blockData.d.ts +42 -2
  59. package/dist/validation/blockData.js +55 -8
  60. package/dist/validation/index.d.ts +2 -0
  61. package/dist/validation/index.js +2 -0
  62. package/dist/validation/v8ValidationService.d.ts +29 -0
  63. package/dist/validation/v8ValidationService.js +48 -0
  64. package/dist/validation/validationService.d.ts +132 -17
  65. package/dist/validation/validationService.js +88 -42
  66. package/dist/validation/versionedValidation.d.ts +46 -0
  67. package/dist/validation/versionedValidation.js +55 -0
  68. package/package.json +23 -9
package/README.md CHANGED
@@ -94,9 +94,7 @@ const recipe = getBalanceRecipe({
94
94
  import { getBalanceRecipe, InsufficientFundsError } from '@midnight-ntwrk/wallet-sdk-capabilities';
95
95
 
96
96
  try {
97
- const recipe = getBalanceRecipe({
98
- /* ... */
99
- });
97
+ const recipe = getBalanceRecipe({/* ... */});
100
98
  } catch (error) {
101
99
  if (error instanceof InsufficientFundsError) {
102
100
  console.log(`Cannot balance: insufficient ${error.tokenType}`);
@@ -104,6 +102,32 @@ try {
104
102
  }
105
103
  ```
106
104
 
105
+ ### Proving, either side of a protocol boundary
106
+
107
+ A proving backend is written against one ledger version — it drives that version's `Transaction.prove` with that
108
+ version's cost model, and frames its proof-server requests with that version's payload helpers. Backends are therefore
109
+ named per ledger version, keyed the way `forks` is, and the range each serves is read off the same fork schedule the
110
+ wallets are built with:
111
+
112
+ ```typescript
113
+ import { makeDefaultVersionedProvingService } from '@midnight-ntwrk/wallet-sdk-capabilities/proving';
114
+
115
+ const service = makeDefaultVersionedProvingService(
116
+ { provers: { v8: { kind: 'server', url: v8ProofServer }, v9: { kind: 'wasm' } } },
117
+ forks,
118
+ ); // Either<VersionedProvingService, ProvingConfigurationError>
119
+ ```
120
+
121
+ - `provers` wins over `provingServerUrl`; naming neither is a `ProvingConfigurationError`. `v9` is required and `v8` is
122
+ optional: a version below `forks.v9` with no `v8` backend fails with `UnsupportedProvingVersionError`.
123
+ - `provingServerUrl` is one server under every key, driven by each ledger version on its own side of `forks.v9`. That is
124
+ what makes the single-URL form frame correctly on both sides; whether one server can actually prove both is an
125
+ operational fact about that server, not something the SDK enforces.
126
+ - Each registered backend refuses the other ledger version's transaction with `ProvingEpochMismatchError` rather than
127
+ handing it to a ledger that cannot read it.
128
+ - `makeV9ServerProvingServiceEffect` / `makeV9WasmProvingServiceEffect` build a single ledger-v9 backend;
129
+ `makeV8ServerProvingServiceEffect` / `makeV8WasmProvingServiceEffect` are their ledger-v8 twins.
130
+
107
131
  ## Exports
108
132
 
109
133
  ### Balancer
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Asking the chain which protocol version its timeline starts under.
3
+ *
4
+ * @remarks
5
+ * A wallet that registers a variant either side of a protocol boundary learns the chain's version from the events it
6
+ * observes, which is too late for one decision: which variant to start on. Until an event arrives its only guess is
7
+ * the bottom of the timeline, so it starts on V1 — and on a chain that is entirely past the boundary that costs a
8
+ * hand-over per start, or, on a chain that has produced no event this wallet can see, is simply wrong for as long as
9
+ * the wallet runs.
10
+ *
11
+ * This is the question that removes the guess, and it is asked about the **first** block rather than the latest. A
12
+ * fresh wallet reads a timeline from its start, so what decides where it begins is which ledger version can
13
+ * deserialize the first event it will fetch — a fact about the bottom of the timeline. On a chain that forked with
14
+ * history behind it the tip's version answers a different question and answers this one wrongly: it starts the wallet
15
+ * on a ledger version that cannot read a byte of the history it is about to be served. A chain whose genesis is
16
+ * already past the boundary has no such history, so it still starts a fresh wallet on V2 directly; a chain that
17
+ * forked over its own past routes it to V1, where its coins are readable, and the hand-over carries them across.
18
+ *
19
+ * The answer is read from the same block query validation already reads, so nothing new is asked of the indexer, and it
20
+ * is best-effort by design: a chain that will not answer leaves the wallet exactly where it was.
21
+ */
22
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
23
+ import { Effect, Option } from 'effect';
24
+ /**
25
+ * Asks the chain which protocol version its timeline starts under.
26
+ *
27
+ * @remarks
28
+ * The version of the chain's **first** block, not its latest one. A wallet uses the answer to pick the variant it will
29
+ * start reading history on, so what it needs to know is which ledger version wrote the beginning of that history —
30
+ * the tip's version is a different fact, and on a chain that forked over existing history it is the wrong one. An
31
+ * application supplying its own probe answers that question or the wallet starts on a ledger version that cannot read
32
+ * what it is served.
33
+ *
34
+ * Promise-shaped rather than `Effect`-shaped because it is a wallet-configuration field, and an application supplying
35
+ * its own — pointing at a cache, a node RPC, or a value it already holds — should not have to speak `Effect` to do
36
+ * it. Every caller treats it as best-effort: a rejection, including a timeout, means "the chain did not say", never
37
+ * "the wallet cannot start".
38
+ */
39
+ export type ChainVersionProbe = () => Promise<ProtocolVersion.ProtocolVersion>;
40
+ /** The indexer's answer about one block, cut down to the one field a version probe reads. */
41
+ export type BlockVersionAnswer = Readonly<{
42
+ block: Readonly<{
43
+ protocolVersion: number;
44
+ }> | null;
45
+ }>;
46
+ declare const ChainVersionUnavailableError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
47
+ readonly _tag: "@midnight-ntwrk/wallet-sdk-capabilities/chainVersion/chainVersionProbe/ChainVersionUnavailableError";
48
+ } & Readonly<A>;
49
+ /** Raised when the chain named no version, because it has produced no block for one to be reported under. */
50
+ export declare class ChainVersionUnavailableError extends ChainVersionUnavailableError_base<{
51
+ readonly message: string;
52
+ }> {
53
+ }
54
+ /**
55
+ * Reads the protocol version out of the indexer's answer about a block.
56
+ *
57
+ * @remarks
58
+ * Absent rather than zero when there is no block. A chain that has produced nothing has not said which side of any
59
+ * boundary it is on, and the bottom of the timeline is precisely the wrong guess — it is the one the probe exists to
60
+ * replace.
61
+ * @param answer The indexer's answer about the block that was asked for.
62
+ * @returns The version that block was reported under, or nothing when there is no block.
63
+ */
64
+ export declare const chainVersionOf: (answer: BlockVersionAnswer) => Option.Option<ProtocolVersion.ProtocolVersion>;
65
+ /**
66
+ * The protocol version the chain's timeline starts under, read through whichever query client is provided.
67
+ *
68
+ * @remarks
69
+ * The same `BlockHash` query the default block-data fetcher runs, so nothing new is asked of the indexer — pointed at
70
+ * height zero, the first block the chain ever produced, rather than at the absent offset that means "whatever the tip
71
+ * is". That block is the one whose ledger version has to be able to read the first event a fresh wallet fetches,
72
+ * which is the only thing this answer decides.
73
+ */
74
+ export declare const timelineStartChainVersion: Effect.Effect<Option.Option<ProtocolVersion.ProtocolVersion>, import("@midnight-ntwrk/wallet-sdk-utilities/networking").ClientError | import("@midnight-ntwrk/wallet-sdk-utilities/networking").ServerError, import("@midnight-ntwrk/wallet-sdk-indexer-client/effect").QueryClient>;
75
+ /** What building the default {@link ChainVersionProbe} needs: somewhere to ask. */
76
+ export type DefaultChainVersionProbeConfiguration = {
77
+ indexerClientConnection: {
78
+ indexerHttpUrl: string;
79
+ };
80
+ };
81
+ /**
82
+ * Builds a {@link ChainVersionProbe} that asks the indexer over HTTP for the version its first block was reported under.
83
+ *
84
+ * @remarks
85
+ * Each call opens a short-lived query client and closes it again, as the default block-data fetcher does. It carries no
86
+ * timeout of its own: the caller that blocks on it owns how long it is prepared to wait, and the wallet that starts
87
+ * with one applies its own bound.
88
+ *
89
+ * A chain that reports no block at all is not an answer: the failure below is what every caller reads as "the chain did
90
+ * not say", and leaves the wallet starting ledger-v8 exactly as a wallet with no probe does.
91
+ * @param config Where to ask.
92
+ * @returns The probe.
93
+ */
94
+ export declare const makeIndexerChainVersionProbe: (config: DefaultChainVersionProbeConfiguration) => ChainVersionProbe;
95
+ export {};
@@ -0,0 +1,83 @@
1
+ // This file is part of MIDNIGHT-WALLET-SDK.
2
+ // Copyright (C) Midnight Foundation
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // You may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ /**
14
+ * Asking the chain which protocol version its timeline starts under.
15
+ *
16
+ * @remarks
17
+ * A wallet that registers a variant either side of a protocol boundary learns the chain's version from the events it
18
+ * observes, which is too late for one decision: which variant to start on. Until an event arrives its only guess is
19
+ * the bottom of the timeline, so it starts on V1 — and on a chain that is entirely past the boundary that costs a
20
+ * hand-over per start, or, on a chain that has produced no event this wallet can see, is simply wrong for as long as
21
+ * the wallet runs.
22
+ *
23
+ * This is the question that removes the guess, and it is asked about the **first** block rather than the latest. A
24
+ * fresh wallet reads a timeline from its start, so what decides where it begins is which ledger version can
25
+ * deserialize the first event it will fetch — a fact about the bottom of the timeline. On a chain that forked with
26
+ * history behind it the tip's version answers a different question and answers this one wrongly: it starts the wallet
27
+ * on a ledger version that cannot read a byte of the history it is about to be served. A chain whose genesis is
28
+ * already past the boundary has no such history, so it still starts a fresh wallet on V2 directly; a chain that
29
+ * forked over its own past routes it to V1, where its coins are readable, and the hand-over carries them across.
30
+ *
31
+ * The answer is read from the same block query validation already reads, so nothing new is asked of the indexer, and it
32
+ * is best-effort by design: a chain that will not answer leaves the wallet exactly where it was.
33
+ */
34
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
35
+ import { BlockHash } from '@midnight-ntwrk/wallet-sdk-indexer-client';
36
+ import { HttpQueryClient } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
37
+ import { Data, Effect, Option, pipe } from 'effect';
38
+ /** Raised when the chain named no version, because it has produced no block for one to be reported under. */
39
+ export class ChainVersionUnavailableError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-capabilities/chainVersion/chainVersionProbe/ChainVersionUnavailableError') {
40
+ }
41
+ /**
42
+ * Reads the protocol version out of the indexer's answer about a block.
43
+ *
44
+ * @remarks
45
+ * Absent rather than zero when there is no block. A chain that has produced nothing has not said which side of any
46
+ * boundary it is on, and the bottom of the timeline is precisely the wrong guess — it is the one the probe exists to
47
+ * replace.
48
+ * @param answer The indexer's answer about the block that was asked for.
49
+ * @returns The version that block was reported under, or nothing when there is no block.
50
+ */
51
+ export const chainVersionOf = (answer) => pipe(Option.fromNullable(answer.block), Option.map((block) => ProtocolVersion.ProtocolVersion(BigInt(block.protocolVersion))));
52
+ /**
53
+ * The protocol version the chain's timeline starts under, read through whichever query client is provided.
54
+ *
55
+ * @remarks
56
+ * The same `BlockHash` query the default block-data fetcher runs, so nothing new is asked of the indexer — pointed at
57
+ * height zero, the first block the chain ever produced, rather than at the absent offset that means "whatever the tip
58
+ * is". That block is the one whose ledger version has to be able to read the first event a fresh wallet fetches,
59
+ * which is the only thing this answer decides.
60
+ */
61
+ export const timelineStartChainVersion = Effect.gen(function* () {
62
+ const query = yield* BlockHash;
63
+ return chainVersionOf(yield* query({ offset: { height: 0 } }));
64
+ });
65
+ /**
66
+ * Builds a {@link ChainVersionProbe} that asks the indexer over HTTP for the version its first block was reported under.
67
+ *
68
+ * @remarks
69
+ * Each call opens a short-lived query client and closes it again, as the default block-data fetcher does. It carries no
70
+ * timeout of its own: the caller that blocks on it owns how long it is prepared to wait, and the wallet that starts
71
+ * with one applies its own bound.
72
+ *
73
+ * A chain that reports no block at all is not an answer: the failure below is what every caller reads as "the chain did
74
+ * not say", and leaves the wallet starting ledger-v8 exactly as a wallet with no probe does.
75
+ * @param config Where to ask.
76
+ * @returns The probe.
77
+ */
78
+ export const makeIndexerChainVersionProbe = (config) => () => pipe(timelineStartChainVersion, Effect.provide(HttpQueryClient.layer({ url: config.indexerClientConnection.indexerHttpUrl })), Effect.scoped, Effect.flatMap(Option.match({
79
+ onNone: () => Effect.fail(new ChainVersionUnavailableError({
80
+ message: 'The indexer reports no block, so the chain has named no protocol version.',
81
+ })),
82
+ onSome: Effect.succeed,
83
+ })), Effect.runPromise);
@@ -0,0 +1 @@
1
+ export * from './chainVersionProbe.js';
@@ -0,0 +1,13 @@
1
+ // This file is part of MIDNIGHT-WALLET-SDK.
2
+ // Copyright (C) Midnight Foundation
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // You may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ export * from './chainVersionProbe.js';
@@ -0,0 +1 @@
1
+ export * as LedgerParametersCodec from './ledgerParameters.js';
@@ -0,0 +1,13 @@
1
+ // This file is part of MIDNIGHT-WALLET-SDK.
2
+ // Copyright (C) Midnight Foundation
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // You may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ export * as LedgerParametersCodec from './ledgerParameters.js';
@@ -0,0 +1,81 @@
1
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
2
+ import { Either } from 'effect';
3
+ /**
4
+ * Reads the indexer's hex-encoded ledger parameters as one ledger version understands them.
5
+ *
6
+ * @remarks
7
+ * A codec is deliberately allowed to throw: it wraps a WASM deserializer, and what it throws on is precisely the other
8
+ * ledger version's bytes. {@link decode} is the only way to call one, and it turns that into a typed failure.
9
+ * @typeParam TParameters The `LedgerParameters` type of the ledger version this codec speaks.
10
+ */
11
+ export type LedgerParametersCodec<TParameters> = Readonly<{
12
+ decode: (hex: string) => TParameters;
13
+ }>;
14
+ /**
15
+ * The codecs a caller is willing to decode with, keyed by the protocol version range each one serves.
16
+ *
17
+ * @remarks
18
+ * Registration is per caller, not global, and a caller registers only the ledger versions its own types are written
19
+ * against — which is why the registry stays homogeneous in `TParameters` while the SDK as a whole spans two ledgers.
20
+ * A version outside every registered range therefore means "this block belongs to a different variant", and
21
+ * {@link decode} says so instead of handing the bytes to a deserializer that would reject them.
22
+ * @typeParam TParameters The `LedgerParameters` type the registered codecs produce.
23
+ */
24
+ export type LedgerParametersCodecs<TParameters> = ProtocolVersion.Registry<LedgerParametersCodec<TParameters>>;
25
+ declare const UnsupportedProtocolVersionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
26
+ readonly _tag: "@midnight-ntwrk/wallet-sdk-capabilities/codecs/ledgerParameters/UnsupportedProtocolVersionError";
27
+ } & Readonly<A>;
28
+ /** Raised when no registered codec claims the protocol version a block was reported under. */
29
+ export declare class UnsupportedProtocolVersionError extends UnsupportedProtocolVersionError_base<{
30
+ readonly message: string;
31
+ /** The version the block was reported under, which no registered codec covers. */
32
+ readonly protocolVersion: ProtocolVersion.ProtocolVersion;
33
+ }> {
34
+ }
35
+ declare const LedgerParametersDecodeError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
36
+ readonly _tag: "@midnight-ntwrk/wallet-sdk-capabilities/codecs/ledgerParameters/LedgerParametersDecodeError";
37
+ } & Readonly<A>;
38
+ /** Raised when the codec chosen for a protocol version could not read the bytes it was given. */
39
+ export declare class LedgerParametersDecodeError extends LedgerParametersDecodeError_base<{
40
+ readonly message: string;
41
+ /** The version the block was reported under, and so the codec that was chosen. */
42
+ readonly protocolVersion: ProtocolVersion.ProtocolVersion;
43
+ readonly cause: unknown;
44
+ }> {
45
+ }
46
+ /** Every way a version-routed ledger parameters decode can fail. */
47
+ export type LedgerParametersCodecError = UnsupportedProtocolVersionError | LedgerParametersDecodeError;
48
+ /**
49
+ * Builds a codec from a ledger version's `LedgerParameters.deserialize`.
50
+ *
51
+ * @param deserialize The ledger version's deserializer.
52
+ * @returns A codec that hex-decodes the indexer's encoding before handing the bytes over.
53
+ */
54
+ export declare const fromDeserializer: <TParameters>(deserialize: (bytes: Uint8Array) => TParameters) => LedgerParametersCodec<TParameters>;
55
+ /**
56
+ * Decodes hex-encoded ledger parameters with the codec registered for the protocol version the block was reported
57
+ * under.
58
+ *
59
+ * @param codecs The codecs the caller is willing to decode with.
60
+ * @param protocolVersion The version the indexer reported the block under.
61
+ * @param hex The block's hex-encoded ledger parameters.
62
+ * @returns The decoded parameters, an {@link UnsupportedProtocolVersionError} when no codec claims that version, or a
63
+ * {@link LedgerParametersDecodeError} when the chosen codec could not read the bytes.
64
+ */
65
+ export declare const decode: <TParameters>(codecs: LedgerParametersCodecs<TParameters>, protocolVersion: ProtocolVersion.ProtocolVersion, hex: string) => Either.Either<TParameters, LedgerParametersCodecError>;
66
+ /**
67
+ * Builds a registry from codecs and the versions they activate at.
68
+ *
69
+ * @remarks
70
+ * A thin name over {@link ProtocolVersion.makeRegistryFromActivations} so that codec registration reads the same as
71
+ * variant registration and cannot drift from it. The activation list is a constant at every call site in the SDK, so
72
+ * a rejection here is a programming error rather than a runtime condition — callers that build one at module scope
73
+ * are entitled to let it throw.
74
+ * @param activations The codecs and the versions they start serving, in strictly ascending order.
75
+ * @returns The registry, or the {@link ProtocolVersion.RegistryError} naming the versions that broke the ordering.
76
+ */
77
+ export declare const makeCodecs: <TParameters>(activations: readonly Readonly<{
78
+ sinceVersion: ProtocolVersion.ProtocolVersion;
79
+ codec: LedgerParametersCodec<TParameters>;
80
+ }>[]) => Either.Either<LedgerParametersCodecs<TParameters>, ProtocolVersion.RegistryError>;
81
+ export {};
@@ -0,0 +1,63 @@
1
+ // This file is part of MIDNIGHT-WALLET-SDK.
2
+ // Copyright (C) Midnight Foundation
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // You may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
14
+ import { Buffer } from 'buffer';
15
+ import { Data, Either, pipe } from 'effect';
16
+ /** Raised when no registered codec claims the protocol version a block was reported under. */
17
+ export class UnsupportedProtocolVersionError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-capabilities/codecs/ledgerParameters/UnsupportedProtocolVersionError') {
18
+ }
19
+ /** Raised when the codec chosen for a protocol version could not read the bytes it was given. */
20
+ export class LedgerParametersDecodeError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-capabilities/codecs/ledgerParameters/LedgerParametersDecodeError') {
21
+ }
22
+ /**
23
+ * Builds a codec from a ledger version's `LedgerParameters.deserialize`.
24
+ *
25
+ * @param deserialize The ledger version's deserializer.
26
+ * @returns A codec that hex-decodes the indexer's encoding before handing the bytes over.
27
+ */
28
+ export const fromDeserializer = (deserialize) => ({
29
+ decode: (hex) => deserialize(Buffer.from(hex, 'hex')),
30
+ });
31
+ /**
32
+ * Decodes hex-encoded ledger parameters with the codec registered for the protocol version the block was reported
33
+ * under.
34
+ *
35
+ * @param codecs The codecs the caller is willing to decode with.
36
+ * @param protocolVersion The version the indexer reported the block under.
37
+ * @param hex The block's hex-encoded ledger parameters.
38
+ * @returns The decoded parameters, an {@link UnsupportedProtocolVersionError} when no codec claims that version, or a
39
+ * {@link LedgerParametersDecodeError} when the chosen codec could not read the bytes.
40
+ */
41
+ export const decode = (codecs, protocolVersion, hex) => pipe(ProtocolVersion.select(codecs, protocolVersion), Either.fromOption(() => new UnsupportedProtocolVersionError({
42
+ message: `No ledger parameters codec is registered for protocol version ${protocolVersion}.`,
43
+ protocolVersion,
44
+ })), Either.flatMap((codec) => Either.try({
45
+ try: () => codec.decode(hex),
46
+ catch: (cause) => new LedgerParametersDecodeError({
47
+ message: `Could not decode ledger parameters reported at protocol version ${protocolVersion}.`,
48
+ protocolVersion,
49
+ cause,
50
+ }),
51
+ })));
52
+ /**
53
+ * Builds a registry from codecs and the versions they activate at.
54
+ *
55
+ * @remarks
56
+ * A thin name over {@link ProtocolVersion.makeRegistryFromActivations} so that codec registration reads the same as
57
+ * variant registration and cannot drift from it. The activation list is a constant at every call site in the SDK, so
58
+ * a rejection here is a programming error rather than a runtime condition — callers that build one at module scope
59
+ * are entitled to let it throw.
60
+ * @param activations The codecs and the versions they start serving, in strictly ascending order.
61
+ * @returns The registry, or the {@link ProtocolVersion.RegistryError} naming the versions that broke the ordering.
62
+ */
63
+ export const makeCodecs = (activations) => ProtocolVersion.makeRegistryFromActivations(activations.map(({ sinceVersion, codec }) => ({ sinceVersion, value: codec })));
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  export * from './balancer/index.js';
2
+ export * from './chainVersion/index.js';
3
+ export * from './codecs/index.js';
2
4
  export * from './pendingTransactions/index.js';
3
5
  export * from './proving/index.js';
6
+ export * from './signatures/index.js';
4
7
  export * from './simulation/index.js';
5
8
  export * from './submission/index.js';
6
9
  export * from './validation/index.js';
package/dist/index.js CHANGED
@@ -11,8 +11,11 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  export * from './balancer/index.js';
14
+ export * from './chainVersion/index.js';
15
+ export * from './codecs/index.js';
14
16
  export * from './pendingTransactions/index.js';
15
17
  export * from './proving/index.js';
18
+ export * from './signatures/index.js';
16
19
  export * from './simulation/index.js';
17
20
  export * from './submission/index.js';
18
21
  export * from './validation/index.js';
@@ -1,4 +1,5 @@
1
- import { type DateTime, Either, type ParseResult, Schema } from 'effect';
1
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
2
+ import { type DateTime, Either, Option, ParseResult, Schema } from 'effect';
2
3
  export type TransactionTrait<TTransaction> = {
3
4
  ids: (tx: TTransaction) => readonly string[];
4
5
  firstId: (tx: TTransaction) => string;
@@ -12,6 +13,47 @@ export type TransactionTrait<TTransaction> = {
12
13
  export type HasTransactionTrait<TTransaction> = {
13
14
  txTrait: TransactionTrait<TTransaction>;
14
15
  };
16
+ /**
17
+ * The transaction traits a wallet can read pending transactions with, keyed by the protocol version range each one
18
+ * serves.
19
+ *
20
+ * @remarks
21
+ * A transaction's bytes, identifiers and TTL are only meaningful under the ledger version it was authored against, so
22
+ * which trait applies is a property of the transaction rather than of the wallet holding it. Keeping that in the
23
+ * shared {@link ProtocolVersion.Registry} means the boundary between two traits is the same boundary variant selection
24
+ * and codec selection use.
25
+ */
26
+ export type VersionedTransactionTrait<TTransaction> = ProtocolVersion.Registry<TransactionTrait<TTransaction>>;
27
+ export type HasVersionedTransactionTrait<TTransaction> = {
28
+ txTraits: VersionedTransactionTrait<TTransaction>;
29
+ };
30
+ /** Registers one trait for every protocol version: the shape a wallet that speaks a single ledger version has. */
31
+ export declare const singleTrait: <TTransaction>(trait: TransactionTrait<TTransaction>) => VersionedTransactionTrait<TTransaction>;
32
+ /** The oldest registered trait: what an envelope that carries no version stamp is read with. */
33
+ export declare const headTrait: <TTransaction>(traits: VersionedTransactionTrait<TTransaction>) => Option.Option<TransactionTrait<TTransaction>>;
34
+ /**
35
+ * The trait registered for a protocol version, or the oldest trait when nothing says which version applies.
36
+ *
37
+ * @remarks
38
+ * Falling back to the oldest trait is the same convention snapshot restore uses for an envelope written before versions
39
+ * were recorded: the only envelopes without a stamp are ones written before stamping existed, and those necessarily
40
+ * predate every version boundary since.
41
+ * @param traits The registered traits.
42
+ * @param protocolVersion The version the transaction was authored for, when one is known.
43
+ * @returns The trait to read with, or `Option.none()` when the version falls outside every registered range.
44
+ */
45
+ export declare const traitForVersion: <TTransaction>(traits: VersionedTransactionTrait<TTransaction>, protocolVersion: Option.Option<ProtocolVersion.ProtocolVersion>) => Option.Option<TransactionTrait<TTransaction>>;
46
+ /** The registered trait that recognises a transaction object as its own, if any does. */
47
+ export declare const recognisingTrait: <TTransaction>(traits: VersionedTransactionTrait<TTransaction>, tx: unknown) => Option.Option<TransactionTrait<TTransaction>>;
48
+ /**
49
+ * The trait to read an incoming transaction with: the one that recognises it, or the oldest as a last resort.
50
+ *
51
+ * @param traits The registered traits.
52
+ * @param tx The transaction to place.
53
+ * @returns The trait that owns `tx`, the oldest trait when none claims it, or `Option.none()` when nothing is
54
+ * registered at all.
55
+ */
56
+ export declare const traitForTx: <TTransaction>(traits: VersionedTransactionTrait<TTransaction>, tx: TTransaction) => Option.Option<TransactionTrait<TTransaction>>;
15
57
  export type FailedTransactionResult = Readonly<{
16
58
  segments: ReadonlyArray<{
17
59
  id: number;
@@ -26,10 +68,34 @@ export type SuccessTransactionResult = Readonly<{
26
68
  }>;
27
69
  status: 'SUCCESS';
28
70
  }>;
29
- export type TransactionResult = FailedTransactionResult | SuccessTransactionResult;
71
+ /**
72
+ * The verdict on a transaction a protocol upgrade left behind.
73
+ *
74
+ * @remarks
75
+ * Deliberately not one of the indexer's statuses: the chain never reported anything about this transaction and never
76
+ * will, because bytes authored under the previous protocol version cannot be included under the new one. Saying so in
77
+ * its own arm keeps "the node rejected this" and "this can no longer be submitted at all" distinguishable.
78
+ */
79
+ export type OrphanedByForkResult = Readonly<{
80
+ status: 'ORPHANED_BY_FORK';
81
+ /** The protocol version the transaction was authored for. */
82
+ authoredFor: ProtocolVersion.ProtocolVersion;
83
+ /** The protocol version the chain had reached when the wallet gave up on it. */
84
+ chainNow: ProtocolVersion.ProtocolVersion;
85
+ }>;
86
+ export type TransactionResult = FailedTransactionResult | SuccessTransactionResult | OrphanedByForkResult;
30
87
  export type PendingItem<TTransaction> = Readonly<{
31
88
  tx: TTransaction;
32
89
  creationTime: DateTime.Utc;
90
+ /**
91
+ * The protocol version the transaction was authored for, when the wallet had observed one.
92
+ *
93
+ * @remarks
94
+ * `Option.none()` means the wallet never learned what version it was authored against — either the envelope predates
95
+ * stamping, or no wallet had reported a version yet. That is not evidence the transaction was left behind, so such
96
+ * an item is read with the oldest trait but never orphaned.
97
+ */
98
+ protocolVersion: Option.Option<ProtocolVersion.ProtocolVersion>;
33
99
  }>;
34
100
  export type CheckedItem<TTransaction> = PendingItem<TTransaction> & {
35
101
  result: TransactionResult;
@@ -38,24 +104,62 @@ export type PendingTransactionsItem<TTransaction> = PendingItem<TTransaction> |
38
104
  export type FailedTransactionItem<TTransaction> = PendingTransactionsItem<TTransaction> & {
39
105
  result: FailedTransactionResult;
40
106
  };
107
+ export type OrphanedTransactionItem<TTransaction> = PendingTransactionsItem<TTransaction> & {
108
+ result: OrphanedByForkResult;
109
+ };
110
+ /** An item the wallet has given up on, whether the chain rejected it or a protocol upgrade stranded it. */
111
+ export type RejectedTransactionItem<TTransaction> = PendingTransactionsItem<TTransaction> & {
112
+ result: FailedTransactionResult | OrphanedByForkResult;
113
+ };
41
114
  export type PendingTransactions<TTransaction> = Readonly<{
42
115
  all: ReadonlyArray<PendingTransactionsItem<TTransaction>>;
43
116
  }>;
44
- export declare const has: <TTransaction>(transactions: PendingTransactions<TTransaction>, transaction: TTransaction, txTrait: TransactionTrait<TTransaction>) => boolean;
117
+ export declare const has: <TTransaction>(transactions: PendingTransactions<TTransaction>, transaction: TTransaction, traits: VersionedTransactionTrait<TTransaction>) => boolean;
45
118
  export declare const all: <TTransaction>(transactions: PendingTransactions<TTransaction>) => readonly TTransaction[];
46
119
  export declare const allFailed: <TTransaction>(transactions: PendingTransactions<TTransaction>) => ReadonlyArray<FailedTransactionItem<TTransaction>>;
120
+ /** Transactions a protocol upgrade stranded: they were authored for a version the chain has moved past. */
121
+ export declare const allOrphaned: <TTransaction>(transactions: PendingTransactions<TTransaction>) => ReadonlyArray<OrphanedTransactionItem<TTransaction>>;
122
+ /**
123
+ * Everything the wallet has given up on, in the order it was added: reported failures and orphaned transactions alike.
124
+ * Both need the same treatment — unbook the coins, record the rejection — so both belong on one list.
125
+ */
126
+ export declare const allRejected: <TTransaction>(transactions: PendingTransactions<TTransaction>) => ReadonlyArray<RejectedTransactionItem<TTransaction>>;
47
127
  export declare const allPending: <TTransaction>(state: PendingTransactions<TTransaction>) => readonly PendingItem<TTransaction>[];
48
128
  export declare const empty: <TTransaction>() => PendingTransactions<TTransaction>;
49
- export declare const addPendingTransaction: <TTransaction>(state: PendingTransactions<TTransaction>, tx: TTransaction, now: DateTime.Utc, txTrait: TransactionTrait<TTransaction>) => PendingTransactions<TTransaction>;
50
- export declare const clear: <TTransaction>(state: PendingTransactions<TTransaction>, tx: TTransaction, txTrait: TransactionTrait<TTransaction>) => PendingTransactions<TTransaction>;
51
- export declare const saveResult: <TTransaction>(state: PendingTransactions<TTransaction>, tx: TTransaction, result: TransactionResult, txTrait: TransactionTrait<TTransaction>) => PendingTransactions<TTransaction>;
129
+ /**
130
+ * Records a transaction as pending, stamped with the protocol version it was authored for.
131
+ *
132
+ * @param state The pending transactions.
133
+ * @param tx The transaction to record.
134
+ * @param now The time it was authored.
135
+ * @param traits The traits pending transactions are read with.
136
+ * @param protocolVersion The version the chain had reached when it was authored, when the wallet knew one.
137
+ * @returns The pending transactions including `tx`, with any superseded entry from the same version epoch replaced.
138
+ */
139
+ export declare const addPendingTransaction: <TTransaction>(state: PendingTransactions<TTransaction>, tx: TTransaction, now: DateTime.Utc, traits: VersionedTransactionTrait<TTransaction>, protocolVersion: Option.Option<ProtocolVersion.ProtocolVersion>) => PendingTransactions<TTransaction>;
140
+ export declare const clear: <TTransaction>(state: PendingTransactions<TTransaction>, tx: TTransaction, traits: VersionedTransactionTrait<TTransaction>) => PendingTransactions<TTransaction>;
141
+ export declare const saveResult: <TTransaction>(state: PendingTransactions<TTransaction>, tx: TTransaction, result: TransactionResult, traits: VersionedTransactionTrait<TTransaction>) => PendingTransactions<TTransaction>;
142
+ /**
143
+ * Gives up on every unresolved transaction whose version epoch the chain has moved past.
144
+ *
145
+ * @remarks
146
+ * The epochs are the registry's own ranges, so "the chain has moved past" means exactly "a different trait answers for
147
+ * the chain now than answered for this transaction". A transaction authored under the previous protocol version can
148
+ * never be included afterwards, so waiting for its TTL only delays the inevitable and holds its coins hostage
149
+ * meanwhile.
150
+ * @param state The pending transactions.
151
+ * @param traits The traits pending transactions are read with, whose ranges define the epochs.
152
+ * @param chainNow The protocol version the wallets have reached.
153
+ * @returns The pending transactions with the stranded ones given an {@link OrphanedByForkResult}.
154
+ */
155
+ export declare const orphanBeyond: <TTransaction>(state: PendingTransactions<TTransaction>, traits: VersionedTransactionTrait<TTransaction>, chainNow: ProtocolVersion.ProtocolVersion) => PendingTransactions<TTransaction>;
52
156
  type Serialized<TTransaction> = Readonly<{
53
157
  version: 'v1';
54
158
  transactions: readonly PendingItem<TTransaction>[];
55
159
  }>;
56
- export declare const SerializedSchema: <TTransaction>(txTrait: TransactionTrait<TTransaction>) => Schema.Schema<Serialized<TTransaction>, any>;
57
- export declare const serialize: <TTransaction>(state: PendingTransactions<TTransaction>, txTrait: TransactionTrait<TTransaction>) => string;
58
- export declare const deserialize: <TTransaction>(serialized: string, txTrait: TransactionTrait<TTransaction>) => Either.Either<PendingTransactions<TTransaction>, ParseResult.ParseError>;
160
+ export declare const SerializedSchema: <TTransaction>(traits: VersionedTransactionTrait<TTransaction>) => Schema.Schema<Serialized<TTransaction>, any>;
161
+ export declare const serialize: <TTransaction>(state: PendingTransactions<TTransaction>, traits: VersionedTransactionTrait<TTransaction>) => string;
162
+ export declare const deserialize: <TTransaction>(serialized: string, traits: VersionedTransactionTrait<TTransaction>) => Either.Either<PendingTransactions<TTransaction>, ParseResult.ParseError>;
59
163
  export declare const toSerialized: <TTransaction>(pendingTransactions: PendingTransactions<TTransaction>) => Serialized<TTransaction>;
60
164
  export declare const fromSerialized: <TTransaction>(serialized: Serialized<TTransaction>) => PendingTransactions<TTransaction>;
61
165
  export {};