Skip to content

Commit 5692041

Browse files
authored
fix: update empire digest (#16475)
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 c294f91 + 2e19271 commit 5692041

File tree

14 files changed

+51
-114
lines changed

14 files changed

+51
-114
lines changed

l1-contracts/gas_benchmark.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
|----------------------|---------|---------|---------------|--------------|
3030
| propose | 206,977 | 226,338 | 2,852 | 45,632 |
3131
| submitEpochRootProof | 679,690 | 701,700 | 5,092 | 81,472 |
32-
| aggregate3 | 256,810 | 286,606 | - | - |
32+
| aggregate3 | 244,745 | 264,110 | - | - |
3333
| setupEpoch | 38,145 | 327,074 | - | - |
3434

3535
**Avg Gas Cost per Second**: 1,229.6 gas/second
@@ -65,7 +65,7 @@
6565
|----------------------|---------|---------|---------------|--------------|
6666
| propose | 334,025 | 351,385 | 4,580 | 73,280 |
6767
| submitEpochRootProof | 895,025 | 933,081 | 6,308 | 100,928 |
68-
| aggregate3 | 386,274 | 412,016 | - | - |
68+
| aggregate3 | 372,157 | 389,520 | - | - |
6969
| setupEpoch | 49,728 | 542,190 | - | - |
7070

7171
**Avg Gas Cost per Second**: 10,875.5 gas/second

l1-contracts/gas_benchmark_results.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@
5555
},
5656
"aggregate3": {
5757
"calls": 100,
58-
"min": 243874,
59-
"mean": 256810,
60-
"median": 253736,
61-
"max": 286606
58+
"min": 238466,
59+
"mean": 244745,
60+
"median": 245098,
61+
"max": 264110
6262
}
6363
}
6464
},
@@ -118,10 +118,10 @@
118118
},
119119
"aggregate3": {
120120
"calls": 100,
121-
"min": 362682,
122-
"mean": 386274,
123-
"median": 384758,
124-
"max": 412016
121+
"min": 356583,
122+
"mean": 372157,
123+
"median": 372603,
124+
"max": 389520
125125
}
126126
}
127127
}

l1-contracts/src/governance/proposer/EmpireBase.sol

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ struct CompressedRoundAccounting {
8484
* 1. Direct signal: Current signaler calls `signal()`
8585
* 2. Delegated signal: Anyone submits with signaler's signature via `signalWithSig()`
8686
* - Uses EIP-712 for signature verification
87-
* - Includes nonce and round number to prevent replay attacks
87+
* - Includes slot and instance to prevent replay attacks
8888
*
8989
* @dev ABSTRACT FUNCTIONS:
9090
* Implementing contracts must provide:
@@ -107,8 +107,7 @@ abstract contract EmpireBase is EIP712, IEmpire {
107107
using CompressedTimeMath for CompressedSlot;
108108

109109
// EIP-712 type hash for the Signal struct
110-
bytes32 public constant SIGNAL_TYPEHASH =
111-
keccak256("Signal(address payload,uint256 nonce,uint256 round,address instance)");
110+
bytes32 public constant SIGNAL_TYPEHASH = keccak256("Signal(address payload,uint256 slot,address instance)");
112111

113112
// The number of signals needed for a payload to be considered submittable.
114113
uint256 public immutable QUORUM_SIZE;
@@ -121,8 +120,6 @@ abstract contract EmpireBase is EIP712, IEmpire {
121120

122121
// Mapping of instance to round number to round accounting.
123122
mapping(address instance => mapping(uint256 roundNumber => CompressedRoundAccounting)) internal rounds;
124-
// Mapping of instance signaler to nonce. Used to prevent replay attacks.
125-
mapping(address signaler => uint256 nonce) public nonces;
126123

127124
constructor(uint256 _quorumSize, uint256 _roundSize, uint256 _lifetimeInRounds, uint256 _executionDelayInRounds)
128125
EIP712("EmpireBase", "1")
@@ -269,8 +266,8 @@ abstract contract EmpireBase is EIP712, IEmpire {
269266
return Slot.unwrap(_slot) / ROUND_SIZE;
270267
}
271268

272-
function getSignalSignatureDigest(IPayload _payload, address _signaler, uint256 _round) public view returns (bytes32) {
273-
return _hashTypedDataV4(keccak256(abi.encode(SIGNAL_TYPEHASH, _payload, nonces[_signaler], _round, getInstance())));
269+
function getSignalSignatureDigest(IPayload _payload, Slot _slot) public view returns (bytes32) {
270+
return _hashTypedDataV4(keccak256(abi.encode(SIGNAL_TYPEHASH, _payload, _slot, getInstance())));
274271
}
275272

276273
// Virtual functions
@@ -299,8 +296,7 @@ abstract contract EmpireBase is EIP712, IEmpire {
299296
if (_sig.isEmpty()) {
300297
require(msg.sender == signaler, Errors.GovernanceProposer__OnlyProposerCanSignal(msg.sender, signaler));
301298
} else {
302-
bytes32 digest = getSignalSignatureDigest(_payload, signaler, roundNumber);
303-
nonces[signaler]++;
299+
bytes32 digest = getSignalSignatureDigest(_payload, currentSlot);
304300

305301
// _sig.verify will throw if invalid, it is more my sanity that I am doing this for.
306302
require(_sig.verify(signaler, digest), Errors.GovernanceProposer__OnlyProposerCanSignal(msg.sender, signaler));

l1-contracts/test/benchmark/happy.t.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,14 +353,14 @@ contract BenchmarkRollupTest is FeeModelTestPoints, DecoderBase {
353353
* @param _payload The payload to signal
354354
* @return The EIP-712 signature
355355
*/
356-
function createSignalSignature(address _signer, IPayload _payload, uint256 _round)
356+
function createSignalSignature(address _signer, IPayload _payload, Slot _slot)
357357
internal
358358
view
359359
returns (Signature memory)
360360
{
361361
uint256 privateKey = attesterPrivateKeys[_signer];
362362
require(privateKey != 0, "Private key not found for signer");
363-
bytes32 digest = slashingProposer.getSignalSignatureDigest(_payload, _signer, _round);
363+
bytes32 digest = slashingProposer.getSignalSignatureDigest(_payload, _slot);
364364

365365
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
366366

@@ -383,7 +383,7 @@ contract BenchmarkRollupTest is FeeModelTestPoints, DecoderBase {
383383

384384
if (_slashing && !warmedUp && rollup.getCurrentSlot() == Slot.wrap(EPOCH_DURATION * 2)) {
385385
address proposer = rollup.getCurrentProposer();
386-
Signature memory sig = createSignalSignature(proposer, slashPayload, round);
386+
Signature memory sig = createSignalSignature(proposer, slashPayload, rollup.getCurrentSlot());
387387
slashingProposer.signalWithSig(slashPayload, sig);
388388
warmedUp = true;
389389
}
@@ -404,7 +404,7 @@ contract BenchmarkRollupTest is FeeModelTestPoints, DecoderBase {
404404
blockAttestations[currentBlockNumber] = AttestationLib.packAttestations(b.attestations);
405405

406406
if (_slashing) {
407-
Signature memory sig = createSignalSignature(proposer, slashPayload, round);
407+
Signature memory sig = createSignalSignature(proposer, slashPayload, rollup.getCurrentSlot());
408408
Multicall3.Call3[] memory calls = new Multicall3.Call3[](2);
409409
calls[0] = Multicall3.Call3({
410410
target: address(rollup),

l1-contracts/test/compression/PreHeating.t.sol

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -405,25 +405,4 @@ contract PreHeatingTest is FeeModelTestPoints, DecoderBase {
405405
Signature memory emptySignature = Signature({v: 0, r: 0, s: 0});
406406
return CommitteeAttestation({addr: _signer, signature: emptySignature});
407407
}
408-
409-
/**
410-
* @notice Creates an EIP-712 signature for signalWithSig
411-
* @param _signer The address that should sign (must match a block proposer)
412-
* @param _payload The payload to signal
413-
* @param _round The round to signal in
414-
* @return The EIP-712 signature
415-
*/
416-
function createSignalSignature(address _signer, IPayload _payload, uint256 _round)
417-
internal
418-
view
419-
returns (Signature memory)
420-
{
421-
uint256 privateKey = attesterPrivateKeys[_signer];
422-
require(privateKey != 0, "Private key not found for signer");
423-
bytes32 digest = slashingProposer.getSignalSignatureDigest(_payload, _signer, _round);
424-
425-
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
426-
427-
return Signature({v: v, r: r, s: s});
428-
}
429408
}

l1-contracts/test/governance/governance-proposer/scenario/15123.t.sol

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,39 +55,23 @@ contract Test15123 is GovernanceProposerBase {
5555
vm.warp(Timestamp.unwrap(validatorSelection.getTimestampForSlot(Slot.wrap(1))));
5656

5757
uint256 round = governanceProposer.getCurrentRound();
58-
signature = createSignature(privateKey, address(proposal), 0, round);
58+
signature = createSignature(privateKey, proposal, validatorSelection.getCurrentSlot());
5959

6060
governanceProposer.signalWithSig(proposal, signature);
6161

62-
assertEq(governanceProposer.signalCount(address(validatorSelection), 0, proposal), 1, "invalid number of votes");
62+
assertEq(governanceProposer.signalCount(address(validatorSelection), round, proposal), 1, "invalid number of votes");
6363

6464
vm.warp(Timestamp.unwrap(validatorSelection.getTimestampForSlot(Slot.wrap(2))));
6565

6666
vm.expectRevert();
6767
governanceProposer.signalWithSig(proposal, signature);
6868

69-
assertEq(governanceProposer.signalCount(address(validatorSelection), 0, proposal), 1, "invalid number of votes");
69+
assertEq(governanceProposer.signalCount(address(validatorSelection), round, proposal), 1, "invalid number of votes");
7070
}
7171

72-
function createSignature(uint256 _privateKey, address _payload, uint256 _nonce, uint256 _round)
73-
internal
74-
view
75-
returns (Signature memory)
76-
{
77-
bytes32 TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
78-
bytes32 hashedName = keccak256(bytes("EmpireBase"));
79-
bytes32 hashedVersion = keccak256(bytes("1"));
80-
bytes32 domainSeparator =
81-
keccak256(abi.encode(TYPE_HASH, hashedName, hashedVersion, block.chainid, address(governanceProposer)));
82-
bytes32 digest = MessageHashUtils.toTypedDataHash(
83-
domainSeparator,
84-
keccak256(
85-
abi.encode(governanceProposer.SIGNAL_TYPEHASH(), _payload, _nonce, _round, governanceProposer.getInstance())
86-
)
87-
);
88-
72+
function createSignature(uint256 _privateKey, IPayload _payload, Slot _slot) internal view returns (Signature memory) {
73+
bytes32 digest = governanceProposer.getSignalSignatureDigest(_payload, _slot);
8974
(uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, digest);
90-
9175
return Signature({v: v, r: r, s: s});
9276
}
9377
}

l1-contracts/test/governance/governance-proposer/scenario/tmnt144.t.sol

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ contract TestTmnt144 is GovernanceProposerBase {
3838

3939
// Create a signature for the proposer
4040
uint256 round = governanceProposer.getCurrentRound();
41-
signature = createSignature(privateKey, address(proposal), round);
41+
signature = createSignature(privateKey, address(proposal), rollup1.getCurrentSlot());
4242

4343
// For some reason, before he signals, the time progresses and he is no longer the proposer!
4444
// It is not even the same round actually
@@ -67,13 +67,8 @@ contract TestTmnt144 is GovernanceProposerBase {
6767
assertEq(governanceProposer.signalCount(address(rollup2), 0, proposal), 0, "invalid number of votes");
6868
}
6969

70-
function createSignature(uint256 _privateKey, address _payload, uint256 _round)
71-
internal
72-
view
73-
returns (Signature memory)
74-
{
75-
address signer = vm.addr(_privateKey);
76-
bytes32 digest = governanceProposer.getSignalSignatureDigest(IPayload(_payload), signer, _round);
70+
function createSignature(uint256 _privateKey, address _payload, Slot _slot) internal view returns (Signature memory) {
71+
bytes32 digest = governanceProposer.getSignalSignatureDigest(IPayload(_payload), _slot);
7772

7873
(uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, digest);
7974

l1-contracts/test/governance/governance-proposer/scenario/tmnt150.t.sol

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,26 +69,21 @@ contract TestTmnt150 is GovernanceProposerBase {
6969

7070
// Create a signature for the proposer
7171
uint256 round = governanceProposer.getCurrentRound();
72-
signature = createSignature(privateKey, address(proposal), round);
72+
signature = createSignature(privateKey, address(proposal), rollup1.getCurrentSlot());
7373

7474
rollup1.maliciousValues(5, governanceProposer, proposal);
7575

76-
assertEq(governanceProposer.signalCount(address(rollup1), 0, proposal), 0, "invalid number of votes");
76+
assertEq(governanceProposer.signalCount(address(rollup1), round, proposal), 0, "invalid number of votes");
7777

7878
vm.expectRevert(abi.encodeWithSelector(Errors.GovernanceProposer__SignalAlreadyCastForSlot.selector, 1));
7979

8080
rollup1.commenceAttack();
8181

82-
assertEq(governanceProposer.signalCount(address(rollup1), 0, proposal), 0, "invalid number of votes");
82+
assertEq(governanceProposer.signalCount(address(rollup1), round, proposal), 0, "invalid number of votes");
8383
}
8484

85-
function createSignature(uint256 _privateKey, address _payload, uint256 _round)
86-
internal
87-
view
88-
returns (Signature memory)
89-
{
90-
address signer = vm.addr(_privateKey);
91-
bytes32 digest = governanceProposer.getSignalSignatureDigest(IPayload(_payload), signer, _round);
85+
function createSignature(uint256 _privateKey, address _payload, Slot _slot) internal view returns (Signature memory) {
86+
bytes32 digest = governanceProposer.getSignalSignatureDigest(IPayload(_payload), _slot);
9287

9388
(uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, digest);
9489

l1-contracts/test/governance/governance-proposer/voteWithsig.t.sol

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ contract SignalWithSigTest is GovernanceProposerBase {
2424

2525
function setUp() public override {
2626
super.setUp();
27+
validatorSelection = new Fakerollup();
2728
}
2829

2930
// Skipping this test since the it matches the for now skipped check in `EmpireBase::signal`
@@ -54,7 +55,6 @@ contract SignalWithSigTest is GovernanceProposerBase {
5455
}
5556

5657
modifier givenCanonicalRollupHoldCode() {
57-
validatorSelection = new Fakerollup();
5858
proposer = vm.addr(privateKey);
5959
validatorSelection.setProposer(proposer);
6060

@@ -95,8 +95,7 @@ contract SignalWithSigTest is GovernanceProposerBase {
9595
// fast forward a round
9696
vm.warp(block.timestamp + _slotsToFastForward * validatorSelection.getSlotDuration());
9797

98-
uint256 round = governanceProposer.getCurrentRound();
99-
bytes32 digest = getDigest(privateKey, proposal, round);
98+
bytes32 digest = getDigest(proposal, validatorSelection.getCurrentSlot());
10099

101100
// signal
102101
address expectedInvalidSigner = ecrecover(digest, signature.v, signature.r, signature.s);
@@ -186,7 +185,7 @@ contract SignalWithSigTest is GovernanceProposerBase {
186185
Slot freshSlot = freshInstance.getCurrentSlot();
187186
uint256 freshRound = governanceProposer.computeRound(freshSlot);
188187

189-
signature = createSignature(privateKey, proposal);
188+
signature = createSignature(privateKey, proposal, freshSlot);
190189

191190
vm.expectEmit(true, true, true, true, address(governanceProposer));
192191
emit IEmpire.SignalCast(proposal, freshRound, proposer);
@@ -360,14 +359,16 @@ contract SignalWithSigTest is GovernanceProposerBase {
360359
}
361360
}
362361

363-
function getDigest(uint256 _privateKey, IPayload _payload, uint256 _round) internal view returns (bytes32) {
364-
address p = vm.addr(_privateKey);
365-
return governanceProposer.getSignalSignatureDigest(_payload, p, _round);
362+
function getDigest(IPayload _payload, Slot _slot) internal view returns (bytes32) {
363+
return governanceProposer.getSignalSignatureDigest(_payload, _slot);
366364
}
367365

368366
function createSignature(uint256 _privateKey, IPayload _payload) internal view returns (Signature memory) {
369-
uint256 round = governanceProposer.getCurrentRound();
370-
bytes32 digest = getDigest(_privateKey, _payload, round);
367+
return createSignature(_privateKey, _payload, validatorSelection.getCurrentSlot());
368+
}
369+
370+
function createSignature(uint256 _privateKey, IPayload _payload, Slot _slot) internal view returns (Signature memory) {
371+
bytes32 digest = getDigest(_payload, _slot);
371372

372373
(uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, digest);
373374

yarn-project/ethereum/src/contracts/empire_base.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ export function encodeSignalWithSignature(payload: Hex, signature: Signature) {
5151
export async function signSignalWithSig(
5252
signer: (msg: TypedDataDefinition) => Promise<Hex>,
5353
payload: Hex,
54-
nonce: bigint,
55-
round: bigint,
54+
slot: bigint,
5655
instance: Hex,
5756
verifyingContract: Hex,
5857
chainId: number,
@@ -67,16 +66,14 @@ export async function signSignalWithSig(
6766
const types = {
6867
Signal: [
6968
{ name: 'payload', type: 'address' },
70-
{ name: 'nonce', type: 'uint256' },
71-
{ name: 'round', type: 'uint256' },
69+
{ name: 'slot', type: 'uint256' },
7270
{ name: 'instance', type: 'address' },
7371
],
7472
};
7573

7674
const message = {
7775
payload,
78-
nonce,
79-
round,
76+
slot,
8077
instance,
8178
};
8279

0 commit comments

Comments
 (0)