Skip to content

Commit

Permalink
Universal nonce (#119)
Browse files Browse the repository at this point in the history
* Update contracts to use universal nonce.

* Update tests.

* Update Go wrapper.
  • Loading branch information
adlerjohn authored Jun 6, 2022
1 parent c61578f commit 210a1fb
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 126 deletions.
6 changes: 3 additions & 3 deletions src/IDAOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import "./lib/tree/binary/BinaryMerkleProof.sol";
/// @notice Data Availability Oracle interface.
interface IDAOracle {
/// @notice Verify a Data Availability attestation.
/// @param _tupleRootIndex Index of the tuple root to prove against.
/// @param _tupleRootNonce Nonce of the tuple root to prove against.
/// @param _tuple Data root tuple to prove inclusion of.
/// @param _proof Binary Merkle tree proof that `tuple` is in the root at `tupleRootIndex`.
/// @param _proof Binary Merkle tree proof that `tuple` is in the root at `_tupleRootNonce`.
/// @return `true` is proof is valid, `false` otherwise.
function verifyAttestation(
uint256 _tupleRootIndex,
uint256 _tupleRootNonce,
DataRootTuple memory _tuple,
BinaryMerkleProof memory _proof
) external view returns (bool);
Expand Down
64 changes: 31 additions & 33 deletions src/QuantumGravityBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ contract QuantumGravityBridge is IDAOracle {
bytes32 public state_lastValidatorSetCheckpoint;
/// @notice Voting power required to submit a new update.
uint256 public state_powerThreshold;
/// @notice Unique nonce of validator set updates.
uint256 public state_lastValidatorSetNonce;
/// @notice Unique nonce of data root tuple root updates.
uint256 public state_lastDataRootTupleRootNonce;
/// @notice Nonce for bridge events. Must be incremented sequentially.
uint256 public state_eventNonce;
/// @notice Mapping of data root tuple root nonces to data root tuple roots.
mapping(uint256 => bytes32) public state_dataRootTupleRoots;

Expand All @@ -63,13 +61,13 @@ contract QuantumGravityBridge is IDAOracle {
////////////

/// @notice Emitted when a new root of data root tuples is relayed.
/// @param nonce Nonce.
/// @param nonce Event nonce.
/// @param dataRootTupleRoot Merkle root of relayed data root tuples.
/// See `submitDataRootTupleRoot`.
event DataRootTupleRootEvent(uint256 indexed nonce, bytes32 dataRootTupleRoot);

/// @notice Emitted when the validator set is updated.
/// @param nonce Nonce.
/// @param nonce Event nonce.
/// @param powerThreshold New voting power threshold.
/// @param validatorSetHash Hash of new validator set.
/// See `updateValidatorSet`.
Expand Down Expand Up @@ -103,7 +101,7 @@ contract QuantumGravityBridge is IDAOracle {

/// @param _bridge_id Identifier of the bridge, used in signatures for
/// domain separation.
/// @param _nonce Celestia block height at which bridge is initialized.
/// @param _nonce Initial event nonce.
/// @param _powerThreshold Initial voting power that is needed to approve
/// operations.
/// @param _validatorSetHash Initial validator set hash. This does not need
Expand All @@ -123,7 +121,7 @@ contract QuantumGravityBridge is IDAOracle {

// EFFECTS

state_lastValidatorSetNonce = _nonce;
state_eventNonce = _nonce;
state_lastValidatorSetCheckpoint = newCheckpoint;
state_powerThreshold = _powerThreshold;

Expand Down Expand Up @@ -180,19 +178,17 @@ contract QuantumGravityBridge is IDAOracle {
/// @dev Make a domain-separated commitment to a data root tuple root.
/// A hash of all relevant information about a data root tuple root.
/// The format of the hash is:
/// keccak256(bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, oldNonce, newNonce, dataRootTupleRoot)
/// keccak256(bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, nonce, dataRootTupleRoot)
/// @param _bridge_id Bridge ID.
/// @param _oldNonce Celestia block height at which commitment begins.
/// @param _newNonce Celestia block height at which commitment ends.
/// @param _nonce Event nonce.
/// @param _dataRootTupleRoot Data root tuple root.
function domainSeparateDataRootTupleRoot(
bytes32 _bridge_id,
uint256 _oldNonce,
uint256 _newNonce,
uint256 _nonce,
bytes32 _dataRootTupleRoot
) private pure returns (bytes32) {
bytes32 c = keccak256(
abi.encode(_bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, _oldNonce, _newNonce, _dataRootTupleRoot)
abi.encode(_bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, _nonce, _dataRootTupleRoot)
);

return c;
Expand Down Expand Up @@ -248,7 +244,7 @@ contract QuantumGravityBridge is IDAOracle {
/// The validator set hash that is signed over is domain separated as per
/// `domainSeparateValidatorSetHash`.
/// @param _newValidatorSetHash The hash of the new validator set.
/// @param _newNonce The new Celestia block height.
/// @param _newNonce The new event nonce.
/// @param _currentValidatorSet The current validator set.
/// @param _sigs Signatures.
function updateValidatorSet(
Expand All @@ -260,11 +256,11 @@ contract QuantumGravityBridge is IDAOracle {
) external {
// CHECKS

uint256 currentNonce = state_lastValidatorSetNonce;
uint256 currentNonce = state_eventNonce;
uint256 currentPowerThreshold = state_powerThreshold;

// Check that the new validator set nonce is greater than the old one.
if (_newNonce <= currentNonce) {
// Check that the new nonce is one more than the current one.
if (_newNonce != currentNonce + 1) {
revert InvalidValidatorSetNonce();
}

Expand Down Expand Up @@ -295,7 +291,7 @@ contract QuantumGravityBridge is IDAOracle {

state_lastValidatorSetCheckpoint = newCheckpoint;
state_powerThreshold = _newPowerThreshold;
state_lastValidatorSetNonce = _newNonce;
state_eventNonce = _newNonce;

// LOGS

Expand All @@ -314,24 +310,26 @@ contract QuantumGravityBridge is IDAOracle {
///
/// The data tuple root that is signed over is domain separated as per
/// `domainSeparateDataRootTupleRoot`.
/// @param _nonce The Celestia block height up to which the data root tuple
/// root commits to.
/// @param _newNonce The new event nonce.
/// @param _validatorSetNonce The nonce of the latest update to the
/// validator set.
/// @param _dataRootTupleRoot The Merkle root of data root tuples.
/// @param _currentValidatorSet The current validator set.
/// @param _sigs Signatures.
function submitDataRootTupleRoot(
uint256 _nonce,
uint256 _newNonce,
uint256 _validatorSetNonce,
bytes32 _dataRootTupleRoot,
Validator[] calldata _currentValidatorSet,
Signature[] calldata _sigs
) external {
// CHECKS

uint256 currentNonce = state_lastDataRootTupleRootNonce;
uint256 currentNonce = state_eventNonce;
uint256 currentPowerThreshold = state_powerThreshold;

// Check that the data root tuple root nonce is higher than the last nonce.
if (_nonce <= currentNonce) {
// Check that the new nonce is one more than the current one.
if (_newNonce != currentNonce + 1) {
revert InvalidDataRootTupleRootNonce();
}

Expand All @@ -345,7 +343,7 @@ contract QuantumGravityBridge is IDAOracle {
if (
domainSeparateValidatorSetHash(
BRIDGE_ID,
state_lastValidatorSetNonce,
_validatorSetNonce,
currentPowerThreshold,
currentValidatorSetHash
) != state_lastValidatorSetCheckpoint
Expand All @@ -355,32 +353,32 @@ contract QuantumGravityBridge is IDAOracle {

// Check that enough current validators have signed off on the data
// root tuple root and nonce.
bytes32 c = domainSeparateDataRootTupleRoot(BRIDGE_ID, currentNonce, _nonce, _dataRootTupleRoot);
bytes32 c = domainSeparateDataRootTupleRoot(BRIDGE_ID, _newNonce, _dataRootTupleRoot);
checkValidatorSignatures(_currentValidatorSet, _sigs, c, currentPowerThreshold);

// EFFECTS

state_lastDataRootTupleRootNonce = _nonce;
state_dataRootTupleRoots[_nonce] = _dataRootTupleRoot;
state_eventNonce = _newNonce;
state_dataRootTupleRoots[_newNonce] = _dataRootTupleRoot;

// LOGS

emit DataRootTupleRootEvent(_nonce, _dataRootTupleRoot);
emit DataRootTupleRootEvent(_newNonce, _dataRootTupleRoot);
}

/// @dev see "./IDAOracle.sol"
function verifyAttestation(
uint256 _tupleRootIndex,
uint256 _tupleRootNonce,
DataRootTuple memory _tuple,
BinaryMerkleProof memory _proof
) external view override returns (bool) {
// Tuple must have been committed before.
if (_tupleRootIndex > state_lastDataRootTupleRootNonce) {
if (_tupleRootNonce > state_eventNonce) {
return false;
}

// Load the tuple root at the given index from storage.
bytes32 root = state_dataRootTupleRoots[_tupleRootIndex];
bytes32 root = state_dataRootTupleRoots[_tupleRootNonce];

// Verify the proof.
bool isProofValid = BinaryMerkleTree.verify(root, _proof, abi.encode(_tuple));
Expand Down
23 changes: 12 additions & 11 deletions src/test/QuantumGravityBridge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@ contract RelayerTest is DSTest {

Validator[] private validators;
uint256 private votingPower = 5000;
uint256 private valsetNonce = 0;
uint256 private dataTupleRootNonce = 0;

// Set up Foundry cheatcodes.
CheatCodes cheats = CheatCodes(HEVM_ADDRESS);

function setUp() public {
uint256 initialVelsetNonce = 0;

validators.push(Validator(cheats.addr(testPriv1), votingPower));
bytes32 hash = computeValidatorSetHash(validators);
bridge = new QuantumGravityBridge(BRIDGE_ID, valsetNonce, (2 * votingPower) / 3, hash);
bridge = new QuantumGravityBridge(BRIDGE_ID, initialVelsetNonce, (2 * votingPower) / 3, hash);
}

function testUpdateValidatorSet() public {
Expand All @@ -65,17 +66,18 @@ contract RelayerTest is DSTest {

bridge.updateValidatorSet(newNonce, newPowerThreshold, newVSHash, oldVS, sigs);

assertEq(bridge.state_lastValidatorSetNonce(), newNonce);
assertEq(bridge.state_eventNonce(), newNonce);
assertEq(bridge.state_powerThreshold(), newPowerThreshold);
assertEq(bridge.state_lastValidatorSetCheckpoint(), newCheckpoint);
}

function testSubmitDataRootTupleRoot() public {
uint256 newNonce = 1;
uint256 initialVelsetNonce = 0;
uint256 nonce = 1;
// 32 bytes, chosen at random.
bytes32 newTupleRoot = 0x0de92bac0b356560d821f8e7b6f5c9fe4f3f88f6c822283efd7ab51ad56a640e;

bytes32 newDataRootTupleRoot = domainSeparateDataRootTupleRoot(BRIDGE_ID, valsetNonce, newNonce, newTupleRoot);
bytes32 newDataRootTupleRoot = domainSeparateDataRootTupleRoot(BRIDGE_ID, nonce, newTupleRoot);

// Signature for the update.
Signature[] memory sigs = new Signature[](1);
Expand All @@ -86,10 +88,10 @@ contract RelayerTest is DSTest {
Validator[] memory valSet = new Validator[](1);
valSet[0] = Validator(cheats.addr(testPriv1), votingPower);

bridge.submitDataRootTupleRoot(newNonce, newTupleRoot, valSet, sigs);
bridge.submitDataRootTupleRoot(nonce, initialVelsetNonce, newTupleRoot, valSet, sigs);

assertEq(bridge.state_lastDataRootTupleRootNonce(), newNonce);
assertEq(bridge.state_dataRootTupleRoots(newNonce), newTupleRoot);
assertEq(bridge.state_eventNonce(), nonce);
assertEq(bridge.state_dataRootTupleRoots(nonce), newTupleRoot);
}

function computeValidatorSetHash(Validator[] memory _validators) private pure returns (bytes32) {
Expand All @@ -111,12 +113,11 @@ contract RelayerTest is DSTest {

function domainSeparateDataRootTupleRoot(
bytes32 _bridge_id,
uint256 _oldNonce,
uint256 _newNonce,
uint256 _nonce,
bytes32 _dataRootTupleRoot
) private pure returns (bytes32) {
bytes32 c = keccak256(
abi.encode(_bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, _oldNonce, _newNonce, _dataRootTupleRoot)
abi.encode(_bridge_id, DATA_ROOT_TUPLE_ROOT_DOMAIN_SEPARATOR, _nonce, _dataRootTupleRoot)
);

return c;
Expand Down
Loading

0 comments on commit 210a1fb

Please sign in to comment.