Skip to content

Commit

Permalink
feat!: deploy Blobstream at a custom nonce
Browse files Browse the repository at this point in the history
  • Loading branch information
rach-id committed Nov 3, 2023
1 parent 856a9e5 commit 2a01b7a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 23 deletions.
14 changes: 3 additions & 11 deletions src/Blobstream.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,28 +101,20 @@ contract Blobstream is IDAOracle, Initializable, UUPSUpgradeable, OwnableUpgrade
/// @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
/// @param _validatorSetCheckpoint Initial validator set domainSeparateValidatorSetHash. This does not need
/// to be the genesis validator set of the bridged chain, only the initial
/// validator set of the bridge.
/// @dev DO NOT REMOVE THE INITIALIZER! It is mandatory for upgradability.
function initialize(uint256 _nonce, uint256 _powerThreshold, bytes32 _validatorSetHash) public initializer {
// CHECKS

bytes32 newCheckpoint = domainSeparateValidatorSetHash(_nonce, _powerThreshold, _validatorSetHash);

function initialize(uint256 _nonce, uint256 _powerThreshold, bytes32 _validatorSetCheckpoint) public initializer {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

// EFFECTS

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

/// @dev Initialize the OwnableUpgradeable explicitly.
/// DO NOT REMOVE! It is mandatory for allowing the owner to upgrade.
__Ownable_init(_msgSender());

// LOGS

emit ValidatorSetUpdatedEvent(_nonce, _powerThreshold, _validatorSetHash);
}

/// @dev only authorize the upgrade for the owner of the contract.
Expand Down
14 changes: 13 additions & 1 deletion src/lib/verifier/test/DAVerifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ contract DAVerifierTest is DSTest {

validators.push(Validator(cheats.addr(testPriv1), votingPower));
bytes32 hash = computeValidatorSetHash(validators);
bytes32 checkpoint = domainSeparateValidatorSetHash(initialVelsetNonce, (2 * votingPower) / 3, hash);
bridge = new Blobstream();
bridge.initialize(initialVelsetNonce, (2 * votingPower) / 3, hash);
bridge.initialize(initialVelsetNonce, (2 * votingPower) / 3, checkpoint);

bytes32 newDataRootTupleRoot =
domainSeparateDataRootTupleRoot(fixture.dataRootTupleRootNonce(), fixture.dataRootTupleRoot());
Expand Down Expand Up @@ -198,6 +199,17 @@ contract DAVerifierTest is DSTest {
return keccak256(abi.encode(_validators));
}

function domainSeparateValidatorSetHash(uint256 _nonce, uint256 _powerThreshold, bytes32 _validatorSetHash)
private
pure
returns (bytes32)
{
bytes32 c =
keccak256(abi.encode(VALIDATOR_SET_HASH_DOMAIN_SEPARATOR, _nonce, _powerThreshold, _validatorSetHash));

return c;
}

function domainSeparateDataRootTupleRoot(uint256 _nonce, bytes32 _dataRootTupleRoot)
private
pure
Expand Down
14 changes: 13 additions & 1 deletion src/lib/verifier/test/RollupInclusionProofs.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@ contract RollupInclusionProofTest is DSTest {

validators.push(Validator(cheats.addr(testPriv1), votingPower));
bytes32 hash = computeValidatorSetHash(validators);
bytes32 checkpoint = domainSeparateValidatorSetHash(initialVelsetNonce, (2 * votingPower) / 3, hash);
bridge = new Blobstream();
bridge.initialize(initialVelsetNonce, (2 * votingPower) / 3, hash);
bridge.initialize(initialVelsetNonce, (2 * votingPower) / 3, checkpoint);

fixture = new TestFixture();

Expand Down Expand Up @@ -366,6 +367,17 @@ contract RollupInclusionProofTest is DSTest {
return keccak256(abi.encode(_validators));
}

function domainSeparateValidatorSetHash(uint256 _nonce, uint256 _powerThreshold, bytes32 _validatorSetHash)
private
pure
returns (bytes32)
{
bytes32 c =
keccak256(abi.encode(VALIDATOR_SET_HASH_DOMAIN_SEPARATOR, _nonce, _powerThreshold, _validatorSetHash));

return c;
}

function domainSeparateDataRootTupleRoot(uint256 _nonce, bytes32 _dataRootTupleRoot)
private
pure
Expand Down
47 changes: 38 additions & 9 deletions src/test/Blobstream.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,28 @@ contract RelayerTest is DSTest {

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

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

function setUp() public {
uint256 initialVelsetNonce = 0;
uint256 initialVelsetNonce = 1;

validators.push(Validator(cheats.addr(testPriv1), votingPower));
bytes32 hash = computeValidatorSetHash(validators);
bytes32 validatorSetCheckpoint = domainSeparateValidatorSetHash(initialVelsetNonce, (2 * votingPower) / 3, hash);
bridge = new Blobstream();
bridge.initialize(initialVelsetNonce, (2 * votingPower) / 3, hash);
bridge.initialize(initialVelsetNonce, (2 * votingPower) / 3, validatorSetCheckpoint);
}

function testUpdateValidatorSet() public {
uint256 initialVelsetNonce = 0;
uint256 initialVelsetNonce = 1;

// Save the old test validator set before we add to it.
Validator[] memory oldVS = new Validator[](1);
oldVS[0] = Validator(cheats.addr(testPriv1), votingPower);

uint256 newNonce = 1;
uint256 newNonce = 2;
validators.push(Validator(cheats.addr(testPriv2), votingPower));
votingPower += votingPower;
uint256 newPowerThreshold = (2 * votingPower) / 3;
Expand All @@ -67,8 +67,8 @@ contract RelayerTest is DSTest {
}

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

Expand All @@ -89,6 +89,35 @@ contract RelayerTest is DSTest {
assertEq(bridge.state_dataRootTupleRoots(nonce), newTupleRoot);
}

function testDeployContractAtCustomNonce() public {
uint256 initialVelsetNonce = 1;
uint256 targetNonce = 200;

Validator[] memory valSet = new Validator[](1);
valSet[0] = Validator(cheats.addr(testPriv1), votingPower);

bytes32 hash = computeValidatorSetHash(valSet);
bytes32 validatorSetCheckpoint = domainSeparateValidatorSetHash(initialVelsetNonce, (2 * votingPower) / 3, hash);
Blobstream newBridge = new Blobstream();
newBridge.initialize(targetNonce, (2 * votingPower) / 3, validatorSetCheckpoint);

// 32 bytes, chosen at random.
bytes32 newTupleRoot = 0x0de92bac0b356560d821f8e7b6f5c9fe4f3f88f6c822283efd7ab51ad56a640e;

bytes32 newDataRootTupleRoot = domainSeparateDataRootTupleRoot(targetNonce + 1, newTupleRoot);

// Signature for the update.
Signature[] memory sigs = new Signature[](1);
bytes32 digest_eip191 = ECDSA.toEthSignedMessageHash(newDataRootTupleRoot);
(uint8 v, bytes32 r, bytes32 s) = cheats.sign(testPriv1, digest_eip191);
sigs[0] = Signature(v, r, s);

newBridge.submitDataRootTupleRoot(targetNonce + 1, initialVelsetNonce, newTupleRoot, valSet, sigs);

assertEq(newBridge.state_eventNonce(), targetNonce + 1);
assertEq(newBridge.state_dataRootTupleRoots(targetNonce + 1), newTupleRoot);
}

/*
the values used in the verify attestation test are in the format `<height padded to 32 bytes || data root>`, which
represent an encoded `abi.encode(DataRootTuple)`:
Expand All @@ -99,9 +128,9 @@ contract RelayerTest is DSTest {
0x00000000000000000000000000000000000000000000000000000000000000030303030303030303030303030303030303030303030303030303030303030303
*/
function testVerifyAttestation() public {
uint256 initialVelsetNonce = 0;
uint256 initialVelsetNonce = 1;
// data root tuple root nonce.
uint256 nonce = 1;
uint256 nonce = 2;
// commitment to a set of roots.
// these values were generated using the tendermint implementation of binary merkle trees:
// https://github.com/celestiaorg/celestia-core/blob/60310e7aa554bb76b735a010847a6613bcfe06e8/crypto/merkle/proof.go#L33-L48
Expand Down
14 changes: 13 additions & 1 deletion src/test/BlobstreamBenchmark.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ contract Benchmark is DSTest {
validators = initializeValidators(privateKeys);

bytes32 hash = computeValidatorSetHash(validators);
bytes32 checkpoint = domainSeparateValidatorSetHash(initialVelsetNonce, (2 * totalValidatorPower) / 3, hash);
bridge = new Blobstream();
bridge.initialize(initialVelsetNonce, (2 * totalValidatorPower) / 3, hash);
bridge.initialize(initialVelsetNonce, (2 * totalValidatorPower) / 3, checkpoint);
}

function testBenchmarkSubmitDataRootTupleRoot() public {
Expand Down Expand Up @@ -89,6 +90,17 @@ contract Benchmark is DSTest {
return keccak256(abi.encode(_validators));
}

function domainSeparateValidatorSetHash(uint256 _nonce, uint256 _powerThreshold, bytes32 _validatorSetHash)
private
pure
returns (bytes32)
{
bytes32 c =
keccak256(abi.encode(VALIDATOR_SET_HASH_DOMAIN_SEPARATOR, _nonce, _powerThreshold, _validatorSetHash));

return c;
}

function domainSeparateDataRootTupleRoot(uint256 _nonce, bytes32 _dataRootTupleRoot)
private
pure
Expand Down

0 comments on commit 2a01b7a

Please sign in to comment.