Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add the ability to run benchmarks on the QGB contract #218

Merged
merged 7 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[profile.default]
solc_version = "0.8.20"
via_ir = true
gas_reports = ["*"]
111 changes: 111 additions & 0 deletions src/test/QuantumGravityBridgeBenchmark.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.19;

import "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";

import "../Constants.sol";
import "../DataRootTuple.sol";
import "../QuantumGravityBridge.sol";
import "../lib/tree/binary/BinaryMerkleProof.sol";

import "ds-test/test.sol";

interface CheatCodes {
function addr(uint256 privateKey) external returns (address);
function sign(uint256 privateKey, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);
function deriveKey(string calldata, string calldata, uint32) external returns (uint256);
}

/// @notice Example command to run the benchmark:
/// `forge test --match-test testBenchmarkSubmitDataRootTupleRoot -vvvvvv --gas-report`.
/// To change the validator set size, change the `numberOfValidators` constant.
/// To make custom calculations of the gas, you can use the `gasleft()` solidity
/// built-in function.
/// The following answer has some insights on using that:
/// https://ethereum.stackexchange.com/a/132325/65649
/// The gas estimations might not be accurate to the real cost in a real network,
/// and that's because foundry doesn't track calldata cost. source:
/// https://github.com/foundry-rs/foundry/issues/3475#issuecomment-1469940917
/// To have accurate results, make sure to add the following costs:
/// A byte of calldata costs either 4 gas (if it is zero) or 16 gas (if it is any other value).
contract Benchmark is DSTest {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[note for reviewers]
This is always run, but it only takes 5.71ms, so I guess it's okey. we can leave it running with the tests.

uint256 private constant numberOfValidators = 1;

// Private keys used for test signatures.
uint256[] private privateKeys;

QuantumGravityBridge private bridge;

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;
privateKeys = derivePrivateKeys(numberOfValidators);
validators = initializeValidators(privateKeys);

bytes32 hash = computeValidatorSetHash(validators);
bridge = new QuantumGravityBridge();
bridge.initialize(initialVelsetNonce, (2 * votingPower * numberOfValidators) / 3, hash);
}

function testBenchmarkSubmitDataRootTupleRoot() public {
uint256 initialVelsetNonce = 0;
uint256 nonce = 1;

// 32 bytes, chosen at random.
bytes32 newTupleRoot = 0x0de92bac0b356560d821f8e7b6f5c9fe4f3f88f6c822283efd7ab51ad56a640e;
bytes32 newDataRootTupleRoot = domainSeparateDataRootTupleRoot(nonce, newTupleRoot);

// Signature for the update.
Signature[] memory sigs = new Signature[](numberOfValidators);
bytes32 digest_eip191 = ECDSA.toEthSignedMessageHash(newDataRootTupleRoot);
for (uint256 i = 0; i < numberOfValidators; i++) {
(uint8 v, bytes32 r, bytes32 s) = cheats.sign(privateKeys[i], digest_eip191);
sigs[i] = Signature(v, r, s);
}

// these are called here so that they're part of the gas report.
// uint256 currentPowerThreshold = (2 * votingPower * numberOfValidators) / 3;
// bytes32 currentValidatorSetHash = bridge.computeValidatorSetHash(validators);
// bridge.domainSeparateValidatorSetHash(nonce, currentPowerThreshold, currentValidatorSetHash);
// bridge.checkValidatorSignatures(validators, sigs, newDataRootTupleRoot, currentPowerThreshold);

bridge.submitDataRootTupleRoot(nonce, initialVelsetNonce, newTupleRoot, validators, sigs);
}

function computeValidatorSetHash(Validator[] memory _validators) private pure returns (bytes32) {
return keccak256(abi.encode(_validators));
}

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

return c;
}

function derivePrivateKeys(uint256 count) private returns (uint256[] memory) {
string memory mnemonic = "test test test test test test test test test test test junk";
uint256[] memory keys = new uint256[](count);
for (uint32 i = 0; i < count; i++) {
keys[i] = cheats.deriveKey(mnemonic, "m/44'/60'/0'/0", i);
}
return keys;
}

function initializeValidators(uint256[] memory keys) private returns (Validator[] memory) {
Validator[] memory vs = new Validator[](keys.length);
for (uint256 i = 0; i < keys.length; i++) {
vs[i] = Validator(cheats.addr(keys[i]), votingPower);
}
return vs;
}
}
Loading