@typeberry/lib 0.5.8-857ec24 → 0.5.8-b3da767
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/package.json +1 -1
- package/packages/jam/database/serialized-states-db.d.ts +1 -0
- package/packages/jam/database/serialized-states-db.d.ts.map +1 -1
- package/packages/jam/database/serialized-states-db.js +3 -0
- package/packages/jam/database/states.d.ts +3 -0
- package/packages/jam/database/states.d.ts.map +1 -1
- package/packages/jam/database/states.js +3 -0
- package/packages/jam/database-lmdb/states.d.ts +1 -0
- package/packages/jam/database-lmdb/states.d.ts.map +1 -1
- package/packages/jam/database-lmdb/states.js +3 -0
- package/packages/jam/node/main-fuzz.d.ts.map +1 -1
- package/packages/jam/node/main-fuzz.js +4 -1
- package/packages/jam/node/main-importer.d.ts +1 -0
- package/packages/jam/node/main-importer.d.ts.map +1 -1
- package/packages/jam/node/main-importer.js +8 -7
- package/packages/jam/node/main.d.ts.map +1 -1
- package/packages/jam/node/main.js +1 -0
- package/packages/workers/importer/finality.d.ts +32 -0
- package/packages/workers/importer/finality.d.ts.map +1 -0
- package/packages/workers/importer/finality.js +105 -0
- package/packages/workers/importer/finality.test.d.ts +2 -0
- package/packages/workers/importer/finality.test.d.ts.map +1 -0
- package/packages/workers/importer/finality.test.js +369 -0
- package/packages/workers/importer/importer.d.ts +2 -0
- package/packages/workers/importer/importer.d.ts.map +1 -1
- package/packages/workers/importer/importer.js +8 -0
- package/packages/workers/importer/main.d.ts.map +1 -1
- package/packages/workers/importer/main.js +7 -1
- package/packages/workers/importer/protocol.d.ts +7 -3
- package/packages/workers/importer/protocol.d.ts.map +1 -1
- package/packages/workers/importer/protocol.js +9 -4
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@ export declare class InMemorySerializedStates implements StatesDb<SerializedStat
|
|
|
27
27
|
getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
|
|
28
28
|
updateAndSetState(header: HeaderHash, state: SerializedState<LeafDb>, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
|
|
29
29
|
getState(header: HeaderHash): SerializedState<LeafDb> | null;
|
|
30
|
+
markUnused(header: HeaderHash): void;
|
|
30
31
|
close(): Promise<void>;
|
|
31
32
|
}
|
|
32
33
|
//# sourceMappingURL=serialized-states-db.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialized-states-db.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/serialized-states-db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EACL,eAAe,EACf,KAAK,YAAY,EAGlB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE5E,qCAAqC;AACrC,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAEhG,sCAAsC;AACtC,qBAAa,wBAAyB,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAc1G,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAd1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAyE;IAC5F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8D;WAE1E,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE;IAKxD,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;IAIpF,OAAO;IAKD,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAiBxG,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAIpE,iBAAiB,CACrB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAsBxC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"serialized-states-db.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/serialized-states-db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EACL,eAAe,EACf,KAAK,YAAY,EAGlB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE5E,qCAAqC;AACrC,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAEhG,sCAAsC;AACtC,qBAAa,wBAAyB,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAc1G,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAd1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAyE;IAC5F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8D;WAE1E,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE;IAKxD,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;IAIpF,OAAO;IAKD,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAiBxG,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAIpE,iBAAiB,CACrB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAsBxC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAkB5D,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI9B,KAAK;CACZ"}
|
|
@@ -33,6 +33,8 @@ export interface StatesDb<T extends State = State> {
|
|
|
33
33
|
updateAndSetState(header: HeaderHash, state: T, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
|
|
34
34
|
/** Retrieve posterior state of given header. */
|
|
35
35
|
getState(header: HeaderHash): T | null;
|
|
36
|
+
/** Mark state as no longer needed. Backend may remove it asynchronously. */
|
|
37
|
+
markUnused(header: HeaderHash): void;
|
|
36
38
|
/** Close the database and free resources. */
|
|
37
39
|
close(): Promise<void>;
|
|
38
40
|
}
|
|
@@ -46,6 +48,7 @@ export declare class InMemoryStates implements StatesDb<InMemoryState> {
|
|
|
46
48
|
/** Insert a full state into the database. */
|
|
47
49
|
insertInitialState(headerHash: HeaderHash, state: InMemoryState): Promise<Result<OK, StateUpdateError>>;
|
|
48
50
|
getState(headerHash: HeaderHash): InMemoryState | null;
|
|
51
|
+
markUnused(header: HeaderHash): void;
|
|
49
52
|
close(): Promise<void>;
|
|
50
53
|
}
|
|
51
54
|
//# sourceMappingURL=states.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,KAAK,EAAe,MAAM,kBAAkB,CAAC;AAE/F,OAAO,EAAe,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE3D,0DAA0D;AAC1D,oBAAY,gBAAgB;IAC1B,oDAAoD;IACpD,QAAQ,IAAI;IACZ,iDAAiD;IACjD,MAAM,IAAI;CACX;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,KAAK;IACrC,qEAAqE;IACrE,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CACpG;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK;IAC/C,4CAA4C;IAC5C,YAAY,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAE/C;;;;OAIG;IACH,iBAAiB,CACf,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAEzC,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC;IAEvC,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,cAAe,YAAW,QAAQ,CAAC,aAAa,CAAC;IAIhD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmE;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;gBAEd,IAAI,EAAE,SAAS;IAItC,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAgBlC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAKhE,6CAA6C;IACvC,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAM7G,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,KAAK,EAAe,MAAM,kBAAkB,CAAC;AAE/F,OAAO,EAAe,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE3D,0DAA0D;AAC1D,oBAAY,gBAAgB;IAC1B,oDAAoD;IACpD,QAAQ,IAAI;IACZ,iDAAiD;IACjD,MAAM,IAAI;CACX;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,KAAK;IACrC,qEAAqE;IACrE,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CACpG;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK;IAC/C,4CAA4C;IAC5C,YAAY,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAE/C;;;;OAIG;IACH,iBAAiB,CACf,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAEzC,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC;IAEvC,4EAA4E;IAC5E,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAErC,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,cAAe,YAAW,QAAQ,CAAC,aAAa,CAAC;IAIhD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmE;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;gBAEd,IAAI,EAAE,SAAS;IAItC,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAgBlC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAKhE,6CAA6C;IACvC,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAM7G,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI;IAStD,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI9B,KAAK;CACZ"}
|
|
@@ -67,6 +67,7 @@ export declare class LmdbStates implements StatesDb<SerializedState<LeafDb>>, In
|
|
|
67
67
|
updateAndSetState(headerHash: HeaderHash, state: SerializedState<LeafDb>, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
|
|
68
68
|
getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
|
|
69
69
|
getState(root: HeaderHash): SerializedState<LeafDb> | null;
|
|
70
|
+
markUnused(header: HeaderHash): void;
|
|
70
71
|
close(): Promise<void>;
|
|
71
72
|
}
|
|
72
73
|
//# sourceMappingURL=states.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,gBAAgB,EAAe,MAAM,qBAAqB,CAAC;AAC9G,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,iBAAiB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAgD,MAAM,gCAAgC,CAAC;AAG/G,OAAO,EAAE,EAAE,EAAE,MAAM,EAAkB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,WAAW,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,qBAAa,UAAW,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAK5F,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;gBAGZ,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,QAAQ;IAM3B,kBAAkB,CACtB,UAAU,EAAE,UAAU,EACtB,eAAe,EAAE,YAAY,GAC5B,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;YAQ1B,eAAe;IA0BvB,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAWlC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAI1E,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,gBAAgB,EAAe,MAAM,qBAAqB,CAAC;AAC9G,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,iBAAiB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAgD,MAAM,gCAAgC,CAAC;AAG/G,OAAO,EAAE,EAAE,EAAE,MAAM,EAAkB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,WAAW,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,qBAAa,UAAW,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAK5F,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;gBAGZ,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,QAAQ;IAM3B,kBAAkB,CACtB,UAAU,EAAE,UAAU,EACtB,eAAe,EAAE,YAAY,GAC5B,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;YAQ1B,eAAe;IA0BvB,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAWlC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAI1E,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAsB1D,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI9B,KAAK;CAGZ"}
|
|
@@ -125,6 +125,9 @@ export class LmdbStates {
|
|
|
125
125
|
}
|
|
126
126
|
return SerializedState.new(this.spec, this.blake2b, leafDbResult.ok);
|
|
127
127
|
}
|
|
128
|
+
markUnused(header) {
|
|
129
|
+
this.states.remove(header.raw);
|
|
130
|
+
}
|
|
128
131
|
async close() {
|
|
129
132
|
await Promise.all([this.states.close(), this.values.close()]);
|
|
130
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;CAClC,CAAC;AAIF,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;CAClC,CAAC;AAIF,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,uBAuExF"}
|
|
@@ -66,7 +66,10 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
|
|
|
66
66
|
},
|
|
67
67
|
ancestry,
|
|
68
68
|
network: null,
|
|
69
|
-
}, withRelPath, {
|
|
69
|
+
}, withRelPath, {
|
|
70
|
+
initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry,
|
|
71
|
+
dummyFinalityDepth: 10_000,
|
|
72
|
+
});
|
|
70
73
|
runningNode = newNode;
|
|
71
74
|
return await newNode.getBestStateRootHash();
|
|
72
75
|
},
|
|
@@ -2,6 +2,7 @@ import type { JamConfig } from "./jam-config.js";
|
|
|
2
2
|
import type { NodeApi } from "./main.js";
|
|
3
3
|
export type ImporterOptions = {
|
|
4
4
|
initGenesisFromAncestry?: boolean;
|
|
5
|
+
dummyFinalityDepth?: number;
|
|
5
6
|
};
|
|
6
7
|
export declare function mainImporter(config: JamConfig, withRelPath: (v: string) => string, options?: ImporterOptions): Promise<NodeApi>;
|
|
7
8
|
//# sourceMappingURL=main-importer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA+ElB"}
|
|
@@ -2,7 +2,8 @@ import { Bytes } from "#@typeberry/bytes";
|
|
|
2
2
|
import { PvmBackend } from "#@typeberry/config";
|
|
3
3
|
import { bandersnatch, initWasm } from "#@typeberry/crypto";
|
|
4
4
|
import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
|
|
5
|
-
import { createImporter } from "#@typeberry/importer";
|
|
5
|
+
import { createImporter, ImporterConfig } from "#@typeberry/importer";
|
|
6
|
+
import { tryAsU16 } from "#@typeberry/numbers";
|
|
6
7
|
import { CURRENT_SUITE, CURRENT_VERSION, Result, resultToString, version } from "#@typeberry/utils";
|
|
7
8
|
import { InMemWorkerConfig, LmdbWorkerConfig } from "#@typeberry/workers-api-node";
|
|
8
9
|
import { getChainSpec, getDatabasePath, initializeDatabase, logger } from "./common.js";
|
|
@@ -18,23 +19,23 @@ export async function mainImporter(config, withRelPath, options = {}) {
|
|
|
18
19
|
const blake2b = await Blake2b.createHasher();
|
|
19
20
|
const nodeName = config.nodeName;
|
|
20
21
|
const { dbPath, genesisHeaderHash } = getDatabasePath(blake2b, config.nodeName, config.node.chainSpec.genesisHeader, withRelPath(config.node.databaseBasePath ?? "<in-memory>"));
|
|
22
|
+
const workerParams = ImporterConfig.create({
|
|
23
|
+
pvm: config.pvmBackend,
|
|
24
|
+
dummyFinalityDepth: tryAsU16(options.dummyFinalityDepth ?? 0),
|
|
25
|
+
});
|
|
21
26
|
const workerConfig = config.node.databaseBasePath === undefined
|
|
22
27
|
? InMemWorkerConfig.new({
|
|
23
28
|
nodeName,
|
|
24
29
|
chainSpec,
|
|
25
30
|
blake2b,
|
|
26
|
-
workerParams
|
|
27
|
-
pvm: config.pvmBackend,
|
|
28
|
-
},
|
|
31
|
+
workerParams,
|
|
29
32
|
})
|
|
30
33
|
: LmdbWorkerConfig.new({
|
|
31
34
|
nodeName,
|
|
32
35
|
chainSpec,
|
|
33
36
|
blake2b,
|
|
34
37
|
dbPath,
|
|
35
|
-
workerParams
|
|
36
|
-
pvm: config.pvmBackend,
|
|
37
|
-
},
|
|
38
|
+
workerParams,
|
|
38
39
|
});
|
|
39
40
|
// Initialize the database with genesis state and block if there isn't one.
|
|
40
41
|
logger.info `🛢️ Opening database at ${dbPath}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAc,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAe/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAkC,MAAM,EAAW,MAAM,kBAAkB,CAAC;AAKnF,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,iBAAiB,CAAC;AAWhE,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,oBAAoB,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAsB,IAAI,CACxB,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,SAAS,EAAE,SAAS,GAAG,IAAI,GAC1B,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAc,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAe/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAkC,MAAM,EAAW,MAAM,kBAAkB,CAAC;AAKnF,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,iBAAiB,CAAC;AAWhE,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,oBAAoB,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAsB,IAAI,CACxB,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,SAAS,EAAE,SAAS,GAAG,IAAI,GAC1B,OAAO,CAAC,OAAO,CAAC,CAwKlB"}
|
|
@@ -34,6 +34,7 @@ export async function main(config, withRelPath, telemetry) {
|
|
|
34
34
|
...baseConfig,
|
|
35
35
|
workerParams: ImporterConfig.create({
|
|
36
36
|
pvm: config.pvmBackend,
|
|
37
|
+
dummyFinalityDepth: tryAsU16(config.devValidatorIndex !== null ? 100 : 0),
|
|
37
38
|
}),
|
|
38
39
|
};
|
|
39
40
|
const importerConfig = isInMemory
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HeaderHash } from "#@typeberry/block";
|
|
2
|
+
import type { BlocksDb } from "#@typeberry/database";
|
|
3
|
+
/** Result returned when a new block is finalized. */
|
|
4
|
+
export interface FinalityResult {
|
|
5
|
+
/** The newly finalized block hash. */
|
|
6
|
+
finalizedHash: HeaderHash;
|
|
7
|
+
/** Block hashes whose states are no longer needed and can be pruned. */
|
|
8
|
+
prunableStateHashes: HeaderHash[];
|
|
9
|
+
}
|
|
10
|
+
/** An abstraction for deciding which blocks are finalized. */
|
|
11
|
+
export interface Finalizer {
|
|
12
|
+
/** Called after block import. Returns finality info if a new block was finalized, or null. */
|
|
13
|
+
onBlockImported(headerHash: HeaderHash): FinalityResult | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A simple finalizer that considers a block finalized when N blocks
|
|
17
|
+
* have been built on top of it.
|
|
18
|
+
*
|
|
19
|
+
* Maintains an array of fork chains starting from the last finalized block.
|
|
20
|
+
* When any chain reaches `depth`, the earliest blocks are finalized and
|
|
21
|
+
* dead forks (branching from before the finalized point) are discarded.
|
|
22
|
+
*/
|
|
23
|
+
export declare class DummyFinalizer implements Finalizer {
|
|
24
|
+
private readonly blocks;
|
|
25
|
+
private readonly depth;
|
|
26
|
+
private lastFinalizedHash;
|
|
27
|
+
private unfinalized;
|
|
28
|
+
static create(blocks: BlocksDb, depth: number): DummyFinalizer;
|
|
29
|
+
private constructor();
|
|
30
|
+
onBlockImported(headerHash: HeaderHash): FinalityResult | null;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=finality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finality.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/finality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAKpD,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,aAAa,EAAE,UAAU,CAAC;IAC1B,wEAAwE;IACxE,mBAAmB,EAAE,UAAU,EAAE,CAAC;CACnC;AAED,8DAA8D;AAC9D,MAAM,WAAW,SAAS;IACxB,8FAA8F;IAC9F,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAAC;CAChE;AAKD;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,SAAS;IAS5C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IATxB,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,WAAW,CAAe;IAElC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc;IAI9D,OAAO;IAQP,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI;CAyF/D"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Logger } from "#@typeberry/logger";
|
|
2
|
+
const logger = Logger.new(import.meta.filename, "finality");
|
|
3
|
+
/**
|
|
4
|
+
* A simple finalizer that considers a block finalized when N blocks
|
|
5
|
+
* have been built on top of it.
|
|
6
|
+
*
|
|
7
|
+
* Maintains an array of fork chains starting from the last finalized block.
|
|
8
|
+
* When any chain reaches `depth`, the earliest blocks are finalized and
|
|
9
|
+
* dead forks (branching from before the finalized point) are discarded.
|
|
10
|
+
*/
|
|
11
|
+
export class DummyFinalizer {
|
|
12
|
+
blocks;
|
|
13
|
+
depth;
|
|
14
|
+
lastFinalizedHash;
|
|
15
|
+
unfinalized = [];
|
|
16
|
+
static create(blocks, depth) {
|
|
17
|
+
return new DummyFinalizer(blocks, depth);
|
|
18
|
+
}
|
|
19
|
+
constructor(blocks, depth) {
|
|
20
|
+
this.blocks = blocks;
|
|
21
|
+
this.depth = depth;
|
|
22
|
+
this.lastFinalizedHash = blocks.getBestHeaderHash();
|
|
23
|
+
logger.info `🦭 Dummy Finalizer running with depth=${depth}`;
|
|
24
|
+
}
|
|
25
|
+
onBlockImported(headerHash) {
|
|
26
|
+
const header = this.blocks.getHeader(headerHash);
|
|
27
|
+
if (header === null) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const parentHash = header.parentHeaderHash.materialize();
|
|
31
|
+
// Try to attach the block to an existing chain at its tip.
|
|
32
|
+
let extendedChain = null;
|
|
33
|
+
for (const chain of this.unfinalized) {
|
|
34
|
+
if (chain.length > 0 && chain[chain.length - 1].isEqualTo(parentHash)) {
|
|
35
|
+
chain.push(headerHash);
|
|
36
|
+
extendedChain = chain;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (extendedChain === null) {
|
|
41
|
+
if (this.lastFinalizedHash.isEqualTo(parentHash)) {
|
|
42
|
+
// Parent is the finalized block — start a new chain.
|
|
43
|
+
const newChain = [headerHash];
|
|
44
|
+
this.unfinalized.push(newChain);
|
|
45
|
+
extendedChain = newChain;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Fork from the middle of an existing chain — copy the prefix and branch.
|
|
49
|
+
for (const chain of this.unfinalized) {
|
|
50
|
+
const forkIdx = chain.findIndex((h) => h.isEqualTo(parentHash));
|
|
51
|
+
if (forkIdx !== -1) {
|
|
52
|
+
const newChain = [...chain.slice(0, forkIdx + 1), headerHash];
|
|
53
|
+
this.unfinalized.push(newChain);
|
|
54
|
+
extendedChain = newChain;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (extendedChain === null) {
|
|
61
|
+
// Orphan block — cannot attach to any known chain.
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
// Check if the extended chain is long enough to trigger finality.
|
|
65
|
+
// A chain of length N has N-1 blocks built on top of chain[0].
|
|
66
|
+
// We finalize chain[0] when there are >= depth blocks after it,
|
|
67
|
+
// i.e. chain.length > depth.
|
|
68
|
+
if (extendedChain.length <= this.depth) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// The newly finalized block sits at index (length - 1 - depth).
|
|
72
|
+
const finalizedIdx = extendedChain.length - 1 - this.depth;
|
|
73
|
+
const finalizedHash = extendedChain[finalizedIdx];
|
|
74
|
+
// Collect prunable hashes and rebuild the unfinalized set.
|
|
75
|
+
// The previously finalized block's state is no longer needed.
|
|
76
|
+
const prunable = [this.lastFinalizedHash];
|
|
77
|
+
const newUnfinalized = [];
|
|
78
|
+
for (const chain of this.unfinalized) {
|
|
79
|
+
// Find the finalized block in this chain.
|
|
80
|
+
const finIdx = chain.findIndex((h) => h.isEqualTo(finalizedHash));
|
|
81
|
+
if (finIdx !== -1) {
|
|
82
|
+
// Chain contains the finalized block — it's still alive.
|
|
83
|
+
// Prune states for blocks before the finalized block.
|
|
84
|
+
for (let i = 0; i < finIdx; i++) {
|
|
85
|
+
prunable.push(chain[i]);
|
|
86
|
+
}
|
|
87
|
+
// Keep blocks after the finalized block.
|
|
88
|
+
const remaining = chain.slice(finIdx + 1);
|
|
89
|
+
if (remaining.length > 0) {
|
|
90
|
+
newUnfinalized.push(remaining);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Dead fork — branches from a block that is no longer finalized.
|
|
95
|
+
// Prune all its states.
|
|
96
|
+
for (const h of chain) {
|
|
97
|
+
prunable.push(h);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.lastFinalizedHash = finalizedHash;
|
|
102
|
+
this.unfinalized = newUnfinalized;
|
|
103
|
+
return { finalizedHash, prunableStateHashes: prunable };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finality.test.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/finality.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { before, describe, it } from "node:test";
|
|
3
|
+
import { Block, DisputesExtrinsic, Extrinsic, Header, reencodeAsView, tryAsTimeSlot, } from "#@typeberry/block";
|
|
4
|
+
import { asKnownSize } from "#@typeberry/collections";
|
|
5
|
+
import { tinyChainSpec } from "#@typeberry/config";
|
|
6
|
+
import { InMemoryBlocks } from "#@typeberry/database";
|
|
7
|
+
import { Blake2b, WithHash } from "#@typeberry/hash";
|
|
8
|
+
import { DummyFinalizer } from "./finality.js";
|
|
9
|
+
let blake2b;
|
|
10
|
+
before(async () => {
|
|
11
|
+
blake2b = await Blake2b.createHasher();
|
|
12
|
+
});
|
|
13
|
+
function assertExists(value) {
|
|
14
|
+
assert.notStrictEqual(value, null);
|
|
15
|
+
assert.notStrictEqual(value, undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a block with the given parent hash and slot, insert it into the db,
|
|
19
|
+
* and return its hash.
|
|
20
|
+
*/
|
|
21
|
+
async function createBlock(db, parent, slot = 0) {
|
|
22
|
+
const header = Header.create({
|
|
23
|
+
...Header.empty(),
|
|
24
|
+
parentHeaderHash: parent,
|
|
25
|
+
timeSlotIndex: tryAsTimeSlot(slot),
|
|
26
|
+
});
|
|
27
|
+
const block = Block.create({
|
|
28
|
+
header,
|
|
29
|
+
extrinsic: Extrinsic.create({
|
|
30
|
+
tickets: asKnownSize([]),
|
|
31
|
+
preimages: [],
|
|
32
|
+
assurances: asKnownSize([]),
|
|
33
|
+
guarantees: asKnownSize([]),
|
|
34
|
+
disputes: DisputesExtrinsic.create({ verdicts: [], culprits: [], faults: [] }),
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
const blockView = reencodeAsView(Block.Codec, block, tinyChainSpec);
|
|
38
|
+
const headerHash = blake2b.hashBytes(blockView.header.view().encoded()).asOpaque();
|
|
39
|
+
await db.insertBlock(new WithHash(headerHash, blockView));
|
|
40
|
+
return headerHash;
|
|
41
|
+
}
|
|
42
|
+
/** Build a linear chain of `length` blocks starting from `parentHash`. */
|
|
43
|
+
async function buildLinearChain(db, parentHash, length) {
|
|
44
|
+
const hashes = [];
|
|
45
|
+
let parent = parentHash;
|
|
46
|
+
for (let i = 0; i < length; i++) {
|
|
47
|
+
const h = await createBlock(db, parent, i);
|
|
48
|
+
hashes.push(h);
|
|
49
|
+
parent = h;
|
|
50
|
+
}
|
|
51
|
+
return hashes;
|
|
52
|
+
}
|
|
53
|
+
describe("DummyFinalizer", () => {
|
|
54
|
+
describe("linear chain", () => {
|
|
55
|
+
it("should return null when chain is shorter than depth", async () => {
|
|
56
|
+
const db = InMemoryBlocks.new();
|
|
57
|
+
const genesis = db.getBestHeaderHash();
|
|
58
|
+
const finalizer = DummyFinalizer.create(db, 3);
|
|
59
|
+
// Build a chain of 3 blocks: genesis -> 1 -> 2 -> 3
|
|
60
|
+
const chain = await buildLinearChain(db, genesis, 3);
|
|
61
|
+
// Import all 3 — chain length = depth, not > depth, so no finality.
|
|
62
|
+
for (const h of chain) {
|
|
63
|
+
const result = finalizer.onBlockImported(h);
|
|
64
|
+
assert.strictEqual(result, null);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
it("should finalize when chain exceeds depth", async () => {
|
|
68
|
+
const db = InMemoryBlocks.new();
|
|
69
|
+
const genesis = db.getBestHeaderHash();
|
|
70
|
+
const finalizer = DummyFinalizer.create(db, 3);
|
|
71
|
+
// Build: genesis -> 1 -> 2 -> 3 -> 4
|
|
72
|
+
const chain = await buildLinearChain(db, genesis, 4);
|
|
73
|
+
// First 3 imports: no finality.
|
|
74
|
+
for (let i = 0; i < 3; i++) {
|
|
75
|
+
assert.strictEqual(finalizer.onBlockImported(chain[i]), null);
|
|
76
|
+
}
|
|
77
|
+
// 4th import: chain length = 4 > depth(3), finalize block at index 0.
|
|
78
|
+
const result = finalizer.onBlockImported(chain[3]);
|
|
79
|
+
assertExists(result);
|
|
80
|
+
assert.strictEqual(result.finalizedHash.isEqualTo(chain[0]), true);
|
|
81
|
+
});
|
|
82
|
+
it("should prune the previously finalized block on first finality", async () => {
|
|
83
|
+
const db = InMemoryBlocks.new();
|
|
84
|
+
const genesis = db.getBestHeaderHash();
|
|
85
|
+
const finalizer = DummyFinalizer.create(db, 3);
|
|
86
|
+
const chain = await buildLinearChain(db, genesis, 4);
|
|
87
|
+
for (let i = 0; i < 3; i++) {
|
|
88
|
+
finalizer.onBlockImported(chain[i]);
|
|
89
|
+
}
|
|
90
|
+
const result = finalizer.onBlockImported(chain[3]);
|
|
91
|
+
assertExists(result);
|
|
92
|
+
// Block 1 is finalized. The previously finalized block (genesis) is pruned.
|
|
93
|
+
assert.strictEqual(result.prunableStateHashes.length, 1);
|
|
94
|
+
assert.ok(result.prunableStateHashes[0].isEqualTo(genesis));
|
|
95
|
+
});
|
|
96
|
+
it("should advance finality one block at a time, pruning previous finalized each time", async () => {
|
|
97
|
+
const db = InMemoryBlocks.new();
|
|
98
|
+
const genesis = db.getBestHeaderHash();
|
|
99
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
100
|
+
// Build: genesis -> 1 -> 2 -> 3 -> 4 -> 5
|
|
101
|
+
const chain = await buildLinearChain(db, genesis, 5);
|
|
102
|
+
// Import 1, 2: no finality (length <= depth)
|
|
103
|
+
assert.strictEqual(finalizer.onBlockImported(chain[0]), null);
|
|
104
|
+
assert.strictEqual(finalizer.onBlockImported(chain[1]), null);
|
|
105
|
+
// Import 3: length=3 > depth=2, finalize block 1. Prune genesis.
|
|
106
|
+
const r1 = finalizer.onBlockImported(chain[2]);
|
|
107
|
+
assertExists(r1);
|
|
108
|
+
assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
|
|
109
|
+
assert.strictEqual(r1.prunableStateHashes.length, 1);
|
|
110
|
+
assert.ok(r1.prunableStateHashes[0].isEqualTo(genesis));
|
|
111
|
+
// Import 4: finalize block 2. Prune block 1 (previous finalized).
|
|
112
|
+
const r2 = finalizer.onBlockImported(chain[3]);
|
|
113
|
+
assertExists(r2);
|
|
114
|
+
assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
|
|
115
|
+
assert.strictEqual(r2.prunableStateHashes.length, 1);
|
|
116
|
+
assert.ok(r2.prunableStateHashes[0].isEqualTo(chain[0]));
|
|
117
|
+
// Import 5: finalize block 3. Prune block 2 (previous finalized).
|
|
118
|
+
const r3 = finalizer.onBlockImported(chain[4]);
|
|
119
|
+
assertExists(r3);
|
|
120
|
+
assert.strictEqual(r3.finalizedHash.isEqualTo(chain[2]), true);
|
|
121
|
+
assert.strictEqual(r3.prunableStateHashes.length, 1);
|
|
122
|
+
assert.ok(r3.prunableStateHashes[0].isEqualTo(chain[1]));
|
|
123
|
+
});
|
|
124
|
+
it("should advance finality on every import even when blocks arrive in a burst", async () => {
|
|
125
|
+
const db = InMemoryBlocks.new();
|
|
126
|
+
const genesis = db.getBestHeaderHash();
|
|
127
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
128
|
+
// Build: genesis -> 1 -> 2 -> 3 -> 4 -> 5
|
|
129
|
+
const chain = await buildLinearChain(db, genesis, 5);
|
|
130
|
+
// Import blocks 1..4 — finality fires on block 3 and block 4.
|
|
131
|
+
assert.strictEqual(finalizer.onBlockImported(chain[0]), null);
|
|
132
|
+
assert.strictEqual(finalizer.onBlockImported(chain[1]), null);
|
|
133
|
+
// Block 3: chain [1,2,3] length=3 > depth=2 → finalize block 1.
|
|
134
|
+
const r1 = finalizer.onBlockImported(chain[2]);
|
|
135
|
+
assertExists(r1);
|
|
136
|
+
assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
|
|
137
|
+
// Block 4: chain [2,3,4] length=3 > depth=2 → finalize block 2.
|
|
138
|
+
const r2 = finalizer.onBlockImported(chain[3]);
|
|
139
|
+
assertExists(r2);
|
|
140
|
+
assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
|
|
141
|
+
// Block 5: chain [3,4,5] length=3 > depth=2 → finalize block 3.
|
|
142
|
+
const r3 = finalizer.onBlockImported(chain[4]);
|
|
143
|
+
assertExists(r3);
|
|
144
|
+
assert.strictEqual(r3.finalizedHash.isEqualTo(chain[2]), true);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe("with depth=1", () => {
|
|
148
|
+
it("should finalize immediately after 2 blocks", async () => {
|
|
149
|
+
const db = InMemoryBlocks.new();
|
|
150
|
+
const genesis = db.getBestHeaderHash();
|
|
151
|
+
const finalizer = DummyFinalizer.create(db, 1);
|
|
152
|
+
const chain = await buildLinearChain(db, genesis, 3);
|
|
153
|
+
// Import 1: length=1, not > 1. No finality.
|
|
154
|
+
assert.strictEqual(finalizer.onBlockImported(chain[0]), null);
|
|
155
|
+
// Import 2: length=2 > 1. Finalize block 1.
|
|
156
|
+
const r = finalizer.onBlockImported(chain[1]);
|
|
157
|
+
assertExists(r);
|
|
158
|
+
assert.strictEqual(r.finalizedHash.isEqualTo(chain[0]), true);
|
|
159
|
+
// Import 3: finalize block 2.
|
|
160
|
+
const r2 = finalizer.onBlockImported(chain[2]);
|
|
161
|
+
assertExists(r2);
|
|
162
|
+
assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe("forks", () => {
|
|
166
|
+
it("should track two forks from the finalized block", async () => {
|
|
167
|
+
const db = InMemoryBlocks.new();
|
|
168
|
+
const genesis = db.getBestHeaderHash();
|
|
169
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
170
|
+
// Fork A: genesis -> A1 -> A2 -> A3
|
|
171
|
+
const a1 = await createBlock(db, genesis, 1);
|
|
172
|
+
const a2 = await createBlock(db, a1, 2);
|
|
173
|
+
const a3 = await createBlock(db, a2, 3);
|
|
174
|
+
// Fork B: genesis -> B1 -> B2
|
|
175
|
+
const b1 = await createBlock(db, genesis, 10);
|
|
176
|
+
const b2 = await createBlock(db, b1, 11);
|
|
177
|
+
// Import A1, A2, B1, B2 — no finality yet.
|
|
178
|
+
assert.strictEqual(finalizer.onBlockImported(a1), null);
|
|
179
|
+
assert.strictEqual(finalizer.onBlockImported(a2), null);
|
|
180
|
+
assert.strictEqual(finalizer.onBlockImported(b1), null);
|
|
181
|
+
assert.strictEqual(finalizer.onBlockImported(b2), null);
|
|
182
|
+
// Import A3: fork A has length 3 > depth 2. Finalize A1.
|
|
183
|
+
const result = finalizer.onBlockImported(a3);
|
|
184
|
+
assertExists(result);
|
|
185
|
+
assert.strictEqual(result.finalizedHash.isEqualTo(a1), true);
|
|
186
|
+
// Fork B is dead (doesn't contain A1). B1 and B2 should be pruned.
|
|
187
|
+
// Also, the previous finalized (genesis) is pruned.
|
|
188
|
+
const prunedStrings = result.prunableStateHashes.map((h) => h.toString());
|
|
189
|
+
assert.ok(prunedStrings.includes(genesis.toString()), "Genesis (prev finalized) should be pruned");
|
|
190
|
+
assert.ok(prunedStrings.includes(b1.toString()), "B1 should be pruned");
|
|
191
|
+
assert.ok(prunedStrings.includes(b2.toString()), "B2 should be pruned");
|
|
192
|
+
// A1 is the finalized block — should NOT be pruned.
|
|
193
|
+
assert.ok(!prunedStrings.includes(a1.toString()), "A1 (finalized) should not be pruned");
|
|
194
|
+
});
|
|
195
|
+
it("should keep alive forks that diverge after the finalized block", async () => {
|
|
196
|
+
const db = InMemoryBlocks.new();
|
|
197
|
+
const genesis = db.getBestHeaderHash();
|
|
198
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
199
|
+
// Main chain: genesis -> 1 -> 2 -> 3
|
|
200
|
+
const b1 = await createBlock(db, genesis, 1);
|
|
201
|
+
const b2 = await createBlock(db, b1, 2);
|
|
202
|
+
const b3 = await createBlock(db, b2, 3);
|
|
203
|
+
// Fork from block 2: 2 -> F1
|
|
204
|
+
const f1 = await createBlock(db, b2, 20);
|
|
205
|
+
// Import 1, 2 — no finality yet (chain length 2 = depth 2).
|
|
206
|
+
finalizer.onBlockImported(b1);
|
|
207
|
+
finalizer.onBlockImported(b2);
|
|
208
|
+
// Import F1: fork chain [b1, b2, f1] has length 3 > depth 2.
|
|
209
|
+
// This triggers finality for b1.
|
|
210
|
+
const r1 = finalizer.onBlockImported(f1);
|
|
211
|
+
assertExists(r1);
|
|
212
|
+
assert.strictEqual(r1.finalizedHash.isEqualTo(b1), true);
|
|
213
|
+
// Both chains contain b1: main [b1, b2] → alive, trimmed to [b2].
|
|
214
|
+
// Fork [b1, b2, f1] → alive, trimmed to [b2, f1].
|
|
215
|
+
// Only the previous finalized (genesis) is pruned.
|
|
216
|
+
assert.strictEqual(r1.prunableStateHashes.length, 1);
|
|
217
|
+
assert.ok(r1.prunableStateHashes[0].isEqualTo(genesis));
|
|
218
|
+
// Now import b3: it extends the main chain [b2] → [b2, b3].
|
|
219
|
+
// Length 2, not > depth 2. No finality.
|
|
220
|
+
const r2 = finalizer.onBlockImported(b3);
|
|
221
|
+
assert.strictEqual(r2, null);
|
|
222
|
+
// Extend the fork: F1 -> F2 -> F3
|
|
223
|
+
const f2 = await createBlock(db, f1, 21);
|
|
224
|
+
const f3 = await createBlock(db, f2, 22);
|
|
225
|
+
// Import F2: fork chain becomes [b2, f1, f2], length=3 > depth=2.
|
|
226
|
+
// Finalize b2 (index 0). Both chains contain b2, so both are alive.
|
|
227
|
+
// unfinalized becomes [[b3], [f1, f2]]. Only prev finalized (b1) pruned.
|
|
228
|
+
const r3 = finalizer.onBlockImported(f2);
|
|
229
|
+
assertExists(r3);
|
|
230
|
+
assert.strictEqual(r3.finalizedHash.isEqualTo(b2), true);
|
|
231
|
+
assert.strictEqual(r3.prunableStateHashes.length, 1);
|
|
232
|
+
assert.ok(r3.prunableStateHashes[0].isEqualTo(b1));
|
|
233
|
+
// Import F3: fork chain becomes [f1, f2, f3], length=3 > depth=2.
|
|
234
|
+
// Finalize f1 (index 0). Main chain [b3] doesn't contain f1 → dead.
|
|
235
|
+
const r4 = finalizer.onBlockImported(f3);
|
|
236
|
+
assertExists(r4);
|
|
237
|
+
assert.strictEqual(r4.finalizedHash.isEqualTo(f1), true);
|
|
238
|
+
const pruned = r4.prunableStateHashes.map((h) => h.toString());
|
|
239
|
+
assert.ok(pruned.includes(b2.toString()), "B2 (prev finalized) should be pruned");
|
|
240
|
+
assert.ok(pruned.includes(b3.toString()), "B3 should be pruned (dead fork)");
|
|
241
|
+
assert.ok(!pruned.includes(f1.toString()), "F1 should not be pruned (finalized)");
|
|
242
|
+
assert.ok(!pruned.includes(f2.toString()), "F2 should not be pruned (after finalized)");
|
|
243
|
+
assert.ok(!pruned.includes(f3.toString()), "F3 should not be pruned (after finalized)");
|
|
244
|
+
});
|
|
245
|
+
it("should prune a dead fork that diverged before the finalized block", async () => {
|
|
246
|
+
const db = InMemoryBlocks.new();
|
|
247
|
+
const genesis = db.getBestHeaderHash();
|
|
248
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
249
|
+
// Main: genesis -> 1 -> 2 -> 3 -> 4
|
|
250
|
+
const chain = await buildLinearChain(db, genesis, 4);
|
|
251
|
+
// Fork from genesis: genesis -> F1 -> F2
|
|
252
|
+
const f1 = await createBlock(db, genesis, 100);
|
|
253
|
+
const f2 = await createBlock(db, f1, 101);
|
|
254
|
+
// Import: 1, 2, F1, F2, 3 (finalize block 1), 4 (finalize block 2)
|
|
255
|
+
finalizer.onBlockImported(chain[0]);
|
|
256
|
+
finalizer.onBlockImported(chain[1]);
|
|
257
|
+
finalizer.onBlockImported(f1);
|
|
258
|
+
finalizer.onBlockImported(f2);
|
|
259
|
+
// Import 3: main chain length=3 > depth=2, finalize block 1.
|
|
260
|
+
const r1 = finalizer.onBlockImported(chain[2]);
|
|
261
|
+
assertExists(r1);
|
|
262
|
+
assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
|
|
263
|
+
// Fork [F1, F2] doesn't contain block 1, so it's dead.
|
|
264
|
+
// Also, the previous finalized (genesis) is pruned.
|
|
265
|
+
const pruned1 = r1.prunableStateHashes.map((h) => h.toString());
|
|
266
|
+
assert.ok(pruned1.includes(genesis.toString()), "Genesis (prev finalized) should be pruned");
|
|
267
|
+
assert.ok(pruned1.includes(f1.toString()), "F1 should be pruned");
|
|
268
|
+
assert.ok(pruned1.includes(f2.toString()), "F2 should be pruned");
|
|
269
|
+
});
|
|
270
|
+
it("should handle fork from the middle of a chain", async () => {
|
|
271
|
+
const db = InMemoryBlocks.new();
|
|
272
|
+
const genesis = db.getBestHeaderHash();
|
|
273
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
274
|
+
// Main: genesis -> 1 -> 2 -> 3
|
|
275
|
+
const b1 = await createBlock(db, genesis, 1);
|
|
276
|
+
const b2 = await createBlock(db, b1, 2);
|
|
277
|
+
const b3 = await createBlock(db, b2, 3);
|
|
278
|
+
// Fork from block 1: 1 -> F1 -> F2 -> F3
|
|
279
|
+
const f1 = await createBlock(db, b1, 10);
|
|
280
|
+
const f2 = await createBlock(db, f1, 11);
|
|
281
|
+
const f3 = await createBlock(db, f2, 12);
|
|
282
|
+
// Import main chain first.
|
|
283
|
+
finalizer.onBlockImported(b1);
|
|
284
|
+
finalizer.onBlockImported(b2);
|
|
285
|
+
finalizer.onBlockImported(b3);
|
|
286
|
+
// Import fork — F1's parent is b1 which is mid-chain, not a tip.
|
|
287
|
+
finalizer.onBlockImported(f1);
|
|
288
|
+
finalizer.onBlockImported(f2);
|
|
289
|
+
// Import F3: fork chain = [b1, f1, f2, f3], length=4 > depth=2.
|
|
290
|
+
// Finalize block at index 4-1-2 = 1 → f1.
|
|
291
|
+
// But wait — the main chain [b1, b2, b3] also has length 3 > 2.
|
|
292
|
+
// Block 3 import already triggered finality for b1.
|
|
293
|
+
// After that, main chain trimmed to [b2, b3].
|
|
294
|
+
// Fork was created from mid-chain of [b1, b2, b3] at b1.
|
|
295
|
+
// But b1 was already part of the chain before finality.
|
|
296
|
+
// After finality of b1: unfinalized = [[b2, b3]].
|
|
297
|
+
// Now F1's parent is b1 = lastFinalized, so it starts a new chain: [f1].
|
|
298
|
+
// F2 extends it: [f1, f2]. F3 extends: [f1, f2, f3]. Length=3 > 2.
|
|
299
|
+
// Finalize f1. Main chain [b2, b3] doesn't contain f1 → dead fork.
|
|
300
|
+
const r = finalizer.onBlockImported(f3);
|
|
301
|
+
assertExists(r);
|
|
302
|
+
assert.strictEqual(r.finalizedHash.isEqualTo(f1), true);
|
|
303
|
+
const pruned = r.prunableStateHashes.map((h) => h.toString());
|
|
304
|
+
// Previous finalized (b1) is pruned, plus main chain [b2, b3] is dead.
|
|
305
|
+
assert.ok(pruned.includes(b1.toString()), "B1 (prev finalized) should be pruned");
|
|
306
|
+
assert.ok(pruned.includes(b2.toString()), "B2 should be pruned (dead fork)");
|
|
307
|
+
assert.ok(pruned.includes(b3.toString()), "B3 should be pruned (dead fork)");
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
describe("edge cases", () => {
|
|
311
|
+
it("should return null for unknown block hash", async () => {
|
|
312
|
+
const db = InMemoryBlocks.new();
|
|
313
|
+
const finalizer = DummyFinalizer.create(db, 3);
|
|
314
|
+
// Create a block hash that was never inserted into the db.
|
|
315
|
+
const unknownHash = await createBlock(InMemoryBlocks.new(), db.getBestHeaderHash());
|
|
316
|
+
const result = finalizer.onBlockImported(unknownHash);
|
|
317
|
+
assert.strictEqual(result, null);
|
|
318
|
+
});
|
|
319
|
+
it("should return null for orphan block (parent not in any chain)", async () => {
|
|
320
|
+
const db = InMemoryBlocks.new();
|
|
321
|
+
const genesis = db.getBestHeaderHash();
|
|
322
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
323
|
+
// Block whose parent is some unknown hash not in any chain.
|
|
324
|
+
// We create a separate db to get a "foreign" hash, then insert the orphan
|
|
325
|
+
// into our db with that foreign hash as parent.
|
|
326
|
+
const foreignDb = InMemoryBlocks.new();
|
|
327
|
+
const foreignParent = await createBlock(foreignDb, genesis, 99);
|
|
328
|
+
const orphan = await createBlock(db, foreignParent, 50);
|
|
329
|
+
const result = finalizer.onBlockImported(orphan);
|
|
330
|
+
assert.strictEqual(result, null);
|
|
331
|
+
});
|
|
332
|
+
it("should always advance finality forward, never re-finalizing earlier blocks", async () => {
|
|
333
|
+
const db = InMemoryBlocks.new();
|
|
334
|
+
const genesis = db.getBestHeaderHash();
|
|
335
|
+
const finalizer = DummyFinalizer.create(db, 2);
|
|
336
|
+
// genesis -> 1 -> 2 -> 3 -> 4
|
|
337
|
+
const chain = await buildLinearChain(db, genesis, 4);
|
|
338
|
+
finalizer.onBlockImported(chain[0]);
|
|
339
|
+
finalizer.onBlockImported(chain[1]);
|
|
340
|
+
// Block 3 finalizes block 1.
|
|
341
|
+
const r1 = finalizer.onBlockImported(chain[2]);
|
|
342
|
+
assertExists(r1);
|
|
343
|
+
assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
|
|
344
|
+
// Block 4 finalizes block 2. Block 1 (prev finalized) is pruned.
|
|
345
|
+
const r2 = finalizer.onBlockImported(chain[3]);
|
|
346
|
+
assertExists(r2);
|
|
347
|
+
assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
|
|
348
|
+
// Block 1 appears exactly once (as previous finalized, not re-finalized).
|
|
349
|
+
const pruned = r2.prunableStateHashes.map((h) => h.toString());
|
|
350
|
+
assert.strictEqual(pruned.filter((h) => h === chain[0].toString()).length, 1);
|
|
351
|
+
// The newly finalized block (chain[1]) should NOT be pruned.
|
|
352
|
+
assert.ok(!pruned.includes(chain[1].toString()), "Newly finalized block should not be pruned");
|
|
353
|
+
});
|
|
354
|
+
it("should work with depth=0", async () => {
|
|
355
|
+
const db = InMemoryBlocks.new();
|
|
356
|
+
const genesis = db.getBestHeaderHash();
|
|
357
|
+
// depth=0 means finalize as soon as any block exists.
|
|
358
|
+
const finalizer = DummyFinalizer.create(db, 0);
|
|
359
|
+
const b1 = await createBlock(db, genesis, 1);
|
|
360
|
+
// Chain length = 1 > 0 → finalize block at index 1-1-0 = 0 → b1.
|
|
361
|
+
// Genesis (prev finalized) is pruned.
|
|
362
|
+
const result = finalizer.onBlockImported(b1);
|
|
363
|
+
assertExists(result);
|
|
364
|
+
assert.strictEqual(result.finalizedHash.isEqualTo(b1), true);
|
|
365
|
+
assert.strictEqual(result.prunableStateHashes.length, 1);
|
|
366
|
+
assert.ok(result.prunableStateHashes[0].isEqualTo(genesis));
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
|
@@ -8,6 +8,7 @@ import type { TransitionHasher } from "#@typeberry/transition";
|
|
|
8
8
|
import { BlockVerifierError } from "#@typeberry/transition/block-verifier.js";
|
|
9
9
|
import { type StfError } from "#@typeberry/transition/chain-stf.js";
|
|
10
10
|
import { Result, type TaggedError } from "#@typeberry/utils";
|
|
11
|
+
import type { Finalizer } from "./finality.js";
|
|
11
12
|
export declare enum ImporterErrorKind {
|
|
12
13
|
Verifier = 0,
|
|
13
14
|
Stf = 1,
|
|
@@ -16,6 +17,7 @@ export declare enum ImporterErrorKind {
|
|
|
16
17
|
export type ImporterError = TaggedError<ImporterErrorKind.Verifier, BlockVerifierError> | TaggedError<ImporterErrorKind.Stf, StfError> | TaggedError<ImporterErrorKind.Update, StateUpdateError>;
|
|
17
18
|
export type ImporterOptions = {
|
|
18
19
|
initGenesisFromAncestry?: boolean;
|
|
20
|
+
finalizer?: Finalizer;
|
|
19
21
|
};
|
|
20
22
|
export declare class Importer {
|
|
21
23
|
private readonly hasher;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AACvH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAiB,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,EAA0B,KAAK,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC3F,OAAO,EAAkC,MAAM,EAAkB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AACvH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAiB,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,EAA0B,KAAK,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC3F,OAAO,EAAkC,MAAM,EAAkB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC5G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAG/C,oBAAY,iBAAiB;IAC3B,QAAQ,IAAI;IACZ,GAAG,IAAI;IACP,MAAM,IAAI;CACX;AAED,MAAM,MAAM,aAAa,GACrB,WAAW,CAAC,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,GAC3D,WAAW,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,GAC5C,WAAW,CAAC,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAO5D,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF,qBAAa,QAAQ;IAajB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhB1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAU;IAG9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAEhD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;gBAGjE,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,UAAU,EACE,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EACzC,OAAO,GAAE,eAAoB;IAkBhD,6DAA6D;IAChD,mBAAmB;IAQnB,wBAAwB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAQzF,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,aAAa,CAAC,CAAC;YAwB9F,mBAAmB;IA2FjC,oBAAoB;IAMpB,gBAAgB;IAIhB,eAAe,CAAC,UAAU,EAAE,UAAU;IAKhC,KAAK;CAIZ"}
|
|
@@ -144,6 +144,14 @@ export class Importer {
|
|
|
144
144
|
logger.log `${timerDb()}`;
|
|
145
145
|
// finally update the best block
|
|
146
146
|
await this.blocks.setBestHeaderHash(headerHash);
|
|
147
|
+
// check for finality and prune old states
|
|
148
|
+
const finality = this.options.finalizer?.onBlockImported(headerHash) ?? null;
|
|
149
|
+
if (finality !== null) {
|
|
150
|
+
this.logger.info `🦭 Finalized block: ${finality.finalizedHash} (${finality.prunableStateHashes.length} to prune)`;
|
|
151
|
+
for (const hash of finality.prunableStateHashes) {
|
|
152
|
+
this.states.markUnused(hash);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
147
155
|
return Result.ok(new WithHash(headerHash, block.header.view()));
|
|
148
156
|
}
|
|
149
157
|
getBestStateRootHash() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMtE,MAAM,MAAM,MAAM,GAAG,YAAY,CAAC,cAAc,EAAE,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAE/F,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC;IACT,QAAQ,EAAE,QAAQ,CAAC;IACnB,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;CACxC,CAAC,CAoBD;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,iBAwCjE"}
|
|
@@ -3,6 +3,7 @@ import { Blake2b, keccak, ZERO_HASH } from "#@typeberry/hash";
|
|
|
3
3
|
import { Logger } from "#@typeberry/logger";
|
|
4
4
|
import { TransitionHasher } from "#@typeberry/transition";
|
|
5
5
|
import { Result, resultToString } from "#@typeberry/utils";
|
|
6
|
+
import { DummyFinalizer } from "./finality.js";
|
|
6
7
|
import { Importer } from "./importer.js";
|
|
7
8
|
const logger = Logger.new(import.meta.filename, "importer");
|
|
8
9
|
const keccakHasher = keccak.KeccakHasher.create();
|
|
@@ -13,8 +14,13 @@ export async function createImporter(config, options = {}) {
|
|
|
13
14
|
const pvm = config.workerParams.pvm;
|
|
14
15
|
const blocks = db.getBlocksDb();
|
|
15
16
|
const states = db.getStatesDb();
|
|
17
|
+
const dummyFinalityDepth = config.workerParams.dummyFinalityDepth ?? 0;
|
|
18
|
+
const finalizer = dummyFinalityDepth > 0 ? DummyFinalizer.create(blocks, dummyFinalityDepth) : undefined;
|
|
16
19
|
const hasher = new TransitionHasher(await keccakHasher, await blake2b);
|
|
17
|
-
const importer = new Importer(chainSpec, pvm, hasher, logger, blocks, states,
|
|
20
|
+
const importer = new Importer(chainSpec, pvm, hasher, logger, blocks, states, {
|
|
21
|
+
...options,
|
|
22
|
+
finalizer,
|
|
23
|
+
});
|
|
18
24
|
return {
|
|
19
25
|
importer,
|
|
20
26
|
db,
|
|
@@ -3,6 +3,7 @@ import { BytesBlob } from "#@typeberry/bytes";
|
|
|
3
3
|
import { type CodecRecord } from "#@typeberry/codec";
|
|
4
4
|
import { PvmBackend } from "#@typeberry/config";
|
|
5
5
|
import { type OpaqueHash } from "#@typeberry/hash";
|
|
6
|
+
import { type U16 } from "#@typeberry/numbers";
|
|
6
7
|
import { StateEntries } from "#@typeberry/state-merkleization";
|
|
7
8
|
import { Result } from "#@typeberry/utils";
|
|
8
9
|
import { type Api, type Internal } from "#@typeberry/workers-api";
|
|
@@ -57,7 +58,7 @@ export declare const protocol: import("@typeberry/workers-api").LousyProtocol<{
|
|
|
57
58
|
length: import("@typeberry/codec").Descriptor<import("@typeberry/numbers").U32, import("@typeberry/bytes").Bytes<4>>;
|
|
58
59
|
erasureRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32>, import("@typeberry/bytes").Bytes<32>>;
|
|
59
60
|
exportsRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"ExportsRootHash">, import("@typeberry/bytes").Bytes<32>>;
|
|
60
|
-
exportsCount: import("@typeberry/codec").Descriptor<
|
|
61
|
+
exportsCount: import("@typeberry/codec").Descriptor<U16, import("@typeberry/bytes").Bytes<2>>;
|
|
61
62
|
}>>;
|
|
62
63
|
context: import("@typeberry/codec").Descriptor<import("@typeberry/block").RefineContext, import("@typeberry/codec").ViewOf<import("@typeberry/block").RefineContext, {
|
|
63
64
|
anchor: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"HeaderHash">, import("@typeberry/bytes").Bytes<32>>;
|
|
@@ -181,7 +182,7 @@ export declare const protocol: import("@typeberry/workers-api").LousyProtocol<{
|
|
|
181
182
|
length: import("@typeberry/codec").Descriptor<import("@typeberry/numbers").U32, import("@typeberry/bytes").Bytes<4>>;
|
|
182
183
|
erasureRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32>, import("@typeberry/bytes").Bytes<32>>;
|
|
183
184
|
exportsRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"ExportsRootHash">, import("@typeberry/bytes").Bytes<32>>;
|
|
184
|
-
exportsCount: import("@typeberry/codec").Descriptor<
|
|
185
|
+
exportsCount: import("@typeberry/codec").Descriptor<U16, import("@typeberry/bytes").Bytes<2>>;
|
|
185
186
|
}>>;
|
|
186
187
|
context: import("@typeberry/codec").Descriptor<import("@typeberry/block").RefineContext, import("@typeberry/codec").ViewOf<import("@typeberry/block").RefineContext, {
|
|
187
188
|
anchor: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"HeaderHash">, import("@typeberry/bytes").Bytes<32>>;
|
|
@@ -376,10 +377,13 @@ export type ImporterInternal = Internal<typeof protocol>;
|
|
|
376
377
|
export type ImporterApi = Api<typeof protocol>;
|
|
377
378
|
export declare class ImporterConfig {
|
|
378
379
|
readonly pvm: PvmBackend;
|
|
380
|
+
/** Dummy finality depth. 0 means disabled, any positive value enables dummy finality with that depth. */
|
|
381
|
+
readonly dummyFinalityDepth: U16;
|
|
379
382
|
static Codec: import("@typeberry/codec").Descriptor<ImporterConfig, import("@typeberry/codec").ViewOf<ImporterConfig, {
|
|
380
383
|
pvm: import("@typeberry/codec").Descriptor<PvmBackend, import("@typeberry/numbers").U8>;
|
|
384
|
+
dummyFinalityDepth: import("@typeberry/codec").Descriptor<U16, import("@typeberry/bytes").Bytes<2>>;
|
|
381
385
|
}>>;
|
|
382
|
-
static create({ pvm }: CodecRecord<ImporterConfig>): ImporterConfig;
|
|
386
|
+
static create({ pvm, dummyFinalityDepth }: CodecRecord<ImporterConfig>): ImporterConfig;
|
|
383
387
|
private constructor();
|
|
384
388
|
}
|
|
385
389
|
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAA+B,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,KAAK,GAAG,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AA0CjF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBnB,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC;AACzD,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC;AAE/C,qBAAa,cAAc;aAsBP,GAAG,EAAE,UAAU;IAC/B,yGAAyG;aACzF,kBAAkB,EAAE,GAAG;IAvBzC,MAAM,CAAC,KAAK;;;QAcT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,kBAAkB,EAAE,EAAE,WAAW,CAAC,cAAc,CAAC;IAItE,OAAO;CAKR"}
|
|
@@ -3,7 +3,7 @@ import { BytesBlob } from "#@typeberry/bytes";
|
|
|
3
3
|
import { codec } from "#@typeberry/codec";
|
|
4
4
|
import { PvmBackend } from "#@typeberry/config";
|
|
5
5
|
import { HASH_SIZE } from "#@typeberry/hash";
|
|
6
|
-
import { tryAsU8, tryAsU32 } from "#@typeberry/numbers";
|
|
6
|
+
import { tryAsU8, tryAsU16, tryAsU32 } from "#@typeberry/numbers";
|
|
7
7
|
import { StateEntries } from "#@typeberry/state-merkleization";
|
|
8
8
|
import { Result } from "#@typeberry/utils";
|
|
9
9
|
import { createProtocol } from "#@typeberry/workers-api";
|
|
@@ -70,6 +70,7 @@ export const protocol = createProtocol("importer", {
|
|
|
70
70
|
});
|
|
71
71
|
export class ImporterConfig {
|
|
72
72
|
pvm;
|
|
73
|
+
dummyFinalityDepth;
|
|
73
74
|
static Codec = codec.Class(ImporterConfig, {
|
|
74
75
|
pvm: codec.u8.convert((i) => tryAsU8(i), (o) => {
|
|
75
76
|
if (o === PvmBackend.BuiltIn) {
|
|
@@ -80,11 +81,15 @@ export class ImporterConfig {
|
|
|
80
81
|
}
|
|
81
82
|
throw new Error(`Invalid PvmBackend: ${o}`);
|
|
82
83
|
}),
|
|
84
|
+
dummyFinalityDepth: codec.u16,
|
|
83
85
|
});
|
|
84
|
-
static create({ pvm }) {
|
|
85
|
-
return new ImporterConfig(pvm);
|
|
86
|
+
static create({ pvm, dummyFinalityDepth }) {
|
|
87
|
+
return new ImporterConfig(pvm, dummyFinalityDepth);
|
|
86
88
|
}
|
|
87
|
-
constructor(pvm
|
|
89
|
+
constructor(pvm,
|
|
90
|
+
/** Dummy finality depth. 0 means disabled, any positive value enables dummy finality with that depth. */
|
|
91
|
+
dummyFinalityDepth = tryAsU16(0)) {
|
|
88
92
|
this.pvm = pvm;
|
|
93
|
+
this.dummyFinalityDepth = dummyFinalityDepth;
|
|
89
94
|
}
|
|
90
95
|
}
|