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

Add ProofHandler.sol lib #155

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
203 changes: 95 additions & 108 deletions contracts/variants/crosschain/Gov.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "./ProofHandler.sol";

/**
* @title Cross-chain Governance Contract
Expand All @@ -27,10 +28,8 @@ contract Gov is
/// @notice IPFS CID of the DAO's manifesto
string public manifesto;

/// @notice Emitted when the manifesto is updated
/// @param oldManifesto Previous manifesto CID
/// @param newManifesto New manifesto CID
event ManifestoUpdated(string oldManifesto, string newManifesto);
/// @notice Storage for proof handling
ProofHandler.ProofStorage private _proofStorage;

/// @notice Types of operations that can be synchronized across chains
enum OperationType {
Expand All @@ -41,14 +40,22 @@ contract Gov is
UPDATE_QUORUM
}

/// @notice Emitted when the manifesto is updated
/// @param oldManifesto Previous manifesto CID
/// @param newManifesto New manifesto CID
/// @param nonce Update sequence number
event ManifestoUpdated(string oldManifesto, string newManifesto, uint256 nonce);

/// @notice Emitted when a governance parameter is updated
/// @param operationType Type of parameter that was updated
/// @param oldValue Previous value of the parameter
/// @param newValue New value of the parameter
/// @param operationType Type of parameter updated
/// @param oldValue Previous value
/// @param newValue New value
/// @param nonce Update sequence number
event GovernanceParameterUpdated(
OperationType indexed operationType,
uint256 oldValue,
uint256 newValue
uint256 newValue,
uint256 nonce
);

/// @notice Restricts functions to be called only on the home chain
Expand Down Expand Up @@ -89,208 +96,188 @@ contract Gov is
}

/**
* @notice Updates the DAO's manifesto
* @dev Can only be called through governance on home chain
* @notice Updates the DAO's manifesto on the home chain
* @param newManifesto New manifesto CID
*/
function setManifesto(string memory newManifesto) public onlyGovernance onlyHomeChain {
string memory oldManifesto = manifesto;
manifesto = newManifesto;
emit ManifestoUpdated(oldManifesto, newManifesto);
}

/**
* @notice Generates proof for cross-chain manifesto update
* @dev Can only be called on home chain
* @param newManifesto New manifesto CID to generate proof for
* @return Encoded proof data for manifesto update
*/
function generateManifestoProof(
string memory newManifesto
) external view returns (bytes memory) {
require(block.chainid == home, "Proofs can only be generated on home chain");
bytes32 message = keccak256(
abi.encodePacked(address(this), uint8(OperationType.SET_MANIFESTO), newManifesto)
);
bytes32 digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", message));
return abi.encode(newManifesto, digest);
}

/**
* @notice Claims a manifesto update on a foreign chain
* @dev Verifies and applies manifesto updates from home chain
* @param proof Proof generated by home chain
*/
function claimManifestoUpdate(bytes memory proof) external {
(string memory newManifesto, bytes32 digest) = abi.decode(proof, (string, bytes32));

bytes32 message = keccak256(
abi.encodePacked(address(this), uint8(OperationType.SET_MANIFESTO), newManifesto)
uint256 nonce = ProofHandler.incrementNonce(
_proofStorage,
uint8(OperationType.SET_MANIFESTO)
);
bytes32 expectedDigest = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", message)
);
require(digest == expectedDigest, "Invalid manifesto proof");

string memory oldManifesto = manifesto;
manifesto = newManifesto;
emit ManifestoUpdated(oldManifesto, newManifesto);
emit ManifestoUpdated(oldManifesto, newManifesto, nonce);
}

/**
* @notice Updates the voting delay parameter
* @dev Can only be called through governance on home chain
* @param newVotingDelay New voting delay value (in blocks)
* @notice Updates the voting delay parameter on the home chain
* @param newVotingDelay New voting delay value
*/
function setVotingDelay(
uint48 newVotingDelay
) public virtual override onlyGovernance onlyHomeChain {
uint256 nonce = ProofHandler.incrementNonce(
_proofStorage,
uint8(OperationType.UPDATE_VOTING_DELAY)
);
uint256 oldValue = votingDelay();
_setVotingDelay(newVotingDelay);
emit GovernanceParameterUpdated(
OperationType.UPDATE_VOTING_DELAY,
oldValue,
newVotingDelay
newVotingDelay,
nonce
);
}

/**
* @notice Updates the voting period parameter
* @dev Can only be called through governance on home chain
* @param newVotingPeriod New voting period value (in blocks)
* @notice Updates the voting period parameter on the home chain
* @param newVotingPeriod New voting period value
*/
function setVotingPeriod(
uint32 newVotingPeriod
) public virtual override onlyGovernance onlyHomeChain {
uint256 nonce = ProofHandler.incrementNonce(
_proofStorage,
uint8(OperationType.UPDATE_VOTING_PERIOD)
);
uint256 oldValue = votingPeriod();
_setVotingPeriod(newVotingPeriod);
emit GovernanceParameterUpdated(
OperationType.UPDATE_VOTING_PERIOD,
oldValue,
newVotingPeriod
newVotingPeriod,
nonce
);
}

/**
* @notice Updates the proposal threshold parameter
* @dev Can only be called through governance on home chain
* @notice Updates the proposal threshold parameter on the home chain
* @param newProposalThreshold New proposal threshold value
*/
function setProposalThreshold(
uint256 newProposalThreshold
) public virtual override onlyGovernance onlyHomeChain {
uint256 nonce = ProofHandler.incrementNonce(
_proofStorage,
uint8(OperationType.UPDATE_PROPOSAL_THRESHOLD)
);
uint256 oldValue = proposalThreshold();
_setProposalThreshold(newProposalThreshold);
emit GovernanceParameterUpdated(
OperationType.UPDATE_PROPOSAL_THRESHOLD,
oldValue,
newProposalThreshold
newProposalThreshold,
nonce
);
}

/**
* @notice Updates the quorum numerator
* @dev Can only be called through governance on home chain
* @param newQuorumNumerator New quorum numerator value (percentage * 100)
* @notice Updates the quorum numerator on the home chain
* @param newQuorumNumerator New quorum numerator value
*/
function updateQuorumNumerator(
uint256 newQuorumNumerator
) public virtual override(GovernorVotesQuorumFraction) onlyGovernance onlyHomeChain {
) public virtual override onlyGovernance onlyHomeChain {
uint256 nonce = ProofHandler.incrementNonce(
_proofStorage,
uint8(OperationType.UPDATE_QUORUM)
);
uint256 oldValue = quorumNumerator();
_updateQuorumNumerator(newQuorumNumerator);
emit GovernanceParameterUpdated(OperationType.UPDATE_QUORUM, oldValue, newQuorumNumerator);
emit GovernanceParameterUpdated(
OperationType.UPDATE_QUORUM,
oldValue,
newQuorumNumerator,
nonce
);
}

/**
* @notice Generates proof for cross-chain parameter updates
* @dev Can only be called on home chain
* @notice Generates proof for parameter updates
* @param operationType Type of parameter being updated
* @param value Encoded value for the parameter update
* @return Encoded proof data for parameter update
* @param value Encoded parameter value
* @return proof Encoded proof data
*/
function generateParameterProof(
OperationType operationType,
uint8 operationType,
bytes memory value
) external view returns (bytes memory) {
require(block.chainid == home, "Proofs can only be generated on home chain");
bytes32 message = keccak256(abi.encodePacked(address(this), uint8(operationType), value));
bytes32 digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", message));
return abi.encode(operationType, value, digest);
) external view returns (bytes memory proof) {
require(block.chainid == home, "Proofs only generated on home chain");
uint256 nextNonce = ProofHandler.getNextNonce(_proofStorage, operationType);
return ProofHandler.generateProof(address(this), operationType, value, nextNonce);
}

/**
* @notice Claims a parameter update on a foreign chain
* @dev Verifies and applies parameter updates from home chain
* @param proof Proof generated by home chain
*/
function claimParameterUpdate(bytes memory proof) external {
(OperationType operationType, bytes memory value, bytes32 digest) = abi.decode(
(uint8 operationType, bytes memory value, uint256 nonce) = ProofHandler.verifyAndClaimProof(
proof,
(OperationType, bytes, bytes32)
address(this),
_proofStorage
);

bytes32 message = keccak256(abi.encodePacked(address(this), uint8(operationType), value));
bytes32 expectedDigest = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", message)
);
require(digest == expectedDigest, "Invalid parameter update proof");

if (operationType == OperationType.UPDATE_VOTING_DELAY) {
if (operationType == uint8(OperationType.SET_MANIFESTO)) {
string memory newManifesto = abi.decode(value, (string));
string memory oldManifesto = manifesto;
manifesto = newManifesto;
emit ManifestoUpdated(oldManifesto, newManifesto, nonce);
} else if (operationType == uint8(OperationType.UPDATE_VOTING_DELAY)) {
uint48 newValue = uint48(bytes6(value));
uint256 oldValue = votingDelay();
_setVotingDelay(newValue);
emit GovernanceParameterUpdated(operationType, oldValue, newValue);
} else if (operationType == OperationType.UPDATE_VOTING_PERIOD) {
emit GovernanceParameterUpdated(
OperationType.UPDATE_VOTING_DELAY,
oldValue,
newValue,
nonce
);
} else if (operationType == uint8(OperationType.UPDATE_VOTING_PERIOD)) {
uint32 newValue = uint32(bytes4(value));
uint256 oldValue = votingPeriod();
_setVotingPeriod(newValue);
emit GovernanceParameterUpdated(operationType, oldValue, newValue);
} else if (operationType == OperationType.UPDATE_PROPOSAL_THRESHOLD) {
emit GovernanceParameterUpdated(
OperationType.UPDATE_VOTING_PERIOD,
oldValue,
newValue,
nonce
);
} else if (operationType == uint8(OperationType.UPDATE_PROPOSAL_THRESHOLD)) {
uint256 newValue = abi.decode(value, (uint256));
uint256 oldValue = proposalThreshold();
_setProposalThreshold(newValue);
emit GovernanceParameterUpdated(operationType, oldValue, newValue);
} else if (operationType == OperationType.UPDATE_QUORUM) {
emit GovernanceParameterUpdated(
OperationType.UPDATE_PROPOSAL_THRESHOLD,
oldValue,
newValue,
nonce
);
} else if (operationType == uint8(OperationType.UPDATE_QUORUM)) {
uint256 newValue = abi.decode(value, (uint256));
uint256 oldValue = quorumNumerator();
_updateQuorumNumerator(newValue);
emit GovernanceParameterUpdated(operationType, oldValue, newValue);
emit GovernanceParameterUpdated(OperationType.UPDATE_QUORUM, oldValue, newValue, nonce);
}
}

// Required overrides

/**
* @notice Gets the current voting delay
* @return Current voting delay in blocks
*/
function votingDelay() public view override(Governor, GovernorSettings) returns (uint256) {
return super.votingDelay();
}

/**
* @notice Gets the current voting period
* @return Current voting period in blocks
*/
function votingPeriod() public view override(Governor, GovernorSettings) returns (uint256) {
return super.votingPeriod();
}

/**
* @notice Gets the quorum required for a specific block
* @param blockNumber Block number to check quorum for
* @return Minimum number of votes required for quorum
*/
function quorum(
uint256 blockNumber
) public view override(Governor, GovernorVotesQuorumFraction) returns (uint256) {
return super.quorum(blockNumber);
}

/**
* @notice Gets the current proposal threshold
* @return Minimum number of votes required to create a proposal
*/
function proposalThreshold()
public
view
Expand Down
Loading