Skip to content

Commit 7297d7e

Browse files
authored
fix: address tmnt 141 (#16447)
Please read [contributing guidelines](CONTRIBUTING.md) and remove this line. For audit-related pull requests, please use the [audit PR template](?expand=1&template=audit.md).
2 parents f2279e5 + 7d1ec0d commit 7297d7e

File tree

6 files changed

+305
-26
lines changed

6 files changed

+305
-26
lines changed

l1-contracts/gas_benchmark.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727

2828
| Function | Avg Gas | Max Gas | Calldata Size | Calldata Gas |
2929
|----------------------|---------|---------|---------------|--------------|
30-
| propose | 206,977 | 226,338 | 2,852 | 45,632 |
30+
| propose | 207,888 | 227,249 | 2,852 | 45,632 |
3131
| submitEpochRootProof | 679,690 | 701,700 | 5,092 | 81,472 |
32-
| aggregate3 | 244,745 | 264,110 | - | - |
32+
| aggregate3 | 245,657 | 265,021 | - | - |
3333
| setupEpoch | 38,145 | 327,074 | - | - |
3434

35-
**Avg Gas Cost per Second**: 1,229.6 gas/second
35+
**Avg Gas Cost per Second**: 1,234.4 gas/second
3636
*Epoch duration*: 2h 33m 36s
3737

3838

@@ -63,11 +63,11 @@
6363

6464
| Function | Avg Gas | Max Gas | Calldata Size | Calldata Gas |
6565
|----------------------|---------|---------|---------------|--------------|
66-
| propose | 334,025 | 351,385 | 4,580 | 73,280 |
66+
| propose | 334,960 | 352,320 | 4,580 | 73,280 |
6767
| submitEpochRootProof | 895,025 | 933,081 | 6,308 | 100,928 |
68-
| aggregate3 | 372,157 | 389,520 | - | - |
68+
| aggregate3 | 373,091 | 390,443 | - | - |
6969
| setupEpoch | 49,728 | 542,190 | - | - |
7070

71-
**Avg Gas Cost per Second**: 10,875.5 gas/second
71+
**Avg Gas Cost per Second**: 10,901.5 gas/second
7272
*Epoch duration*: 0h 19m 12s
7373

l1-contracts/gas_benchmark_results.json

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030
"validators": {
3131
"propose": {
3232
"calls": 100,
33-
"min": 200706,
34-
"mean": 206977,
35-
"median": 207326,
36-
"max": 226338,
33+
"min": 201617,
34+
"mean": 207888,
35+
"median": 208237,
36+
"max": 227249,
3737
"calldata_size": 2852,
3838
"calldata_gas": 45632
3939
},
@@ -55,10 +55,10 @@
5555
},
5656
"aggregate3": {
5757
"calls": 100,
58-
"min": 238466,
59-
"mean": 244745,
60-
"median": 245098,
61-
"max": 264110
58+
"min": 239389,
59+
"mean": 245657,
60+
"median": 245991,
61+
"max": 265021
6262
}
6363
}
6464
},
@@ -93,10 +93,10 @@
9393
"validators": {
9494
"propose": {
9595
"calls": 100,
96-
"min": 318448,
97-
"mean": 334025,
98-
"median": 334471,
99-
"max": 351385,
96+
"min": 319383,
97+
"mean": 334960,
98+
"median": 335406,
99+
"max": 352320,
100100
"calldata_size": 4580,
101101
"calldata_gas": 73280
102102
},
@@ -118,10 +118,10 @@
118118
},
119119
"aggregate3": {
120120
"calls": 100,
121-
"min": 356583,
122-
"mean": 372157,
123-
"median": 372603,
124-
"max": 389520
121+
"min": 357506,
122+
"mean": 373091,
123+
"median": 373535,
124+
"max": 390443
125125
}
126126
}
127127
}

l1-contracts/src/core/libraries/Errors.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ library Errors {
162162
// SignatureLib (duplicated)
163163
error SignatureLib__InvalidSignature(address, address); // 0xd9cbae6c
164164

165+
error AttestationLib__OutOfBounds(uint256, uint256);
166+
error AttestationLib__SignatureIndicesSizeMismatch(uint256, uint256);
167+
error AttestationLib__SignaturesOrAddressesSizeMismatch(uint256, uint256);
168+
error AttestationLib__NotASignatureAtIndex(uint256 index);
169+
error AttestationLib__NotAnAddressAtIndex(uint256 index);
170+
165171
// RewardBooster
166172
error RewardBooster__OnlyRollup(address caller);
167173
}

l1-contracts/src/core/libraries/rollup/AttestationLib.sol

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright 2024 Aztec Labs.
33
pragma solidity ^0.8.27;
44

5+
import {Errors} from "@aztec/core/libraries/Errors.sol";
56
import {Signature, SignatureLib} from "@aztec/shared/libraries/SignatureLib.sol";
67

78
/**
@@ -71,7 +72,7 @@ library AttestationLib {
7172
returns (Signature memory)
7273
{
7374
bytes memory signaturesOrAddresses = _attestations.signaturesOrAddresses;
74-
require(isSignature(_attestations, _index), "Not a signature at this index");
75+
require(isSignature(_attestations, _index), Errors.AttestationLib__NotASignatureAtIndex(_index));
7576

7677
uint256 dataPtr;
7778
assembly {
@@ -105,7 +106,7 @@ library AttestationLib {
105106
*/
106107
function getAddress(CommitteeAttestations memory _attestations, uint256 _index) internal pure returns (address) {
107108
bytes memory signaturesOrAddresses = _attestations.signaturesOrAddresses;
108-
require(!isSignature(_attestations, _index), "A signature at this index");
109+
require(!isSignature(_attestations, _index), Errors.AttestationLib__NotAnAddressAtIndex(_index));
109110

110111
uint256 dataPtr;
111112
assembly {
@@ -126,9 +127,56 @@ library AttestationLib {
126127
return addr;
127128
}
128129

130+
/**
131+
* @notice Assert that the size of `_attestations` is as expected, throw otherwise
132+
*
133+
* @custom:reverts SignatureIndicesSizeMismatch if the signature indices have a wrong size
134+
* @custom:reverts SignaturesOrAddressesSizeMismatch if the signatures or addresses object has wrong size
135+
*
136+
* @param _attestations - The attestation struct
137+
* @param _expectedCount - The expected size of the validator set
138+
*/
139+
function assertSizes(CommitteeAttestations memory _attestations, uint256 _expectedCount) internal pure {
140+
// Count signatures (1s) and addresses (0s) from bitmap
141+
uint256 signatureCount = 0;
142+
uint256 addressCount = 0;
143+
uint256 bitmapBytes = (_expectedCount + 7) / 8; // Round up to nearest byte
144+
require(
145+
bitmapBytes == _attestations.signatureIndices.length,
146+
Errors.AttestationLib__SignatureIndicesSizeMismatch(bitmapBytes, _attestations.signatureIndices.length)
147+
);
148+
149+
for (uint256 i = 0; i < _expectedCount; i++) {
150+
uint256 byteIndex = i / 8;
151+
uint256 bitIndex = 7 - (i % 8);
152+
uint8 bitMask = uint8(1 << bitIndex);
153+
154+
if (uint8(_attestations.signatureIndices[byteIndex]) & bitMask != 0) {
155+
signatureCount++;
156+
} else {
157+
addressCount++;
158+
}
159+
}
160+
161+
// Calculate expected size
162+
uint256 sizeOfSignaturesAndAddresses = (signatureCount * SIGNATURE_LENGTH) + (addressCount * ADDRESS_LENGTH);
163+
164+
// Validate actual size matches expected
165+
require(
166+
sizeOfSignaturesAndAddresses == _attestations.signaturesOrAddresses.length,
167+
Errors.AttestationLib__SignaturesOrAddressesSizeMismatch(
168+
sizeOfSignaturesAndAddresses, _attestations.signaturesOrAddresses.length
169+
)
170+
);
171+
}
172+
129173
/**
130174
* Recovers the committee from the addresses in the attestations and signers.
131175
*
176+
* @custom:reverts SignatureIndicesSizeMismatch if the signature indices have a wrong size
177+
* @custom:reverts OutOfBounds throws if reading data beyond the `_attestations`
178+
* @custom:reverts SignaturesOrAddressesSizeMismatch if the signatures or addresses object has wrong size
179+
*
132180
* @param _attestations - The committee attestations
133181
* @param _signers The addresses of the committee members that signed the attestations. Provided in order to not have
134182
* to recover them from their attestations' signatures (and hence save gas). The addresses of the non-signing
@@ -141,8 +189,14 @@ library AttestationLib {
141189
address[] memory _signers,
142190
uint256 _length
143191
) internal pure returns (address[] memory) {
192+
uint256 bitmapBytes = (_length + 7) / 8; // Round up to nearest byte
193+
require(
194+
bitmapBytes == _attestations.signatureIndices.length,
195+
Errors.AttestationLib__SignatureIndicesSizeMismatch(bitmapBytes, _attestations.signatureIndices.length)
196+
);
197+
198+
// To get a ref that we can easily use with the assembly down below.
144199
bytes memory signaturesOrAddresses = _attestations.signaturesOrAddresses;
145-
bytes memory signatureIndices = _attestations.signatureIndices;
146200
address[] memory addresses = new address[](_length);
147201

148202
uint256 signersIndex;
@@ -154,12 +208,13 @@ library AttestationLib {
154208
// Skip length
155209
dataPtr := add(signaturesOrAddresses, 0x20)
156210
}
211+
uint256 offset = dataPtr;
157212

158213
for (uint256 i = 0; i < _length; ++i) {
159214
// Load new byte every 8 iterations
160215
if (i % 8 == 0) {
161216
uint256 byteIndex = i / 8;
162-
currentByte = uint8(signatureIndices[byteIndex]);
217+
currentByte = uint8(_attestations.signatureIndices[byteIndex]);
163218
bitMask = 128; // 0b10000000
164219
}
165220

@@ -180,6 +235,23 @@ library AttestationLib {
180235
}
181236
}
182237

238+
// Ensure that the reads were within the boundaries of the data.
239+
// As `dataPtr` will always be increasing (and unlikely to wrap around because it would require insane size)
240+
// we can just check that the last dataPtr value is inside the limit, as all the others would be as well then.
241+
uint256 upperLimit = offset + _attestations.signaturesOrAddresses.length;
242+
// As the offset was added already part of both values, we can subtract to give a more meaningful error.
243+
require(dataPtr <= upperLimit, Errors.AttestationLib__OutOfBounds(dataPtr - offset, upperLimit - offset));
244+
245+
// Ensure that the size of data provided actually matches what we expect
246+
uint256 sizeOfSignaturesAndAddresses =
247+
(signersIndex * SIGNATURE_LENGTH) + ((_length - signersIndex) * ADDRESS_LENGTH);
248+
require(
249+
sizeOfSignaturesAndAddresses == _attestations.signaturesOrAddresses.length,
250+
Errors.AttestationLib__SignaturesOrAddressesSizeMismatch(
251+
sizeOfSignaturesAndAddresses, _attestations.signaturesOrAddresses.length
252+
)
253+
);
254+
183255
return addresses;
184256
}
185257

l1-contracts/src/core/libraries/rollup/ValidatorSelectionLib.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ library ValidatorSelectionLib {
235235
proposer = committee[proposerIndex];
236236

237237
setCachedProposer(_slot, proposer, proposerIndex);
238+
} else {
239+
// Assert that the size of the attestations is as expected, to avoid memory abuse on sizes.
240+
// These checks are also performed inside `reconstructCommitteeFromSigners`.
241+
_attestations.assertSizes(getStorage().targetCommitteeSize);
238242
}
239243

240244
// We check that the proposer agrees with the proposal by checking that he attested to it. If we fail to get

0 commit comments

Comments
 (0)