@aztec/stdlib 5.2.0-nightly.20260807 → 5.2.0-nightly.20260809
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/dest/block/l2_block_stream/interfaces.d.ts +7 -5
- package/dest/block/l2_block_stream/interfaces.d.ts.map +1 -1
- package/dest/block/l2_block_stream/l2_block_stream.d.ts +2 -1
- package/dest/block/l2_block_stream/l2_block_stream.d.ts.map +1 -1
- package/dest/block/l2_block_stream/l2_block_stream.js +47 -20
- package/dest/block/test/l2_tips_store_test_suite.d.ts +1 -1
- package/dest/block/test/l2_tips_store_test_suite.d.ts.map +1 -1
- package/dest/block/test/l2_tips_store_test_suite.js +9 -4
- package/package.json +8 -8
- package/src/block/l2_block_stream/interfaces.ts +6 -4
- package/src/block/l2_block_stream/l2_block_stream.ts +58 -21
- package/src/block/test/l2_tips_store_test_suite.ts +5 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import type { BlockHeader } from '../../tx/block_header.js';
|
|
2
3
|
import type { L2Block } from '../l2_block.js';
|
|
3
4
|
import type { CheckpointId, L2BlockId, L2TipId, L2Tips, LocalL2Tips } from '../l2_block_source.js';
|
|
4
5
|
/** Provides the current chain tips. Implemented by world-state, l2-tips-store, and AztecNode. */
|
|
@@ -62,13 +63,14 @@ export type L2BlockStreamEvent = /** Emits blocks added to the chain. */ {
|
|
|
62
63
|
blocks: L2Block[];
|
|
63
64
|
} | /**
|
|
64
65
|
* Reports the new proposed tip of the chain. Emitted once per sync pass when the source's proposed tip differs
|
|
65
|
-
* from the pre-pass local one (downloads, a prune, or a thin tip movement).
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
66
|
+
* from the pre-pass local one (downloads, a prune, or a thin tip movement). In block mode the corresponding
|
|
67
|
+
* payloads also arrive via preceding `blocks-added` events, while in tips-only mode this is the sole signal that
|
|
68
|
+
* the proposed tip moved. Consumers that only track the proposed tip can ignore `blocks-added` entirely and
|
|
69
|
+
* anchor on this event instead.
|
|
69
70
|
*/ {
|
|
70
71
|
type: 'chain-proposed';
|
|
71
72
|
block: L2BlockId;
|
|
73
|
+
header: BlockHeader;
|
|
72
74
|
} | /**
|
|
73
75
|
* Reports a new checkpointed tip. Emitted at most once per sync pass when the source's checkpointed tip
|
|
74
76
|
* leads the local one. Carries only the block + checkpoint ids; consumers that need the full checkpoint
|
|
@@ -106,4 +108,4 @@ export type L2TipsStore = L2BlockStreamEventHandler & L2TipsProvider & Pick<L2Bl
|
|
|
106
108
|
*/
|
|
107
109
|
recordBlockHashes(blocks: L2BlockId[]): Promise<void>;
|
|
108
110
|
};
|
|
109
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
111
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2Jsb2NrL2wyX2Jsb2NrX3N0cmVhbS9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRW5FLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzVELE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzlDLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUVuRyxpR0FBaUc7QUFDakcsTUFBTSxXQUFXLGNBQWM7SUFDN0IsU0FBUyxJQUFJLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztDQUNuQztBQUVEOzs7R0FHRztBQUNILE1BQU0sTUFBTSxjQUFjLEdBQUc7SUFBRSxNQUFNLEVBQUUsV0FBVyxDQUFDO0lBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxDQUFBO0NBQUUsQ0FBQztBQUVwRTs7O0dBR0c7QUFDSCxNQUFNLE1BQU0sY0FBYyxHQUFHO0lBQzNCLFFBQVEsRUFBRSxjQUFjLENBQUM7SUFDekIsWUFBWSxDQUFDLEVBQUU7UUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDO1FBQUMsVUFBVSxFQUFFLFlBQVksQ0FBQTtLQUFFLENBQUM7SUFDbkUsTUFBTSxFQUFFO1FBQUUsS0FBSyxFQUFFLGNBQWMsQ0FBQTtLQUFFLENBQUM7SUFDbEMsU0FBUyxFQUFFO1FBQUUsS0FBSyxFQUFFLGNBQWMsQ0FBQTtLQUFFLENBQUM7Q0FDdEMsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFNLFdBQVcsOEJBQThCO0lBQzdDLFNBQVMsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDckMsY0FBYyxDQUFDLE1BQU0sRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUMsQ0FBQztDQUM3RDtBQUVEOzs7OztHQUtHO0FBQ0gsd0JBQWdCLG1CQUFtQixDQUFDLFVBQVUsRUFBRSxjQUFjLEdBQUcsU0FBUyxFQUFFLFdBQVcsRUFBRSxTQUFTLEdBQUcsT0FBTyxDQVczRztBQUVEOzs7OztHQUtHO0FBQ0gsd0JBQWdCLGNBQWMsQ0FBQyxLQUFLLEVBQUUsY0FBYyxFQUFFLE1BQU0sRUFBRSxNQUFNLEdBQUcsT0FBTyxDQU83RTtBQUVELGdEQUFnRDtBQUNoRCxNQUFNLFdBQVcseUJBQXlCO0lBQ3hDLHNCQUFzQixDQUFDLEtBQUssRUFBRSxrQkFBa0IsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7Q0FDbEU7QUFFRCxNQUFNLE1BQU0sa0JBQWtCLEdBQzFCLHVDQUF1QyxDQUFDO0lBQ3RDLElBQUksRUFBRSxjQUFjLENBQUM7SUFDckIsTUFBTSxFQUFFLE9BQU8sRUFBRSxDQUFDO0NBQ25CLEdBQ0Q7Ozs7OztHQU1DLENBQUM7SUFDQSxJQUFJLEVBQUUsZ0JBQWdCLENBQUM7SUFDdkIsS0FBSyxFQUFFLFNBQVMsQ0FBQztJQUNqQixNQUFNLEVBQUUsV0FBVyxDQUFDO0NBQ3JCLEdBQ0Q7Ozs7R0FJQyxDQUFDO0lBQ0EsSUFBSSxFQUFFLG9CQUFvQixDQUFDO0lBQzNCLEtBQUssRUFBRSxTQUFTLENBQUM7SUFDakIsVUFBVSxFQUFFLFlBQVksQ0FBQztDQUMxQixHQUNEOzs7Ozs7O0dBT0MsQ0FBQztJQUNBLElBQUksRUFBRSxjQUFjLENBQUM7SUFDckIsS0FBSyxFQUFFLFNBQVMsQ0FBQztJQUNqQixZQUFZLEVBQUUsT0FBTyxDQUFDO0lBQ3RCLE1BQU0sRUFBRSxPQUFPLENBQUM7Q0FDakIsR0FDRCxnQ0FBZ0MsQ0FBQztJQUMvQixJQUFJLEVBQUUsY0FBYyxDQUFDO0lBQ3JCLEtBQUssRUFBRSxTQUFTLENBQUM7SUFDakIsVUFBVSxFQUFFLFlBQVksQ0FBQztDQUMxQixHQUNELGdFQUFnRSxDQUFDO0lBQy9ELElBQUksRUFBRSxpQkFBaUIsQ0FBQztJQUN4QixLQUFLLEVBQUUsU0FBUyxDQUFDO0lBQ2pCLFVBQVUsRUFBRSxZQUFZLENBQUM7Q0FDMUIsQ0FBQztBQUVOLE1BQU0sTUFBTSxXQUFXLEdBQUcseUJBQXlCLEdBQ2pELGNBQWMsR0FDZCxJQUFJLENBQUMsOEJBQThCLEVBQUUsZ0JBQWdCLENBQUMsR0FBRztJQUN2RDs7OztPQUlHO0lBQ0gsaUJBQWlCLENBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztDQUN2RCxDQUFDIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/block/l2_block_stream/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEnG,iGAAiG;AACjG,MAAM,WAAW,cAAc;IAC7B,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,UAAU,EAAE,YAAY,CAAA;KAAE,CAAC;IACnE,MAAM,EAAE;QAAE,KAAK,EAAE,cAAc,CAAA;KAAE,CAAC;IAClC,SAAS,EAAE;QAAE,KAAK,EAAE,cAAc,CAAA;KAAE,CAAC;CACtC,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,8BAA8B;IAC7C,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IACrC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC7D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,cAAc,GAAG,SAAS,EAAE,WAAW,EAAE,SAAS,GAAG,OAAO,CAW3G;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAO7E;AAED,gDAAgD;AAChD,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED,MAAM,MAAM,kBAAkB,GAC1B,uCAAuC,CAAC;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB,GACD;;;;;;GAMC,CAAC;IACA,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/block/l2_block_stream/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEnG,iGAAiG;AACjG,MAAM,WAAW,cAAc;IAC7B,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,cAAc,CAAC;QAAC,UAAU,EAAE,YAAY,CAAA;KAAE,CAAC;IACnE,MAAM,EAAE;QAAE,KAAK,EAAE,cAAc,CAAA;KAAE,CAAC;IAClC,SAAS,EAAE;QAAE,KAAK,EAAE,cAAc,CAAA;KAAE,CAAC;CACtC,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,8BAA8B;IAC7C,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IACrC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC7D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,cAAc,GAAG,SAAS,EAAE,WAAW,EAAE,SAAS,GAAG,OAAO,CAW3G;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAO7E;AAED,gDAAgD;AAChD,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED,MAAM,MAAM,kBAAkB,GAC1B,uCAAuC,CAAC;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB,GACD;;;;;;GAMC,CAAC;IACA,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;CACrB,GACD;;;;GAIC,CAAC;IACA,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,YAAY,CAAC;CAC1B,GACD;;;;;;;GAOC,CAAC;IACA,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB,GACD,gCAAgC,CAAC;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,YAAY,CAAC;CAC1B,GACD,gEAAgE,CAAC;IAC/D,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,YAAY,CAAC;CAC1B,CAAC;AAEN,MAAM,MAAM,WAAW,GAAG,yBAAyB,GACjD,cAAc,GACd,IAAI,CAAC,8BAA8B,EAAE,gBAAgB,CAAC,GAAG;IACvD;;;;OAIG;IACH,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD,CAAC"}
|
|
@@ -43,6 +43,7 @@ export declare class L2BlockStream {
|
|
|
43
43
|
private downloadBlocks;
|
|
44
44
|
private areBlockHashesEqualAt;
|
|
45
45
|
private getBlockHashFromSource;
|
|
46
|
+
private getHeaderFromSource;
|
|
46
47
|
private emitEvent;
|
|
47
48
|
}
|
|
48
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDJfYmxvY2tfc3RyZWFtLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYmxvY2svbDJfYmxvY2tfc3RyZWFtL2wyX2Jsb2NrX3N0cmVhbS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFRQSxPQUFPLEVBQWtCLEtBQUssYUFBYSxFQUFtQyxNQUFNLHVCQUF1QixDQUFDO0FBQzVHLE9BQU8sRUFFTCxLQUFLLHlCQUF5QixFQUM5QixLQUFLLDhCQUE4QixFQUVwQyxNQUFNLGlCQUFpQixDQUFDO0FBRXpCLHdHQUF3RztBQUN4RyxNQUFNLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDLGFBQWEsRUFBRSxXQUFXLEdBQUcsY0FBYyxHQUFHLFdBQVcsQ0FBQyxDQUFDO0FBRWxHLHNGQUFzRjtBQUN0RixNQUFNLE1BQU0sb0JBQW9CLEdBQUc7SUFDakMsY0FBYyxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ3hCLFNBQVMsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUNuQixhQUFhLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDdkIsa0hBQWtIO0lBQ2xILGFBQWEsQ0FBQyxFQUFFLE9BQU8sQ0FBQztJQUN4QiwySEFBMkg7SUFDM0gsaUJBQWlCLENBQUMsRUFBRSxPQUFPLENBQUM7SUFDNUI7Ozs7O09BS0c7SUFDSCxRQUFRLENBQUMsRUFBRSxPQUFPLENBQUM7Q0FDcEIsQ0FBQztBQUVGLHVIQUF1SDtBQUN2SCxxQkFBYSxhQUFhO0lBTXRCLE9BQU8sQ0FBQyxhQUFhO0lBQ3JCLE9BQU8sQ0FBQyxTQUFTO0lBQ2pCLE9BQU8sQ0FBQyxPQUFPO0lBQ2YsT0FBTyxDQUFDLFFBQVEsQ0FBQyxHQUFHO0lBQ3BCLE9BQU8sQ0FBQyxJQUFJO0lBVGQsT0FBTyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQWlCO0lBQ2hELE9BQU8sQ0FBQyxTQUFTLENBQVM7SUFDMUIsT0FBTyxDQUFDLFVBQVUsQ0FBUztJQUUzQixZQUNVLGFBQWEsRUFBRSxtQkFBbUIsRUFDbEMsU0FBUyxFQUFFLDhCQUE4QixFQUN6QyxPQUFPLEVBQUUseUJBQXlCLEVBQ3pCLEdBQUcseUNBQXFDLEVBQ2pELElBQUksR0FBRSxvQkFBeUIsRUFheEM7SUFFTSxLQUFLLFNBR1g7SUFFWSxJQUFJLGtCQUVoQjtJQUVNLFNBQVMsWUFFZjtJQUVEOzs7O09BSUc7SUFDVSxJQUFJLGtCQUloQjtJQUVELFVBQWdCLElBQUksa0JBMkpuQjtZQVFhLGNBQWM7WUF1RWQscUJBQXFCO0lBd0NuQyxPQUFPLENBQUMsc0JBQXNCO0lBTzlCLE9BQU8sQ0FBQyxtQkFBbUI7WUFVYixTQUFTO0NBZXhCIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"l2_block_stream.d.ts","sourceRoot":"","sources":["../../../src/block/l2_block_stream/l2_block_stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"l2_block_stream.d.ts","sourceRoot":"","sources":["../../../src/block/l2_block_stream/l2_block_stream.ts"],"names":[],"mappings":"AAQA,OAAO,EAAkB,KAAK,aAAa,EAAmC,MAAM,uBAAuB,CAAC;AAC5G,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,8BAA8B,EAEpC,MAAM,iBAAiB,CAAC;AAEzB,wGAAwG;AACxG,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,cAAc,GAAG,WAAW,CAAC,CAAC;AAElG,sFAAsF;AACtF,MAAM,MAAM,oBAAoB,GAAG;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kHAAkH;IAClH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2HAA2H;IAC3H,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,uHAAuH;AACvH,qBAAa,aAAa;IAMtB,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,IAAI;IATd,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAE3B,YACU,aAAa,EAAE,mBAAmB,EAClC,SAAS,EAAE,8BAA8B,EACzC,OAAO,EAAE,yBAAyB,EACzB,GAAG,yCAAqC,EACjD,IAAI,GAAE,oBAAyB,EAaxC;IAEM,KAAK,SAGX;IAEY,IAAI,kBAEhB;IAEM,SAAS,YAEf;IAED;;;;OAIG;IACU,IAAI,kBAIhB;IAED,UAAgB,IAAI,kBA2JnB;YAQa,cAAc;YAuEd,qBAAqB;IAwCnC,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,mBAAmB;YAUb,SAAS;CAexB"}
|
|
@@ -2,6 +2,7 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
|
2
2
|
import { AbortError } from '@aztec/foundation/error';
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
5
|
+
import { BlockHash } from '../block_hash.js';
|
|
5
6
|
import { makeL2BlockId } from '../l2_block_source.js';
|
|
6
7
|
import { localBlockIdDiffers } from './interfaces.js';
|
|
7
8
|
/** Creates a stream of events for new blocks, chain tips updates, and reorgs, out of polling an archiver or a node. */ export class L2BlockStream {
|
|
@@ -63,6 +64,12 @@ import { localBlockIdDiffers } from './interfaces.js';
|
|
|
63
64
|
}
|
|
64
65
|
// Baseline for the chain-proposed event; captured before the local store mutates during the pass.
|
|
65
66
|
const prePassProposed = localTips.proposed;
|
|
67
|
+
// In tips-only mode there is no download loop to supply the chain-proposed event's header, so it must be
|
|
68
|
+
// fetched. Start the read now so it runs in parallel with the walk-back below.
|
|
69
|
+
const prefetch = this.opts.tipsOnly && localBlockIdDiffers(prePassProposed, sourceTips.proposed) ? {
|
|
70
|
+
hash: sourceTips.proposed.hash,
|
|
71
|
+
header: this.getHeaderFromSource(sourceTips.proposed.hash)
|
|
72
|
+
} : undefined;
|
|
66
73
|
// Walk back to find a reorg, floored at the local finalized tip (a legitimate reorg can never reach it, since
|
|
67
74
|
// finalized means the proving tx is itself L1-finalized). Seed the cache with ONLY the proposed tip: a stale
|
|
68
75
|
// tier seed at a reorged height equals the local old-fork hash, faking agreement and stopping the walk above the
|
|
@@ -122,18 +129,28 @@ import { localBlockIdDiffers } from './interfaces.js';
|
|
|
122
129
|
pruned = true;
|
|
123
130
|
}
|
|
124
131
|
// Pass atomicity: a prune mid-download leaves the source unable to serve the planned blocks, so the snapshot's
|
|
125
|
-
// tier tips may reference blocks the consumer never saw —
|
|
126
|
-
// has no download plan (the snapshot is one atomic getL2Tips read), so it always reconciles.
|
|
127
|
-
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
132
|
+
// tier tips may reference blocks the consumer never saw — downloadBlocks aborts the pass in that case.
|
|
133
|
+
// Tips-only mode has no download plan (the snapshot is one atomic getL2Tips read), so it always reconciles.
|
|
134
|
+
const deliveredProposedHeader = this.opts.tipsOnly ? undefined : await this.downloadBlocks(latestBlockNumber, sourceTips);
|
|
130
135
|
// End-of-pass reconciliation: chain-proposed fires against the pre-pass baseline (a post-prune re-read would
|
|
131
136
|
// equal the source tip and suppress it), then the tiers highest-to-lowest so the finalized <= proven <=
|
|
132
137
|
// checkpointed <= proposed invariant holds mid-pass.
|
|
133
138
|
if (localBlockIdDiffers(prePassProposed, sourceTips.proposed)) {
|
|
139
|
+
const availableHeader = prefetch?.hash === sourceTips.proposed.hash ? await prefetch.header : deliveredProposedHeader;
|
|
140
|
+
const header = availableHeader ?? await this.getHeaderFromSource(sourceTips.proposed.hash);
|
|
141
|
+
if (header === undefined) {
|
|
142
|
+
// Abort the pass rather than skip just this event: the later tier events must not get ahead of a
|
|
143
|
+
// proposed tip the consumer never received. Nothing advanced, so the next pass retries.
|
|
144
|
+
this.log.warn(`No header for the proposed tip; aborting this sync pass`, {
|
|
145
|
+
blockNumber: sourceTips.proposed.number,
|
|
146
|
+
blockHash: sourceTips.proposed.hash
|
|
147
|
+
});
|
|
148
|
+
throw new AbortError();
|
|
149
|
+
}
|
|
134
150
|
await this.emitEvent({
|
|
135
151
|
type: 'chain-proposed',
|
|
136
|
-
block: sourceTips.proposed
|
|
152
|
+
block: sourceTips.proposed,
|
|
153
|
+
header
|
|
137
154
|
});
|
|
138
155
|
}
|
|
139
156
|
const reconcileTips = pruned ? await this.localData.getL2Tips() : localTips;
|
|
@@ -166,11 +183,10 @@ import { localBlockIdDiffers } from './interfaces.js';
|
|
|
166
183
|
}
|
|
167
184
|
}
|
|
168
185
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* the
|
|
172
|
-
*
|
|
173
|
-
* the source no longer has a promised block, or served a fork at the proposed height mid-pass.
|
|
186
|
+
* Delivers every block from the agreed-on point through the advertised proposed tip as `blocks-added` events,
|
|
187
|
+
* aborting the pass when the source's own answers prove that tip is no longer real.
|
|
188
|
+
* @returns The delivered tip's header, so the caller can avoid a re-fetch; undefined when there was nothing to
|
|
189
|
+
* download (already at the tip, or startingBlock past it).
|
|
174
190
|
*/ async downloadBlocks(latestBlockNumber, sourceTips) {
|
|
175
191
|
// The post-prune cursor: the highest block number both sides agree on. Block downloads resume from here.
|
|
176
192
|
let nextBlockNumber = latestBlockNumber + 1;
|
|
@@ -199,12 +215,12 @@ import { localBlockIdDiffers } from './interfaces.js';
|
|
|
199
215
|
if (blocks.length === 0) {
|
|
200
216
|
// The source no longer has a block the snapshot promised: the snapshot is provably stale, so report the plan
|
|
201
217
|
// incomplete and skip reconciliation this pass.
|
|
202
|
-
this.log.warn(`Block source returned no blocks for a promised range;
|
|
218
|
+
this.log.warn(`Block source returned no blocks for a promised range; aborting this sync pass`, {
|
|
203
219
|
from: nextBlockNumber,
|
|
204
220
|
limit,
|
|
205
221
|
sourceProposed: sourceTips.proposed.number
|
|
206
222
|
});
|
|
207
|
-
|
|
223
|
+
throw new AbortError();
|
|
208
224
|
}
|
|
209
225
|
await this.emitEvent({
|
|
210
226
|
type: 'blocks-added',
|
|
@@ -214,21 +230,22 @@ import { localBlockIdDiffers } from './interfaces.js';
|
|
|
214
230
|
nextBlockNumber = lastDeliveredBlock.number + 1;
|
|
215
231
|
}
|
|
216
232
|
if (lastDeliveredBlock === undefined) {
|
|
217
|
-
// Loop never ran
|
|
218
|
-
|
|
233
|
+
// Loop never ran (caught up before the plan started, or startingBlock past the tip, see A-1061), so nothing
|
|
234
|
+
// was downloaded that could disprove the snapshot.
|
|
235
|
+
return undefined;
|
|
219
236
|
}
|
|
220
|
-
//
|
|
221
|
-
//
|
|
237
|
+
// The block delivered at the proposed height must carry the snapshot's proposed hash; a different hash means a
|
|
238
|
+
// same-height fork swap happened mid-pass, so the snapshot is stale.
|
|
222
239
|
const deliveredHash = (await lastDeliveredBlock.hash()).toString();
|
|
223
240
|
if (deliveredHash !== sourceTips.proposed.hash) {
|
|
224
|
-
this.log.warn(`Delivered proposed-block hash differs from snapshot;
|
|
241
|
+
this.log.warn(`Delivered proposed-block hash differs from snapshot; aborting this sync pass`, {
|
|
225
242
|
blockNumber: lastDeliveredBlock.number,
|
|
226
243
|
deliveredHash,
|
|
227
244
|
snapshotHash: sourceTips.proposed.hash
|
|
228
245
|
});
|
|
229
|
-
|
|
246
|
+
throw new AbortError();
|
|
230
247
|
}
|
|
231
|
-
return
|
|
248
|
+
return lastDeliveredBlock.header;
|
|
232
249
|
}
|
|
233
250
|
/**
|
|
234
251
|
* Returns whether the source and local agree on the block hash at a given height.
|
|
@@ -280,6 +297,16 @@ import { localBlockIdDiffers } from './interfaces.js';
|
|
|
280
297
|
number: blockNumber
|
|
281
298
|
}).then((d)=>d?.header.hash()).then((hash)=>hash?.toString());
|
|
282
299
|
}
|
|
300
|
+
getHeaderFromSource(hash) {
|
|
301
|
+
return this.l2BlockSource.getBlockData({
|
|
302
|
+
hash: BlockHash.fromString(hash)
|
|
303
|
+
}).then((data)=>data?.header).catch((err)=>{
|
|
304
|
+
this.log.error(`Failed to read header for block`, err, {
|
|
305
|
+
blockHash: hash
|
|
306
|
+
});
|
|
307
|
+
return undefined;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
283
310
|
async emitEvent(event) {
|
|
284
311
|
this.log.debug(`Emitting ${event.type} (${event.type === 'blocks-added' ? event.blocks.length : event.type === 'chain-checkpointed' ? event.checkpoint.number : event.block.number})`);
|
|
285
312
|
await this.handler.handleBlockStreamEvent(event);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { L2TipsStore } from '../l2_block_stream/index.js';
|
|
2
2
|
export declare function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>): void;
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDJfdGlwc19zdG9yZV90ZXN0X3N1aXRlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYmxvY2svdGVzdC9sMl90aXBzX3N0b3JlX3Rlc3Rfc3VpdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBaUJBLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRS9ELHdCQUFnQixlQUFlLENBQUMsYUFBYSxFQUFFLE1BQU0sT0FBTyxDQUFDLFdBQVcsQ0FBQyxRQW13QnhFIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"l2_tips_store_test_suite.d.ts","sourceRoot":"","sources":["../../../src/block/test/l2_tips_store_test_suite.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"l2_tips_store_test_suite.d.ts","sourceRoot":"","sources":["../../../src/block/test/l2_tips_store_test_suite.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,QAmwBxE"}
|
|
@@ -3,6 +3,7 @@ import { times } from '@aztec/foundation/collection';
|
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
4
|
import { GENESIS_CHECKPOINT_HEADER_HASH, L2Block } from '@aztec/stdlib/block';
|
|
5
5
|
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
6
|
+
import { BlockHeader } from '@aztec/stdlib/tx';
|
|
6
7
|
import { expect } from 'vitest';
|
|
7
8
|
import { GENESIS_BLOCK_HEADER_HASH } from '../block_hash.js';
|
|
8
9
|
export function testL2TipsStore(makeTipsStore) {
|
|
@@ -121,7 +122,8 @@ export function testL2TipsStore(makeTipsStore) {
|
|
|
121
122
|
block: {
|
|
122
123
|
number: BlockNumber(5),
|
|
123
124
|
hash: new Fr(500).toString()
|
|
124
|
-
}
|
|
125
|
+
},
|
|
126
|
+
header: BlockHeader.empty()
|
|
125
127
|
});
|
|
126
128
|
const tips = await tipsStore.getL2Tips();
|
|
127
129
|
expect(tips.proposed).toEqual({
|
|
@@ -138,14 +140,16 @@ export function testL2TipsStore(makeTipsStore) {
|
|
|
138
140
|
block: {
|
|
139
141
|
number: BlockNumber(4),
|
|
140
142
|
hash: new Fr(400).toString()
|
|
141
|
-
}
|
|
143
|
+
},
|
|
144
|
+
header: BlockHeader.empty()
|
|
142
145
|
});
|
|
143
146
|
await tipsStore.handleBlockStreamEvent({
|
|
144
147
|
type: 'chain-proposed',
|
|
145
148
|
block: {
|
|
146
149
|
number: BlockNumber(9),
|
|
147
150
|
hash: new Fr(900).toString()
|
|
148
|
-
}
|
|
151
|
+
},
|
|
152
|
+
header: BlockHeader.empty()
|
|
149
153
|
});
|
|
150
154
|
// Both recorded heights resolve; the heights between them were never seen and stay undefined.
|
|
151
155
|
expect(await tipsStore.getL2BlockHash(4)).toEqual(new Fr(400).toString());
|
|
@@ -164,7 +168,8 @@ export function testL2TipsStore(makeTipsStore) {
|
|
|
164
168
|
block: {
|
|
165
169
|
number: BlockNumber(9),
|
|
166
170
|
hash: new Fr(900).toString()
|
|
167
|
-
}
|
|
171
|
+
},
|
|
172
|
+
header: BlockHeader.empty()
|
|
168
173
|
});
|
|
169
174
|
// Record sparse witnesses at heights below the tip (e.g. a consumer materializing per-height state).
|
|
170
175
|
await tipsStore.recordBlockHashes([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/stdlib",
|
|
3
|
-
"version": "5.2.0-nightly.
|
|
3
|
+
"version": "5.2.0-nightly.20260809",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"inherits": [
|
|
6
6
|
"../package.common.json",
|
|
@@ -92,13 +92,13 @@
|
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
94
|
"@aws-sdk/client-s3": "^3.892.0",
|
|
95
|
-
"@aztec/bb.js": "5.2.0-nightly.
|
|
96
|
-
"@aztec/blob-lib": "5.2.0-nightly.
|
|
97
|
-
"@aztec/constants": "5.2.0-nightly.
|
|
98
|
-
"@aztec/ethereum": "5.2.0-nightly.
|
|
99
|
-
"@aztec/foundation": "5.2.0-nightly.
|
|
100
|
-
"@aztec/l1-artifacts": "5.2.0-nightly.
|
|
101
|
-
"@aztec/noir-noirc_abi": "5.2.0-nightly.
|
|
95
|
+
"@aztec/bb.js": "5.2.0-nightly.20260809",
|
|
96
|
+
"@aztec/blob-lib": "5.2.0-nightly.20260809",
|
|
97
|
+
"@aztec/constants": "5.2.0-nightly.20260809",
|
|
98
|
+
"@aztec/ethereum": "5.2.0-nightly.20260809",
|
|
99
|
+
"@aztec/foundation": "5.2.0-nightly.20260809",
|
|
100
|
+
"@aztec/l1-artifacts": "5.2.0-nightly.20260809",
|
|
101
|
+
"@aztec/noir-noirc_abi": "5.2.0-nightly.20260809",
|
|
102
102
|
"@google-cloud/storage": "^7.15.0",
|
|
103
103
|
"axios": "^1.15.1",
|
|
104
104
|
"json-stringify-deterministic": "1.0.12",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
|
|
3
|
+
import type { BlockHeader } from '../../tx/block_header.js';
|
|
3
4
|
import type { L2Block } from '../l2_block.js';
|
|
4
5
|
import type { CheckpointId, L2BlockId, L2TipId, L2Tips, LocalL2Tips } from '../l2_block_source.js';
|
|
5
6
|
|
|
@@ -81,13 +82,14 @@ export type L2BlockStreamEvent =
|
|
|
81
82
|
}
|
|
82
83
|
| /**
|
|
83
84
|
* Reports the new proposed tip of the chain. Emitted once per sync pass when the source's proposed tip differs
|
|
84
|
-
* from the pre-pass local one (downloads, a prune, or a thin tip movement).
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
85
|
+
* from the pre-pass local one (downloads, a prune, or a thin tip movement). In block mode the corresponding
|
|
86
|
+
* payloads also arrive via preceding `blocks-added` events, while in tips-only mode this is the sole signal that
|
|
87
|
+
* the proposed tip moved. Consumers that only track the proposed tip can ignore `blocks-added` entirely and
|
|
88
|
+
* anchor on this event instead.
|
|
88
89
|
*/ {
|
|
89
90
|
type: 'chain-proposed';
|
|
90
91
|
block: L2BlockId;
|
|
92
|
+
header: BlockHeader;
|
|
91
93
|
}
|
|
92
94
|
| /**
|
|
93
95
|
* Reports a new checkpointed tip. Emitted at most once per sync pass when the source's checkpointed tip
|
|
@@ -3,6 +3,8 @@ import { AbortError } from '@aztec/foundation/error';
|
|
|
3
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
5
5
|
|
|
6
|
+
import type { BlockHeader } from '../../tx/block_header.js';
|
|
7
|
+
import { BlockHash } from '../block_hash.js';
|
|
6
8
|
import type { L2Block } from '../l2_block.js';
|
|
7
9
|
import { type L2BlockId, type L2BlockSource, type LocalL2Tips, makeL2BlockId } from '../l2_block_source.js';
|
|
8
10
|
import {
|
|
@@ -100,6 +102,13 @@ export class L2BlockStream {
|
|
|
100
102
|
// Baseline for the chain-proposed event; captured before the local store mutates during the pass.
|
|
101
103
|
const prePassProposed = localTips.proposed;
|
|
102
104
|
|
|
105
|
+
// In tips-only mode there is no download loop to supply the chain-proposed event's header, so it must be
|
|
106
|
+
// fetched. Start the read now so it runs in parallel with the walk-back below.
|
|
107
|
+
const prefetch: ProposedHeaderPrefetch | undefined =
|
|
108
|
+
this.opts.tipsOnly && localBlockIdDiffers(prePassProposed, sourceTips.proposed)
|
|
109
|
+
? { hash: sourceTips.proposed.hash, header: this.getHeaderFromSource(sourceTips.proposed.hash) }
|
|
110
|
+
: undefined;
|
|
111
|
+
|
|
103
112
|
// Walk back to find a reorg, floored at the local finalized tip (a legitimate reorg can never reach it, since
|
|
104
113
|
// finalized means the proving tx is itself L1-finalized). Seed the cache with ONLY the proposed tip: a stale
|
|
105
114
|
// tier seed at a reorged height equals the local old-fork hash, faking agreement and stopping the walk above the
|
|
@@ -169,17 +178,29 @@ export class L2BlockStream {
|
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
// Pass atomicity: a prune mid-download leaves the source unable to serve the planned blocks, so the snapshot's
|
|
172
|
-
// tier tips may reference blocks the consumer never saw —
|
|
173
|
-
// has no download plan (the snapshot is one atomic getL2Tips read), so it always reconciles.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
// tier tips may reference blocks the consumer never saw — downloadBlocks aborts the pass in that case.
|
|
182
|
+
// Tips-only mode has no download plan (the snapshot is one atomic getL2Tips read), so it always reconciles.
|
|
183
|
+
const deliveredProposedHeader = this.opts.tipsOnly
|
|
184
|
+
? undefined
|
|
185
|
+
: await this.downloadBlocks(latestBlockNumber, sourceTips);
|
|
177
186
|
|
|
178
187
|
// End-of-pass reconciliation: chain-proposed fires against the pre-pass baseline (a post-prune re-read would
|
|
179
188
|
// equal the source tip and suppress it), then the tiers highest-to-lowest so the finalized <= proven <=
|
|
180
189
|
// checkpointed <= proposed invariant holds mid-pass.
|
|
181
190
|
if (localBlockIdDiffers(prePassProposed, sourceTips.proposed)) {
|
|
182
|
-
|
|
191
|
+
const availableHeader =
|
|
192
|
+
prefetch?.hash === sourceTips.proposed.hash ? await prefetch.header : deliveredProposedHeader;
|
|
193
|
+
const header = availableHeader ?? (await this.getHeaderFromSource(sourceTips.proposed.hash));
|
|
194
|
+
if (header === undefined) {
|
|
195
|
+
// Abort the pass rather than skip just this event: the later tier events must not get ahead of a
|
|
196
|
+
// proposed tip the consumer never received. Nothing advanced, so the next pass retries.
|
|
197
|
+
this.log.warn(`No header for the proposed tip; aborting this sync pass`, {
|
|
198
|
+
blockNumber: sourceTips.proposed.number,
|
|
199
|
+
blockHash: sourceTips.proposed.hash,
|
|
200
|
+
});
|
|
201
|
+
throw new AbortError();
|
|
202
|
+
}
|
|
203
|
+
await this.emitEvent({ type: 'chain-proposed', block: sourceTips.proposed, header });
|
|
183
204
|
}
|
|
184
205
|
|
|
185
206
|
const reconcileTips = pruned ? await this.localData.getL2Tips() : localTips;
|
|
@@ -222,13 +243,15 @@ export class L2BlockStream {
|
|
|
222
243
|
}
|
|
223
244
|
|
|
224
245
|
/**
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* the
|
|
228
|
-
*
|
|
229
|
-
* the source no longer has a promised block, or served a fork at the proposed height mid-pass.
|
|
246
|
+
* Delivers every block from the agreed-on point through the advertised proposed tip as `blocks-added` events,
|
|
247
|
+
* aborting the pass when the source's own answers prove that tip is no longer real.
|
|
248
|
+
* @returns The delivered tip's header, so the caller can avoid a re-fetch; undefined when there was nothing to
|
|
249
|
+
* download (already at the tip, or startingBlock past it).
|
|
230
250
|
*/
|
|
231
|
-
private async downloadBlocks(
|
|
251
|
+
private async downloadBlocks(
|
|
252
|
+
latestBlockNumber: BlockNumber,
|
|
253
|
+
sourceTips: LocalL2Tips,
|
|
254
|
+
): Promise<BlockHeader | undefined> {
|
|
232
255
|
// The post-prune cursor: the highest block number both sides agree on. Block downloads resume from here.
|
|
233
256
|
let nextBlockNumber = latestBlockNumber + 1;
|
|
234
257
|
|
|
@@ -258,12 +281,12 @@ export class L2BlockStream {
|
|
|
258
281
|
if (blocks.length === 0) {
|
|
259
282
|
// The source no longer has a block the snapshot promised: the snapshot is provably stale, so report the plan
|
|
260
283
|
// incomplete and skip reconciliation this pass.
|
|
261
|
-
this.log.warn(`Block source returned no blocks for a promised range;
|
|
284
|
+
this.log.warn(`Block source returned no blocks for a promised range; aborting this sync pass`, {
|
|
262
285
|
from: nextBlockNumber,
|
|
263
286
|
limit,
|
|
264
287
|
sourceProposed: sourceTips.proposed.number,
|
|
265
288
|
});
|
|
266
|
-
|
|
289
|
+
throw new AbortError();
|
|
267
290
|
}
|
|
268
291
|
await this.emitEvent({ type: 'blocks-added', blocks });
|
|
269
292
|
lastDeliveredBlock = blocks.at(-1)!;
|
|
@@ -271,22 +294,23 @@ export class L2BlockStream {
|
|
|
271
294
|
}
|
|
272
295
|
|
|
273
296
|
if (lastDeliveredBlock === undefined) {
|
|
274
|
-
// Loop never ran
|
|
275
|
-
|
|
297
|
+
// Loop never ran (caught up before the plan started, or startingBlock past the tip, see A-1061), so nothing
|
|
298
|
+
// was downloaded that could disprove the snapshot.
|
|
299
|
+
return undefined;
|
|
276
300
|
}
|
|
277
301
|
|
|
278
|
-
//
|
|
279
|
-
//
|
|
302
|
+
// The block delivered at the proposed height must carry the snapshot's proposed hash; a different hash means a
|
|
303
|
+
// same-height fork swap happened mid-pass, so the snapshot is stale.
|
|
280
304
|
const deliveredHash = (await lastDeliveredBlock.hash()).toString();
|
|
281
305
|
if (deliveredHash !== sourceTips.proposed.hash) {
|
|
282
|
-
this.log.warn(`Delivered proposed-block hash differs from snapshot;
|
|
306
|
+
this.log.warn(`Delivered proposed-block hash differs from snapshot; aborting this sync pass`, {
|
|
283
307
|
blockNumber: lastDeliveredBlock.number,
|
|
284
308
|
deliveredHash,
|
|
285
309
|
snapshotHash: sourceTips.proposed.hash,
|
|
286
310
|
});
|
|
287
|
-
|
|
311
|
+
throw new AbortError();
|
|
288
312
|
}
|
|
289
|
-
return
|
|
313
|
+
return lastDeliveredBlock.header;
|
|
290
314
|
}
|
|
291
315
|
|
|
292
316
|
/**
|
|
@@ -342,6 +366,16 @@ export class L2BlockStream {
|
|
|
342
366
|
.then(hash => hash?.toString());
|
|
343
367
|
}
|
|
344
368
|
|
|
369
|
+
private getHeaderFromSource(hash: string): Promise<BlockHeader | undefined> {
|
|
370
|
+
return this.l2BlockSource
|
|
371
|
+
.getBlockData({ hash: BlockHash.fromString(hash) })
|
|
372
|
+
.then(data => data?.header)
|
|
373
|
+
.catch(err => {
|
|
374
|
+
this.log.error(`Failed to read header for block`, err, { blockHash: hash });
|
|
375
|
+
return undefined;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
345
379
|
private async emitEvent(event: L2BlockStreamEvent) {
|
|
346
380
|
this.log.debug(
|
|
347
381
|
`Emitting ${event.type} (${
|
|
@@ -359,6 +393,9 @@ export class L2BlockStream {
|
|
|
359
393
|
}
|
|
360
394
|
}
|
|
361
395
|
|
|
396
|
+
/** A speculative proposed-tip header read, tagged with the hash it targets so a moved tip is detectable. */
|
|
397
|
+
type ProposedHeaderPrefetch = { hash: string; header: Promise<BlockHeader | undefined> };
|
|
398
|
+
|
|
362
399
|
class BlockHashCache {
|
|
363
400
|
private readonly cache: Map<number, string> = new Map();
|
|
364
401
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type L2TipId,
|
|
11
11
|
} from '@aztec/stdlib/block';
|
|
12
12
|
import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
13
|
+
import { BlockHeader } from '@aztec/stdlib/tx';
|
|
13
14
|
|
|
14
15
|
import { expect } from 'vitest';
|
|
15
16
|
|
|
@@ -132,6 +133,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
132
133
|
await tipsStore.handleBlockStreamEvent({
|
|
133
134
|
type: 'chain-proposed',
|
|
134
135
|
block: { number: BlockNumber(5), hash: new Fr(500).toString() },
|
|
136
|
+
header: BlockHeader.empty(),
|
|
135
137
|
});
|
|
136
138
|
|
|
137
139
|
const tips = await tipsStore.getL2Tips();
|
|
@@ -145,10 +147,12 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
145
147
|
await tipsStore.handleBlockStreamEvent({
|
|
146
148
|
type: 'chain-proposed',
|
|
147
149
|
block: { number: BlockNumber(4), hash: new Fr(400).toString() },
|
|
150
|
+
header: BlockHeader.empty(),
|
|
148
151
|
});
|
|
149
152
|
await tipsStore.handleBlockStreamEvent({
|
|
150
153
|
type: 'chain-proposed',
|
|
151
154
|
block: { number: BlockNumber(9), hash: new Fr(900).toString() },
|
|
155
|
+
header: BlockHeader.empty(),
|
|
152
156
|
});
|
|
153
157
|
|
|
154
158
|
// Both recorded heights resolve; the heights between them were never seen and stay undefined.
|
|
@@ -165,6 +169,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise<L2TipsStore>) {
|
|
|
165
169
|
await tipsStore.handleBlockStreamEvent({
|
|
166
170
|
type: 'chain-proposed',
|
|
167
171
|
block: { number: BlockNumber(9), hash: new Fr(900).toString() },
|
|
172
|
+
header: BlockHeader.empty(),
|
|
168
173
|
});
|
|
169
174
|
|
|
170
175
|
// Record sparse witnesses at heights below the tip (e.g. a consumer materializing per-height state).
|