@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
@@ -10,12 +10,62 @@
10
10
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
+ import { LedgerParameters as V8LedgerParameters } from '@midnight-ntwrk/ledger-v8';
13
14
  import { LedgerParameters } from '@midnightntwrk/ledger-v9';
15
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
14
16
  import { BlockHash } from '@midnight-ntwrk/wallet-sdk-indexer-client';
15
17
  import { HttpQueryClient } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
16
- import { Buffer } from 'buffer';
17
- import { Effect } from 'effect';
18
+ import { Effect, Either, pipe } from 'effect';
19
+ import { LedgerParametersCodec } from '../codecs/index.js';
18
20
  import { getLastBlock } from '../simulation/index.js';
21
+ /**
22
+ * The ledger parameters codecs validation reads blocks with, split at the version the chain forks at.
23
+ *
24
+ * @remarks
25
+ * A block's parameters are bytes of whichever ledger version produced the block, and the version the indexer reports
26
+ * the block under is what says which. Below the fork version they are read with ledger-v8's deserializer and from it
27
+ * with the current one, so that the object handed to a validator is always one its own `LedgerState` accepts.
28
+ *
29
+ * Nothing in the resulting type distinguishes the two: the ledger versions' `LedgerParameters` are structurally
30
+ * identical, so {@link AnyLedgerParameters} is a statement of intent rather than something the compiler enforces. The
31
+ * distinction is nominal at run time — the classes differ, and each ledger's WASM boundary rejects the other's —
32
+ * which is why the routing has to be right rather than merely well-typed.
33
+ * @param forkVersion The protocol version at which the chain hands over to the ledger-v9.
34
+ * @returns The registry blocks are read with.
35
+ */
36
+ export const defaultLedgerParametersCodecs = (forkVersion) => Either.getOrThrow(LedgerParametersCodec.makeCodecs(forkVersion > ProtocolVersion.MinSupportedVersion
37
+ ? [
38
+ {
39
+ sinceVersion: ProtocolVersion.MinSupportedVersion,
40
+ codec: LedgerParametersCodec.fromDeserializer((bytes) => V8LedgerParameters.deserialize(bytes)),
41
+ },
42
+ {
43
+ sinceVersion: forkVersion,
44
+ codec: LedgerParametersCodec.fromDeserializer((bytes) => LedgerParameters.deserialize(bytes)),
45
+ },
46
+ ]
47
+ : // A chain whose boundary is at or below the minimum supported version has no ledger-v8 epoch to register for.
48
+ [
49
+ {
50
+ sinceVersion: ProtocolVersion.MinSupportedVersion,
51
+ codec: LedgerParametersCodec.fromDeserializer((bytes) => LedgerParameters.deserialize(bytes)),
52
+ },
53
+ ]));
54
+ /**
55
+ * Reads an indexer block into {@link BlockData}, decoding its ledger parameters with whichever registered codec claims
56
+ * the protocol version the block was reported under.
57
+ *
58
+ * @param codecs The ledger parameters codecs the caller is willing to read with.
59
+ * @param block The block as the indexer served it.
60
+ * @returns The block data, or the typed reason its parameters could not be read.
61
+ */
62
+ export const blockDataFrom = (codecs, block) => pipe(LedgerParametersCodec.decode(codecs, ProtocolVersion.ProtocolVersion(BigInt(block.protocolVersion)), block.ledgerParameters), Either.map((ledgerParameters) => ({
63
+ hash: block.hash,
64
+ height: block.height,
65
+ protocolVersion: block.protocolVersion,
66
+ ledgerParameters,
67
+ timestamp: new Date(block.timestamp),
68
+ })));
19
69
  /**
20
70
  * Builds a `BlockDataFetcher` that queries the indexer over HTTP for the latest block.
21
71
  *
@@ -23,18 +73,14 @@ import { getLastBlock } from '../simulation/index.js';
23
73
  */
24
74
  export const makeDefaultBlockDataFetcher = (config) => {
25
75
  const url = config.indexerClientConnection.indexerHttpUrl;
76
+ const codecs = config.ledgerParametersCodecs ?? defaultLedgerParametersCodecs(config.forks.v9);
26
77
  return () => Effect.runPromise(Effect.gen(function* () {
27
78
  const query = yield* BlockHash;
28
79
  const result = yield* query({ offset: null });
29
80
  const block = result.block;
30
81
  if (!block)
31
82
  throw new Error('Unable to fetch latest block from indexer.');
32
- return {
33
- hash: block.hash,
34
- height: block.height,
35
- ledgerParameters: LedgerParameters.deserialize(Buffer.from(block.ledgerParameters, 'hex')),
36
- timestamp: new Date(block.timestamp),
37
- };
83
+ return yield* blockDataFrom(codecs, block);
38
84
  }).pipe(Effect.provide(HttpQueryClient.layer({ url })), Effect.scoped));
39
85
  };
40
86
  /**
@@ -50,6 +96,7 @@ export const makeSimulatorBlockDataFetcher = (simulator) => {
50
96
  return {
51
97
  hash: lastBlock.hash,
52
98
  height: Number(lastBlock.number),
99
+ protocolVersion: Number(lastBlock.protocolVersion),
53
100
  ledgerParameters: state.ledger.parameters,
54
101
  timestamp: state.currentTime,
55
102
  };
@@ -1,2 +1,4 @@
1
1
  export * from './blockData.js';
2
+ export * from './v8ValidationService.js';
2
3
  export * from './validationService.js';
4
+ export * from './versionedValidation.js';
@@ -11,4 +11,6 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  export * from './blockData.js';
14
+ export * from './v8ValidationService.js';
14
15
  export * from './validationService.js';
16
+ export * from './versionedValidation.js';
@@ -0,0 +1,29 @@
1
+ import * as ledger from '@midnight-ntwrk/ledger-v8';
2
+ import type { V8UnboundTransaction } from '../proving/v8ProvingService.js';
3
+ import { type AnyLedgerParameters, type ValidationServiceDependencies, type ValidationServiceEffect, type WellFormedCheck } from './validationService.js';
4
+ /**
5
+ * Every ledger-v8 transaction shape well-formedness can be asked about — the ledger-v8 counterpart of
6
+ * `AnyV9ValidatableTransaction`, and a genuinely different type from it: these are the other ledger version's classes,
7
+ * and neither ledger can read the other's.
8
+ */
9
+ export type AnyV8ValidatableTransaction = ledger.FinalizedTransaction | V8UnboundTransaction | ledger.UnprovenTransaction;
10
+ /**
11
+ * The ledger-v8's well-formedness check.
12
+ *
13
+ * @remarks
14
+ * Structurally the same three steps as ledger-v9's, against a different ledger version's classes. It is the classes,
15
+ * not the steps, that make this a separate check: a ledger-v8 transaction cannot be handed to ledger-v9's
16
+ * `wellFormed`, nor ledger-v9 parameters to this one.
17
+ */
18
+ export declare const v8WellFormedCheck: WellFormedCheck<AnyV8ValidatableTransaction, AnyLedgerParameters>;
19
+ /**
20
+ * Builds the validator for ledger-v8 transactions.
21
+ *
22
+ * @remarks
23
+ * Registered below the fork version by `makeDefaultValidationServices`, against a block-data fetcher whose codec
24
+ * registry is split at the same version — so a block reported from before the boundary reaches this validator as
25
+ * ledger-v8 parameters, which is the only kind its ledger version can build a state from.
26
+ * @param deps The network, clock, and the block-data fetcher, which decodes each block at the version it reports.
27
+ * @returns A validator to register in a `ValidationServices` registry for the version range before the v9 fork.
28
+ */
29
+ export declare const makeV8ValidationServiceEffect: (deps: ValidationServiceDependencies<AnyLedgerParameters>) => ValidationServiceEffect<AnyV8ValidatableTransaction, AnyLedgerParameters>;
@@ -0,0 +1,48 @@
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 * as ledger from '@midnight-ntwrk/ledger-v8';
14
+ import { makeValidationServiceEffect, } from './validationService.js';
15
+ const buildStrictness = (flags) => {
16
+ const strictness = new ledger.WellFormedStrictness();
17
+ strictness.enforceBalancing = flags.enforceBalancing;
18
+ strictness.verifySignatures = flags.verifySignatures;
19
+ strictness.enforceLimits = flags.enforceLimits;
20
+ return strictness;
21
+ };
22
+ const buildBlankLedgerState = (networkId, parameters) => {
23
+ const state = ledger.LedgerState.blank(networkId);
24
+ state.parameters = parameters;
25
+ return state;
26
+ };
27
+ /**
28
+ * The ledger-v8's well-formedness check.
29
+ *
30
+ * @remarks
31
+ * Structurally the same three steps as ledger-v9's, against a different ledger version's classes. It is the classes,
32
+ * not the steps, that make this a separate check: a ledger-v8 transaction cannot be handed to ledger-v9's
33
+ * `wellFormed`, nor ledger-v9 parameters to this one.
34
+ */
35
+ export const v8WellFormedCheck = (tx, { networkId, ledgerParameters, flags, now }) => {
36
+ tx.wellFormed(buildBlankLedgerState(networkId, ledgerParameters), buildStrictness(flags), now);
37
+ };
38
+ /**
39
+ * Builds the validator for ledger-v8 transactions.
40
+ *
41
+ * @remarks
42
+ * Registered below the fork version by `makeDefaultValidationServices`, against a block-data fetcher whose codec
43
+ * registry is split at the same version — so a block reported from before the boundary reaches this validator as
44
+ * ledger-v8 parameters, which is the only kind its ledger version can build a state from.
45
+ * @param deps The network, clock, and the block-data fetcher, which decodes each block at the version it reports.
46
+ * @returns A validator to register in a `ValidationServices` registry for the version range before the v9 fork.
47
+ */
48
+ export const makeV8ValidationServiceEffect = (deps) => makeValidationServiceEffect(v8WellFormedCheck, deps);
@@ -1,27 +1,49 @@
1
- import * as ledger from '@midnightntwrk/ledger-v9';
1
+ import type * as ledgerV8 from '@midnight-ntwrk/ledger-v8';
2
+ import * as ledgerV9 from '@midnightntwrk/ledger-v9';
3
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
2
4
  import type { Clock } from '@midnight-ntwrk/wallet-sdk-utilities';
3
5
  import { Cause, Effect } from 'effect';
4
- import type { UnboundTransaction } from '../proving/provingService.js';
6
+ import type { V9UnboundTransaction } from '../proving/provingService.js';
7
+ /**
8
+ * Ledger parameters as either ledger version reads them: what a block-data fetch spanning a protocol boundary yields.
9
+ *
10
+ * @remarks
11
+ * The two ledger versions' `LedgerParameters` are structurally identical, so this union carries no information the
12
+ * compiler can act on — it names, at the one place a block's parameters cross into validation, that which ledger
13
+ * version produced them is a run-time fact settled by the block's reported protocol version. Each ledger's
14
+ * `LedgerState` still insists on its own class, so handing over the wrong one fails at the WASM boundary and is
15
+ * reported as a {@link WellFormedError}.
16
+ */
17
+ export type AnyLedgerParameters = ledgerV8.LedgerParameters | ledgerV9.LedgerParameters;
5
18
  /**
6
19
  * Snapshot of chain state required for transaction validation. Structurally identical to the dust-wallet's `BlockData`
7
20
  * — a separate declaration here keeps the validation service decoupled from dust-wallet. The two can be passed
8
21
  * interchangeably via structural typing.
22
+ *
23
+ * @typeParam TParameters The `LedgerParameters` type of the ledger version the parameters were decoded at. Defaults to
24
+ * ledger-v9's, so `BlockData` unqualified still names exactly what it always did.
9
25
  */
10
- export interface BlockData {
26
+ export interface BlockData<TParameters = ledgerV9.LedgerParameters> {
11
27
  hash: string;
12
28
  height: number;
13
- ledgerParameters: ledger.LedgerParameters;
29
+ /** The protocol version the indexer reported this block under, and so the ledger version its parameters are in. */
30
+ protocolVersion: number;
31
+ ledgerParameters: TParameters;
14
32
  timestamp: Date;
15
33
  }
16
34
  /**
17
- * Configurable subset of {@link ledger.WellFormedStrictness}. Proof-verification flags (`verifyNativeProofs`,
35
+ * Configurable subset of {@link ledgerV9.WellFormedStrictness}. Proof-verification flags (`verifyNativeProofs`,
18
36
  * `verifyContractProofs`) are intentionally omitted — proof verification requires the complete ledger state and will be
19
37
  * addressed in a future task.
20
38
  */
21
- export type WellFormedStrictnessFlags = Pick<ledger.WellFormedStrictness, 'enforceBalancing' | 'verifySignatures' | 'enforceLimits'>;
22
- export type ValidateTxOptions = {
39
+ export type WellFormedStrictnessFlags = Pick<ledgerV9.WellFormedStrictness, 'enforceBalancing' | 'verifySignatures' | 'enforceLimits'>;
40
+ /**
41
+ * @typeParam TParameters The `LedgerParameters` type of the ledger version `blockData` was decoded at — necessarily the
42
+ * version the validator being called speaks, since well-formedness is checked against a state built from them.
43
+ */
44
+ export type ValidateTxOptions<TParameters = ledgerV9.LedgerParameters> = {
23
45
  flags: WellFormedStrictnessFlags;
24
- blockData?: BlockData | undefined;
46
+ blockData?: BlockData<TParameters> | undefined;
25
47
  };
26
48
  declare const WellFormedError_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]; }>) => Cause.YieldableError & {
27
49
  readonly _tag: "@midnight-ntwrk/wallet-sdk-capabilities/validation/validationService/WellFormedError";
@@ -39,21 +61,114 @@ export declare class ValidationFetchError extends ValidationFetchError_base<{
39
61
  cause: unknown;
40
62
  }> {
41
63
  }
42
- export type AnyValidatableTransaction = ledger.FinalizedTransaction | UnboundTransaction | ledger.UnprovenTransaction;
43
- export interface ValidationServiceEffect {
44
- validateTx(tx: AnyValidatableTransaction, options: ValidateTxOptions): Effect.Effect<void, WellFormedError | ValidationFetchError>;
64
+ export type AnyV9ValidatableTransaction = ledgerV9.FinalizedTransaction | V9UnboundTransaction | ledgerV9.UnprovenTransaction;
65
+ /**
66
+ * Checks a transaction for well-formedness.
67
+ *
68
+ * @typeParam TTransaction The transactions this validator accepts. Defaults to ledger-v9's, because a validator is only
69
+ * ever written against one ledger version — which is exactly why choosing between validators is
70
+ * {@link VersionedValidationServiceEffect}'s job and not this interface's.
71
+ * @typeParam TParameters The `LedgerParameters` type of that same ledger version.
72
+ */
73
+ export interface ValidationServiceEffect<TTransaction = AnyV9ValidatableTransaction, TParameters = ledgerV9.LedgerParameters> {
74
+ validateTx(tx: TTransaction, options: ValidateTxOptions<TParameters>): Effect.Effect<void, WellFormedError | ValidationFetchError>;
75
+ }
76
+ export interface ValidationService<TTransaction = AnyV9ValidatableTransaction, TParameters = ledgerV9.LedgerParameters> {
77
+ validateTx(tx: TTransaction, options: ValidateTxOptions<TParameters>): Promise<void>;
78
+ }
79
+ declare const UnsupportedValidationVersionError_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]; }>) => Cause.YieldableError & {
80
+ readonly _tag: "@midnight-ntwrk/wallet-sdk-capabilities/validation/validationService/UnsupportedValidationVersionError";
81
+ } & Readonly<A>;
82
+ /** Raised when no validator is registered for the protocol version a transaction was authored for. */
83
+ export declare class UnsupportedValidationVersionError extends UnsupportedValidationVersionError_base<{
84
+ readonly message: string;
85
+ /** The version the transaction was authored for, which no registered validator serves. */
86
+ readonly protocolVersion: ProtocolVersion.ProtocolVersion;
87
+ }> {
88
+ }
89
+ /**
90
+ * Checks a transaction with the validator registered for the protocol version it was authored for.
91
+ *
92
+ * @remarks
93
+ * The version is the transaction's own stamp, taken when it was authored, and never the version the chain has reached
94
+ * by the time it is validated. Well-formedness asks whether the ledger that produced these bytes would accept them; a
95
+ * fork landing between authoring and validation does not rewrite the bytes, so it cannot change the answer or who
96
+ * gives it.
97
+ */
98
+ export interface VersionedValidationServiceEffect<TTransaction = AnyV9ValidatableTransaction, TParameters = ledgerV9.LedgerParameters> {
99
+ validateTx(tx: TTransaction, protocolVersion: ProtocolVersion.ProtocolVersion, options: ValidateTxOptions<TParameters>): Effect.Effect<void, WellFormedError | ValidationFetchError | UnsupportedValidationVersionError>;
45
100
  }
46
- export interface ValidationService {
47
- validateTx(tx: AnyValidatableTransaction, options: ValidateTxOptions): Promise<void>;
101
+ export interface VersionedValidationService<TTransaction = AnyV9ValidatableTransaction, TParameters = ledgerV9.LedgerParameters> {
102
+ validateTx(tx: TTransaction, protocolVersion: ProtocolVersion.ProtocolVersion, options: ValidateTxOptions<TParameters>): Promise<void>;
48
103
  }
104
+ /**
105
+ * The validators a caller is willing to check with, keyed by the protocol version range each one serves.
106
+ *
107
+ * @remarks
108
+ * Registration is per caller, not global, and a caller registers only the ledger versions its own types are written
109
+ * against — the same rule the ledger-parameters codecs follow, and for the same reason: a `LedgerState` belongs to
110
+ * one ledger version, so a validator that speaks two would have nothing to build. A version outside every registered
111
+ * range therefore means "this transaction belongs to a different variant", and the router says so with
112
+ * {@link UnsupportedValidationVersionError} instead of handing the bytes to a checker that could only reject them.
113
+ */
114
+ export type ValidationServices<TTransaction = AnyV9ValidatableTransaction, TParameters = ledgerV9.LedgerParameters> = ProtocolVersion.Registry<ValidationServiceEffect<TTransaction, TParameters>>;
115
+ /**
116
+ * Builds a validation service that routes on the version a transaction was authored for.
117
+ *
118
+ * @param services The validators and the version ranges they serve.
119
+ * @returns A validation service that fails with {@link UnsupportedValidationVersionError} for a version nothing serves.
120
+ */
121
+ export declare const makeVersionedValidationServiceEffect: <TTransaction, TParameters>(services: ValidationServices<TTransaction, TParameters>) => VersionedValidationServiceEffect<TTransaction, TParameters>;
122
+ /**
123
+ * Lets one validator answer for every protocol version.
124
+ *
125
+ * @remarks
126
+ * Says out loud what an unversioned validation service was implicitly claiming: that it can judge anything, whatever
127
+ * version authored it. True for a wallet on one side of a fork, and a lie the moment it crosses — so it has to be
128
+ * written down rather than assumed.
129
+ * @param service The validator to use for every version.
130
+ * @returns The same validator, addressed by version.
131
+ */
132
+ export declare const singleVersionValidationServiceEffect: <TTransaction, TParameters>(service: ValidationServiceEffect<TTransaction, TParameters>) => VersionedValidationServiceEffect<TTransaction, TParameters>;
49
133
  export type DefaultValidationConfiguration = {
50
134
  networkId: string;
51
135
  };
52
- export type ValidationServiceDependencies = {
53
- fetchBlockData: () => Promise<BlockData>;
136
+ /**
137
+ * @typeParam TParameters The `LedgerParameters` type of the ledger version this validator speaks, and so the version
138
+ * its `fetchBlockData` must decode at.
139
+ */
140
+ export type ValidationServiceDependencies<TParameters = ledgerV9.LedgerParameters> = {
141
+ fetchBlockData: () => Promise<BlockData<TParameters>>;
54
142
  networkId: string;
55
143
  clock: Clock.Clock;
56
144
  };
57
- export declare const makeDefaultValidationServiceEffect: (deps: ValidationServiceDependencies) => ValidationServiceEffect;
58
- export declare const makeDefaultValidationService: (deps: ValidationServiceDependencies) => ValidationService;
145
+ /**
146
+ * The one thing a ledger version has to supply for its transactions to be checked: run its own well-formedness check
147
+ * against a blank state carrying the block's parameters, throwing whatever that ledger throws.
148
+ *
149
+ * @remarks
150
+ * Deliberately allowed to throw, exactly like a {@link LedgerParametersCodec}: it wraps a WASM call whose failure mode
151
+ * is an exception. {@link makeValidationServiceEffect} is the only way to reach one, and it turns that into a typed
152
+ * {@link WellFormedError}.
153
+ */
154
+ export type WellFormedCheck<TTransaction, TParameters> = (transaction: TTransaction, context: Readonly<{
155
+ networkId: string;
156
+ ledgerParameters: TParameters;
157
+ flags: WellFormedStrictnessFlags;
158
+ now: Date;
159
+ }>) => void;
160
+ /**
161
+ * Builds a validation service for one ledger version from that version's well-formedness check.
162
+ *
163
+ * @param check The ledger version's well-formedness check.
164
+ * @param deps The network, clock, and the block-data fetcher that decodes at the same ledger version.
165
+ * @returns A validator for that ledger version, ready to register in {@link ValidationServices}.
166
+ */
167
+ export declare const makeValidationServiceEffect: <TTransaction, TParameters>(check: WellFormedCheck<TTransaction, TParameters>, deps: ValidationServiceDependencies<TParameters>) => ValidationServiceEffect<TTransaction, TParameters>;
168
+ /** The ledger-v9's well-formedness check. */
169
+ export declare const v9WellFormedCheck: WellFormedCheck<AnyV9ValidatableTransaction, AnyLedgerParameters>;
170
+ export declare const makeV9ValidationServiceEffect: (deps: ValidationServiceDependencies<AnyLedgerParameters>) => ValidationServiceEffect<AnyV9ValidatableTransaction, AnyLedgerParameters>;
171
+ export declare const makeV9ValidationService: (deps: ValidationServiceDependencies<AnyLedgerParameters>) => ValidationService<AnyV9ValidatableTransaction, AnyLedgerParameters>;
172
+ /** Adapts a version-routed validation service to the promise-facing surface the facade exposes. */
173
+ export declare const wrapVersionedValidationService: <TTransaction, TParameters>(effectService: VersionedValidationServiceEffect<TTransaction, TParameters>) => VersionedValidationService<TTransaction, TParameters>;
59
174
  export {};
@@ -1,16 +1,5 @@
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 * as ledger from '@midnightntwrk/ledger-v9';
1
+ import * as ledgerV9 from '@midnightntwrk/ledger-v9';
2
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
14
3
  import { Cause, Data, Effect, Exit, Option, pipe } from 'effect';
15
4
  /** Thrown when a transaction fails the structural well-formedness check. */
16
5
  export class WellFormedError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-capabilities/validation/validationService/WellFormedError') {
@@ -18,19 +7,45 @@ export class WellFormedError extends Data.TaggedError('@midnight-ntwrk/wallet-sd
18
7
  /** Thrown when validation cannot complete because the block-data fetch failed. */
19
8
  export class ValidationFetchError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-capabilities/validation/validationService/ValidationFetchError') {
20
9
  }
21
- const buildStrictness = (flags) => {
22
- const strictness = new ledger.WellFormedStrictness();
23
- strictness.enforceBalancing = flags.enforceBalancing;
24
- strictness.verifySignatures = flags.verifySignatures;
25
- strictness.enforceLimits = flags.enforceLimits;
26
- return strictness;
27
- };
28
- const buildBlankLedgerState = (networkId, parameters) => {
29
- const state = ledger.LedgerState.blank(networkId);
30
- state.parameters = parameters;
31
- return state;
32
- };
33
- export const makeDefaultValidationServiceEffect = (deps) => ({
10
+ /** Raised when no validator is registered for the protocol version a transaction was authored for. */
11
+ export class UnsupportedValidationVersionError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-capabilities/validation/validationService/UnsupportedValidationVersionError') {
12
+ }
13
+ /**
14
+ * Builds a validation service that routes on the version a transaction was authored for.
15
+ *
16
+ * @param services The validators and the version ranges they serve.
17
+ * @returns A validation service that fails with {@link UnsupportedValidationVersionError} for a version nothing serves.
18
+ */
19
+ export const makeVersionedValidationServiceEffect = (services) => ({
20
+ validateTx: (tx, protocolVersion, options) => Option.match(ProtocolVersion.select(services, protocolVersion), {
21
+ onNone: () => Effect.fail(new UnsupportedValidationVersionError({
22
+ message: `No validator is registered for protocol version ${protocolVersion}.`,
23
+ protocolVersion,
24
+ })),
25
+ onSome: (service) => service.validateTx(tx, options),
26
+ }),
27
+ });
28
+ /**
29
+ * Lets one validator answer for every protocol version.
30
+ *
31
+ * @remarks
32
+ * Says out loud what an unversioned validation service was implicitly claiming: that it can judge anything, whatever
33
+ * version authored it. True for a wallet on one side of a fork, and a lie the moment it crosses — so it has to be
34
+ * written down rather than assumed.
35
+ * @param service The validator to use for every version.
36
+ * @returns The same validator, addressed by version.
37
+ */
38
+ export const singleVersionValidationServiceEffect = (service) => ({
39
+ validateTx: (tx, _protocolVersion, options) => service.validateTx(tx, options),
40
+ });
41
+ /**
42
+ * Builds a validation service for one ledger version from that version's well-formedness check.
43
+ *
44
+ * @param check The ledger version's well-formedness check.
45
+ * @param deps The network, clock, and the block-data fetcher that decodes at the same ledger version.
46
+ * @returns A validator for that ledger version, ready to register in {@link ValidationServices}.
47
+ */
48
+ export const makeValidationServiceEffect = (check, deps) => ({
34
49
  validateTx(tx, options) {
35
50
  const fetchOrUse = options.blockData
36
51
  ? Effect.succeed(options.blockData)
@@ -39,26 +54,57 @@ export const makeDefaultValidationServiceEffect = (deps) => ({
39
54
  catch: (cause) => new ValidationFetchError({ cause }),
40
55
  });
41
56
  return pipe(fetchOrUse, Effect.flatMap((blockData) => Effect.try({
42
- try: () => {
43
- const ledgerState = buildBlankLedgerState(deps.networkId, blockData.ledgerParameters);
44
- const strictness = buildStrictness(options.flags);
45
- tx.wellFormed(ledgerState, strictness, deps.clock.now());
46
- },
57
+ try: () => check(tx, {
58
+ networkId: deps.networkId,
59
+ ledgerParameters: blockData.ledgerParameters,
60
+ flags: options.flags,
61
+ now: deps.clock.now(),
62
+ }),
47
63
  catch: (cause) => new WellFormedError({ cause }),
48
64
  })));
49
65
  },
50
66
  });
51
- export const makeDefaultValidationService = (deps) => {
52
- const effectService = makeDefaultValidationServiceEffect(deps);
67
+ const buildStrictness = (flags) => {
68
+ const strictness = new ledgerV9.WellFormedStrictness();
69
+ strictness.enforceBalancing = flags.enforceBalancing;
70
+ strictness.verifySignatures = flags.verifySignatures;
71
+ strictness.enforceLimits = flags.enforceLimits;
72
+ return strictness;
73
+ };
74
+ const buildBlankLedgerState = (networkId, parameters) => {
75
+ const state = ledgerV9.LedgerState.blank(networkId);
76
+ state.parameters = parameters;
77
+ return state;
78
+ };
79
+ /** The ledger-v9's well-formedness check. */
80
+ export const v9WellFormedCheck = (tx, { networkId, ledgerParameters, flags, now }) => {
81
+ tx.wellFormed(buildBlankLedgerState(networkId, ledgerParameters), buildStrictness(flags), now);
82
+ };
83
+ /**
84
+ * Rejects a promise with the typed failure itself rather than the fiber wrapper around it, so a caller can `catch` the
85
+ * error class the signature names.
86
+ *
87
+ * @remarks
88
+ * `E extends Error` is what makes that rejection legitimate rather than a thrown bare value, and every failure on this
89
+ * surface is a `Data.TaggedError`, which is one.
90
+ */
91
+ const runPromiseThrowingFailure = async (effect) => {
92
+ const exit = await Effect.runPromiseExit(effect);
93
+ if (Exit.isSuccess(exit))
94
+ return exit.value;
95
+ const failure = Cause.failureOption(exit.cause);
96
+ if (Option.isSome(failure))
97
+ throw failure.value;
98
+ throw new Error(Cause.pretty(exit.cause));
99
+ };
100
+ export const makeV9ValidationServiceEffect = (deps) => makeValidationServiceEffect(v9WellFormedCheck, deps);
101
+ export const makeV9ValidationService = (deps) => {
102
+ const effectService = makeV9ValidationServiceEffect(deps);
53
103
  return {
54
- validateTx: async (tx, options) => {
55
- const exit = await Effect.runPromiseExit(effectService.validateTx(tx, options));
56
- if (Exit.isSuccess(exit))
57
- return;
58
- const failure = Cause.failureOption(exit.cause);
59
- if (Option.isSome(failure))
60
- throw failure.value;
61
- throw new Error(Cause.pretty(exit.cause));
62
- },
104
+ validateTx: (tx, options) => runPromiseThrowingFailure(effectService.validateTx(tx, options)),
63
105
  };
64
106
  };
107
+ /** Adapts a version-routed validation service to the promise-facing surface the facade exposes. */
108
+ export const wrapVersionedValidationService = (effectService) => ({
109
+ validateTx: (tx, protocolVersion, options) => runPromiseThrowingFailure(effectService.validateTx(tx, protocolVersion, options)),
110
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Well-formedness for an SDK that spans a protocol boundary: one validator per ledger version, chosen by the version a
3
+ * transaction was authored for.
4
+ *
5
+ * @remarks
6
+ * The two halves already exist and are each written against one ledger version. What this module supplies is the only
7
+ * thing neither of them can: the registration that says which range of protocol versions each one answers for, taken
8
+ * from the same fork version the wallets are built with.
9
+ */
10
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
11
+ import { type AnyV8ValidatableTransaction } from './v8ValidationService.js';
12
+ import { type AnyLedgerParameters, type AnyV9ValidatableTransaction, type ValidationServiceDependencies, type ValidationServiceEffect, type ValidationServices, type VersionedValidationService, type VersionedValidationServiceEffect } from './validationService.js';
13
+ /**
14
+ * Every transaction shape either ledger version can be asked about.
15
+ *
16
+ * @remarks
17
+ * A genuine union, unlike {@link AnyLedgerParameters}: the two ledger versions' transaction types are nominally
18
+ * distinct, so a caller holding one of them is holding something the other version's validator provably cannot read.
19
+ */
20
+ export type AnyVersionValidatableTransaction = AnyV9ValidatableTransaction | AnyV8ValidatableTransaction;
21
+ /** A validator registered in a two-version registry, whichever ledger version it was written against. */
22
+ export type VersionValidationServiceEffect = ValidationServiceEffect<AnyVersionValidatableTransaction, AnyLedgerParameters>;
23
+ /**
24
+ * Registers a validator either side of the protocol boundary.
25
+ *
26
+ * @remarks
27
+ * Both validators are handed the same block-data fetcher, and that is correct rather than a shortcut: the fetcher
28
+ * decodes a block's parameters with the codec registered for the version the block itself was reported under, so it
29
+ * already yields the ledger version whose validator will be chosen for a transaction authored in the same epoch. A
30
+ * transaction authored on the other side of the boundary from the chain's current block is the case neither can
31
+ * serve, and it fails at the ledger rather than silently checking against the wrong parameters.
32
+ * @param deps The network, clock, and the block-data fetcher shared by both validators.
33
+ * @param forkVersion The protocol version at which the chain hands over to the ledger-v9.
34
+ * @returns The validators and the version ranges they serve.
35
+ */
36
+ export declare const makeDefaultValidationServices: (deps: ValidationServiceDependencies<AnyLedgerParameters>, forkVersion: ProtocolVersion.ProtocolVersion) => ValidationServices<AnyVersionValidatableTransaction, AnyLedgerParameters>;
37
+ /**
38
+ * Builds the version-routed validator an SDK spanning a protocol boundary checks with.
39
+ *
40
+ * @param deps The network, clock, and the block-data fetcher shared by both validators.
41
+ * @param forkVersion The protocol version at which the chain hands over to the ledger-v9.
42
+ * @returns A validator that routes on the version a transaction was authored for.
43
+ */
44
+ export declare const makeDefaultVersionedValidationServiceEffect: (deps: ValidationServiceDependencies<AnyLedgerParameters>, forkVersion: ProtocolVersion.ProtocolVersion) => VersionedValidationServiceEffect<AnyVersionValidatableTransaction, AnyLedgerParameters>;
45
+ /** The promise-facing surface of {@link makeDefaultVersionedValidationServiceEffect}, for the facade to expose. */
46
+ export declare const makeDefaultVersionedValidationService: (deps: ValidationServiceDependencies<AnyLedgerParameters>, forkVersion: ProtocolVersion.ProtocolVersion) => VersionedValidationService<AnyVersionValidatableTransaction, AnyLedgerParameters>;
@@ -0,0 +1,55 @@
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
+ * Well-formedness for an SDK that spans a protocol boundary: one validator per ledger version, chosen by the version a
15
+ * transaction was authored for.
16
+ *
17
+ * @remarks
18
+ * The two halves already exist and are each written against one ledger version. What this module supplies is the only
19
+ * thing neither of them can: the registration that says which range of protocol versions each one answers for, taken
20
+ * from the same fork version the wallets are built with.
21
+ */
22
+ import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
23
+ import { Either } from 'effect';
24
+ import { makeV8ValidationServiceEffect } from './v8ValidationService.js';
25
+ import { makeV9ValidationServiceEffect, makeVersionedValidationServiceEffect, wrapVersionedValidationService, } from './validationService.js';
26
+ /**
27
+ * Registers a validator either side of the protocol boundary.
28
+ *
29
+ * @remarks
30
+ * Both validators are handed the same block-data fetcher, and that is correct rather than a shortcut: the fetcher
31
+ * decodes a block's parameters with the codec registered for the version the block itself was reported under, so it
32
+ * already yields the ledger version whose validator will be chosen for a transaction authored in the same epoch. A
33
+ * transaction authored on the other side of the boundary from the chain's current block is the case neither can
34
+ * serve, and it fails at the ledger rather than silently checking against the wrong parameters.
35
+ * @param deps The network, clock, and the block-data fetcher shared by both validators.
36
+ * @param forkVersion The protocol version at which the chain hands over to the ledger-v9.
37
+ * @returns The validators and the version ranges they serve.
38
+ */
39
+ export const makeDefaultValidationServices = (deps, forkVersion) => Either.getOrThrow(ProtocolVersion.makeRegistryFromActivations(forkVersion > ProtocolVersion.MinSupportedVersion
40
+ ? [
41
+ { sinceVersion: ProtocolVersion.MinSupportedVersion, value: makeV8ValidationServiceEffect(deps) },
42
+ { sinceVersion: forkVersion, value: makeV9ValidationServiceEffect(deps) },
43
+ ]
44
+ : // A chain whose boundary is at or below the minimum supported version has no ledger-v8 epoch to register for.
45
+ [{ sinceVersion: ProtocolVersion.MinSupportedVersion, value: makeV9ValidationServiceEffect(deps) }]));
46
+ /**
47
+ * Builds the version-routed validator an SDK spanning a protocol boundary checks with.
48
+ *
49
+ * @param deps The network, clock, and the block-data fetcher shared by both validators.
50
+ * @param forkVersion The protocol version at which the chain hands over to the ledger-v9.
51
+ * @returns A validator that routes on the version a transaction was authored for.
52
+ */
53
+ export const makeDefaultVersionedValidationServiceEffect = (deps, forkVersion) => makeVersionedValidationServiceEffect(makeDefaultValidationServices(deps, forkVersion));
54
+ /** The promise-facing surface of {@link makeDefaultVersionedValidationServiceEffect}, for the facade to expose. */
55
+ export const makeDefaultVersionedValidationService = (deps, forkVersion) => wrapVersionedValidationService(makeDefaultVersionedValidationServiceEffect(deps, forkVersion));