@mikeargento/bitgraph-audit 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/audit.d.ts +8 -6
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +28 -8
- package/dist/audit.js.map +1 -1
- package/dist/ceilings.d.ts +25 -0
- package/dist/ceilings.d.ts.map +1 -1
- package/dist/ceilings.js +159 -31
- package/dist/ceilings.js.map +1 -1
- package/dist/cli.js +40 -3
- package/dist/cli.js.map +1 -1
- package/dist/exports.d.ts +39 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/exports.js +371 -0
- package/dist/exports.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest.d.ts.map +1 -1
- package/dist/ingest.js +179 -19
- package/dist/ingest.js.map +1 -1
- package/dist/report-json.d.ts.map +1 -1
- package/dist/report-json.js +15 -0
- package/dist/report-json.js.map +1 -1
- package/dist/report-md.d.ts.map +1 -1
- package/dist/report-md.js +225 -0
- package/dist/report-md.js.map +1 -1
- package/dist/settlement-blobs.d.ts +131 -0
- package/dist/settlement-blobs.d.ts.map +1 -0
- package/dist/settlement-blobs.js +682 -0
- package/dist/settlement-blobs.js.map +1 -0
- package/dist/types.d.ts +254 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/__tests__/fixtures/settlement/0x018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5c.bin +0 -0
- package/src/__tests__/fixtures/settlement/ceiling-51979918.json +101 -0
- package/src/__tests__/fixtures/settlement/pointer-51979918.json +69 -0
- package/src/__tests__/settlement.test.ts +254 -0
- package/src/audit.ts +30 -8
- package/src/ceilings.ts +167 -33
- package/src/cli.ts +39 -3
- package/src/exports.ts +478 -0
- package/src/index.ts +16 -0
- package/src/ingest.ts +192 -18
- package/src/report-json.ts +15 -0
- package/src/report-md.ts +248 -0
- package/src/settlement-blobs.ts +689 -0
- package/src/types.ts +234 -2
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "bitgraph-ceiling/1",
|
|
3
|
+
"proofHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
|
4
|
+
"leafIndex": 0,
|
|
5
|
+
"leafCount": 1,
|
|
6
|
+
"merklePath": [],
|
|
7
|
+
"root": "0xdbd541269066e97c8086b20644de7a849db0e6870c6a99be5d102c7a5a72ed37",
|
|
8
|
+
"anchor": {
|
|
9
|
+
"chainId": 8453,
|
|
10
|
+
"writer": "0xf3972408d853c975f86351c311f4310220bbf2a3",
|
|
11
|
+
"txHash": "0x72cc33632237a96db749836905e92e45bd5cc4167403bc7970a69f250b3e9450",
|
|
12
|
+
"rawTx": "0x02f8c082210581f3830f424083a7d8c082721594f3972408d853c975f86351c311f4310220bbf2a380b85442474331dbd541269066e97c8086b20644de7a849db0e6870c6a99be5d102c7a5a72ed378ba45845d1e6e1f87b07275c96aed4b0dfe64d88ef6a654d8029915798526c2f00000000000011c200000000000011c2c001a0e5af8d93a6d54d30963c11ca829d957cd11ee66531caa49c0ffde149bb5f00cca00a390e583140cf6c1050c432659d36312f5557d947e2981ca65d79f0967753bb",
|
|
13
|
+
"payload": "0x42474331dbd541269066e97c8086b20644de7a849db0e6870c6a99be5d102c7a5a72ed378ba45845d1e6e1f87b07275c96aed4b0dfe64d88ef6a654d8029915798526c2f00000000000011c200000000000011c2",
|
|
14
|
+
"blockNumber": 51979918,
|
|
15
|
+
"blockHash": "0x6844d628876ff2663cbd7f8b9bc39b0dc9a86a1d18d68ac26474ea63c6a81d6f",
|
|
16
|
+
"blockTimestamp": 1790749183,
|
|
17
|
+
"blockHeader": "0xf9027aa0d2b1d2cece1bd23dcd34da3463ce2aa9b78da0f3234d1a5bd2d6c328b3a597daa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347944200000000000000000000000000000000000011a06e3c7ae613005ce466f8654717054737c807f4c247710a888dc96d632c14fe97a013c5fda778ed00c84d82e83371f76758e6f27d06c9f201b831f0a1a65b8186bca0ba92bdf88d5ca0474870d955f92010c8f37943e3f61a3e0cc82eab04e4807be9b9010094f1573bb7737459cee15197e72e33f6bcf74c3062eed8912ba5b611b056aced87b8ef52b2f7a57fd755cf51ab0bf738cf4e934796d8330eb7b10f99df345f90fe5a3a96ecffa53cf60f9539814d66bd04c7281f9b7f3bf90371623fdde3d2fc258924b2d2ce96eaf79e0a419f972b2ee3c63a9ecf0e16d9acc6dc5e525f47f7bb14fb8a7f22771cc58da6dbe5fb13b1f9f1beb95834c25e0c0ae1f4758d9d9da74c061bd2c174b86dd6c1d85ba6f2f7f695d247bc5bc47fb56327ed170740c16992737fc89b9efd19f37cf52969d8c35f5d8dcfb526269a31a3d5df9b29ef33cc14ae8117876c1030c8ba293e8337fa3416bd06b1a665e716b2b8923fb95efc80840319268e8417d78400840366d750846abca9ff9101000000640000000500000000004c4b40a07c729a72f790609e3fded11acc8e07e327fc851192e4853b250d6c112734a8c5880000000000000000834c4b40a0f3e930c14f5657a847200d37072baaeec520c0b18bb1a8e96447993e9073c1128385083080a0f42330fe8a6ac89a286b0998d1d8b64e4b589f20d2c9bfd7eeb8f8825d265a29a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
18
|
+
"txIndex": 39,
|
|
19
|
+
"txInclusionProof": [
|
|
20
|
+
"0xf90131a0be69804be0dcbda3bf05fe812834f3fdef9b549da09fb46ef3229ac7c9fca434a01552d324f554fa4dc75302b67c8d4f6c95839ef6f102566ce5b649bc8069b1e4a05d37222dbcb761956d3f49f5fea434bc54d8f11cc14f2f8491bc2d0f1d6a3004a05f392fdfb31e99909eecf8d2210b3c9efd691bccb07ba8eefc68bc27702feaf3a0dce3951291ca16a11fecd7d780e8f190ec057d7d395979f4001b81ecb9de7bfda06068fb78d9c115e07459c7755194de9392eda67032a12811ec061f8aabc800eba06b8c29af59947deacfda56a72e47e915aa7bbc269ad2ff4f2831c9b6fa41f1bda00cbe0a4ed611ad32b317dd2b9aee8662cf868aed745b90fec59e470f122ea52ea09ccfbc70703bed4fc45d995163f8f8555d3bdc29ae82a33aab2910cf096ecf508080808080808080",
|
|
21
|
+
"0xf90211a0eed9ea4758bfef968546887fedbf9d83e9d2094be7430d186075efa340cf94c4a05603f370f56610111175bad92731a1a09521eac2bbc78deb03e9ce72c7c608bea0f2c2ce9ffc9c3317bf2069d29fb317f19de67072f957c97800ffda8936d00714a00c2c1f480138c2773dc45a5a4d73fa127ad63bfdd48bf050da3a44d333c02e3ea048eddd549ad1f2d13ea9615989b0964881791b701580ad711e0a1a73f274cf4ea0105f1b3bd8f70008d4f5cb6034cf944f6eb0950ce3cc2e10aaffcae685687a87a029c9dfb9c363286f24a04fcee995319181dcb2ec5ecd128bd4d947d542881d60a09ce8bc98c5a8a830c417dacf11ec975fbf91d979ba9b46241d62761abf30f219a03c115a97f043c0beb662747fe5b05ecbe9b5f0b62a230e3d3836860d396061bca040204f0d3070c4f23f52d74ee26f18239e0aa5f1cee64344f8aba4f8afe267afa003471fdcd2e9229871f47179c0fc8a631fcce1ea6c873e34ff4d637da79d0d3da09e6227d6a81591bbaa1ab0c2ace4071e65ec765d335e9fe87428f8d40e6bed2fa052771472ba3d590e71e0bf36d637ec7e1352ed1c77a0eb7f30d67eda5fd1d0dea0a2c479c65c952d40233c15ac4ec831d1c96966d2596113c1e3dd83f87cb26266a03b3018cdfc612efc6a52506030634e07bf32f3cd362a3803e3e9919c01b0e181a02454e7ab077d054cf58ae4413cfd02ccc9b4d313ef1737356bcaa6d7693237ad80",
|
|
22
|
+
"0xf8c620b8c302f8c082210581f3830f424083a7d8c082721594f3972408d853c975f86351c311f4310220bbf2a380b85442474331dbd541269066e97c8086b20644de7a849db0e6870c6a99be5d102c7a5a72ed378ba45845d1e6e1f87b07275c96aed4b0dfe64d88ef6a654d8029915798526c2f00000000000011c200000000000011c2c001a0e5af8d93a6d54d30963c11ca829d957cd11ee66531caa49c0ffde149bb5f00cca00a390e583140cf6c1050c432659d36312f5557d947e2981ca65d79f0967753bb"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"status": "finalized",
|
|
26
|
+
"statusObserved": {
|
|
27
|
+
"included": "2026-09-30T06:19:47.000Z",
|
|
28
|
+
"safe": "2026-09-30T06:21:00.000Z",
|
|
29
|
+
"finalized": "2026-09-30T06:35:00.000Z"
|
|
30
|
+
},
|
|
31
|
+
"floor": null,
|
|
32
|
+
"settlement": {
|
|
33
|
+
"version": "bitgraph-settlement/1",
|
|
34
|
+
"baseBlockNumber": 51979918,
|
|
35
|
+
"baseBlockHash": "0x6844d628876ff2663cbd7f8b9bc39b0dc9a86a1d18d68ac26474ea63c6a81d6f",
|
|
36
|
+
"l1": {
|
|
37
|
+
"chainId": 1,
|
|
38
|
+
"txHash": "0xcb575f2482a33ce5678997adcb082d4b68f7927c932a07a7a89090e61c6ea63a",
|
|
39
|
+
"rawTx": "0x03f9013b01832571d584959b8f808501c0d2ae8082520894ff000000000000000000000000000000000084538080c0843b9aca00f8c6a0018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5ca001c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4a00118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79ba00120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2a0013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17a001b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d08302701a0f48e45e81f193edea8a7f2e319169201ea084490a282ebfa944a53155125ba90a037f9784866387921294f047d9e00eae3acf249add9880889f797784c496ea9b1",
|
|
40
|
+
"blockNumber": 26088463,
|
|
41
|
+
"blockHash": "0x8608654cd9d6d66c2ce8a80108db7e5202a6d9ad0072f8c81353cc988d3a93f2",
|
|
42
|
+
"blockTimestamp": 1790749235,
|
|
43
|
+
"blockHeader": "0xf90289a0bd893b925b3482c78646c1df70696e0d301cd5d09dcc34dc2ecc24441e4bc40ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794396343362be2a4da1ce0c1c210945346fb82aa49a0e6bd281387b53a52427452d620531da5d0c670d1a72feacf6d4534700bbdad93a083e423e98cf59e848d311a37f7b9511e8ecbdf2f69a3d9a0a96df448eb593dd7a0c31336933abc1b6e3e3f2c8dbbe2ccc65d4bb9179e665cbb7a06b7ff14085242b90100fff3f3fbffffb9fdefffebfdfffebfdfdfdfffbdfebffffefbfff5f7fefeffebbedfffffeffdff7ff7f2d7cbbdfbfffbbffffdffdfffefe71fbfefb9fffebfffffffffdf7efffff9797fedffdf7fffbbeef7ffbd7e5efdbfffff7fbfbff7fbffeb7ffffeabef6efcffffbfff6fbfffbff3ff7dffafff7edfbefffff7fdff7fbf9b74bffdfefd7fffffeddc737ffa5fffff7fdde9f7f7bffff7bf6f7ffdfcffffdf7dbfffffffbfdfffffdff7f7effffbffdfdfffbeafffbfff7dbaffffffefe7d7e37faf77dffef3ffcffdfa75fe7b7fbd77fe7fe7ffff77ff3fffffffdfffffffdfff7ff7fffffffffe7fbdafbffffeffffb77fe7ffff7dfbfbfff7dbf6ffdf8084018e140f84039387008401ff939d846abcaa339be29ca82051756173617220287175617361722e77696e2920e29ca8a0aec208249057820706989e2a8d4882c6157bffb6f112847ca25c02b90f8bb17088000000000000000084057644a9a096b80d5bb80122a62a3905a4df56e93396c4cf0170a22efb1fdb0f1fe35307b3830e0000840ac7fb8ba0aa4880a6b37d3438d341144ce67680506b21b26e54a90835ce3e8aee0b44a3c9a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
44
|
+
"txIndex": 18,
|
|
45
|
+
"txInclusionProof": [
|
|
46
|
+
"0xf90131a0fcf4c1b6c3daaf8b4161626c0ed9ed20d4dda4438ac031b46a3a591c70ae3628a0d7b589907c8a5208ea55ae515cf9ee745d8e345f70260f3209e8056af35dbb2fa06ed025ba6929892c332be304b1cae217c390e16e73947c1777023bd6f9aad935a0995d136362f36671287a4eed2b7d0a6648b6d09d1e4c0cbfbf505b40a6452b4fa0c8396e60f6bd3f7eb429d358ecacc46ff149613bf5178c0664dc5070d8e8733ca0aa6c0b1d5e761bf8099cad7b1490374d7a919e439972be8c20a1ae13d1f034c9a0c5836776069aed36f670ecbbb053414f1e1abb8d269b2ce5e48bd45e5cfd768aa07968a3dc7587bd078c625feac0c52804e41040666b8c5e6153c863b1364a5551a013bd6039d33245072b97fc66551522295d420529896ab74387cabdcf9f3d725f8080808080808080",
|
|
47
|
+
"0xf90211a0156d28ae6d5df3abc7463d2cec2ac7fb426e48340f7f88ae36a81ca5f9dee8f6a09beca8566f0cba30be3f059b6aeae896fd3eab724eb84c9a10908c6009601945a07313d28dc0816c86e30466c06ab5474f24af70211db48fa9a734015534bd74d8a088f7968cbd63613ef3b06122a44ebcc7365dd75589f18cc58fce72064e0a0b1ea02b68f6d1691bce365cb8830fcf7543b95d42efe211e953b24cf61db79cb7e960a0325f1a33b83b8d23d38db039878112ee1d5eab679dbce8a41ebce109978c7e33a0425202aa631cc52afc7cccb657d001584be56cd3750ef4d0e131396bb2fd8a9aa0c1e1bcba129c311b46690757edd4c01a78fede9ed22c8ba1ac5fa07798d92f50a036a0cf8638d4d02175c4fc6871f58280d33c04eb543477a8ab214e270b25d0c0a0c60e6d24a1fe65c208f72e2a2449e50d2c02e001d1982c88d20a01ab5dcf8532a05ad4837ed4e5635fb687c5ae2129dea7c861ddffbbee442f5ad9faeb49ea5a07a01c74851d4ab86eec6c7bef0da2c9346ce8b52435a54f8570ade2d8c3c6e72861a0ff11b81c0f0a9fc418d4b9ec7b038c325a0e4d136ea0ffd2ddeba867ce4b377fa0b198d867690556a5dccc61c0e550be17b56ce3e6c5c1cbb20cc08892da37dec3a04bd945772c230c04f49eced9fe8f7d56d1f2c7f74143d35a81ce366c5fec91f2a0d01d267cecd0009859471b913c6d58150bde9a1f9c23e820fb0e723c70ddd77f80",
|
|
48
|
+
"0xf9014320b9013f03f9013b01832571d584959b8f808501c0d2ae8082520894ff000000000000000000000000000000000084538080c0843b9aca00f8c6a0018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5ca001c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4a00118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79ba00120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2a0013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17a001b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d08302701a0f48e45e81f193edea8a7f2e319169201ea084490a282ebfa944a53155125ba90a037f9784866387921294f047d9e00eae3acf249add9880889f797784c496ea9b1"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"blobs": [
|
|
52
|
+
{
|
|
53
|
+
"versionedHash": "0x018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5c",
|
|
54
|
+
"kzgCommitment": "0x803d2d67bd32b2926c8c1f86146f88c2eced163e985d6109c5f399b636f3a81574f46e235ec0bf8138131fdf1ef0ea48",
|
|
55
|
+
"index": 0,
|
|
56
|
+
"file": "0x018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5c.bin"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"versionedHash": "0x01c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4",
|
|
60
|
+
"kzgCommitment": "0x98995a21fb0e2f4e6be022dc43b93a1112d374ac646a8e9589a37708416803db825d5c14de8f26ccb0e1c5681b8321fa",
|
|
61
|
+
"index": 1,
|
|
62
|
+
"file": "0x01c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4.bin"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"versionedHash": "0x0118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79b",
|
|
66
|
+
"kzgCommitment": "0x80f0d5947c18906d06fc484165fb9d03b3e5dce7ac42f6cf7ceaae26eb8618d89950425af9fef93e3742c016b8fd801e",
|
|
67
|
+
"index": 2,
|
|
68
|
+
"file": "0x0118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79b.bin"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"versionedHash": "0x0120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2",
|
|
72
|
+
"kzgCommitment": "0x86727febe394e5f86626a81ce56daa98aa4a1c1a30ce3c3b8fe4dc66755f78ede7b12a073337ef39defaaafd6d6c013d",
|
|
73
|
+
"index": 3,
|
|
74
|
+
"file": "0x0120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2.bin"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"versionedHash": "0x013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17",
|
|
78
|
+
"kzgCommitment": "0x933c609081278e6656f1a772b48597ce5e034e316572182cee0e7814fda66f11e5bdf5988ea6483ab16b8280afda9574",
|
|
79
|
+
"index": 4,
|
|
80
|
+
"file": "0x013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17.bin"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"versionedHash": "0x01b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d083027",
|
|
84
|
+
"kzgCommitment": "0x8d89e4a886782ebac5e2a188268a8bc26485abc259b281dac91a889aaa13c1e3ef8e65577029bc217eabb7d7da774f8a",
|
|
85
|
+
"index": 5,
|
|
86
|
+
"file": "0x01b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d083027.bin"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"channel": {
|
|
90
|
+
"id": "0x42b5f4f4249919699b40a61bc497f4b5",
|
|
91
|
+
"frames": 7,
|
|
92
|
+
"compression": "brotli",
|
|
93
|
+
"firstBaseBlock": 51979915,
|
|
94
|
+
"lastBaseBlock": 51979939
|
|
95
|
+
},
|
|
96
|
+
"located": {
|
|
97
|
+
"baseTxIndex": 38,
|
|
98
|
+
"txHash": "0x72cc33632237a96db749836905e92e45bd5cc4167403bc7970a69f250b3e9450"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "bitgraph-settlement/1",
|
|
3
|
+
"baseBlockNumber": 51979918,
|
|
4
|
+
"baseBlockHash": "0x6844d628876ff2663cbd7f8b9bc39b0dc9a86a1d18d68ac26474ea63c6a81d6f",
|
|
5
|
+
"l1": {
|
|
6
|
+
"chainId": 1,
|
|
7
|
+
"txHash": "0xcb575f2482a33ce5678997adcb082d4b68f7927c932a07a7a89090e61c6ea63a",
|
|
8
|
+
"rawTx": "0x03f9013b01832571d584959b8f808501c0d2ae8082520894ff000000000000000000000000000000000084538080c0843b9aca00f8c6a0018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5ca001c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4a00118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79ba00120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2a0013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17a001b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d08302701a0f48e45e81f193edea8a7f2e319169201ea084490a282ebfa944a53155125ba90a037f9784866387921294f047d9e00eae3acf249add9880889f797784c496ea9b1",
|
|
9
|
+
"blockNumber": 26088463,
|
|
10
|
+
"blockHash": "0x8608654cd9d6d66c2ce8a80108db7e5202a6d9ad0072f8c81353cc988d3a93f2",
|
|
11
|
+
"blockTimestamp": 1790749235,
|
|
12
|
+
"blockHeader": "0xf90289a0bd893b925b3482c78646c1df70696e0d301cd5d09dcc34dc2ecc24441e4bc40ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794396343362be2a4da1ce0c1c210945346fb82aa49a0e6bd281387b53a52427452d620531da5d0c670d1a72feacf6d4534700bbdad93a083e423e98cf59e848d311a37f7b9511e8ecbdf2f69a3d9a0a96df448eb593dd7a0c31336933abc1b6e3e3f2c8dbbe2ccc65d4bb9179e665cbb7a06b7ff14085242b90100fff3f3fbffffb9fdefffebfdfffebfdfdfdfffbdfebffffefbfff5f7fefeffebbedfffffeffdff7ff7f2d7cbbdfbfffbbffffdffdfffefe71fbfefb9fffebfffffffffdf7efffff9797fedffdf7fffbbeef7ffbd7e5efdbfffff7fbfbff7fbffeb7ffffeabef6efcffffbfff6fbfffbff3ff7dffafff7edfbefffff7fdff7fbf9b74bffdfefd7fffffeddc737ffa5fffff7fdde9f7f7bffff7bf6f7ffdfcffffdf7dbfffffffbfdfffffdff7f7effffbffdfdfffbeafffbfff7dbaffffffefe7d7e37faf77dffef3ffcffdfa75fe7b7fbd77fe7fe7ffff77ff3fffffffdfffffffdfff7ff7fffffffffe7fbdafbffffeffffb77fe7ffff7dfbfbfff7dbf6ffdf8084018e140f84039387008401ff939d846abcaa339be29ca82051756173617220287175617361722e77696e2920e29ca8a0aec208249057820706989e2a8d4882c6157bffb6f112847ca25c02b90f8bb17088000000000000000084057644a9a096b80d5bb80122a62a3905a4df56e93396c4cf0170a22efb1fdb0f1fe35307b3830e0000840ac7fb8ba0aa4880a6b37d3438d341144ce67680506b21b26e54a90835ce3e8aee0b44a3c9a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
13
|
+
"txIndex": 18,
|
|
14
|
+
"txInclusionProof": [
|
|
15
|
+
"0xf90131a0fcf4c1b6c3daaf8b4161626c0ed9ed20d4dda4438ac031b46a3a591c70ae3628a0d7b589907c8a5208ea55ae515cf9ee745d8e345f70260f3209e8056af35dbb2fa06ed025ba6929892c332be304b1cae217c390e16e73947c1777023bd6f9aad935a0995d136362f36671287a4eed2b7d0a6648b6d09d1e4c0cbfbf505b40a6452b4fa0c8396e60f6bd3f7eb429d358ecacc46ff149613bf5178c0664dc5070d8e8733ca0aa6c0b1d5e761bf8099cad7b1490374d7a919e439972be8c20a1ae13d1f034c9a0c5836776069aed36f670ecbbb053414f1e1abb8d269b2ce5e48bd45e5cfd768aa07968a3dc7587bd078c625feac0c52804e41040666b8c5e6153c863b1364a5551a013bd6039d33245072b97fc66551522295d420529896ab74387cabdcf9f3d725f8080808080808080",
|
|
16
|
+
"0xf90211a0156d28ae6d5df3abc7463d2cec2ac7fb426e48340f7f88ae36a81ca5f9dee8f6a09beca8566f0cba30be3f059b6aeae896fd3eab724eb84c9a10908c6009601945a07313d28dc0816c86e30466c06ab5474f24af70211db48fa9a734015534bd74d8a088f7968cbd63613ef3b06122a44ebcc7365dd75589f18cc58fce72064e0a0b1ea02b68f6d1691bce365cb8830fcf7543b95d42efe211e953b24cf61db79cb7e960a0325f1a33b83b8d23d38db039878112ee1d5eab679dbce8a41ebce109978c7e33a0425202aa631cc52afc7cccb657d001584be56cd3750ef4d0e131396bb2fd8a9aa0c1e1bcba129c311b46690757edd4c01a78fede9ed22c8ba1ac5fa07798d92f50a036a0cf8638d4d02175c4fc6871f58280d33c04eb543477a8ab214e270b25d0c0a0c60e6d24a1fe65c208f72e2a2449e50d2c02e001d1982c88d20a01ab5dcf8532a05ad4837ed4e5635fb687c5ae2129dea7c861ddffbbee442f5ad9faeb49ea5a07a01c74851d4ab86eec6c7bef0da2c9346ce8b52435a54f8570ade2d8c3c6e72861a0ff11b81c0f0a9fc418d4b9ec7b038c325a0e4d136ea0ffd2ddeba867ce4b377fa0b198d867690556a5dccc61c0e550be17b56ce3e6c5c1cbb20cc08892da37dec3a04bd945772c230c04f49eced9fe8f7d56d1f2c7f74143d35a81ce366c5fec91f2a0d01d267cecd0009859471b913c6d58150bde9a1f9c23e820fb0e723c70ddd77f80",
|
|
17
|
+
"0xf9014320b9013f03f9013b01832571d584959b8f808501c0d2ae8082520894ff000000000000000000000000000000000084538080c0843b9aca00f8c6a0018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5ca001c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4a00118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79ba00120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2a0013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17a001b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d08302701a0f48e45e81f193edea8a7f2e319169201ea084490a282ebfa944a53155125ba90a037f9784866387921294f047d9e00eae3acf249add9880889f797784c496ea9b1"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"blobs": [
|
|
21
|
+
{
|
|
22
|
+
"versionedHash": "0x018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5c",
|
|
23
|
+
"kzgCommitment": "0x803d2d67bd32b2926c8c1f86146f88c2eced163e985d6109c5f399b636f3a81574f46e235ec0bf8138131fdf1ef0ea48",
|
|
24
|
+
"index": 0,
|
|
25
|
+
"file": "0x018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5c.bin"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"versionedHash": "0x01c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4",
|
|
29
|
+
"kzgCommitment": "0x98995a21fb0e2f4e6be022dc43b93a1112d374ac646a8e9589a37708416803db825d5c14de8f26ccb0e1c5681b8321fa",
|
|
30
|
+
"index": 1,
|
|
31
|
+
"file": "0x01c01a93e0e3cd2212776aa2d4cc4e0c07c85cb75b968525f4e5859bae6773c4.bin"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"versionedHash": "0x0118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79b",
|
|
35
|
+
"kzgCommitment": "0x80f0d5947c18906d06fc484165fb9d03b3e5dce7ac42f6cf7ceaae26eb8618d89950425af9fef93e3742c016b8fd801e",
|
|
36
|
+
"index": 2,
|
|
37
|
+
"file": "0x0118b59a40847e830edd78f12799a36b697087938f5be61d6aca024f98c9b79b.bin"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"versionedHash": "0x0120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2",
|
|
41
|
+
"kzgCommitment": "0x86727febe394e5f86626a81ce56daa98aa4a1c1a30ce3c3b8fe4dc66755f78ede7b12a073337ef39defaaafd6d6c013d",
|
|
42
|
+
"index": 3,
|
|
43
|
+
"file": "0x0120c9a9f71037bbe073b4979641a40642a8d6e9babb851bd7e513981f1316d2.bin"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"versionedHash": "0x013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17",
|
|
47
|
+
"kzgCommitment": "0x933c609081278e6656f1a772b48597ce5e034e316572182cee0e7814fda66f11e5bdf5988ea6483ab16b8280afda9574",
|
|
48
|
+
"index": 4,
|
|
49
|
+
"file": "0x013b850872322a5032994968f043cac34ddf72cb04e387d6b86ac268990b5a17.bin"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"versionedHash": "0x01b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d083027",
|
|
53
|
+
"kzgCommitment": "0x8d89e4a886782ebac5e2a188268a8bc26485abc259b281dac91a889aaa13c1e3ef8e65577029bc217eabb7d7da774f8a",
|
|
54
|
+
"index": 5,
|
|
55
|
+
"file": "0x01b2946a71b7c1e08dff34496e274da1e5ef2dbedd62ed6bab6b07c12d083027.bin"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"channel": {
|
|
59
|
+
"id": "0x42b5f4f4249919699b40a61bc497f4b5",
|
|
60
|
+
"frames": 7,
|
|
61
|
+
"compression": "brotli",
|
|
62
|
+
"firstBaseBlock": 51979915,
|
|
63
|
+
"lastBaseBlock": 51979939
|
|
64
|
+
},
|
|
65
|
+
"located": {
|
|
66
|
+
"baseTxIndex": 38,
|
|
67
|
+
"txHash": "0x72cc33632237a96db749836905e92e45bd5cc4167403bc7970a69f250b3e9450"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// Copyright (c) 2024-2026 Argento Computing Inc.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The blob layer of a ceiling's settlement on Ethereum, on REAL data.
|
|
5
|
+
*
|
|
6
|
+
* Base block 51,979,918 holds the ceiling transaction 0x72cc…9450. Its batch
|
|
7
|
+
* went to Ethereum block 26,088,463 in channel 0x42b5…f4b5: 7 frames in 7
|
|
8
|
+
* blobs across two batcher transactions (0xcb57…a63a carries frames 0-5,
|
|
9
|
+
* 0x6165…0190 frame 6). The bundle carries ONE blob, frame 0 (131,072
|
|
10
|
+
* bytes): the prototype showed that frame 0 alone decompresses to 577,321
|
|
11
|
+
* bytes, past the ceiling transaction at offset 501,594, so the transaction
|
|
12
|
+
* is located from that one blob while the batch holding it is cut (it ends
|
|
13
|
+
* at 650,075). Decoding the whole channel needs all 7 blobs, which the repo
|
|
14
|
+
* does not carry. Nothing here touches a network.
|
|
15
|
+
*
|
|
16
|
+
* Run, after `npm run build` in packages/audit:
|
|
17
|
+
* node --test packages/audit/src/__tests__/settlement.test.ts
|
|
18
|
+
*/
|
|
19
|
+
import { test, describe, before, after } from "node:test";
|
|
20
|
+
import assert from "node:assert/strict";
|
|
21
|
+
import { readFileSync } from "node:fs";
|
|
22
|
+
import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
|
|
23
|
+
import { tmpdir } from "node:os";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import {
|
|
26
|
+
verifySettlementBlobs, decodeOpBlob, parseFrames, decodeSpanBatch, verifyCeilings, ingestEntries, runAudit, computeExitFlags,
|
|
27
|
+
buildMarkdownReport, BASE_MAINNET_ROLLUP, BLOB_BYTES,
|
|
28
|
+
} from "@mikeargento/bitgraph-audit";
|
|
29
|
+
import type { SettlementBlobsOptions } from "@mikeargento/bitgraph-audit";
|
|
30
|
+
import {
|
|
31
|
+
verifySettlementPointer, BASE_MAINNET_SETTLEMENT_PINS, evmHexToBytes as hexToBytes, evmBytesToHex as bytesToHex, keccak256, rlpEncode,
|
|
32
|
+
type SettlementPointer,
|
|
33
|
+
} from "@mikeargento/bitgraph-verify";
|
|
34
|
+
|
|
35
|
+
const fixtureUrl = (name: string): URL => new URL(`./fixtures/settlement/${name}`, import.meta.url);
|
|
36
|
+
const pointer = JSON.parse(readFileSync(fixtureUrl("pointer-51979918.json"), "utf8")) as SettlementPointer;
|
|
37
|
+
const ceilingText = readFileSync(fixtureUrl("ceiling-51979918.json"), "utf8");
|
|
38
|
+
const ceiling = JSON.parse(ceilingText) as { anchor: { txHash: string; rawTx: string; blockNumber: number; blockHash: string } };
|
|
39
|
+
const VH0 = "0x018efc046e94610e2530344caf1574e507a3182e1a975fb25b032ec1434a3f5c";
|
|
40
|
+
const BLOB_FILE = `${VH0}.bin`;
|
|
41
|
+
const blob0 = new Uint8Array(readFileSync(fixtureUrl(BLOB_FILE)));
|
|
42
|
+
const CHANNEL = "0x42b5f4f4249919699b40a61bc497f4b5";
|
|
43
|
+
const CEILING_TX = "0x72cc33632237a96db749836905e92e45bd5cc4167403bc7970a69f250b3e9450";
|
|
44
|
+
const BASE_PARENT = "0xd2b1d2cece1bd23dcd34da3463ce2aa9b78da0f3234d1a5bd2d6c328b3a597da";
|
|
45
|
+
const clone = <T>(x: T): T => JSON.parse(JSON.stringify(x)) as T;
|
|
46
|
+
const blobs = (bytes: Uint8Array = blob0, vh = VH0): Map<string, Uint8Array> => new Map([[vh, bytes]]);
|
|
47
|
+
const expect: SettlementBlobsOptions["expect"] = { txHash: ceiling.anchor.txHash, rawTx: ceiling.anchor.rawTx, baseParentHash: BASE_PARENT };
|
|
48
|
+
|
|
49
|
+
describe("the real blob (frame 0 of the channel)", () => {
|
|
50
|
+
test("decodes to one frame of the channel, the pointer layer accepts the pointer", () => {
|
|
51
|
+
assert.equal(blob0.length, BLOB_BYTES);
|
|
52
|
+
const data = decodeOpBlob(blob0);
|
|
53
|
+
assert.equal(data.length, 130044);
|
|
54
|
+
const frames = parseFrames(data);
|
|
55
|
+
assert.equal(frames.length, 1);
|
|
56
|
+
assert.deepEqual({ ...frames[0], frameData: frames[0].frameData.length }, { channelId: CHANNEL, frameNumber: 0, frameData: 130020, isLast: false });
|
|
57
|
+
assert.equal(verifySettlementPointer(pointer, BASE_MAINNET_SETTLEMENT_PINS).ok, true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("KZG-verified, decoded as a prefix, the ceiling transaction is located in Base block 51,979,918 at index 38", async () => {
|
|
61
|
+
const r = await verifySettlementBlobs(pointer, blobs(), { expect });
|
|
62
|
+
assert.equal(r.ok, true, r.reason);
|
|
63
|
+
assert.deepEqual(r.checks.map((c) => c.name), ["blob 0", "channel", "decompress", "batches", "block", "transaction"]);
|
|
64
|
+
assert.ok(r.checks.every((c) => c.ok));
|
|
65
|
+
assert.match(r.checks[0].detail!, /KZG commitment matches/);
|
|
66
|
+
assert.deepEqual(r.channel, { id: CHANNEL, framesDecoded: 1, framesTotal: 7, complete: false, compression: "brotli", decodedBytes: 577321, baseBlocks: [51979915, 51979916, 51979917] });
|
|
67
|
+
assert.deepEqual(r.located, {
|
|
68
|
+
baseBlockNumber: 51979918, baseBlockTimestamp: 1790749183, baseParentHash: BASE_PARENT, baseTxIndex: 38, txHash: CEILING_TX,
|
|
69
|
+
rawTx: ceiling.anchor.rawTx, batchComplete: false,
|
|
70
|
+
});
|
|
71
|
+
assert.match(r.checks[5].detail!, /bytes equal the ceiling's rawTx/);
|
|
72
|
+
assert.match(r.checks[4].detail!, /parent 0xd2b1d2ce…97da as the ceiling's header says/);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("without expectations, the pointer's own located.txHash is what gets located", async () => {
|
|
76
|
+
const r = await verifySettlementBlobs(pointer, blobs());
|
|
77
|
+
assert.equal(r.ok, true, r.reason);
|
|
78
|
+
assert.equal(r.located!.baseTxIndex, 38);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("the 4,067,670-byte channel is not decodable whole from one blob, and the audit says so", async () => {
|
|
82
|
+
const p = clone(pointer);
|
|
83
|
+
p.baseBlockNumber = 51979930; // in the channel, past what frame 0 reaches
|
|
84
|
+
const r = await verifySettlementBlobs(p, blobs());
|
|
85
|
+
assert.equal(r.ok, false);
|
|
86
|
+
assert.match(r.reason!, /^block: Base block 51979930 is not among the decoded batches/);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("rejects", () => {
|
|
91
|
+
const rejects = (name: string, mk: () => { p?: SettlementPointer; b?: Map<string, Uint8Array>; o?: SettlementBlobsOptions }, re: RegExp): void => {
|
|
92
|
+
test(name, async () => {
|
|
93
|
+
const { p, b, o } = mk();
|
|
94
|
+
const r = await verifySettlementBlobs(p ?? pointer, b ?? blobs(), o ?? { expect });
|
|
95
|
+
assert.equal(r.ok, false, `accepted: ${name}`);
|
|
96
|
+
assert.match(r.reason!, re);
|
|
97
|
+
assert.equal(r.located, undefined);
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
rejects("a flipped blob byte (KZG)", () => { const x = blob0.slice(); x[5000] ^= 1; return { b: blobs(x) }; }, /^blob 0: .*do not commit to the listed KZG commitment/);
|
|
101
|
+
rejects("a blob of the wrong size", () => ({ b: blobs(blob0.slice(0, 1000)) }), /^blob 0: 1000 bytes, a blob is 131072/);
|
|
102
|
+
rejects("bytes under a versioned hash the pointer does not list", () => ({ b: blobs(blob0, "0x01" + "ab".repeat(31)) }), /^blobs: no bytes were supplied/);
|
|
103
|
+
rejects("no blobs at all", () => ({ b: new Map() }), /^blobs: no bytes/);
|
|
104
|
+
rejects("a pointer naming no blobs", () => { const p = clone(pointer); p.blobs = []; return { p }; }, /^blobs: the pointer names no blobs/);
|
|
105
|
+
rejects("a listed commitment that is not the blob's", () => {
|
|
106
|
+
const p = clone(pointer);
|
|
107
|
+
const c = hexToBytes(p.blobs[0].kzgCommitment); c[20] ^= 1; p.blobs[0].kzgCommitment = bytesToHex(c);
|
|
108
|
+
return { p };
|
|
109
|
+
}, /^blob 0: .*do not commit/);
|
|
110
|
+
rejects("a pointer naming another channel", () => { const p = clone(pointer); p.channel!.id = "0x" + "11".repeat(16); return { p }; }, /^channel: no supplied blob carries a frame of channel/);
|
|
111
|
+
rejects("a pointer claiming zlib", () => { const p = clone(pointer); p.channel!.compression = "zlib"; return { p }; }, /^decompress: the channel is brotli, the pointer says zlib/);
|
|
112
|
+
rejects("a Base block not in the channel", () => { const p = clone(pointer); p.baseBlockNumber = 51979900; return { p }; }, /^block: .*not among/);
|
|
113
|
+
rejects("a parent hash the batch does not carry", () => ({ o: { expect: { ...expect, baseParentHash: "0x" + "ee".repeat(32) } } }), /^block: the batch's parent_hash is/);
|
|
114
|
+
rejects("a ceiling transaction that is not in the batch", () => ({ o: { expect: { txHash: "0x" + "cd".repeat(32) } } }), /^transaction: 0xcdcdcdcd…cdcd is not among the \d+ transactions readable before the cut/);
|
|
115
|
+
rejects("a located index the pointer states wrongly", () => { const p = clone(pointer); p.located!.baseTxIndex = 37; return { p }; }, /^transaction: located at index 38, the pointer says 37/);
|
|
116
|
+
rejects("raw bytes that are not the ceiling's", () => ({ o: { expect: { txHash: CEILING_TX, rawTx: "0x02c0" } } }), /^transaction: the bytes in the batch are not the ceiling's rawTx/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("through the audit's ceiling check", () => {
|
|
120
|
+
const entries = (files: Record<string, string | Uint8Array>) =>
|
|
121
|
+
ingestEntries(Object.entries(files).map(([path, v]) => ({ path, open: () => (typeof v === "string" ? new TextEncoder().encode(v) : v) })));
|
|
122
|
+
|
|
123
|
+
test("a ceiling with a pointer and no blob bytes: pointer ok, blobs not in bundle, never a failure", async () => {
|
|
124
|
+
const ingest = await entries({ "base-ceiling/ceiling.json": ceilingText });
|
|
125
|
+
const r = await verifyCeilings(ingest);
|
|
126
|
+
const c = r.checks[0];
|
|
127
|
+
assert.equal(c.status, "unmatched"); // the proof is not in this bundle; the settlement is checked all the same
|
|
128
|
+
assert.equal(c.settlement!.status, "pointer-only");
|
|
129
|
+
assert.deepEqual(c.settlement!.lines, [
|
|
130
|
+
"settlement: pointer ok, Ethereum block 26088463 at 2026-09-30 06:20:35 UTC",
|
|
131
|
+
"settlement: blobs not in bundle",
|
|
132
|
+
]);
|
|
133
|
+
assert.deepEqual(c.settlement!.pointer.existedBy, { blockNumber: 26088463, blockHash: "0x8608654cd9d6d66c2ce8a80108db7e5202a6d9ad0072f8c81353cc988d3a93f2", blockTimestamp: 1790749235 });
|
|
134
|
+
assert.deepEqual(c.settlement!.blobs, { status: "absent", detail: "blobs not in bundle", listed: 6, supplied: 0 });
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("the blob next to the ceiling file: both layers verified, one line each", async () => {
|
|
138
|
+
const ingest = await entries({ "base-ceiling/ceiling.json": ceilingText, [`base-ceiling/${BLOB_FILE}`]: blob0 });
|
|
139
|
+
const c = (await verifyCeilings(ingest)).checks[0];
|
|
140
|
+
assert.equal(c.settlement!.status, "verified");
|
|
141
|
+
assert.equal(c.settlement!.lines[1], "settlement: blobs decoded (frames 0..0 of 7), ceiling tx located in Base block 51979918 (batch cut at the last frame supplied)");
|
|
142
|
+
assert.deepEqual(c.settlement!.blobs.located, { baseBlockNumber: 51979918, baseTxIndex: 38, txHash: CEILING_TX, batchComplete: false, framesDecoded: 1, framesTotal: 7 });
|
|
143
|
+
assert.equal(c.settlement!.blobs.supplied, 1);
|
|
144
|
+
assert.equal(c.status, "unmatched");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("the blob under blobs/ at the bundle root is found too", async () => {
|
|
148
|
+
const ingest = await entries({ "base-ceiling/ceiling.json": ceilingText, [`blobs/${BLOB_FILE}`]: blob0 });
|
|
149
|
+
assert.equal((await verifyCeilings(ingest)).checks[0].settlement!.status, "verified");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("a tampered pointer fails the ceiling", async () => {
|
|
153
|
+
const s = JSON.parse(ceilingText);
|
|
154
|
+
const h = hexToBytes(s.settlement.l1.blockHeader); h[300] ^= 1; s.settlement.l1.blockHeader = bytesToHex(h);
|
|
155
|
+
const c = (await verifyCeilings(await entries({ "c.json": JSON.stringify(s) }))).checks[0];
|
|
156
|
+
assert.equal(c.status, "failed");
|
|
157
|
+
assert.match(c.reason!, /^settlement: pointer FAILED \(header: /);
|
|
158
|
+
assert.equal(c.settlement!.status, "failed");
|
|
159
|
+
assert.equal(c.settlement!.blobs.status, "absent");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("a pointer for another Base block than the ceiling's fails the ceiling", async () => {
|
|
163
|
+
const s = JSON.parse(ceilingText);
|
|
164
|
+
s.anchor.blockNumber = 51967531;
|
|
165
|
+
const c = (await verifyCeilings(await entries({ "c.json": JSON.stringify(s) }))).checks[0];
|
|
166
|
+
assert.equal(c.status, "failed");
|
|
167
|
+
assert.match(c.reason!, /the pointer names Base block 51979918 .* this ceiling is in block 51967531/);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("a wrong blob in the bundle fails the ceiling", async () => {
|
|
171
|
+
const bad = blob0.slice(); bad[70000] ^= 1;
|
|
172
|
+
const c = (await verifyCeilings(await entries({ "c.json": ceilingText, [BLOB_FILE]: bad }))).checks[0];
|
|
173
|
+
assert.equal(c.status, "failed");
|
|
174
|
+
assert.match(c.reason!, /^settlement: blobs FAILED \(blob 0: /);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("other pins refuse the pointer", async () => {
|
|
178
|
+
const c = (await verifyCeilings(await entries({ "c.json": ceilingText }), { settlementPins: { ...BASE_MAINNET_SETTLEMENT_PINS, batcher: "0x" + "11".repeat(20) } })).checks[0];
|
|
179
|
+
assert.equal(c.status, "failed");
|
|
180
|
+
assert.match(c.reason!, /pointer FAILED \(batcher:/);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe("through runAudit on a directory bundle", () => {
|
|
185
|
+
let dir: string;
|
|
186
|
+
before(async () => { dir = await mkdtemp(join(tmpdir(), "audit-settlement-")); });
|
|
187
|
+
after(async () => { await rm(dir, { recursive: true, force: true }); });
|
|
188
|
+
|
|
189
|
+
test("exit 0, and the markdown report carries both settlement lines", async () => {
|
|
190
|
+
const b = join(dir, "ok");
|
|
191
|
+
await mkdir(join(b, "base-ceiling", "blobs"), { recursive: true });
|
|
192
|
+
await writeFile(join(b, "base-ceiling", "ceiling.json"), ceilingText);
|
|
193
|
+
await writeFile(join(b, "base-ceiling", "blobs", BLOB_FILE), blob0);
|
|
194
|
+
const r = await runAudit(b);
|
|
195
|
+
const c = r.ceilings!.checks[0];
|
|
196
|
+
assert.equal(c.settlement!.status, "verified");
|
|
197
|
+
assert.equal(computeExitFlags(r).code, 0);
|
|
198
|
+
const md = buildMarkdownReport(r);
|
|
199
|
+
assert.match(md, / - settlement: pointer ok, Ethereum block 26088463 at 2026-09-30 06:20:35 UTC/);
|
|
200
|
+
assert.match(md, / - settlement: blobs decoded \(frames 0\.\.0 of 7\), ceiling tx located in Base block 51979918/);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("a bad pointer sets exit bit 2", async () => {
|
|
204
|
+
const b = join(dir, "bad");
|
|
205
|
+
await mkdir(b, { recursive: true });
|
|
206
|
+
const s = JSON.parse(ceilingText);
|
|
207
|
+
s.settlement.l1.txIndex = 19;
|
|
208
|
+
await writeFile(join(b, "ceiling.json"), JSON.stringify(s));
|
|
209
|
+
const r = await runAudit(b);
|
|
210
|
+
assert.equal(r.ceilings!.checks[0].status, "failed");
|
|
211
|
+
assert.equal(computeExitFlags(r).code & 2, 2);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe("span batches (type 1), synthetic", () => {
|
|
216
|
+
const uvarint = (n: bigint): number[] => { const out: number[] = []; for (;;) { const b = Number(n & 0x7fn); n >>= 7n; if (n === 0n) { out.push(b); return out; } out.push(b | 0x80); } };
|
|
217
|
+
const bitsBytes = (bits: number[]): number[] => { let v = 0n; bits.forEach((b, i) => { if (b) v |= 1n << BigInt(i); }); const n = Math.ceil(bits.length / 8); const out = new Array<number>(n).fill(0); for (let i = n - 1; i >= 0; i--) { out[i] = Number(v & 0xffn); v >>= 8n; } return out; };
|
|
218
|
+
const u = (n: bigint): Uint8Array => { if (n === 0n) return new Uint8Array(0); let h = n.toString(16); if (h.length % 2) h = "0" + h; return hexToBytes(h); };
|
|
219
|
+
|
|
220
|
+
test("a block with a type-2 and a protected legacy transaction is rebuilt into the same raw transactions", () => {
|
|
221
|
+
const chainId = 8453n;
|
|
222
|
+
const to = hexToBytes("0x" + "ab".repeat(20));
|
|
223
|
+
const r1 = hexToBytes("0x" + "11".repeat(32)), s1 = hexToBytes("0x" + "22".repeat(32));
|
|
224
|
+
const r2 = hexToBytes("0x" + "33".repeat(32)), s2 = hexToBytes("0x" + "44".repeat(32));
|
|
225
|
+
const input = hexToBytes("0xdeadbeef");
|
|
226
|
+
// The raw transactions a Base block would carry.
|
|
227
|
+
const type2 = new Uint8Array([0x02, ...rlpEncode([u(chainId), u(7n), u(1000n), u(2000n), u(21000n), to, u(5n), input, [], u(1n), r1, s1])]);
|
|
228
|
+
const legacy = rlpEncode([u(8n), u(3000n), u(50000n), to, u(0n), new Uint8Array(0), u(chainId * 2n + 35n + 0n), r2, s2]);
|
|
229
|
+
// The span batch: prefix, then columns.
|
|
230
|
+
const relTimestamp = 2n * 1000n;
|
|
231
|
+
const bytes = Uint8Array.from([
|
|
232
|
+
...uvarint(relTimestamp), ...uvarint(123n), ...new Array(20).fill(1), ...new Array(20).fill(2),
|
|
233
|
+
...uvarint(1n), ...bitsBytes([0]), ...uvarint(2n),
|
|
234
|
+
...bitsBytes([0, 0]), ...bitsBytes([1, 0]),
|
|
235
|
+
...r1, ...s1, ...r2, ...s2,
|
|
236
|
+
...to, ...to,
|
|
237
|
+
0x02, ...rlpEncode([u(5n), u(1000n), u(2000n), input, []]),
|
|
238
|
+
...rlpEncode([u(0n), u(3000n), new Uint8Array(0)]),
|
|
239
|
+
...uvarint(7n), ...uvarint(8n),
|
|
240
|
+
...uvarint(21000n), ...uvarint(50000n),
|
|
241
|
+
...bitsBytes([1]),
|
|
242
|
+
]);
|
|
243
|
+
const blocks = decodeSpanBatch(bytes, BASE_MAINNET_ROLLUP, chainId);
|
|
244
|
+
assert.equal(blocks.length, 1);
|
|
245
|
+
assert.equal(blocks[0].number, 1000);
|
|
246
|
+
assert.equal(blocks[0].timestamp, BASE_MAINNET_ROLLUP.l2GenesisTime + 2000);
|
|
247
|
+
assert.deepEqual(blocks[0].txs.map((t) => t.hash), [type2, legacy].map((raw) => bytesToHex(keccak256(raw))));
|
|
248
|
+
assert.deepEqual(blocks[0].txs.map((t) => bytesToHex(t.raw)), [type2, legacy].map(bytesToHex));
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("trailing bytes are refused", () => {
|
|
252
|
+
assert.throws(() => decodeSpanBatch(Uint8Array.from([...[0, 0], ...new Array(40).fill(0), 1, 0, 0, 0, 0, 0, 0, 0, 0]), BASE_MAINNET_ROLLUP, 8453n), /span batch/);
|
|
253
|
+
});
|
|
254
|
+
});
|
package/src/audit.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* ingest -> verify tiers -> reconstruct -> classify anomalies ->
|
|
7
7
|
* analyze authorities -> identify anchors -> verify witnesses ->
|
|
8
|
-
* derive temporal bounds -> validate attestations
|
|
8
|
+
* derive temporal bounds -> ceilings in time -> validate attestations ->
|
|
9
|
+
* exports (bitgraph-export/1)
|
|
9
10
|
*
|
|
10
11
|
* Deterministic given the same bundle: the only wall-clock read in the
|
|
11
12
|
* entire pipeline is the runMetadata.startedAt stamp taken here. Every
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
21
|
import { verifyCeilings } from "./ceilings.js";
|
|
22
|
+
import { verifyExports } from "./exports.js";
|
|
21
23
|
import { ingestBundle } from "./ingest.js";
|
|
22
24
|
import { verifyObservedProofs } from "./verify-tiers.js";
|
|
23
25
|
import { reconstructChains } from "./reconstruct.js";
|
|
@@ -38,7 +40,7 @@ import type { AuditOptions, AuditResult, ExitFlags, IngestResult } from "./types
|
|
|
38
40
|
* this equals package.json's version, so the constant cannot drift silently
|
|
39
41
|
* across releases.
|
|
40
42
|
*/
|
|
41
|
-
export const AUDIT_VERSION = "0.
|
|
43
|
+
export const AUDIT_VERSION = "0.9.0";
|
|
42
44
|
|
|
43
45
|
/** The audit package's own version. */
|
|
44
46
|
export function auditToolVersion(): string {
|
|
@@ -89,6 +91,17 @@ export async function auditIngest(
|
|
|
89
91
|
? { trustedRootCaDer: options.trustedRootCaDer }
|
|
90
92
|
: undefined
|
|
91
93
|
);
|
|
94
|
+
// Exports: an export's Base ceiling is held to the same writer and chain as
|
|
95
|
+
// the ceiling files, and a trust policy's measurement allowlist is the PCR0
|
|
96
|
+
// list its attestation claims are judged against, unless given explicitly.
|
|
97
|
+
const exportOptions = options?.exports ?? {};
|
|
98
|
+
const allowed = options?.trustAnchors?.allowedMeasurements;
|
|
99
|
+
const exports = await verifyExports(ingest, {
|
|
100
|
+
...exportOptions,
|
|
101
|
+
...(exportOptions.ceilingWriter === undefined && options?.ceilings?.writer !== undefined ? { ceilingWriter: options.ceilings.writer } : {}),
|
|
102
|
+
...(exportOptions.baseChainId === undefined && options?.ceilings?.chainId !== undefined ? { baseChainId: options.ceilings.chainId } : {}),
|
|
103
|
+
...(exportOptions.pcr0 === undefined && allowed !== undefined ? { pcr0: allowed } : {}),
|
|
104
|
+
});
|
|
92
105
|
|
|
93
106
|
return {
|
|
94
107
|
runMetadata: {
|
|
@@ -107,6 +120,7 @@ export async function auditIngest(
|
|
|
107
120
|
temporal,
|
|
108
121
|
attestations,
|
|
109
122
|
ceilings,
|
|
123
|
+
exports,
|
|
110
124
|
};
|
|
111
125
|
}
|
|
112
126
|
|
|
@@ -143,15 +157,23 @@ const WITNESS_VERIFICATION_FAILURE_CODES: ReadonlySet<string> = new Set([
|
|
|
143
157
|
/**
|
|
144
158
|
* Derive the CLI exit bit flags from an audit result. Semantics are
|
|
145
159
|
* documented on the ExitFlags type: bit 1 is verification failures
|
|
146
|
-
* (including unsupported-version rejections
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
160
|
+
* (including unsupported-version rejections, and any export with a FALSE
|
|
161
|
+
* claim or an export-shaped file that could not be checked), bit 2 is chain
|
|
162
|
+
* or authority anomalies, divergences between valid proofs, or anchor
|
|
163
|
+
* witness verification failures. artifact-unavailable is never a failure by
|
|
164
|
+
* itself; the attestation stage's results and informational anchor findings
|
|
165
|
+
* never set bits (an export's own attestation claims are part of its
|
|
166
|
+
* verdict); benign ingest findings never set bits.
|
|
151
167
|
*/
|
|
152
168
|
export function computeExitFlags(result: AuditResult): ExitFlags {
|
|
169
|
+
// An export is checked as one self-contained object, like a carrier: a
|
|
170
|
+
// FALSE verdict (any FALSE claim) is a verification failure, as a bad proof
|
|
171
|
+
// is, and so is an export-shaped file the audit could not check.
|
|
172
|
+
const exportFailures = (result.exports?.checks ?? []).some(
|
|
173
|
+
(e) => e.status !== "checked" || e.verdict === "FALSE"
|
|
174
|
+
);
|
|
153
175
|
const verificationFailures =
|
|
154
|
-
result.verification.failed > 0 || result.ingest.counts.unsupportedVersion > 0;
|
|
176
|
+
result.verification.failed > 0 || result.ingest.counts.unsupportedVersion > 0 || exportFailures;
|
|
155
177
|
const witnessVerificationFailures = result.witnesses.findings.some((f) =>
|
|
156
178
|
WITNESS_VERIFICATION_FAILURE_CODES.has(f.code)
|
|
157
179
|
);
|