@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.
- package/README.md +27 -3
- package/dist/chainVersion/chainVersionProbe.d.ts +95 -0
- package/dist/chainVersion/chainVersionProbe.js +83 -0
- package/dist/chainVersion/index.d.ts +1 -0
- package/dist/chainVersion/index.js +13 -0
- package/dist/codecs/index.d.ts +1 -0
- package/dist/codecs/index.js +13 -0
- package/dist/codecs/ledgerParameters.d.ts +81 -0
- package/dist/codecs/ledgerParameters.js +63 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/pendingTransactions/pendingTransactions.d.ts +113 -9
- package/dist/pendingTransactions/pendingTransactions.js +147 -30
- package/dist/pendingTransactions/pendingTransactionsService.d.ts +25 -9
- package/dist/pendingTransactions/pendingTransactionsService.js +33 -21
- package/dist/proving/index.d.ts +2 -0
- package/dist/proving/index.js +2 -0
- package/dist/proving/provingService.d.ts +175 -15
- package/dist/proving/provingService.js +104 -11
- package/dist/proving/v8ProvingService.d.ts +53 -0
- package/dist/proving/v8ProvingService.js +71 -0
- package/dist/proving/versionedProving.d.ts +42 -0
- package/dist/proving/versionedProving.js +103 -0
- package/dist/signatures/index.d.ts +2 -0
- package/dist/signatures/index.js +14 -0
- package/dist/signatures/signing.d.ts +37 -0
- package/dist/signatures/signing.js +13 -0
- package/dist/signatures/v8Signatures.d.ts +54 -0
- package/dist/signatures/v8Signatures.js +62 -0
- package/dist/simulation/ForkSimulator.d.ts +114 -0
- package/dist/simulation/ForkSimulator.js +209 -0
- package/dist/simulation/LedgerTranslation.d.ts +53 -0
- package/dist/simulation/LedgerTranslation.js +56 -0
- package/dist/simulation/core/VersionTimeline.d.ts +54 -0
- package/dist/simulation/core/VersionTimeline.js +56 -0
- package/dist/simulation/core/blocks.d.ts +38 -0
- package/dist/simulation/core/blocks.js +52 -0
- package/dist/simulation/core/index.d.ts +13 -0
- package/dist/simulation/core/index.js +25 -0
- package/dist/simulation/core/strictness.d.ts +29 -0
- package/dist/simulation/core/strictness.js +39 -0
- package/dist/simulation/index.d.ts +22 -2
- package/dist/simulation/index.js +25 -14
- package/dist/simulation/v8/Simulator.d.ts +231 -0
- package/dist/simulation/v8/Simulator.js +503 -0
- package/dist/simulation/{SimulatorState.d.ts → v8/SimulatorState.d.ts} +31 -44
- package/dist/simulation/v8/SimulatorState.js +290 -0
- package/dist/simulation/v8/index.d.ts +2 -0
- package/dist/simulation/v8/index.js +26 -0
- package/dist/simulation/{Simulator.d.ts → v9/Simulator.d.ts} +57 -6
- package/dist/simulation/{Simulator.js → v9/Simulator.js} +68 -18
- package/dist/simulation/v9/SimulatorState.d.ts +336 -0
- package/dist/simulation/{SimulatorState.js → v9/SimulatorState.js} +34 -67
- package/dist/simulation/v9/index.d.ts +2 -0
- package/dist/simulation/v9/index.js +26 -0
- package/dist/submission/submissionService.d.ts +2 -1
- package/dist/submission/submissionService.js +1 -1
- package/dist/validation/blockData.d.ts +42 -2
- package/dist/validation/blockData.js +55 -8
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.js +2 -0
- package/dist/validation/v8ValidationService.d.ts +29 -0
- package/dist/validation/v8ValidationService.js +48 -0
- package/dist/validation/validationService.d.ts +132 -17
- package/dist/validation/validationService.js +88 -42
- package/dist/validation/versionedValidation.d.ts +46 -0
- package/dist/validation/versionedValidation.js +55 -0
- package/package.json +23 -9
|
@@ -12,16 +12,76 @@
|
|
|
12
12
|
* See the License for the specific language governing permissions and
|
|
13
13
|
* limitations under the License.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
16
|
+
import { Buffer } from 'buffer';
|
|
17
|
+
import { Array as Arr, Either, Option, Order, ParseResult, pipe, Schema } from 'effect';
|
|
18
|
+
/** Registers one trait for every protocol version: the shape a wallet that speaks a single ledger version has. */
|
|
19
|
+
export const singleTrait = (trait) => ({
|
|
20
|
+
entries: [
|
|
21
|
+
{
|
|
22
|
+
range: ProtocolVersion.makeRange(ProtocolVersion.MinSupportedVersion, ProtocolVersion.MaxSupportedVersion),
|
|
23
|
+
value: trait,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
/** The oldest registered trait: what an envelope that carries no version stamp is read with. */
|
|
28
|
+
export const headTrait = (traits) => Option.fromNullable(traits.entries.at(0)).pipe(Option.map((entry) => entry.value));
|
|
29
|
+
/**
|
|
30
|
+
* The trait registered for a protocol version, or the oldest trait when nothing says which version applies.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Falling back to the oldest trait is the same convention snapshot restore uses for an envelope written before versions
|
|
34
|
+
* were recorded: the only envelopes without a stamp are ones written before stamping existed, and those necessarily
|
|
35
|
+
* predate every version boundary since.
|
|
36
|
+
* @param traits The registered traits.
|
|
37
|
+
* @param protocolVersion The version the transaction was authored for, when one is known.
|
|
38
|
+
* @returns The trait to read with, or `Option.none()` when the version falls outside every registered range.
|
|
39
|
+
*/
|
|
40
|
+
export const traitForVersion = (traits, protocolVersion) => Option.match(protocolVersion, {
|
|
41
|
+
onNone: () => headTrait(traits),
|
|
42
|
+
onSome: (version) => ProtocolVersion.select(traits, version),
|
|
43
|
+
});
|
|
44
|
+
/** The registered trait that recognises a transaction object as its own, if any does. */
|
|
45
|
+
export const recognisingTrait = (traits, tx) => Option.fromNullable(traits.entries.find((entry) => entry.value.isTx(tx))).pipe(Option.map((entry) => entry.value));
|
|
46
|
+
/**
|
|
47
|
+
* The trait to read an incoming transaction with: the one that recognises it, or the oldest as a last resort.
|
|
48
|
+
*
|
|
49
|
+
* @param traits The registered traits.
|
|
50
|
+
* @param tx The transaction to place.
|
|
51
|
+
* @returns The trait that owns `tx`, the oldest trait when none claims it, or `Option.none()` when nothing is
|
|
52
|
+
* registered at all.
|
|
53
|
+
*/
|
|
54
|
+
export const traitForTx = (traits, tx) => Option.orElse(recognisingTrait(traits, tx), () => headTrait(traits));
|
|
55
|
+
const traitOfItem = (traits, item) => traitForVersion(traits, item.protocolVersion);
|
|
56
|
+
/** Two versions are in the same epoch when the registry answers them with the same trait. */
|
|
57
|
+
const sameEpoch = (a, b) => Option.getEquivalence((left, right) => left === right)(a, b);
|
|
58
|
+
const idsOf = (traits, tx) => traitForTx(traits, tx).pipe(Option.map((trait) => trait.ids(tx)), Option.getOrElse(() => []));
|
|
59
|
+
/** A stored item's identifiers, read with the trait registered for the version it was stamped with. */
|
|
60
|
+
const idsOfItem = (traits, item) => traitOfItem(traits, item).pipe(Option.map((trait) => trait.ids(item.tx)), Option.getOrElse(() => []));
|
|
61
|
+
/** Whether a stored item is covered by the identifiers of an incoming transaction, each read with its own trait. */
|
|
62
|
+
const isCoveredBy = (traits, item, txIds) => traitOfItem(traits, item).pipe(Option.map((trait) => trait.areAllTxIdsIncluded(item.tx, txIds)), Option.getOrElse(() => false));
|
|
63
|
+
export const has = (transactions, transaction, traits) => traitForTx(traits, transaction).pipe(Option.map((trait) => transactions.all.some((item) => trait.areAllTxIdsIncluded(transaction, idsOfItem(traits, item)))), Option.getOrElse(() => false));
|
|
19
64
|
export const all = (transactions) => {
|
|
20
65
|
return transactions.all.map((item) => item.tx);
|
|
21
66
|
};
|
|
67
|
+
const resultOf = (item) => 'result' in item ? Option.fromNullable(item.result) : Option.none();
|
|
22
68
|
export const allFailed = (transactions) => {
|
|
23
69
|
return transactions.all.filter((item) => 'result' in item && (item.result?.status === 'FAILURE' || item.result?.status === 'PARTIAL_SUCCESS'));
|
|
24
70
|
};
|
|
71
|
+
/** Transactions a protocol upgrade stranded: they were authored for a version the chain has moved past. */
|
|
72
|
+
export const allOrphaned = (transactions) => {
|
|
73
|
+
return transactions.all.filter((item) => 'result' in item && item.result?.status === 'ORPHANED_BY_FORK');
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Everything the wallet has given up on, in the order it was added: reported failures and orphaned transactions alike.
|
|
77
|
+
* Both need the same treatment — unbook the coins, record the rejection — so both belong on one list.
|
|
78
|
+
*/
|
|
79
|
+
export const allRejected = (transactions) => {
|
|
80
|
+
return transactions.all.filter((item) => 'result' in item &&
|
|
81
|
+
(item.result?.status === 'FAILURE' ||
|
|
82
|
+
item.result?.status === 'PARTIAL_SUCCESS' ||
|
|
83
|
+
item.result?.status === 'ORPHANED_BY_FORK'));
|
|
84
|
+
};
|
|
25
85
|
export const allPending = (state) => {
|
|
26
86
|
return state.all.filter((item) => !('result' in item) || item.result === undefined);
|
|
27
87
|
};
|
|
@@ -30,47 +90,104 @@ export const empty = () => {
|
|
|
30
90
|
all: [],
|
|
31
91
|
};
|
|
32
92
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Records a transaction as pending, stamped with the protocol version it was authored for.
|
|
95
|
+
*
|
|
96
|
+
* @param state The pending transactions.
|
|
97
|
+
* @param tx The transaction to record.
|
|
98
|
+
* @param now The time it was authored.
|
|
99
|
+
* @param traits The traits pending transactions are read with.
|
|
100
|
+
* @param protocolVersion The version the chain had reached when it was authored, when the wallet knew one.
|
|
101
|
+
* @returns The pending transactions including `tx`, with any superseded entry from the same version epoch replaced.
|
|
102
|
+
*/
|
|
103
|
+
export const addPendingTransaction = (state, tx, now, traits, protocolVersion) => {
|
|
104
|
+
const trait = traitForVersion(traits, protocolVersion);
|
|
105
|
+
const item = { tx, creationTime: now, protocolVersion };
|
|
106
|
+
// Whether one transaction supersedes another is a question only a single ledger version can answer — across a
|
|
107
|
+
// version boundary the two were authored against different rules, so they are simply different transactions.
|
|
108
|
+
const [rest, foundMatching] = pipe(state.all, Arr.partition((existing) => sameEpoch(traitOfItem(traits, existing), trait) &&
|
|
109
|
+
Option.match(trait, {
|
|
110
|
+
onNone: () => false,
|
|
111
|
+
onSome: (found) => found.isOneIncludedInOther(tx, existing.tx),
|
|
112
|
+
})));
|
|
113
|
+
const idCount = (candidate) => Option.match(trait, { onNone: () => 0, onSome: (found) => found.ids(candidate.tx).length });
|
|
114
|
+
const theBiggestMatchingTx = Arr.max(Arr.append(foundMatching, item), pipe(Order.number, Order.mapInput(idCount)));
|
|
37
115
|
return {
|
|
38
116
|
...state,
|
|
39
117
|
all: Arr.append(rest, theBiggestMatchingTx),
|
|
40
118
|
};
|
|
41
119
|
};
|
|
42
|
-
export const clear = (state, tx,
|
|
120
|
+
export const clear = (state, tx, traits) => {
|
|
121
|
+
const txIds = idsOf(traits, tx);
|
|
43
122
|
return {
|
|
44
123
|
...state,
|
|
45
|
-
all: Arr.filter(state.all, (item) => !
|
|
124
|
+
all: Arr.filter(state.all, (item) => !isCoveredBy(traits, item, txIds)),
|
|
46
125
|
};
|
|
47
126
|
};
|
|
48
|
-
export const saveResult = (state, tx, result,
|
|
127
|
+
export const saveResult = (state, tx, result, traits) => {
|
|
128
|
+
const txIds = idsOf(traits, tx);
|
|
49
129
|
return {
|
|
50
130
|
...state,
|
|
51
|
-
all: Arr.map(state.all, (item) => {
|
|
52
|
-
return txTrait.areAllTxIdsIncluded(item.tx, txTrait.ids(tx)) ? { ...item, result } : item;
|
|
53
|
-
}),
|
|
131
|
+
all: Arr.map(state.all, (item) => (isCoveredBy(traits, item, txIds) ? { ...item, result } : item)),
|
|
54
132
|
};
|
|
55
133
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Gives up on every unresolved transaction whose version epoch the chain has moved past.
|
|
136
|
+
*
|
|
137
|
+
* @remarks
|
|
138
|
+
* The epochs are the registry's own ranges, so "the chain has moved past" means exactly "a different trait answers for
|
|
139
|
+
* the chain now than answered for this transaction". A transaction authored under the previous protocol version can
|
|
140
|
+
* never be included afterwards, so waiting for its TTL only delays the inevitable and holds its coins hostage
|
|
141
|
+
* meanwhile.
|
|
142
|
+
* @param state The pending transactions.
|
|
143
|
+
* @param traits The traits pending transactions are read with, whose ranges define the epochs.
|
|
144
|
+
* @param chainNow The protocol version the wallets have reached.
|
|
145
|
+
* @returns The pending transactions with the stranded ones given an {@link OrphanedByForkResult}.
|
|
146
|
+
*/
|
|
147
|
+
export const orphanBeyond = (state, traits, chainNow) => ({
|
|
148
|
+
...state,
|
|
149
|
+
all: Arr.map(state.all, (item) => Option.isSome(resultOf(item))
|
|
150
|
+
? item
|
|
151
|
+
: Option.match(item.protocolVersion, {
|
|
152
|
+
onNone: () => item,
|
|
153
|
+
onSome: (authoredFor) => hasEpochEnded(traits, authoredFor, chainNow)
|
|
154
|
+
? { ...item, result: { status: 'ORPHANED_BY_FORK', authoredFor, chainNow } }
|
|
155
|
+
: item,
|
|
156
|
+
})),
|
|
157
|
+
});
|
|
158
|
+
const hasEpochEnded = (traits, authoredFor, chainNow) => ProtocolVersion.selectEntry(traits, authoredFor).pipe(Option.map((entry) => chainNow >= entry.range[1]), Option.getOrElse(() => false));
|
|
159
|
+
const WireItemSchema = Schema.Struct({
|
|
160
|
+
tx: Schema.String,
|
|
161
|
+
creationTime: Schema.DateTimeUtc,
|
|
162
|
+
// Additive: envelopes written before transactions were stamped simply omit it, and decode to `Option.none()`.
|
|
163
|
+
protocolVersion: Schema.optionalWith(ProtocolVersion.ProtocolVersionSchema, { as: 'Option' }),
|
|
164
|
+
});
|
|
165
|
+
const PendingItemSchema = (traits) => {
|
|
166
|
+
const TxSchema = Schema.declare((tx) => Option.isSome(recognisingTrait(traits, tx)));
|
|
167
|
+
const DomainSchema = Schema.typeSchema(Schema.Struct({
|
|
168
|
+
tx: TxSchema,
|
|
64
169
|
creationTime: Schema.DateTimeUtc,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
170
|
+
protocolVersion: Schema.OptionFromSelf(ProtocolVersion.ProtocolVersionSchema),
|
|
171
|
+
}));
|
|
172
|
+
return Schema.asSchema(Schema.transformOrFail(WireItemSchema, DomainSchema, {
|
|
173
|
+
strict: true,
|
|
174
|
+
decode: (wire, _options, ast) => pipe(traitForVersion(traits, wire.protocolVersion), Either.fromOption(() => new ParseResult.Type(ast, wire, `No transaction trait is registered for protocol version ${Option.getOrElse(wire.protocolVersion, () => 'unknown')}.`)), Either.flatMap((trait) => Either.try({
|
|
175
|
+
try: () => trait.deserialize(Buffer.from(wire.tx, 'hex')),
|
|
176
|
+
catch: (cause) => new ParseResult.Type(ast, wire, `Could not read the pending transaction: ${String(cause)}`),
|
|
177
|
+
})), Either.map((tx) => ({ tx, creationTime: wire.creationTime, protocolVersion: wire.protocolVersion }))),
|
|
178
|
+
encode: (item, _options, ast) => pipe(traitForTx(traits, item.tx), Either.fromOption(() => new ParseResult.Type(ast, item, 'No transaction trait is registered.')), Either.flatMap((trait) => Either.try({
|
|
179
|
+
try: () => Buffer.from(trait.serialize(item.tx)).toString('hex'),
|
|
180
|
+
catch: (cause) => new ParseResult.Type(ast, item, `Could not write the pending transaction: ${String(cause)}`),
|
|
181
|
+
})), Either.map((tx) => ({ tx, creationTime: item.creationTime, protocolVersion: item.protocolVersion }))),
|
|
182
|
+
}));
|
|
70
183
|
};
|
|
71
|
-
export const
|
|
72
|
-
|
|
73
|
-
|
|
184
|
+
export const SerializedSchema = (traits) => Schema.Struct({
|
|
185
|
+
version: Schema.Literal('v1'),
|
|
186
|
+
transactions: Schema.Array(PendingItemSchema(traits)),
|
|
187
|
+
});
|
|
188
|
+
export const serialize = (state, traits) => pipe(state, toSerialized, Schema.encodeSync(SerializedSchema(traits)), JSON.stringify);
|
|
189
|
+
export const deserialize = (serialized, traits) => {
|
|
190
|
+
return pipe(serialized, Schema.decodeUnknownEither(Schema.parseJson(SerializedSchema(traits))), Either.map((data) => fromSerialized(data)));
|
|
74
191
|
};
|
|
75
192
|
export const toSerialized = (pendingTransactions) => {
|
|
76
193
|
return {
|
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import { type Clock, Effect, type ParseResult, Scope, Stream } from 'effect';
|
|
1
|
+
import { type Clock, Effect, Option, type ParseResult, Scope, Stream } from 'effect';
|
|
2
2
|
import * as PendingTransactions from './pendingTransactions.js';
|
|
3
|
+
import { type ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
3
4
|
import type * as rx from 'rxjs';
|
|
4
5
|
import { type QueryClient } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
|
|
5
6
|
export type PendingTransactionsService<TTransaction> = {
|
|
6
7
|
start: () => Promise<void>;
|
|
7
8
|
stop: () => Promise<void>;
|
|
8
9
|
state: () => rx.Observable<PendingTransactions.PendingTransactions<TTransaction>>;
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Records a transaction as pending, stamped with the protocol version the chain had reached when it was authored.
|
|
12
|
+
*
|
|
13
|
+
* @param tx The transaction.
|
|
14
|
+
* @param protocolVersion The observed chain version, or `Option.none()` when no wallet has reported one yet.
|
|
15
|
+
*/
|
|
16
|
+
addPendingTransaction: (tx: TTransaction, protocolVersion: Option.Option<ProtocolVersion.ProtocolVersion>) => Promise<void>;
|
|
10
17
|
clear: (tx: TTransaction) => Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Gives up on every pending transaction whose protocol version epoch the chain has moved past.
|
|
20
|
+
*
|
|
21
|
+
* @param chainNow The protocol version the wallets have reached.
|
|
22
|
+
*/
|
|
23
|
+
orphanBeyond: (chainNow: ProtocolVersion.ProtocolVersion) => Promise<void>;
|
|
11
24
|
};
|
|
12
25
|
export type IndexerClientConnection = {
|
|
13
26
|
indexerHttpUrl: string;
|
|
@@ -17,18 +30,19 @@ export type DefaultPendingTransactionsServiceConfiguration = {
|
|
|
17
30
|
indexerClientConnection: IndexerClientConnection;
|
|
18
31
|
};
|
|
19
32
|
export type InitParams<TTransaction> = {
|
|
20
|
-
|
|
33
|
+
txTraits: PendingTransactions.VersionedTransactionTrait<TTransaction>;
|
|
21
34
|
initialState?: PendingTransactions.PendingTransactions<TTransaction>;
|
|
22
35
|
configuration: DefaultPendingTransactionsServiceConfiguration;
|
|
23
36
|
};
|
|
24
37
|
export declare class PendingTransactionsServiceImpl<TTransaction> implements PendingTransactionsService<TTransaction> {
|
|
25
38
|
#private;
|
|
26
39
|
static init<TTransaction>(initParams: InitParams<TTransaction>): Promise<PendingTransactionsServiceImpl<TTransaction>>;
|
|
27
|
-
static restore<TTransaction>(data: string,
|
|
40
|
+
static restore<TTransaction>(data: string, txTraits: PendingTransactions.VersionedTransactionTrait<TTransaction>, configuration: DefaultPendingTransactionsServiceConfiguration): Promise<PendingTransactionsServiceImpl<TTransaction>>;
|
|
28
41
|
private static initEffect;
|
|
29
42
|
constructor(effectService: PendingTransactionsServiceEffect<TTransaction>, scope: Scope.CloseableScope, configuration: DefaultPendingTransactionsServiceConfiguration);
|
|
30
|
-
addPendingTransaction(tx: TTransaction): Promise<void>;
|
|
43
|
+
addPendingTransaction(tx: TTransaction, protocolVersion: Option.Option<ProtocolVersion.ProtocolVersion>): Promise<void>;
|
|
31
44
|
clear(tx: TTransaction): Promise<void>;
|
|
45
|
+
orphanBeyond(chainNow: ProtocolVersion.ProtocolVersion): Promise<void>;
|
|
32
46
|
start(): Promise<void>;
|
|
33
47
|
state(): rx.Observable<PendingTransactions.PendingTransactions<TTransaction>>;
|
|
34
48
|
stop(): Promise<void>;
|
|
@@ -36,17 +50,19 @@ export declare class PendingTransactionsServiceImpl<TTransaction> implements Pen
|
|
|
36
50
|
export type PendingTransactionsServiceEffect<TTransaction> = {
|
|
37
51
|
startPolling: (ticks: Stream.Stream<unknown>) => Effect.Effect<void, Error, QueryClient | Scope.Scope | Clock.Clock>;
|
|
38
52
|
state: () => Stream.Stream<PendingTransactions.PendingTransactions<TTransaction>>;
|
|
39
|
-
addPendingTransaction: (tx: TTransaction) => Effect.Effect<void, never, never>;
|
|
53
|
+
addPendingTransaction: (tx: TTransaction, protocolVersion: Option.Option<ProtocolVersion.ProtocolVersion>) => Effect.Effect<void, never, never>;
|
|
40
54
|
clear: (tx: TTransaction) => Effect.Effect<void, never, never>;
|
|
55
|
+
orphanBeyond: (chainNow: ProtocolVersion.ProtocolVersion) => Effect.Effect<void, never, never>;
|
|
41
56
|
};
|
|
42
57
|
export declare class PendingTransactionsServiceEffectImpl<TTransaction> implements PendingTransactionsServiceEffect<TTransaction> {
|
|
43
58
|
#private;
|
|
44
|
-
static restore<TTransaction>(data: string,
|
|
45
|
-
constructor(
|
|
59
|
+
static restore<TTransaction>(data: string, txTraits: PendingTransactions.VersionedTransactionTrait<TTransaction>): Effect.Effect<PendingTransactionsServiceEffectImpl<TTransaction>, ParseResult.ParseError>;
|
|
60
|
+
constructor(txTraits: PendingTransactions.VersionedTransactionTrait<TTransaction>, initialState?: PendingTransactions.PendingTransactions<TTransaction>);
|
|
46
61
|
state(): Stream.Stream<PendingTransactions.PendingTransactions<TTransaction>>;
|
|
47
62
|
startPolling(ticks: Stream.Stream<unknown>): Effect.Effect<void, Error, QueryClient | Scope.Scope>;
|
|
48
|
-
addPendingTransaction(tx: TTransaction): Effect.Effect<void>;
|
|
63
|
+
addPendingTransaction(tx: TTransaction, protocolVersion: Option.Option<ProtocolVersion.ProtocolVersion>): Effect.Effect<void>;
|
|
49
64
|
clear(tx: TTransaction): Effect.Effect<void>;
|
|
65
|
+
orphanBeyond(chainNow: ProtocolVersion.ProtocolVersion): Effect.Effect<void>;
|
|
50
66
|
private saveResult;
|
|
51
67
|
private queryForStatus;
|
|
52
68
|
}
|
|
@@ -21,12 +21,12 @@ export class PendingTransactionsServiceImpl {
|
|
|
21
21
|
static init(initParams) {
|
|
22
22
|
return PendingTransactionsServiceImpl.initEffect(initParams).pipe(Effect.runPromise);
|
|
23
23
|
}
|
|
24
|
-
static restore(data,
|
|
25
|
-
return pipe(PendingTransactions.deserialize(data,
|
|
24
|
+
static restore(data, txTraits, configuration) {
|
|
25
|
+
return pipe(PendingTransactions.deserialize(data, txTraits), EitherOps.toEffect, Effect.andThen((state) => PendingTransactionsServiceImpl.initEffect({ txTraits, initialState: state, configuration })), Effect.runPromise);
|
|
26
26
|
}
|
|
27
27
|
static initEffect(initParams) {
|
|
28
28
|
return Effect.gen(function* () {
|
|
29
|
-
const service = new PendingTransactionsServiceEffectImpl(initParams.
|
|
29
|
+
const service = new PendingTransactionsServiceEffectImpl(initParams.txTraits, initParams.initialState);
|
|
30
30
|
const scope = yield* Scope.make();
|
|
31
31
|
return new PendingTransactionsServiceImpl(service, scope, initParams.configuration);
|
|
32
32
|
});
|
|
@@ -39,12 +39,15 @@ export class PendingTransactionsServiceImpl {
|
|
|
39
39
|
this.#scope = scope;
|
|
40
40
|
this.#configuration = configuration;
|
|
41
41
|
}
|
|
42
|
-
addPendingTransaction(tx) {
|
|
43
|
-
return this.#effectService.addPendingTransaction(tx).pipe(Effect.runPromise);
|
|
42
|
+
addPendingTransaction(tx, protocolVersion) {
|
|
43
|
+
return this.#effectService.addPendingTransaction(tx, protocolVersion).pipe(Effect.runPromise);
|
|
44
44
|
}
|
|
45
45
|
clear(tx) {
|
|
46
46
|
return this.#effectService.clear(tx).pipe(Effect.runPromise);
|
|
47
47
|
}
|
|
48
|
+
orphanBeyond(chainNow) {
|
|
49
|
+
return this.#effectService.orphanBeyond(chainNow).pipe(Effect.runPromise);
|
|
50
|
+
}
|
|
48
51
|
start() {
|
|
49
52
|
return this.#effectService.startPolling(Stream.tick(Duration.seconds(1))).pipe(Effect.provide(HttpQueryClient.layer({
|
|
50
53
|
url: this.#configuration.indexerClientConnection.indexerHttpUrl,
|
|
@@ -59,12 +62,12 @@ export class PendingTransactionsServiceImpl {
|
|
|
59
62
|
}
|
|
60
63
|
export class PendingTransactionsServiceEffectImpl {
|
|
61
64
|
#state;
|
|
62
|
-
#
|
|
63
|
-
static restore(data,
|
|
64
|
-
return pipe(data, (data) => PendingTransactions.deserialize(data,
|
|
65
|
+
#txTraits;
|
|
66
|
+
static restore(data, txTraits) {
|
|
67
|
+
return pipe(data, (data) => PendingTransactions.deserialize(data, txTraits), EitherOps.toEffect, Effect.map((state) => new PendingTransactionsServiceEffectImpl(txTraits, state)));
|
|
65
68
|
}
|
|
66
|
-
constructor(
|
|
67
|
-
this.#
|
|
69
|
+
constructor(txTraits, initialState) {
|
|
70
|
+
this.#txTraits = txTraits;
|
|
68
71
|
this.#state = SubscriptionRef.make(initialState ?? PendingTransactions.empty()).pipe(Effect.runSync); // Should not be here, but otherwise initialization would be too involved
|
|
69
72
|
}
|
|
70
73
|
state() {
|
|
@@ -74,7 +77,10 @@ export class PendingTransactionsServiceEffectImpl {
|
|
|
74
77
|
return ticks.pipe(Stream.mapEffect(() => SubscriptionRef.get(this.#state)), Stream.mapConcat(PendingTransactions.allPending), Stream.mapConcatEffect((item) => {
|
|
75
78
|
return Effect.gen(this, function* () {
|
|
76
79
|
const now = yield* DateTime.now;
|
|
77
|
-
const
|
|
80
|
+
const trait = PendingTransactions.traitForVersion(this.#txTraits, item.protocolVersion);
|
|
81
|
+
if (Option.isNone(trait))
|
|
82
|
+
return [];
|
|
83
|
+
const result = yield* this.queryForStatus(item.tx, trait.value);
|
|
78
84
|
return Option.match(result, {
|
|
79
85
|
onSome: (status) => [{ ...item, result: status }],
|
|
80
86
|
onNone: () => {
|
|
@@ -82,7 +88,7 @@ export class PendingTransactionsServiceEffectImpl {
|
|
|
82
88
|
status: 'FAILURE',
|
|
83
89
|
segments: [],
|
|
84
90
|
};
|
|
85
|
-
return
|
|
91
|
+
return trait.value.hasTTLExpired(item.tx, item.creationTime, now)
|
|
86
92
|
? [{ ...item, result: failedResult }]
|
|
87
93
|
: [];
|
|
88
94
|
},
|
|
@@ -94,14 +100,19 @@ export class PendingTransactionsServiceEffectImpl {
|
|
|
94
100
|
return this.saveResult(item.tx, item.result);
|
|
95
101
|
}));
|
|
96
102
|
}
|
|
97
|
-
addPendingTransaction(tx) {
|
|
103
|
+
addPendingTransaction(tx, protocolVersion) {
|
|
98
104
|
return SubscriptionRef.updateEffect(this.#state, (state) => {
|
|
99
|
-
return DateTime.now.pipe(Effect.andThen((now) => PendingTransactions.addPendingTransaction(state, tx, now, this.#
|
|
105
|
+
return DateTime.now.pipe(Effect.andThen((now) => PendingTransactions.addPendingTransaction(state, tx, now, this.#txTraits, protocolVersion)));
|
|
100
106
|
});
|
|
101
107
|
}
|
|
102
108
|
clear(tx) {
|
|
103
109
|
return SubscriptionRef.update(this.#state, (state) => {
|
|
104
|
-
return PendingTransactions.clear(state, tx, this.#
|
|
110
|
+
return PendingTransactions.clear(state, tx, this.#txTraits);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
orphanBeyond(chainNow) {
|
|
114
|
+
return SubscriptionRef.update(this.#state, (state) => {
|
|
115
|
+
return PendingTransactions.orphanBeyond(state, this.#txTraits, chainNow);
|
|
105
116
|
});
|
|
106
117
|
}
|
|
107
118
|
saveResult(tx, result) {
|
|
@@ -110,20 +121,21 @@ export class PendingTransactionsServiceEffectImpl {
|
|
|
110
121
|
return this.clear(tx);
|
|
111
122
|
case 'FAILURE':
|
|
112
123
|
case 'PARTIAL_SUCCESS':
|
|
124
|
+
case 'ORPHANED_BY_FORK':
|
|
113
125
|
return SubscriptionRef.update(this.#state, (state) => {
|
|
114
|
-
return PendingTransactions.saveResult(state, tx, result, this.#
|
|
126
|
+
return PendingTransactions.saveResult(state, tx, result, this.#txTraits);
|
|
115
127
|
});
|
|
116
128
|
}
|
|
117
129
|
}
|
|
118
|
-
queryForStatus(tx) {
|
|
119
|
-
return Effect.gen(
|
|
130
|
+
queryForStatus(tx, txTrait) {
|
|
131
|
+
return Effect.gen(function* () {
|
|
120
132
|
const statusQuery = yield* TransactionStatus;
|
|
121
|
-
const result = yield* statusQuery({ transactionId:
|
|
133
|
+
const result = yield* statusQuery({ transactionId: txTrait.firstId(tx) }).pipe(Effect.catchAll((error) => {
|
|
122
134
|
const fallback = { transactions: [] };
|
|
123
135
|
return pipe(Effect.logWarning(error, 'Observed error in PendingTransactionsService, retrying'), Effect.as(fallback));
|
|
124
136
|
}));
|
|
125
137
|
return pipe(result.transactions, Iterable.filterMap((res) => {
|
|
126
|
-
if (res.__typename
|
|
138
|
+
if (res.__typename !== 'RegularTransaction') {
|
|
127
139
|
return Option.none();
|
|
128
140
|
}
|
|
129
141
|
if (res.transactionResult.status != 'SUCCESS' &&
|
|
@@ -131,7 +143,7 @@ export class PendingTransactionsServiceEffectImpl {
|
|
|
131
143
|
res.transactionResult.status != 'PARTIAL_SUCCESS') {
|
|
132
144
|
return Option.none();
|
|
133
145
|
}
|
|
134
|
-
if (
|
|
146
|
+
if (txTrait.areAllTxIdsIncluded(tx, res.identifiers)) {
|
|
135
147
|
return Option.some({
|
|
136
148
|
status: res.transactionResult.status,
|
|
137
149
|
segments: res.transactionResult.segments ?? [],
|
package/dist/proving/index.d.ts
CHANGED
package/dist/proving/index.js
CHANGED