Skip to content

Commit 1c0ce83

Browse files
authored
fix: Change nonce to height. (#105)
* Change nonce to height. * Update wrapper. * Fix typo. * Fix to use old -> new height contiguous. * Refactor tests to use cheatcode sign. * Update wrappers.
1 parent 6a3cea3 commit 1c0ce83

File tree

4 files changed

+99
-68
lines changed

4 files changed

+99
-68
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.4;
3+
4+
/// @dev bytes32 encoding of the string "checkpoint"
5+
bytes32 constant VALIDATOR_SET_HASH_DOMAIN_SEPARATOR = 0x636865636b706f696e7400000000000000000000000000000000000000000000;
6+
7+
/// @dev bytes32 encoding of the string "transactionBatch"
8+
bytes32 constant DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR = 0x7472616e73616374696f6e426174636800000000000000000000000000000000;

ethereum/solidity/src/QuantumGravityBridge.sol

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity ^0.8.4;
33

44
import "./lib/openzeppelin/contracts/utils/cryptography/ECDSA.sol";
55

6+
import "./Constants.sol";
67
import "./DataRootTuple.sol";
78
import "./IDAOracle.sol";
89
import "./lib/tree/binary/BinaryMerkleProof.sol";
@@ -36,18 +37,6 @@ contract QuantumGravityBridge is IDAOracle {
3637
// be accounted for in any upgrades. Always test an exact upgrade on testnet
3738
// and localhost before mainnet upgrades.
3839

39-
///////////////
40-
// Constants //
41-
///////////////
42-
43-
/// @dev bytes32 encoding of the string "checkpoint"
44-
bytes32 constant VALIDATOR_SET_HASH_DOMAIN_SEPARATOR =
45-
0x636865636b706f696e7400000000000000000000000000000000000000000000;
46-
47-
/// @dev bytes32 encoding of the string "transactionBatch"
48-
bytes32 constant DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR =
49-
0x7472616e73616374696f6e426174636800000000000000000000000000000000;
50-
5140
////////////////
5241
// Immutables //
5342
////////////////
@@ -114,31 +103,33 @@ contract QuantumGravityBridge is IDAOracle {
114103

115104
/// @param _bridge_id Identifier of the bridge, used in signatures for
116105
/// domain separation.
106+
/// @param _nonce Celestia block height at which bridge is initialized.
117107
/// @param _powerThreshold Initial voting power that is needed to approve
118108
/// operations.
119109
/// @param _validatorSetHash Initial validator set hash. This does not need
120110
/// to be the genesis validator set of the bridged chain, only the initial
121111
/// validator set of the bridge.
122112
constructor(
123113
bytes32 _bridge_id,
114+
uint256 _nonce,
124115
uint256 _powerThreshold,
125116
bytes32 _validatorSetHash
126117
) {
127118
BRIDGE_ID = _bridge_id;
128119

129120
// CHECKS
130121

131-
uint256 nonce = 0;
132-
bytes32 newCheckpoint = domainSeparateValidatorSetHash(_bridge_id, nonce, _powerThreshold, _validatorSetHash);
122+
bytes32 newCheckpoint = domainSeparateValidatorSetHash(_bridge_id, _nonce, _powerThreshold, _validatorSetHash);
133123

134124
// EFFECTS
135125

126+
state_lastValidatorSetNonce = _nonce;
136127
state_lastValidatorSetCheckpoint = newCheckpoint;
137128
state_powerThreshold = _powerThreshold;
138129

139130
// LOGS
140131

141-
emit ValidatorSetUpdatedEvent(nonce, _powerThreshold, _validatorSetHash);
132+
emit ValidatorSetUpdatedEvent(_nonce, _powerThreshold, _validatorSetHash);
142133
}
143134

144135
/// @notice Utility function to check if a signature is nil.
@@ -189,17 +180,19 @@ contract QuantumGravityBridge is IDAOracle {
189180
/// @dev Make a domain-separated commitment to a data root tuple root.
190181
/// A hash of all relevant information about a data root tuple root.
191182
/// The format of the hash is:
192-
/// keccak256(bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, nonce, _dataRootTupleRoot)
183+
/// keccak256(bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, oldNonce, newNonce, dataRootTupleRoot)
193184
/// @param _bridge_id Bridge ID.
194-
/// @param _nonce Nonce.
185+
/// @param _oldNonce Celestia block height at which commitment begins.
186+
/// @param _newNonce Celestia block height at which commitment ends.
195187
/// @param _dataRootTupleRoot Data root tuple root.
196188
function domainSeparateDataRootTupleRoot(
197189
bytes32 _bridge_id,
198-
uint256 _nonce,
190+
uint256 _oldNonce,
191+
uint256 _newNonce,
199192
bytes32 _dataRootTupleRoot
200193
) private pure returns (bytes32) {
201194
bytes32 c = keccak256(
202-
abi.encode(_bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, _nonce, _dataRootTupleRoot)
195+
abi.encode(_bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, _oldNonce, _newNonce, _dataRootTupleRoot)
203196
);
204197

205198
return c;
@@ -255,7 +248,7 @@ contract QuantumGravityBridge is IDAOracle {
255248
/// The validator set hash that is signed over is domain separated as per
256249
/// `domainSeparateValidatorSetHash`.
257250
/// @param _newValidatorSetHash The hash of the new validator set.
258-
/// @param _newNonce The new nonce.
251+
/// @param _newNonce The new Celestia block height.
259252
/// @param _currentValidatorSet The current validator set.
260253
/// @param _sigs Signatures.
261254
function updateValidatorSet(
@@ -270,7 +263,7 @@ contract QuantumGravityBridge is IDAOracle {
270263
uint256 currentNonce = state_lastValidatorSetNonce;
271264
uint256 currentPowerThreshold = state_powerThreshold;
272265

273-
// Check that the valset nonce is greater than the old one.
266+
// Check that the new validator set nonce is greater than the old one.
274267
if (_newNonce <= currentNonce) {
275268
revert InvalidValidatorSetNonce();
276269
}
@@ -321,7 +314,8 @@ contract QuantumGravityBridge is IDAOracle {
321314
///
322315
/// The data tuple root that is signed over is domain separated as per
323316
/// `domainSeparateDataRootTupleRoot`.
324-
/// @param _nonce The data root tuple root nonce.
317+
/// @param _nonce The Celestia block height up to which the data root tuple
318+
/// root commits to.
325319
/// @param _dataRootTupleRoot The Merkle root of data root tuples.
326320
/// @param _currentValidatorSet The current validator set.
327321
/// @param _sigs Signatures.
@@ -333,10 +327,11 @@ contract QuantumGravityBridge is IDAOracle {
333327
) external {
334328
// CHECKS
335329

330+
uint256 currentNonce = state_lastDataRootTupleRootNonce;
336331
uint256 currentPowerThreshold = state_powerThreshold;
337332

338333
// Check that the data root tuple root nonce is higher than the last nonce.
339-
if (_nonce <= state_lastDataRootTupleRootNonce) {
334+
if (_nonce <= currentNonce) {
340335
revert InvalidDataRootTupleRootNonce();
341336
}
342337

@@ -360,7 +355,7 @@ contract QuantumGravityBridge is IDAOracle {
360355

361356
// Check that enough current validators have signed off on the data
362357
// root tuple root and nonce.
363-
bytes32 c = domainSeparateDataRootTupleRoot(BRIDGE_ID, _nonce, _dataRootTupleRoot);
358+
bytes32 c = domainSeparateDataRootTupleRoot(BRIDGE_ID, currentNonce, _nonce, _dataRootTupleRoot);
364359
checkValidatorSignatures(_currentValidatorSet, _sigs, c, currentPowerThreshold);
365360

366361
// EFFECTS

ethereum/solidity/src/test/QuantumGravityBridge.t.sol

Lines changed: 68 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,75 +3,90 @@ pragma solidity ^0.8.4;
33

44
import "../lib/openzeppelin/contracts/utils/cryptography/ECDSA.sol";
55

6+
import "../Constants.sol";
67
import "../DataRootTuple.sol";
78
import "../QuantumGravityBridge.sol";
89

910
import "ds-test/test.sol";
1011

11-
contract RelayerTest is DSTest {
12-
// private keys used for hardcoded signatures
13-
// testAddr = "0x9c2B12b5a07FC6D719Ed7646e5041A7E85758329"
14-
// testPriv = "64a1d6f0e760a8d62b4afdde4096f16f51b401eaaecc915740f71770ea76a8ad"
15-
// testAddr2 = "0xe650B084f05C6194f6e552e3b9f08718Bc8a9d56"
16-
// testPriv2 = "6e8bdfa979ab645b41c4d17cb1329b2a44684c82b61b1b060ea9b6e1c927a4f4"
12+
interface CheatCodes {
13+
function addr(uint256 privateKey) external returns (address);
1714

18-
bytes32 constant VALIDATOR_SET_HASH_DOMAIN_SEPARATOR =
19-
0x636865636b706f696e7400000000000000000000000000000000000000000000;
15+
function sign(uint256 privateKey, bytes32 digest)
16+
external
17+
returns (
18+
uint8 v,
19+
bytes32 r,
20+
bytes32 s
21+
);
22+
}
2023

21-
bytes32 constant DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR =
22-
0x7472616e73616374696f6e426174636800000000000000000000000000000000;
24+
contract RelayerTest is DSTest {
25+
// Private keys used for test signatures.
26+
uint256 constant testPriv1 = 0x64a1d6f0e760a8d62b4afdde4096f16f51b401eaaecc915740f71770ea76a8ad;
27+
uint256 constant testPriv2 = 0x6e8bdfa979ab645b41c4d17cb1329b2a44684c82b61b1b060ea9b6e1c927a4f4;
2328

24-
bytes32 constant BRIDGE_ID =
25-
VALIDATOR_SET_HASH_DOMAIN_SEPARATOR;
29+
// 32 bytes, chosen at random.
30+
bytes32 constant BRIDGE_ID = 0x6de92bac0b357560d821f8e7b6f5c9fe4f3f88f6c822283efd7ab51ad56a640e;
2631

2732
QuantumGravityBridge bridge;
28-
33+
2934
Validator[] private validators;
30-
uint256 private votingPower = 3334;
35+
uint256 private votingPower = 5000;
3136
uint256 private valsetNonce = 0;
3237
uint256 private dataTupleRootNonce = 0;
38+
39+
// Set up Foundry cheatcodes.
40+
CheatCodes cheats = CheatCodes(HEVM_ADDRESS);
41+
3342
function setUp() public {
34-
validators.push(Validator(0x9c2B12b5a07FC6D719Ed7646e5041A7E85758329, 5000));
43+
validators.push(Validator(cheats.addr(testPriv1), votingPower));
3544
bytes32 hash = computeValidatorSetHash(validators);
36-
bridge = new QuantumGravityBridge(BRIDGE_ID, votingPower, hash);
45+
bridge = new QuantumGravityBridge(BRIDGE_ID, valsetNonce, (2 * votingPower) / 3, hash);
3746
}
3847

3948
function testUpdateValidatorSet() public {
40-
// save the old test validator set before we add to it
49+
// Save the old test validator set before we add to it.
4150
Validator[] memory oldVS = new Validator[](1);
42-
oldVS[0] = Validator(0x9c2B12b5a07FC6D719Ed7646e5041A7E85758329, 5000);
51+
oldVS[0] = Validator(cheats.addr(testPriv1), votingPower);
4352

44-
// hardcoded signature for the first validator set update
53+
uint256 newNonce = 1;
54+
validators.push(Validator(cheats.addr(testPriv2), votingPower));
55+
votingPower += votingPower;
56+
uint256 newPowerThreshold = (2 * votingPower) / 3;
57+
bytes32 newVSHash = keccak256(abi.encode(validators));
58+
bytes32 newCheckpoint = domainSeparateValidatorSetHash(BRIDGE_ID, newNonce, newPowerThreshold, newVSHash);
59+
60+
// Signature for the first validator set update.
4561
Signature[] memory sigs = new Signature[](1);
46-
sigs[0] = Signature(27, 0xbe1d908f5f4f307230f7f6489253ea3051096f0de57377bf1ac218cf7c560a08, 0x4eed1044b69cebcaf45aabc2363b55a7047ce7dd0dd219baec4c4ac3d097c6f8);
62+
bytes32 digest_eip191 = ECDSA.toEthSignedMessageHash(newCheckpoint);
63+
(uint8 v, bytes32 r, bytes32 s) = cheats.sign(testPriv1, digest_eip191);
64+
sigs[0] = Signature(v, r, s);
4765

48-
// change test validator set
49-
validators.push(Validator(0xe650B084f05C6194f6e552e3b9f08718Bc8a9d56, 5000));
50-
bytes32 newVSHash = keccak256(abi.encode(validators));
66+
bridge.updateValidatorSet(newNonce, newPowerThreshold, newVSHash, oldVS, sigs);
5167

52-
uint256 newNonce = 1;
53-
uint256 newPowerThreshhold = 6668;
54-
bridge.updateValidatorSet(newNonce, newPowerThreshhold, newVSHash, oldVS, sigs);
55-
bytes32 newCheckpoint = domainSeparateValidatorSetHash(BRIDGE_ID, newNonce, newPowerThreshhold, newVSHash);
5668
assertEq(bridge.state_lastValidatorSetNonce(), newNonce);
57-
assertEq(bridge.state_powerThreshold(), newPowerThreshhold);
58-
assertEq(
59-
bridge.state_lastValidatorSetCheckpoint(),
60-
newCheckpoint
61-
);
69+
assertEq(bridge.state_powerThreshold(), newPowerThreshold);
70+
assertEq(bridge.state_lastValidatorSetCheckpoint(), newCheckpoint);
6271
}
6372

6473
function testSubmitDataRootTupleRoot() public {
65-
// hardcoded signature for the first validator set update
74+
uint256 newNonce = 1;
75+
// 32 bytes, chosen at random.
76+
bytes32 newTupleRoot = 0x0de92bac0b356560d821f8e7b6f5c9fe4f3f88f6c822283efd7ab51ad56a640e;
77+
78+
bytes32 newDataRootTupleRoot = domainSeparateDataRootTupleRoot(BRIDGE_ID, valsetNonce, newNonce, newTupleRoot);
79+
80+
// Signature for the update.
6681
Signature[] memory sigs = new Signature[](1);
67-
sigs[0] = Signature(28, 0x8a71b11dfc2f5bf6ac3a9e7f8e74a3e6d58aa24957c4a6a8fb6021b6b3c9a57e, 0x796de792753d60cdd79a922516c8345db6661fe1762a59c0aff7698ac4cb7eea);
68-
69-
Validator[] memory vals = new Validator[](1);
70-
vals[0] = Validator(0x9c2B12b5a07FC6D719Ed7646e5041A7E85758329, 5000);
82+
bytes32 digest_eip191 = ECDSA.toEthSignedMessageHash(newDataRootTupleRoot);
83+
(uint8 v, bytes32 r, bytes32 s) = cheats.sign(testPriv1, digest_eip191);
84+
sigs[0] = Signature(v, r, s);
7185

72-
uint256 newNonce = 1;
73-
bytes32 newTupleRoot = VALIDATOR_SET_HASH_DOMAIN_SEPARATOR;
74-
bridge.submitDataRootTupleRoot(newNonce, newTupleRoot, vals, sigs);
86+
Validator[] memory valSet = new Validator[](1);
87+
valSet[0] = Validator(cheats.addr(testPriv1), votingPower);
88+
89+
bridge.submitDataRootTupleRoot(newNonce, newTupleRoot, valSet, sigs);
7590

7691
assertEq(bridge.state_lastDataRootTupleRootNonce(), newNonce);
7792
assertEq(bridge.state_dataRootTupleRoots(newNonce), newTupleRoot);
@@ -93,4 +108,17 @@ contract RelayerTest is DSTest {
93108

94109
return c;
95110
}
111+
112+
function domainSeparateDataRootTupleRoot(
113+
bytes32 _bridge_id,
114+
uint256 _oldNonce,
115+
uint256 _newNonce,
116+
bytes32 _dataRootTupleRoot
117+
) private pure returns (bytes32) {
118+
bytes32 c = keccak256(
119+
abi.encode(_bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, _oldNonce, _newNonce, _dataRootTupleRoot)
120+
);
121+
122+
return c;
123+
}
96124
}

0 commit comments

Comments
 (0)