Skip to content

Commit

Permalink
Add ProofHandler.sol lib (#155)
Browse files Browse the repository at this point in the history
* rm json

* add ProofHandler
  • Loading branch information
julienbrg authored Dec 2, 2024
1 parent bbde7a2 commit e279711
Show file tree
Hide file tree
Showing 27 changed files with 2,091 additions and 3,168 deletions.
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

0 comments on commit e279711

Please sign in to comment.