@aztec/archiver 3.0.0-nightly.20251015 → 3.0.0-nightly.20251022
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/archiver/validation.d.ts.map +1 -1
- package/dest/archiver/validation.js +45 -34
- package/package.json +14 -14
- package/src/archiver/validation.ts +45 -36
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/archiver/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/archiver/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,mBAAmB,EAEzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,6BAA6B,CAAC;AAErF,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,cAAc,EAAE,gBAAgB,EAChC,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,EACnD,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CA4E9B"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { compactArray } from '@aztec/foundation/collection';
|
|
2
|
+
import { getAttestationInfoFromPublishedL2Block } from '@aztec/stdlib/block';
|
|
2
3
|
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
3
4
|
/**
|
|
4
5
|
* Validates the attestations submitted for the given block.
|
|
5
6
|
* Returns true if the attestations are valid and sufficient, false otherwise.
|
|
6
7
|
*/ export async function validateBlockAttestations(publishedBlock, epochCache, constants, logger) {
|
|
7
|
-
const
|
|
8
|
-
const attestors =
|
|
8
|
+
const attestorInfos = getAttestationInfoFromPublishedL2Block(publishedBlock);
|
|
9
|
+
const attestors = compactArray(attestorInfos.map((info)=>'address' in info ? info.address : undefined));
|
|
9
10
|
const { block } = publishedBlock;
|
|
10
11
|
const blockHash = await block.hash().then((hash)=>hash.toString());
|
|
11
12
|
const archiveRoot = block.archive.root.toString();
|
|
@@ -21,8 +22,8 @@ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
|
21
22
|
};
|
|
22
23
|
logger?.debug(`Validating attestations for block ${block.number} at slot ${slot} in epoch ${epoch}`, {
|
|
23
24
|
committee: (committee ?? []).map((member)=>member.toString()),
|
|
24
|
-
recoveredAttestors:
|
|
25
|
-
postedAttestations: publishedBlock.attestations.map((a)=>a.address.isZero() ? a.signature
|
|
25
|
+
recoveredAttestors: attestorInfos,
|
|
26
|
+
postedAttestations: publishedBlock.attestations.map((a)=>(a.address.isZero() ? a.signature : a.address).toString()),
|
|
26
27
|
...logData
|
|
27
28
|
});
|
|
28
29
|
if (!committee || committee.length === 0) {
|
|
@@ -33,44 +34,54 @@ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
|
33
34
|
}
|
|
34
35
|
const committeeSet = new Set(committee.map((member)=>member.toString()));
|
|
35
36
|
const requiredAttestationCount = Math.floor(committee.length * 2 / 3) + 1;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
const failedValidationResult = (reason)=>({
|
|
38
|
+
valid: false,
|
|
39
|
+
reason,
|
|
40
|
+
block: publishedBlock.block.toBlockInfo(),
|
|
41
|
+
committee,
|
|
42
|
+
seed,
|
|
43
|
+
epoch,
|
|
44
|
+
attestors,
|
|
45
|
+
attestations: publishedBlock.attestations
|
|
46
|
+
});
|
|
47
|
+
for(let i = 0; i < attestorInfos.length; i++){
|
|
48
|
+
const info = attestorInfos[i];
|
|
49
|
+
// Fail on invalid signatures (no address recovered)
|
|
50
|
+
if (info.status === 'invalid-signature' || info.status === 'empty') {
|
|
51
|
+
logger?.warn(`Attestation with empty or invalid signature at slot ${slot}`, {
|
|
52
|
+
committee,
|
|
53
|
+
invalidIndex: i,
|
|
54
|
+
...logData
|
|
42
55
|
});
|
|
43
|
-
const reason = 'invalid-attestation';
|
|
44
56
|
return {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
invalidIndex: i,
|
|
48
|
-
block: publishedBlock.block.toBlockInfo(),
|
|
49
|
-
committee,
|
|
50
|
-
seed,
|
|
51
|
-
epoch,
|
|
52
|
-
attestors,
|
|
53
|
-
attestations: publishedBlock.attestations
|
|
57
|
+
...failedValidationResult('invalid-attestation'),
|
|
58
|
+
invalidIndex: i
|
|
54
59
|
};
|
|
55
60
|
}
|
|
61
|
+
// Check if the attestor is in the committee
|
|
62
|
+
if (info.status === 'recovered-from-signature' || info.status === 'provided-as-address') {
|
|
63
|
+
const signer = info.address.toString();
|
|
64
|
+
if (!committeeSet.has(signer)) {
|
|
65
|
+
logger?.warn(`Attestation from non-committee member ${signer} at slot ${slot}`, {
|
|
66
|
+
committee,
|
|
67
|
+
invalidIndex: i,
|
|
68
|
+
...logData
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
...failedValidationResult('invalid-attestation'),
|
|
72
|
+
invalidIndex: i
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
56
76
|
}
|
|
57
|
-
|
|
77
|
+
const validAttestationCount = attestorInfos.filter((info)=>info.status === 'recovered-from-signature').length;
|
|
78
|
+
if (validAttestationCount < requiredAttestationCount) {
|
|
58
79
|
logger?.warn(`Insufficient attestations for block at slot ${slot}`, {
|
|
59
80
|
requiredAttestations: requiredAttestationCount,
|
|
60
|
-
actualAttestations:
|
|
81
|
+
actualAttestations: validAttestationCount,
|
|
61
82
|
...logData
|
|
62
83
|
});
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
valid: false,
|
|
66
|
-
reason,
|
|
67
|
-
block: publishedBlock.block.toBlockInfo(),
|
|
68
|
-
committee,
|
|
69
|
-
seed,
|
|
70
|
-
epoch,
|
|
71
|
-
attestors,
|
|
72
|
-
attestations: publishedBlock.attestations
|
|
73
|
-
};
|
|
84
|
+
return failedValidationResult('insufficient-attestations');
|
|
74
85
|
}
|
|
75
86
|
logger?.debug(`Block attestations validated successfully for block ${block.number} at slot ${slot}`, logData);
|
|
76
87
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/archiver",
|
|
3
|
-
"version": "3.0.0-nightly.
|
|
3
|
+
"version": "3.0.0-nightly.20251022",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -66,23 +66,23 @@
|
|
|
66
66
|
]
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@aztec/blob-lib": "3.0.0-nightly.
|
|
70
|
-
"@aztec/blob-sink": "3.0.0-nightly.
|
|
71
|
-
"@aztec/constants": "3.0.0-nightly.
|
|
72
|
-
"@aztec/epoch-cache": "3.0.0-nightly.
|
|
73
|
-
"@aztec/ethereum": "3.0.0-nightly.
|
|
74
|
-
"@aztec/foundation": "3.0.0-nightly.
|
|
75
|
-
"@aztec/kv-store": "3.0.0-nightly.
|
|
76
|
-
"@aztec/l1-artifacts": "3.0.0-nightly.
|
|
77
|
-
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.
|
|
78
|
-
"@aztec/protocol-contracts": "3.0.0-nightly.
|
|
79
|
-
"@aztec/stdlib": "3.0.0-nightly.
|
|
80
|
-
"@aztec/telemetry-client": "3.0.0-nightly.
|
|
69
|
+
"@aztec/blob-lib": "3.0.0-nightly.20251022",
|
|
70
|
+
"@aztec/blob-sink": "3.0.0-nightly.20251022",
|
|
71
|
+
"@aztec/constants": "3.0.0-nightly.20251022",
|
|
72
|
+
"@aztec/epoch-cache": "3.0.0-nightly.20251022",
|
|
73
|
+
"@aztec/ethereum": "3.0.0-nightly.20251022",
|
|
74
|
+
"@aztec/foundation": "3.0.0-nightly.20251022",
|
|
75
|
+
"@aztec/kv-store": "3.0.0-nightly.20251022",
|
|
76
|
+
"@aztec/l1-artifacts": "3.0.0-nightly.20251022",
|
|
77
|
+
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251022",
|
|
78
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20251022",
|
|
79
|
+
"@aztec/stdlib": "3.0.0-nightly.20251022",
|
|
80
|
+
"@aztec/telemetry-client": "3.0.0-nightly.20251022",
|
|
81
81
|
"lodash.groupby": "^4.6.0",
|
|
82
82
|
"lodash.omit": "^4.5.0",
|
|
83
83
|
"tsc-watch": "^6.0.0",
|
|
84
84
|
"tslib": "^2.5.0",
|
|
85
|
-
"viem": "2.
|
|
85
|
+
"viem": "npm:@spalladino/viem@2.38.2-eip7594.0"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
88
|
"@jest/globals": "^30.0.0",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
+
import { compactArray } from '@aztec/foundation/collection';
|
|
2
3
|
import type { Logger } from '@aztec/foundation/log';
|
|
3
4
|
import {
|
|
4
5
|
type PublishedL2Block,
|
|
6
|
+
type ValidateBlockNegativeResult,
|
|
5
7
|
type ValidateBlockResult,
|
|
6
|
-
|
|
8
|
+
getAttestationInfoFromPublishedL2Block,
|
|
7
9
|
} from '@aztec/stdlib/block';
|
|
8
10
|
import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
9
11
|
|
|
@@ -19,8 +21,8 @@ export async function validateBlockAttestations(
|
|
|
19
21
|
constants: Pick<L1RollupConstants, 'epochDuration'>,
|
|
20
22
|
logger?: Logger,
|
|
21
23
|
): Promise<ValidateBlockResult> {
|
|
22
|
-
const
|
|
23
|
-
const attestors =
|
|
24
|
+
const attestorInfos = getAttestationInfoFromPublishedL2Block(publishedBlock);
|
|
25
|
+
const attestors = compactArray(attestorInfos.map(info => ('address' in info ? info.address : undefined)));
|
|
24
26
|
const { block } = publishedBlock;
|
|
25
27
|
const blockHash = await block.hash().then(hash => hash.toString());
|
|
26
28
|
const archiveRoot = block.archive.root.toString();
|
|
@@ -31,10 +33,8 @@ export async function validateBlockAttestations(
|
|
|
31
33
|
|
|
32
34
|
logger?.debug(`Validating attestations for block ${block.number} at slot ${slot} in epoch ${epoch}`, {
|
|
33
35
|
committee: (committee ?? []).map(member => member.toString()),
|
|
34
|
-
recoveredAttestors:
|
|
35
|
-
postedAttestations: publishedBlock.attestations.map(a =>
|
|
36
|
-
a.address.isZero() ? a.signature.toString() : a.address.toString(),
|
|
37
|
-
),
|
|
36
|
+
recoveredAttestors: attestorInfos,
|
|
37
|
+
postedAttestations: publishedBlock.attestations.map(a => (a.address.isZero() ? a.signature : a.address).toString()),
|
|
38
38
|
...logData,
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -46,43 +46,52 @@ export async function validateBlockAttestations(
|
|
|
46
46
|
const committeeSet = new Set(committee.map(member => member.toString()));
|
|
47
47
|
const requiredAttestationCount = Math.floor((committee.length * 2) / 3) + 1;
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
const failedValidationResult = <TReason extends ValidateBlockNegativeResult['reason']>(reason: TReason) => ({
|
|
50
|
+
valid: false as const,
|
|
51
|
+
reason,
|
|
52
|
+
block: publishedBlock.block.toBlockInfo(),
|
|
53
|
+
committee,
|
|
54
|
+
seed,
|
|
55
|
+
epoch,
|
|
56
|
+
attestors,
|
|
57
|
+
attestations: publishedBlock.attestations,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < attestorInfos.length; i++) {
|
|
61
|
+
const info = attestorInfos[i];
|
|
62
|
+
|
|
63
|
+
// Fail on invalid signatures (no address recovered)
|
|
64
|
+
if (info.status === 'invalid-signature' || info.status === 'empty') {
|
|
65
|
+
logger?.warn(`Attestation with empty or invalid signature at slot ${slot}`, {
|
|
60
66
|
committee,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
invalidIndex: i,
|
|
68
|
+
...logData,
|
|
69
|
+
});
|
|
70
|
+
return { ...failedValidationResult('invalid-attestation'), invalidIndex: i };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if the attestor is in the committee
|
|
74
|
+
if (info.status === 'recovered-from-signature' || info.status === 'provided-as-address') {
|
|
75
|
+
const signer = info.address.toString();
|
|
76
|
+
if (!committeeSet.has(signer)) {
|
|
77
|
+
logger?.warn(`Attestation from non-committee member ${signer} at slot ${slot}`, {
|
|
78
|
+
committee,
|
|
79
|
+
invalidIndex: i,
|
|
80
|
+
...logData,
|
|
81
|
+
});
|
|
82
|
+
return { ...failedValidationResult('invalid-attestation'), invalidIndex: i };
|
|
83
|
+
}
|
|
66
84
|
}
|
|
67
85
|
}
|
|
68
86
|
|
|
69
|
-
|
|
87
|
+
const validAttestationCount = attestorInfos.filter(info => info.status === 'recovered-from-signature').length;
|
|
88
|
+
if (validAttestationCount < requiredAttestationCount) {
|
|
70
89
|
logger?.warn(`Insufficient attestations for block at slot ${slot}`, {
|
|
71
90
|
requiredAttestations: requiredAttestationCount,
|
|
72
|
-
actualAttestations:
|
|
91
|
+
actualAttestations: validAttestationCount,
|
|
73
92
|
...logData,
|
|
74
93
|
});
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
valid: false,
|
|
78
|
-
reason,
|
|
79
|
-
block: publishedBlock.block.toBlockInfo(),
|
|
80
|
-
committee,
|
|
81
|
-
seed,
|
|
82
|
-
epoch,
|
|
83
|
-
attestors,
|
|
84
|
-
attestations: publishedBlock.attestations,
|
|
85
|
-
};
|
|
94
|
+
return failedValidationResult('insufficient-attestations');
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
logger?.debug(`Block attestations validated successfully for block ${block.number} at slot ${slot}`, logData);
|