-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: quorumFactory, interface for quorumFactory and deployment scrip…
…t for new contracts.
- Loading branch information
Daniel Ortega
committed
Oct 9, 2023
1 parent
a3b1038
commit 3f5cede
Showing
6 changed files
with
240 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
56 changes: 56 additions & 0 deletions
56
onchain/rollups/contracts/consensus/quorum/IQuorumFactory.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
pragma solidity ^0.8.8; | ||
|
||
import {Quorum} from "./Quorum.sol"; | ||
import {IHistory} from "../../history/IHistory.sol"; | ||
|
||
|
||
/// @title Quorum factory interface | ||
interface IQuorumFactory { | ||
// Events | ||
|
||
/// @notice A new quorum was deployed. | ||
/// @param _quorumValidators The initial set of quorum validators | ||
/// @param quorum The quorum | ||
/// @dev MUST be triggered on a successful call to `newQuorum`. | ||
event QuorumCreated(address[] _quorumValidators, Quorum quorum); | ||
|
||
// Permissionless functions | ||
|
||
/// @notice Deploy a new quorum. | ||
/// @param _quorumValidators The initial set of quorum validators | ||
/// @return The quorum | ||
/// @dev On success, MUST emit an `QuorumCreated` event. | ||
function newQuorum( | ||
address[] calldata _quorumValidators, | ||
uint256[] calldata _shares, | ||
IHistory _history | ||
) external returns (Quorum); | ||
|
||
/// @notice Deploy a new quorum deterministically. | ||
/// @param _quorumValidators The initial set of quorum validators | ||
/// @param _salt The salt used to deterministically generate the quorum address | ||
/// @return The quorum | ||
/// @dev On success, MUST emit an `QuorumCreated` event. | ||
function newQuorum( | ||
address[] calldata _quorumValidators, | ||
uint256[] calldata _shares, | ||
IHistory _history, | ||
bytes32 _salt | ||
) external returns (Quorum); | ||
|
||
/// @notice Calculate the address of an quorum to be deployed deterministically. | ||
/// @param _quorumValidators The initial set of quorum validators | ||
/// @param _salt The salt used to deterministically generate the quorum address | ||
/// @return The deterministic quorum address | ||
/// @dev Beware that only the `newQuorum` function with the `_salt` parameter | ||
/// is able to deterministically deploy an quorum. | ||
function calculateQuorumAddress( | ||
address[] calldata _quorumValidators, | ||
uint256[] calldata _shares, | ||
IHistory _history, | ||
bytes32 _salt | ||
) external view returns (address); | ||
} |
57 changes: 57 additions & 0 deletions
57
onchain/rollups/contracts/consensus/quorum/QuorumFactory.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
pragma solidity ^0.8.8; | ||
|
||
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; | ||
|
||
import {IQuorumFactory} from "./IQuorumFactory.sol"; | ||
import {Quorum} from "./Quorum.sol"; | ||
import {IHistory} from "../../history/IHistory.sol"; | ||
|
||
/// @title Quorum factory | ||
/// @notice Allows anyone to reliably deploy a new `Quorum` contract. | ||
contract QuorumFactory is IQuorumFactory { | ||
function newQuorum( | ||
address[] calldata _quorumValidators, | ||
uint256[] calldata _shares, | ||
IHistory _history | ||
) external override returns (Quorum) { | ||
Quorum quorum = new Quorum(_quorumValidators,_shares,_history); | ||
|
||
emit QuorumCreated(_quorumValidators, quorum); | ||
|
||
return quorum; | ||
} | ||
|
||
function newQuorum( | ||
address[] calldata _quorumValidators, | ||
uint256[] calldata _shares, | ||
IHistory _history, | ||
bytes32 _salt | ||
) external override returns (Quorum) { | ||
Quorum quorum = new Quorum{salt: _salt}(_quorumValidators, _shares, _history); | ||
|
||
emit QuorumCreated(_quorumValidators, quorum); | ||
|
||
return quorum; | ||
} | ||
|
||
function calculateQuorumAddress( | ||
address[] calldata _quorumValidators, | ||
uint256[] calldata _shares, | ||
IHistory _history, | ||
bytes32 _salt | ||
) external view override returns (address) { | ||
return | ||
Create2.computeAddress( | ||
_salt, | ||
keccak256( | ||
abi.encodePacked( | ||
type(Quorum).creationCode, | ||
abi.encode(_quorumValidators, _shares, _history) | ||
) | ||
) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
onchain/rollups/test/foundry/consensus/quorum/QuorumFactory.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
pragma solidity ^0.8.8; | ||
|
||
import {Vm} from "forge-std/Vm.sol"; | ||
|
||
import {QuorumFactory} from "contracts/consensus/quorum/QuorumFactory.sol"; | ||
import {Quorum} from "contracts/consensus/quorum/Quorum.sol"; | ||
import {History} from "contracts/history/History.sol"; | ||
import {IHistory} from "contracts/history/IHistory.sol"; | ||
|
||
import {TestBase} from "../../util/TestBase.sol"; | ||
|
||
import "forge-std/console.sol"; | ||
|
||
contract QuorumFactoryTest is TestBase { | ||
|
||
QuorumFactory factory; | ||
|
||
event QuorumCreated(address[] quorumValidators, Quorum quorum); | ||
|
||
function setUp() public { | ||
factory = new QuorumFactory(); | ||
} | ||
|
||
function testNewQuorum( | ||
uint256 _numValidators | ||
) public { | ||
vm.assume(_numValidators>1); | ||
vm.assume(_numValidators<50); | ||
|
||
address[] memory quorumValidators = generateValidators(_numValidators); | ||
uint256[] memory shares = generateShares(quorumValidators); | ||
|
||
IHistory history = new History(msg.sender); | ||
|
||
vm.recordLogs(); | ||
|
||
Quorum quorum = factory.newQuorum(quorumValidators, shares, history); | ||
|
||
emit QuorumCreated(quorumValidators, quorum); | ||
|
||
//assertEq(quorum.getHistory() == bytes32(history); | ||
//decodeFactoryLogs(quorumValidators, quorum); | ||
} | ||
|
||
function testNewQuorumDeterministic( | ||
uint256 _numValidators, | ||
bytes32 _salt | ||
) public { | ||
vm.assume(_numValidators>1); | ||
vm.assume(_numValidators<50); | ||
|
||
address[] memory quorumValidators = generateValidators(_numValidators); | ||
uint256[] memory shares = generateShares(quorumValidators); | ||
|
||
IHistory history = new History(msg.sender); | ||
|
||
address precalculatedAddress = factory.calculateQuorumAddress(quorumValidators, shares, history, _salt); | ||
|
||
vm.recordLogs(); | ||
|
||
Quorum quorum = factory.newQuorum(quorumValidators, shares, history, _salt); | ||
|
||
emit QuorumCreated(quorumValidators, quorum); | ||
|
||
// Precalculated address must match actual address | ||
assertEq(precalculatedAddress, address(quorum)); | ||
} | ||
|
||
function testAlreadyDeployedNewQuorumDeterministic( | ||
uint256 _numValidators, | ||
bytes32 _salt | ||
) public { | ||
vm.assume(_numValidators>1); | ||
vm.assume(_numValidators<50); | ||
|
||
address[] memory quorumValidators = generateValidators(_numValidators); | ||
uint256[] memory shares = generateShares(quorumValidators); | ||
|
||
IHistory history = new History(msg.sender); | ||
|
||
factory.newQuorum(quorumValidators, shares, history, _salt); | ||
|
||
//Deploy already deployed quorum | ||
vm.expectRevert(); | ||
factory.newQuorum(quorumValidators, shares, history, _salt); | ||
} | ||
|
||
// HELPER FUNCTIONS | ||
function generateValidators(uint256 _numValidators) internal returns(address[] memory){ | ||
address[] memory validators = new address[](_numValidators); | ||
for (uint256 i = 0; i < _numValidators; i++) { | ||
validators[i] = vm.addr(i+1); | ||
} | ||
return validators; | ||
} | ||
|
||
function generateShares(address[] memory validators) internal returns(uint256[] memory){ | ||
//generate a random number of shares for each validator | ||
uint256[] memory shares = new uint256[](validators.length); | ||
for (uint256 i; i < shares.length; ++i) { | ||
uint256 share = uint256( | ||
keccak256(abi.encodePacked(i, validators[i]))) % 100; | ||
shares[i] = (share > 0) ? share : validators.length; | ||
} | ||
return shares; | ||
} | ||
|
||
/*function decodeFactoryLogs(address[] memory _quorumValidators, Quorum quorum) internal { | ||
Vm.Log[] memory entries = vm.getRecordedLogs(); | ||
address[] memory a; | ||
address b; | ||
(a, b) = abi.decode(entries[0].data, (address[], address)); | ||
assertEq(_quorumValidators, a); //entry.emitter == address(factory) | ||
assertEq(address(quorum), b); | ||
}*/ | ||
} | ||
|
||
|