@xyo-network/chain-services 1.5.11 → 1.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/neutral/index.mjs +96 -59
- package/dist/neutral/index.mjs.map +1 -1
- package/dist/types/BlockProducer/XyoBlockProducer.d.ts +2 -2
- package/dist/types/BlockProducer/XyoBlockProducer.d.ts.map +1 -1
- package/dist/types/ChainValidator/XyoValidator.d.ts +2 -2
- package/dist/types/ChainValidator/XyoValidator.d.ts.map +1 -1
- package/dist/types/PendingTransactions/PendingTransactions.d.ts +9 -8
- package/dist/types/PendingTransactions/PendingTransactions.d.ts.map +1 -1
- package/package.json +38 -38
- package/src/BlockProducer/XyoBlockProducer.ts +4 -4
- package/src/ChainValidator/XyoValidator.ts +3 -3
- package/src/PendingTransactions/PendingTransactions.ts +104 -57
|
@@ -9,7 +9,7 @@ export interface XyoValidatorParams extends BaseServiceParams {
|
|
|
9
9
|
chainArchivist: ReadArchivist;
|
|
10
10
|
chainInformation: ChainInformation;
|
|
11
11
|
electionService: ElectionService;
|
|
12
|
-
|
|
12
|
+
pendingBundledTransactionsArchivist: ArchivistInstance;
|
|
13
13
|
rewardService: BlockRewardService;
|
|
14
14
|
stakeIntentService: StakeIntentService;
|
|
15
15
|
validateHydratedBlockState: HydratedBlockStateValidationFunction;
|
|
@@ -20,7 +20,7 @@ export declare class XyoValidator<TParams extends XyoValidatorParams = XyoValida
|
|
|
20
20
|
protected get chainArchivist(): TParams["chainArchivist"];
|
|
21
21
|
protected get chainInfo(): TParams["chainInformation"];
|
|
22
22
|
protected get electionService(): TParams["electionService"];
|
|
23
|
-
protected get
|
|
23
|
+
protected get pendingBundledTransactionsArchivist(): TParams["pendingBundledTransactionsArchivist"];
|
|
24
24
|
protected get rewardService(): TParams["rewardService"];
|
|
25
25
|
validatePendingBlock(_block: BlockBoundWitness): Promisable<Error[]>;
|
|
26
26
|
validatePendingTransaction(hydratedTransaction: HydratedTransaction): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XyoValidator.d.ts","sourceRoot":"","sources":["../../../src/ChainValidator/XyoValidator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE/E,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oCAAoC,EACpC,mBAAmB,EAEnB,kBAAkB,EACnB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAM5C,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,OAAO,EAAE,eAAe,CAAA;IACxB,cAAc,EAAE,aAAa,CAAA;IAC7B,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,eAAe,EAAE,eAAe,CAAA;IAChC,
|
|
1
|
+
{"version":3,"file":"XyoValidator.d.ts","sourceRoot":"","sources":["../../../src/ChainValidator/XyoValidator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE/E,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oCAAoC,EACpC,mBAAmB,EAEnB,kBAAkB,EACnB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAM5C,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,OAAO,EAAE,eAAe,CAAA;IACxB,cAAc,EAAE,aAAa,CAAA;IAC7B,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,eAAe,EAAE,eAAe,CAAA;IAChC,mCAAmC,EAAE,iBAAiB,CAAA;IACtD,aAAa,EAAE,kBAAkB,CAAA;IACjC,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,0BAA0B,EAAE,oCAAoC,CAAA;CACjE;AAED,qBACa,YAAY,CAAC,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,CAAE,SAAQ,WAAW,CAAC,OAAO,CAAE,YAAW,SAAS;IAClI,IAAI,OAAO,sBAEV;IAED,SAAS,KAAK,OAAO,uBAEpB;IAED,SAAS,KAAK,cAAc,8BAE3B;IAED,SAAS,KAAK,SAAS,gCAEtB;IAED,SAAS,KAAK,eAAe,+BAE5B;IAED,SAAS,KAAK,mCAAmC,mDAEhD;IAED,SAAS,KAAK,aAAa,6BAE1B;IAED,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,UAAU,CAAC,KAAK,EAAE,CAAC;IAK9D,0BAA0B,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;CAQ7F"}
|
|
@@ -5,8 +5,8 @@ import { BaseService } from '../BaseService.ts';
|
|
|
5
5
|
export interface XyoPendingTransactionsServiceParams extends BaseServiceParams {
|
|
6
6
|
chainArchivist?: ArchivistInstance;
|
|
7
7
|
chainIdentification?: ChainIdentification;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
pendingBundledTransactionsArchivist?: ArchivistInstance;
|
|
9
|
+
rejectedBundledTransactionsArchivist?: ArchivistInstance;
|
|
10
10
|
}
|
|
11
11
|
export declare class XyoPendingTransactionsService extends BaseService<XyoPendingTransactionsServiceParams> implements PendingTransactionsService {
|
|
12
12
|
private static readonly MutexPriority;
|
|
@@ -19,11 +19,12 @@ export declare class XyoPendingTransactionsService extends BaseService<XyoPendin
|
|
|
19
19
|
* A local Archivist optimized for fast retrieval that stores only validated
|
|
20
20
|
* pending transactions
|
|
21
21
|
*/
|
|
22
|
-
private
|
|
22
|
+
private _curatedPendingBundledTransactionsArchivist;
|
|
23
23
|
/**
|
|
24
24
|
* The last count of total pending transactions
|
|
25
25
|
*/
|
|
26
26
|
private _pendingTransactionsCount;
|
|
27
|
+
private _removablePendingTransactionHashes;
|
|
27
28
|
/**
|
|
28
29
|
* A mutex to ensure that the curated pending transactions archivist is
|
|
29
30
|
* updated in a thread-safe manner
|
|
@@ -31,17 +32,17 @@ export declare class XyoPendingTransactionsService extends BaseService<XyoPendin
|
|
|
31
32
|
private _updateCuratedPendingTransactionsArchivistMutex;
|
|
32
33
|
private get chainArchivist();
|
|
33
34
|
private get chainIdentification();
|
|
34
|
-
private get
|
|
35
|
+
private get pendingBundledTransactionsArchivist();
|
|
36
|
+
private get pendingBundledTransactionsLocalArchivist();
|
|
35
37
|
private get pendingTransactionsCount();
|
|
36
|
-
private get
|
|
37
|
-
private get rejectedTransactionsArchivist();
|
|
38
|
+
private get rejectedBundledTransactionsArchivist();
|
|
38
39
|
createHandler(): Promise<void>;
|
|
39
40
|
getPendingTransactions(head: Hash, limit: number): Promise<HydratedTransactionWithStorageMeta[]>;
|
|
40
41
|
private countPendingTransactions;
|
|
41
42
|
private filterAlreadyFinalizedTransactions;
|
|
42
43
|
private insertNewTransactions;
|
|
44
|
+
private removeBundledTransactions;
|
|
43
45
|
private removeFinalizedTransactions;
|
|
44
|
-
private
|
|
45
|
-
private removeTransactions;
|
|
46
|
+
private removeRejectedBundledTransactions;
|
|
46
47
|
}
|
|
47
48
|
//# sourceMappingURL=PendingTransactions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PendingTransactions.d.ts","sourceRoot":"","sources":["../../../src/PendingTransactions/PendingTransactions.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAElC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"PendingTransactions.d.ts","sourceRoot":"","sources":["../../../src/PendingTransactions/PendingTransactions.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAElC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAQhE,OAAO,EAC6C,iBAAiB,EACnE,mBAAmB,EAAE,kCAAkC,EAEvD,0BAA0B,EAC3B,MAAM,2BAA2B,CAAA;AAIlC,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAA;AAEjE,MAAM,WAAW,mCAAoC,SAAQ,iBAAiB;IAC5E,cAAc,CAAC,EAAE,iBAAiB,CAAA;IAClC,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC,mCAAmC,CAAC,EAAE,iBAAiB,CAAA;IACvD,oCAAoC,CAAC,EAAE,iBAAiB,CAAA;CACzD;AAcD,qBACa,6BAA8B,SAAQ,WAAW,CAAC,mCAAmC,CAAE,YAAW,0BAA0B;IACvI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAiB3B;IAEV;;;OAGG;IACH,OAAO,CAAC,8BAA8B,CAAc;IAEpD;;;OAGG;IACH,OAAO,CAAC,2CAA2C,CAA6B;IAEhF;;OAEG;IACH,OAAO,CAAC,yBAAyB,CAAY;IAE7C,OAAO,CAAC,kCAAkC,CAAuB;IAEjE;;;OAGG;IACH,OAAO,CAAC,+CAA+C,CAAc;IAErE,OAAO,KAAK,cAAc,GAEzB;IAED,OAAO,KAAK,mBAAmB,GAE9B;IAED,OAAO,KAAK,mCAAmC,GAE9C;IAED,OAAO,KAAK,wCAAwC,GAEnD;IAED,OAAO,KAAK,wBAAwB,GAGnC;IAED,OAAO,KAAK,oCAAoC,GAE/C;IAEc,aAAa;IA+BtB,sBAAsB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kCAAkC,EAAE,CAAC;YAgCxF,wBAAwB;YAQxB,kCAAkC;YAUlC,qBAAqB;YA8BrB,yBAAyB;IAgBvC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,iCAAiC;CAM1C"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@xyo-network/chain-services",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.13",
|
|
5
5
|
"description": "XYO Layer One SDK Services",
|
|
6
6
|
"homepage": "https://xylabs.com",
|
|
7
7
|
"bugs": {
|
|
@@ -35,35 +35,35 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@opentelemetry/api": "^1.9.0",
|
|
38
|
-
"@xylabs/array": "^4.11.
|
|
39
|
-
"@xylabs/assert": "^4.11.
|
|
40
|
-
"@xylabs/base": "^4.11.
|
|
41
|
-
"@xylabs/decimal-precision": "^4.11.
|
|
42
|
-
"@xylabs/events": "^4.11.
|
|
43
|
-
"@xylabs/exists": "^4.11.
|
|
44
|
-
"@xylabs/forget": "^4.11.
|
|
45
|
-
"@xylabs/hex": "^4.11.
|
|
46
|
-
"@xylabs/promise": "^4.11.
|
|
47
|
-
"@xylabs/telemetry": "^4.11.
|
|
48
|
-
"@xylabs/typeof": "^4.11.
|
|
49
|
-
"@xyo-network/account-model": "^3.18.
|
|
50
|
-
"@xyo-network/archivist-memory": "^3.18.
|
|
51
|
-
"@xyo-network/archivist-model": "^3.18.
|
|
52
|
-
"@xyo-network/boundwitness-model": "^3.18.
|
|
53
|
-
"@xyo-network/boundwitness-validator": "^3.18.
|
|
54
|
-
"@xyo-network/boundwitness-wrapper": "^3.18.
|
|
55
|
-
"@xyo-network/chain-analyze": "^1.5.
|
|
56
|
-
"@xyo-network/chain-ethereum": "^1.5.
|
|
57
|
-
"@xyo-network/chain-modules": "^1.5.
|
|
58
|
-
"@xyo-network/chain-protocol": "^1.5.
|
|
59
|
-
"@xyo-network/chain-utils": "^1.5.
|
|
60
|
-
"@xyo-network/chain-validation": "^1.5.
|
|
61
|
-
"@xyo-network/chain-wrappers": "^1.5.
|
|
62
|
-
"@xyo-network/payload-builder": "^3.18.
|
|
63
|
-
"@xyo-network/payload-model": "^3.18.
|
|
38
|
+
"@xylabs/array": "^4.11.11",
|
|
39
|
+
"@xylabs/assert": "^4.11.11",
|
|
40
|
+
"@xylabs/base": "^4.11.11",
|
|
41
|
+
"@xylabs/decimal-precision": "^4.11.11",
|
|
42
|
+
"@xylabs/events": "^4.11.11",
|
|
43
|
+
"@xylabs/exists": "^4.11.11",
|
|
44
|
+
"@xylabs/forget": "^4.11.11",
|
|
45
|
+
"@xylabs/hex": "^4.11.11",
|
|
46
|
+
"@xylabs/promise": "^4.11.11",
|
|
47
|
+
"@xylabs/telemetry": "^4.11.11",
|
|
48
|
+
"@xylabs/typeof": "^4.11.11",
|
|
49
|
+
"@xyo-network/account-model": "^3.18.2",
|
|
50
|
+
"@xyo-network/archivist-memory": "^3.18.2",
|
|
51
|
+
"@xyo-network/archivist-model": "^3.18.2",
|
|
52
|
+
"@xyo-network/boundwitness-model": "^3.18.2",
|
|
53
|
+
"@xyo-network/boundwitness-validator": "^3.18.2",
|
|
54
|
+
"@xyo-network/boundwitness-wrapper": "^3.18.2",
|
|
55
|
+
"@xyo-network/chain-analyze": "^1.5.13",
|
|
56
|
+
"@xyo-network/chain-ethereum": "^1.5.13",
|
|
57
|
+
"@xyo-network/chain-modules": "^1.5.13",
|
|
58
|
+
"@xyo-network/chain-protocol": "^1.5.13",
|
|
59
|
+
"@xyo-network/chain-utils": "^1.5.13",
|
|
60
|
+
"@xyo-network/chain-validation": "^1.5.13",
|
|
61
|
+
"@xyo-network/chain-wrappers": "^1.5.13",
|
|
62
|
+
"@xyo-network/payload-builder": "^3.18.2",
|
|
63
|
+
"@xyo-network/payload-model": "^3.18.2",
|
|
64
64
|
"@xyo-network/typechain": "^3.5.4",
|
|
65
65
|
"@xyo-network/xl1-protocol": "^1.4.16",
|
|
66
|
-
"@xyo-network/xl1-protocol-sdk": "^1.5.
|
|
66
|
+
"@xyo-network/xl1-protocol-sdk": "^1.5.13",
|
|
67
67
|
"async-mutex": "^0.5.0",
|
|
68
68
|
"ethers": "6.14.3",
|
|
69
69
|
"lru-cache": "^11.1.0",
|
|
@@ -71,17 +71,17 @@
|
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@types/node": "^22.15.29",
|
|
74
|
-
"@xylabs/delay": "^4.11.
|
|
75
|
-
"@xylabs/ts-scripts-yarn3": "^6.5.
|
|
76
|
-
"@xylabs/tsconfig": "^6.5.
|
|
77
|
-
"@xylabs/vitest-extended": "^4.11.
|
|
78
|
-
"@xyo-network/account": "^3.18.
|
|
79
|
-
"@xyo-network/account-model": "^3.18.
|
|
80
|
-
"@xyo-network/chain-validation": "^1.5.
|
|
81
|
-
"@xyo-network/wallet": "^3.18.
|
|
82
|
-
"knip": "^5.
|
|
74
|
+
"@xylabs/delay": "^4.11.11",
|
|
75
|
+
"@xylabs/ts-scripts-yarn3": "^6.5.8",
|
|
76
|
+
"@xylabs/tsconfig": "^6.5.8",
|
|
77
|
+
"@xylabs/vitest-extended": "^4.11.11",
|
|
78
|
+
"@xyo-network/account": "^3.18.2",
|
|
79
|
+
"@xyo-network/account-model": "^3.18.2",
|
|
80
|
+
"@xyo-network/chain-validation": "^1.5.13",
|
|
81
|
+
"@xyo-network/wallet": "^3.18.2",
|
|
82
|
+
"knip": "^5.60.0",
|
|
83
83
|
"typescript": "^5.8.3",
|
|
84
|
-
"vitest": "^3.2.
|
|
84
|
+
"vitest": "^3.2.1",
|
|
85
85
|
"vitest-mock-extended": "^3.1.0"
|
|
86
86
|
},
|
|
87
87
|
"engines": {
|
|
@@ -40,7 +40,7 @@ export const XYO_PRODUCER_RESTAKE_WINDOW = 500n
|
|
|
40
40
|
export interface XyoBlockProducerParams extends XyoValidatorParams {
|
|
41
41
|
balanceService: AccountBalanceService
|
|
42
42
|
pendingTransactionsService: PendingTransactionsService
|
|
43
|
-
|
|
43
|
+
rejectedBundledTransactionsArchivist: ArchivistInstance
|
|
44
44
|
rewardAddress: Address
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -80,8 +80,8 @@ export class XyoBlockProducer extends BaseService<XyoBlockProducerParams> implem
|
|
|
80
80
|
return assertEx(this.params.pendingTransactionsService, () => 'Missing pendingTransactionsService')
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
protected get
|
|
84
|
-
return assertEx(this.params.
|
|
83
|
+
protected get rejectedBundledTransactionsArchivist() {
|
|
84
|
+
return assertEx(this.params.rejectedBundledTransactionsArchivist, () => 'No rejected bundled transactions archivist')
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
protected get rewardAddress(): Address {
|
|
@@ -206,7 +206,7 @@ export class XyoBlockProducer extends BaseService<XyoBlockProducerParams> implem
|
|
|
206
206
|
if (errors.length > 0) {
|
|
207
207
|
this.logger?.warn(`Validation of produced block failed: ${errors.at(0)?.message}`)
|
|
208
208
|
const rejectedTransactions = block[1]
|
|
209
|
-
await this.
|
|
209
|
+
await this.rejectedBundledTransactionsArchivist.insert(rejectedTransactions)
|
|
210
210
|
} else {
|
|
211
211
|
return block
|
|
212
212
|
}
|
|
@@ -27,7 +27,7 @@ export interface XyoValidatorParams extends BaseServiceParams {
|
|
|
27
27
|
chainArchivist: ReadArchivist
|
|
28
28
|
chainInformation: ChainInformation
|
|
29
29
|
electionService: ElectionService
|
|
30
|
-
|
|
30
|
+
pendingBundledTransactionsArchivist: ArchivistInstance
|
|
31
31
|
rewardService: BlockRewardService
|
|
32
32
|
stakeIntentService: StakeIntentService
|
|
33
33
|
validateHydratedBlockState: HydratedBlockStateValidationFunction
|
|
@@ -55,8 +55,8 @@ export class XyoValidator<TParams extends XyoValidatorParams = XyoValidatorParam
|
|
|
55
55
|
return assertEx(this.params.electionService, () => 'electionService is required')
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
protected get
|
|
59
|
-
return assertEx(this.params.
|
|
58
|
+
protected get pendingBundledTransactionsArchivist() {
|
|
59
|
+
return assertEx(this.params.pendingBundledTransactionsArchivist, () => 'pendingBundledTransactions is required')
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
protected get rewardService() {
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { ValueType } from '@opentelemetry/api'
|
|
2
|
-
import {
|
|
2
|
+
import { filterAsync } from '@xylabs/array'
|
|
3
3
|
import { assertEx } from '@xylabs/assert'
|
|
4
4
|
import { exists } from '@xylabs/exists'
|
|
5
5
|
import { forget } from '@xylabs/forget'
|
|
6
6
|
import { Hash } from '@xylabs/hex'
|
|
7
7
|
import { MemoryArchivist } from '@xyo-network/archivist-memory'
|
|
8
8
|
import { ArchivistInstance } from '@xyo-network/archivist-model'
|
|
9
|
+
import { globalAttributes } from '@xyo-network/chain-telemetry'
|
|
9
10
|
import { validateTransaction } from '@xyo-network/chain-validation'
|
|
11
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
10
12
|
import {
|
|
11
|
-
Payload,
|
|
13
|
+
Payload,
|
|
14
|
+
PayloadBundle, Sequence, WithStorageMeta,
|
|
12
15
|
} from '@xyo-network/payload-model'
|
|
13
16
|
import {
|
|
14
|
-
asOptionalTransactionBoundWitnessWithStorageMeta, BaseServiceParams,
|
|
15
|
-
|
|
17
|
+
asOptionalTransactionBoundWitnessWithStorageMeta, BaseServiceParams,
|
|
18
|
+
ChainIdentification, HydratedTransactionWithStorageMeta,
|
|
19
|
+
isTransactionBoundWitnessWithStorageMeta,
|
|
20
|
+
PendingTransactionsService,
|
|
16
21
|
} from '@xyo-network/xl1-protocol'
|
|
17
|
-
import {
|
|
22
|
+
import { flattenHydratedTransaction } from '@xyo-network/xl1-protocol-sdk'
|
|
18
23
|
import { Mutex } from 'async-mutex'
|
|
19
24
|
|
|
20
25
|
import { BaseService, creatableService } from '../BaseService.ts'
|
|
@@ -22,10 +27,22 @@ import { BaseService, creatableService } from '../BaseService.ts'
|
|
|
22
27
|
export interface XyoPendingTransactionsServiceParams extends BaseServiceParams {
|
|
23
28
|
chainArchivist?: ArchivistInstance
|
|
24
29
|
chainIdentification?: ChainIdentification
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
pendingBundledTransactionsArchivist?: ArchivistInstance
|
|
31
|
+
rejectedBundledTransactionsArchivist?: ArchivistInstance
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
async function bundledPayloadsToHydratedTransaction(
|
|
35
|
+
payload: WithStorageMeta<PayloadBundle>,
|
|
36
|
+
): Promise<HydratedTransactionWithStorageMeta | undefined> {
|
|
37
|
+
const withStorageMeta = await PayloadBuilder.addStorageMeta(payload.payloads)
|
|
38
|
+
const tx = asOptionalTransactionBoundWitnessWithStorageMeta(withStorageMeta.find(p => p._hash === payload.root))
|
|
39
|
+
if (tx) {
|
|
40
|
+
return [tx, withStorageMeta.map(p => p._hash === payload.root ? undefined : p).filter(exists)]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
globalAttributes.setAttribute('XyoPendingTransactionsService:status', 'unknown')
|
|
45
|
+
|
|
29
46
|
@creatableService()
|
|
30
47
|
export class XyoPendingTransactionsService extends BaseService<XyoPendingTransactionsServiceParams> implements PendingTransactionsService {
|
|
31
48
|
private static readonly MutexPriority = {
|
|
@@ -57,13 +74,15 @@ export class XyoPendingTransactionsService extends BaseService<XyoPendingTransac
|
|
|
57
74
|
* A local Archivist optimized for fast retrieval that stores only validated
|
|
58
75
|
* pending transactions
|
|
59
76
|
*/
|
|
60
|
-
private
|
|
77
|
+
private _curatedPendingBundledTransactionsArchivist: MemoryArchivist | undefined
|
|
61
78
|
|
|
62
79
|
/**
|
|
63
80
|
* The last count of total pending transactions
|
|
64
81
|
*/
|
|
65
82
|
private _pendingTransactionsCount: number = 0
|
|
66
83
|
|
|
84
|
+
private _removablePendingTransactionHashes: Set<Hash> = new Set()
|
|
85
|
+
|
|
67
86
|
/**
|
|
68
87
|
* A mutex to ensure that the curated pending transactions archivist is
|
|
69
88
|
* updated in a thread-safe manner
|
|
@@ -78,8 +97,12 @@ export class XyoPendingTransactionsService extends BaseService<XyoPendingTransac
|
|
|
78
97
|
return assertEx(this.params.chainIdentification, () => 'No chain id')
|
|
79
98
|
}
|
|
80
99
|
|
|
81
|
-
private get
|
|
82
|
-
return assertEx(this.params.
|
|
100
|
+
private get pendingBundledTransactionsArchivist() {
|
|
101
|
+
return assertEx(this.params.pendingBundledTransactionsArchivist, () => 'No pending bundled transactions archivist')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private get pendingBundledTransactionsLocalArchivist() {
|
|
105
|
+
return assertEx(this._curatedPendingBundledTransactionsArchivist, () => 'No pending bundled transactions curated archivist')
|
|
83
106
|
}
|
|
84
107
|
|
|
85
108
|
private get pendingTransactionsCount() {
|
|
@@ -87,60 +110,69 @@ export class XyoPendingTransactionsService extends BaseService<XyoPendingTransac
|
|
|
87
110
|
return this._pendingTransactionsCount
|
|
88
111
|
}
|
|
89
112
|
|
|
90
|
-
private get
|
|
91
|
-
return assertEx(this.
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private get rejectedTransactionsArchivist() {
|
|
95
|
-
return assertEx(this.params.rejectedTransactionsArchivist, () => 'No rejected transactions archivist')
|
|
113
|
+
private get rejectedBundledTransactionsArchivist() {
|
|
114
|
+
return assertEx(this.params.rejectedBundledTransactionsArchivist, () => 'No rejected bundled transactions archivist')
|
|
96
115
|
}
|
|
97
116
|
|
|
98
117
|
override async createHandler() {
|
|
99
118
|
await super.createHandler()
|
|
100
|
-
this.
|
|
119
|
+
this._curatedPendingBundledTransactionsArchivist = await MemoryArchivist.create({ account: 'random' })
|
|
101
120
|
|
|
102
121
|
// On new pending transactions, insert them into the curated archivist
|
|
103
|
-
this.
|
|
104
|
-
await this.insertNewTransactions(payloads)
|
|
122
|
+
this.pendingBundledTransactionsArchivist.on('inserted', async ({ payloads }) => {
|
|
123
|
+
await this.insertNewTransactions(payloads as WithStorageMeta<PayloadBundle>[])
|
|
105
124
|
})
|
|
106
125
|
|
|
107
126
|
// On new finalized blocks, remove the transactions from the curated archivist
|
|
108
|
-
this.chainArchivist.on('inserted',
|
|
109
|
-
|
|
127
|
+
this.chainArchivist.on('inserted', ({ payloads }) => {
|
|
128
|
+
this.removeFinalizedTransactions(payloads)
|
|
110
129
|
})
|
|
111
130
|
|
|
112
131
|
// On new rejected blocks, remove the transactions from the curated archivist
|
|
113
|
-
this.
|
|
114
|
-
|
|
132
|
+
this.rejectedBundledTransactionsArchivist.on('inserted', ({ payloads }) => {
|
|
133
|
+
this.removeRejectedBundledTransactions(payloads)
|
|
115
134
|
})
|
|
116
135
|
|
|
117
|
-
const
|
|
118
|
-
'
|
|
119
|
-
{
|
|
136
|
+
const pendingTransactionsCounter = this.meter?.createObservableUpDownCounter(
|
|
137
|
+
'xyo_pending_transactions_counter',
|
|
138
|
+
{
|
|
139
|
+
description: 'The current number of pending transactions', valueType: ValueType.INT, unit: '1',
|
|
140
|
+
},
|
|
120
141
|
)
|
|
121
|
-
|
|
142
|
+
pendingTransactionsCounter?.addCallback((observer) => {
|
|
122
143
|
observer.observe(this.pendingTransactionsCount)
|
|
123
144
|
})
|
|
145
|
+
globalAttributes.setAttribute('XyoPendingTransactionsService:status', 'created')
|
|
124
146
|
}
|
|
125
147
|
|
|
126
148
|
async getPendingTransactions(head: Hash, limit: number): Promise<HydratedTransactionWithStorageMeta[]> {
|
|
127
149
|
return await this.spanAsync('getPendingTransactions', async () => {
|
|
128
150
|
return await this._updateCuratedPendingTransactionsArchivistMutex.runExclusive(async () => {
|
|
129
|
-
const foundPendingTransactions:
|
|
151
|
+
const foundPendingTransactions: HydratedTransactionWithStorageMeta[] = []
|
|
152
|
+
const foundPendingTransactionsToDeleteHashes: Hash[] = []
|
|
130
153
|
let cursor: Sequence | undefined
|
|
131
154
|
while (foundPendingTransactions.length < limit) {
|
|
132
|
-
const payloads = await this.
|
|
155
|
+
const payloads = await this.pendingBundledTransactionsLocalArchivist.next({
|
|
133
156
|
limit: 100, order: 'asc', cursor,
|
|
134
|
-
})
|
|
157
|
+
}) as WithStorageMeta<PayloadBundle>[]
|
|
135
158
|
if (payloads.length === 0) break
|
|
136
159
|
cursor = payloads.at(-1)?._sequence
|
|
137
|
-
const
|
|
160
|
+
const deletedTransactionBundles = payloads.filter(tx => this._removablePendingTransactionHashes.has(tx.root))
|
|
161
|
+
foundPendingTransactionsToDeleteHashes.push(...deletedTransactionBundles.map(tx => tx._hash).filter(exists))
|
|
162
|
+
const undeletedTransactionBundles = payloads.filter(tx => !this._removablePendingTransactionHashes.has(tx.root))
|
|
163
|
+
// add the items that are not to be deleted
|
|
164
|
+
const transactions = (await Promise.all(
|
|
165
|
+
undeletedTransactionBundles.map(p => bundledPayloadsToHydratedTransaction(p)),
|
|
166
|
+
)).filter(exists)
|
|
138
167
|
foundPendingTransactions.push(...transactions)
|
|
139
168
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
169
|
+
// remove the hashes from delete queue
|
|
170
|
+
for (const hash of foundPendingTransactionsToDeleteHashes) {
|
|
171
|
+
this._removablePendingTransactionHashes.delete(hash)
|
|
172
|
+
}
|
|
173
|
+
// actually delete the bundles
|
|
174
|
+
await this.pendingBundledTransactionsLocalArchivist.delete(foundPendingTransactionsToDeleteHashes)
|
|
175
|
+
return foundPendingTransactions
|
|
144
176
|
}, XyoPendingTransactionsService.MutexPriority.ReadTransactions)
|
|
145
177
|
})
|
|
146
178
|
}
|
|
@@ -148,30 +180,30 @@ export class XyoPendingTransactionsService extends BaseService<XyoPendingTransac
|
|
|
148
180
|
private async countPendingTransactions() {
|
|
149
181
|
if (this._countPendingTransactionsMutex.isLocked()) return
|
|
150
182
|
await this._countPendingTransactionsMutex.runExclusive(async () => {
|
|
151
|
-
const payloads = (await this.
|
|
152
|
-
|
|
153
|
-
this._pendingTransactionsCount = pendingTransactions.length
|
|
183
|
+
const payloads = (await this._curatedPendingBundledTransactionsArchivist?.all()) ?? []
|
|
184
|
+
this._pendingTransactionsCount = payloads.length
|
|
154
185
|
})
|
|
155
186
|
}
|
|
156
187
|
|
|
157
|
-
private async filterAlreadyFinalizedTransactions(
|
|
158
|
-
|
|
159
|
-
|
|
188
|
+
private async filterAlreadyFinalizedTransactions(
|
|
189
|
+
incomingTransactions: WithStorageMeta<PayloadBundle>[],
|
|
190
|
+
): Promise<WithStorageMeta<PayloadBundle>[]> {
|
|
191
|
+
const incomingTransactionHashes = incomingTransactions.map(payload => payload.root)
|
|
160
192
|
const finalizedTransactions = await this.chainArchivist.get(incomingTransactionHashes)
|
|
161
193
|
const finalizedTransactionHashes = new Set(finalizedTransactions.map(item => item._hash))
|
|
162
194
|
const nonFinalizedTransactions = incomingTransactions.filter(item => !finalizedTransactionHashes.has(item._hash))
|
|
163
195
|
return nonFinalizedTransactions
|
|
164
196
|
}
|
|
165
197
|
|
|
166
|
-
private async insertNewTransactions(payloads: WithStorageMeta<
|
|
198
|
+
private async insertNewTransactions(payloads: WithStorageMeta<PayloadBundle>[]) {
|
|
167
199
|
this.logger?.log('insertNewTransactions', payloads)
|
|
168
200
|
return await this.spanAsync('InsertNewTransactions', async () => {
|
|
169
201
|
return await this._updateCuratedPendingTransactionsArchivistMutex.runExclusive(async () => {
|
|
170
202
|
// Check incoming transactions against finalized transactions
|
|
171
203
|
const unprocessedTransactions = await this.filterAlreadyFinalizedTransactions(payloads)
|
|
172
204
|
// Hydrate all unprocessed transactions
|
|
173
|
-
const hydratedUnprocessedTransactions = (await Promise.all(unprocessedTransactions.map((tx) => {
|
|
174
|
-
return
|
|
205
|
+
const hydratedUnprocessedTransactions = (await Promise.all(unprocessedTransactions.map(async (tx) => {
|
|
206
|
+
return await bundledPayloadsToHydratedTransaction(tx)
|
|
175
207
|
}))).filter(exists)
|
|
176
208
|
// Filter to only valid transactions
|
|
177
209
|
const validTransactions = await filterAsync(hydratedUnprocessedTransactions, async (tx) => {
|
|
@@ -182,29 +214,44 @@ export class XyoPendingTransactionsService extends BaseService<XyoPendingTransac
|
|
|
182
214
|
return errors.length > 0 ? false : true
|
|
183
215
|
})
|
|
184
216
|
if (validTransactions.length > 0) {
|
|
185
|
-
await this.
|
|
217
|
+
await this.pendingBundledTransactionsLocalArchivist.insert(validTransactions.map((tx) => {
|
|
218
|
+
return {
|
|
219
|
+
root: tx[0]._hash,
|
|
220
|
+
payloads: flattenHydratedTransaction(tx),
|
|
221
|
+
} as PayloadBundle
|
|
222
|
+
}))
|
|
186
223
|
}
|
|
187
224
|
}, XyoPendingTransactionsService.MutexPriority.InsertNewTransactions)
|
|
188
225
|
})
|
|
189
226
|
}
|
|
190
227
|
|
|
191
|
-
private async
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private async removeRejectedTransactions(payloads: WithStorageMeta<Payload>[]) {
|
|
196
|
-
return await this.removeTransactions(payloads, 'RemoveRejectedTransactions')
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private async removeTransactions(
|
|
200
|
-
payloads: WithStorageMeta<Payload>[],
|
|
228
|
+
private async removeBundledTransactions(
|
|
229
|
+
bundledPayloads: WithStorageMeta<PayloadBundle>[],
|
|
201
230
|
priority: keyof typeof XyoPendingTransactionsService.MutexPriority,
|
|
202
231
|
) {
|
|
203
232
|
return await this.spanAsync(priority, async () => {
|
|
204
233
|
return await this._updateCuratedPendingTransactionsArchivistMutex.runExclusive(async () => {
|
|
205
|
-
const
|
|
206
|
-
|
|
234
|
+
const bundledTransactions = (await Promise.all(bundledPayloads.map(async (payload) => {
|
|
235
|
+
const withStorageMeta = await PayloadBuilder.addStorageMeta(payload.payloads)
|
|
236
|
+
const tx = asOptionalTransactionBoundWitnessWithStorageMeta(withStorageMeta.find(p => p._hash === payload.root))
|
|
237
|
+
return tx !== undefined && payload._hash !== undefined ? tx : undefined
|
|
238
|
+
}))).filter(exists)
|
|
239
|
+
await this.pendingBundledTransactionsLocalArchivist.delete(bundledTransactions.map(btx => btx._hash))
|
|
207
240
|
}, XyoPendingTransactionsService.MutexPriority[priority])
|
|
208
241
|
})
|
|
209
242
|
}
|
|
243
|
+
|
|
244
|
+
private removeFinalizedTransactions(payloads: WithStorageMeta<Payload>[]) {
|
|
245
|
+
const finalizedTransactionHashes = payloads.filter(isTransactionBoundWitnessWithStorageMeta).map(p => p._hash)
|
|
246
|
+
for (const hash of finalizedTransactionHashes) {
|
|
247
|
+
this._removablePendingTransactionHashes.add(hash)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private removeRejectedBundledTransactions(payloads: WithStorageMeta<Payload>[]) {
|
|
252
|
+
const rejectedTransactionHashes = payloads.filter(isTransactionBoundWitnessWithStorageMeta).map(p => p._hash)
|
|
253
|
+
for (const hash of rejectedTransactionHashes) {
|
|
254
|
+
this._removablePendingTransactionHashes.add(hash)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
210
257
|
}
|