Skip to content

Commit

Permalink
feat: consensus activate/deactivate
Browse files Browse the repository at this point in the history
  • Loading branch information
adu-web3 committed Nov 25, 2024
1 parent fc377af commit 02bb6e9
Show file tree
Hide file tree
Showing 4 changed files with 357 additions and 84 deletions.
120 changes: 91 additions & 29 deletions src/core/UTXOGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,6 @@ contract UTXOGateway is
using ExocoreBytes for address;
using SignatureVerifier for bytes32;

/**
* @dev Modifier to restrict access to authorized witnesses only.
*/
modifier onlyAuthorizedWitness() {
if (!_isAuthorizedWitness(msg.sender)) {
revert Errors.UnauthorizedWitness();
}
_;
}

/**
* @notice Pauses the contract.
* @dev Can only be called by the contract owner.
*/
function pause() external onlyOwner {
_pause();
}

/**
* @notice Unpauses the contract.
* @dev Can only be called by the contract owner.
*/
function unpause() external onlyOwner {
_unpause();
}

/**
* @notice Constructor to initialize the contract with the client chain ID.
* @dev Sets up initial configuration for testing purposes.
Expand All @@ -65,14 +39,23 @@ contract UTXOGateway is
}

/**
* @notice Initializes the contract with the Exocore witness address and owner address.
* @notice Initializes the contract with the Exocore witness address, owner address and required proofs.
* @dev If the witnesses length is greater or equal to the required proofs, the consensus requirement for stake
* message
* would be activated.
* @param owner_ The address of the owner.
* @param witnesses The addresses of the witnesses.
* @param requiredProofs_ The number of required proofs.
*/
function initialize(address owner_, address[] calldata witnesses) external initializer {
function initialize(address owner_, address[] calldata witnesses, uint256 requiredProofs_) external initializer {
if (owner_ == address(0) || witnesses.length == 0) {
revert Errors.ZeroAddress();
}
if (requiredProofs_ < MIN_REQUIRED_PROOFS || requiredProofs_ > MAX_REQUIRED_PROOFS) {
revert Errors.InvalidRequiredProofs();
}

requiredProofs = requiredProofs_;
for (uint256 i = 0; i < witnesses.length; i++) {
_addWitness(witnesses[i]);
}
Expand All @@ -81,6 +64,22 @@ contract UTXOGateway is
_transferOwnership(owner_);
}

/**
* @notice Pauses the contract.
* @dev Can only be called by the contract owner.
*/
function pause() external onlyOwner {
_pause();
}

/**
* @notice Unpauses the contract.
* @dev Can only be called by the contract owner.
*/
function unpause() external onlyOwner {
_unpause();
}

/**
* @notice Activates token staking by registering or updating the chain and token with the Exocore system.
*/
Expand All @@ -95,6 +94,33 @@ contract UTXOGateway is
}
}

/**
* @notice Updates the required proofs for consensus.
* @notice The consensus requirement for stake message would be activated if the current authorized witness count is
* greater than or equal to the new required proofs.
* @dev Can only be called by the contract owner.
* @param newRequiredProofs The new required proofs.
*/
function updateRequiredProofs(uint256 newRequiredProofs) external onlyOwner whenNotPaused {
if (newRequiredProofs < MIN_REQUIRED_PROOFS || newRequiredProofs > MAX_REQUIRED_PROOFS) {
revert Errors.InvalidRequiredProofs();
}

bool wasConsensusRequired = _isConsensusRequired();
uint256 oldRequiredProofs = requiredProofs;
requiredProofs = newRequiredProofs;

emit RequiredProofsUpdated(oldRequiredProofs, newRequiredProofs);

// Check if consensus state changed due to new requirement
bool isConsensusRequired_ = _isConsensusRequired();
if (!wasConsensusRequired && isConsensusRequired_) {
emit ConsensusActivated(requiredProofs, authorizedWitnessCount);
} else if (wasConsensusRequired && !isConsensusRequired_) {
emit ConsensusDeactivated(requiredProofs, authorizedWitnessCount);
}
}

/**
* @notice Adds a new authorized witness.
* @param _witness The address of the witness to be added.
Expand All @@ -117,9 +143,17 @@ contract UTXOGateway is
if (!authorizedWitnesses[_witness]) {
revert Errors.WitnessNotAuthorized(_witness);
}

bool wasConsensusRequired = _isConsensusRequired();

authorizedWitnesses[_witness] = false;
authorizedWitnessCount--;
emit WitnessRemoved(_witness);

// Emit only when crossing the threshold from true to false
if (wasConsensusRequired && !_isConsensusRequired()) {
emit ConsensusDeactivated(requiredProofs, authorizedWitnessCount);
}
}

/**
Expand Down Expand Up @@ -148,6 +182,10 @@ contract UTXOGateway is
nonReentrant
whenNotPaused
{
if (!_isConsensusRequired()) {
revert Errors.ConsensusNotRequired();
}

if (!_isAuthorizedWitness(witness)) {
revert Errors.WitnessNotAuthorized(witness);
}
Expand Down Expand Up @@ -178,7 +216,7 @@ contract UTXOGateway is
emit ProofSubmitted(messageHash, witness);

// Check for consensus
if (txn.proofCount >= REQUIRED_PROOFS) {
if (txn.proofCount >= requiredProofs) {
processedTransactions[messageHash] = true;
_processStakeMsg(txn.stakeMsg);
// we delete the transaction after it has been processed to refund some gas, so no need to worry about
Expand All @@ -200,6 +238,10 @@ contract UTXOGateway is
nonReentrant
whenNotPaused
{
if (_isConsensusRequired()) {
revert Errors.ConsensusRequired();
}

if (!_isAuthorizedWitness(witness)) {
revert Errors.WitnessNotAuthorized(witness);
}
Expand Down Expand Up @@ -432,6 +474,18 @@ contract UTXOGateway is
return transactions[messageHash].witnessTime[witness];
}

function isConsensusRequired() external view returns (bool) {
return _isConsensusRequired();
}

/**
* @notice Checks if consensus is required for a stake message.
* @return True if count of authorized witnesses is greater than or equal to REQUIRED_PROOFS, false otherwise.
*/
function _isConsensusRequired() internal view returns (bool) {
return authorizedWitnessCount >= requiredProofs;
}

/**
* @notice Checks if a witness is authorized.
* @param witness The witness address.
Expand All @@ -448,9 +502,17 @@ contract UTXOGateway is
if (_isAuthorizedWitness(_witness)) {
revert Errors.WitnessAlreadyAuthorized(_witness);
}

bool wasConsensusRequired = _isConsensusRequired();

authorizedWitnesses[_witness] = true;
authorizedWitnessCount++;
emit WitnessAdded(_witness);

// Emit only when crossing the threshold from false to true
if (!wasConsensusRequired && _isConsensusRequired()) {
emit ConsensusActivated(requiredProofs, authorizedWitnessCount);
}
}

/**
Expand Down
47 changes: 28 additions & 19 deletions src/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -312,61 +312,70 @@ library Errors {
error InsufficientBalance();

/* -------------------------------------------------------------------------- */
/* ExocoreBtcGateway Errors */
/* UTXOGateway Errors */
/* -------------------------------------------------------------------------- */

/// @dev ExocoreBtcGateway: witness has already submitted proof
/// @dev UTXOGateway: witness has already submitted proof
error WitnessAlreadySubmittedProof();

/// @dev ExocoreBtcGateway: invalid stake message
/// @dev UTXOGateway: invalid stake message
error InvalidStakeMessage();

/// @dev ExocoreBtcGateway: transaction tag has already been processed
/// @dev UTXOGateway: transaction tag has already been processed
error TxTagAlreadyProcessed();

/// @dev ExocoreBtcGateway: invalid operator address
/// @dev UTXOGateway: invalid operator address
error InvalidOperator();

/// @dev ExocoreBtcGateway: invalid token
/// @dev UTXOGateway: invalid token
error InvalidToken();

/// @dev ExocoreBtcGateway: witness has already been authorized
/// @dev UTXOGateway: witness has already been authorized
error WitnessAlreadyAuthorized(address witness);

/// @dev ExocoreBtcGateway: witness has not been authorized
/// @dev UTXOGateway: witness has not been authorized
error WitnessNotAuthorized(address witness);

/// @dev ExocoreBtcGateway: cannot remove the last witness
/// @dev UTXOGateway: cannot remove the last witness
error CannotRemoveLastWitness();

/// @dev ExocoreBtcGateway: invalid client chain
/// @dev UTXOGateway: invalid client chain
error InvalidClientChain();

/// @dev ExocoreBtcGateway: deposit failed
/// @dev UTXOGateway: deposit failed
error DepositFailed(bytes txTag);

/// @dev ExocoreBtcGateway: address not registered
/// @dev UTXOGateway: address not registered
error AddressNotRegistered();

/// @dev ExocoreBtcGateway: delegation failed
/// @dev UTXOGateway: delegation failed
error DelegationFailed();

/// @dev ExocoreBtcGateway: withdraw principal failed
/// @dev UTXOGateway: withdraw principal failed
error WithdrawPrincipalFailed();

/// @dev ExocoreBtcGateway: undelegation failed
/// @dev UTXOGateway: undelegation failed
error UndelegationFailed();

/// @dev ExocoreBtcGateway: withdraw reward failed
/// @dev UTXOGateway: withdraw reward failed
error WithdrawRewardFailed();

/// @dev ExocoreBtcGateway: request not found
/// @dev UTXOGateway: request not found
error RequestNotFound(uint64 requestId);

/// @dev ExocoreBtcGateway: request already exists
/// @dev UTXOGateway: request already exists
error RequestAlreadyExists(uint32 clientChain, uint64 requestId);

/// @dev ExocoreBtcGateway: witness not authorized
/// @dev UTXOGateway: witness not authorized
error UnauthorizedWitness();

/// @dev UTXOGateway: consensus is not activated
error ConsensusNotRequired();

/// @dev UTXOGateway: consensus is required
error ConsensusRequired();

/// @dev UTXOGateway: invalid required proofs
error InvalidRequiredProofs();

}
36 changes: 34 additions & 2 deletions src/storage/UTXOGatewayStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,18 @@ contract UTXOGatewayStorage {
string public constant BTC_METADATA = "BTC";
string public constant BTC_ORACLE_INFO = "BTC,BITCOIN,8";

address public constant EXOCORE_WITNESS = address(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
uint256 public constant REQUIRED_PROOFS = 2;
uint256 public constant PROOF_TIMEOUT = 1 days;
uint256 public bridgeFeeRate; // e.g., 100 (basis points) means 1%
uint256 public constant BASIS_POINTS = 10_000; // 100% = 10000 basis points
uint256 public constant MAX_BRIDGE_FEE_RATE = 1000; // 10%

// Add min/max bounds for safety
uint256 public constant MIN_REQUIRED_PROOFS = 1;
uint256 public constant MAX_REQUIRED_PROOFS = 10;

/// @notice The number of proofs required for consensus
uint256 public requiredProofs;

/// @notice The count of authorized witnesses
uint256 public authorizedWitnessCount;

Expand Down Expand Up @@ -195,6 +200,13 @@ contract UTXOGatewayStorage {

// Events

/**
* @dev Emitted when the required proofs is updated
* @param oldRequired The old required proofs
* @param newRequired The new required proofs
*/
event RequiredProofsUpdated(uint256 oldRequired, uint256 newRequired);

/**
* @dev Emitted when a stake message is executed
* @param chainId The chain ID of the client chain, should not violate the layerzero chain id
Expand Down Expand Up @@ -435,6 +447,16 @@ contract UTXOGatewayStorage {
/// @param token The address of the token.
event WhitelistTokenUpdated(uint32 clientChainId, address indexed token);

/// @notice Emitted when consensus is activated
/// @param requiredWitnessesCount The number of required witnesses
/// @param authorizedWitnessesCount The number of authorized witnesses
event ConsensusActivated(uint256 requiredWitnessesCount, uint256 authorizedWitnessesCount);

/// @notice Emitted when consensus is deactivated
/// @param requiredWitnessesCount The number of required witnesses
/// @param authorizedWitnessesCount The number of authorized witnesses
event ConsensusDeactivated(uint256 requiredWitnessesCount, uint256 authorizedWitnessesCount);

/**
* @dev Modifier to check if an amount is valid
* @param amount The amount to check
Expand All @@ -453,6 +475,16 @@ contract UTXOGatewayStorage {
_;
}

/**
* @dev Modifier to restrict access to authorized witnesses only.
*/
modifier onlyAuthorizedWitness() {
if (!authorizedWitnesses[msg.sender]) {
revert Errors.UnauthorizedWitness();
}
_;
}

/// @notice Checks if the provided string is a valid Exocore address.
/// @param addressToValidate The string to check.
/// @return True if the string is valid, false otherwise.
Expand Down
Loading

0 comments on commit 02bb6e9

Please sign in to comment.