Bluwhale Protocol Service
Bluwhale Protocol Service
Utilizes a proxy contract.
Manages system parameters separately through the Settings contract.
Does not store funds; all funds are managed by the Vault contract. This contract only has operational authority for fixed roles.
Inherits from AccessControlUpgradeable and VRFConsumerBaseV2 (for generating random numbers using Chainlink).
Implements the main logic of the Bluwhale protocol.
Core Function Explanations
Trusted Execution Environment (TEE)
Function Definitions & Explanations
Copy
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
interface IProtocolService {
/**
* @notice This enum represents result of an attestation reported by verifier
*
* `Valid`: This attestation can be parsed successfully and is not malicious
* `Invalid`: This attestation cannot be parsed successfully
* `Malicious`: This attestation can be parsed successfully but is malicious
*/
enum AttestationResult {
Valid,
Invalid,
Malicious
}
/**
* @notice This struct represents information of a node
*
* `id`: The globally unique ID of the node. The node is automatically registered when
* it calls `nodeEnter` for the first time. Up to 65535 nodes can be registered.
* `listIndex`: The position of the node in the current `activeVrfNodeList`
* `active`: Is the node active?
* `lastConfirmDate`: The date on which reward was last confirmed.
* `missedVerifyCount`: The number of verifications the node currently misses
* `selfTotalRewards`: The total rewards of the node itself
* `selfClaimedRewards`: The rewards claimed by the node itself
* `delegationRewards`: The rewards of the node’s delegator
* `lastEnterTime`: The time when the node last called `nodeEnter` to go online
* `commissionRate`: rate of commission, which is given by NFT holders to the verifier node.(decimals: 4)
* `commissionRateLastModifyAt`: timestamp of last modifying commission rate
*/
struct NodeInfo {
uint16 id;
uint16 listIndex;
bool active;
uint32 lastConfirmDate;
uint64 missedVerifyCount;
int256 selfTotalRewards;
uint256 selfClaimedRewards;
uint256 delegationRewards;
uint256 lastEnterTime;
address claimer;
uint32 commissionRate; // decimals: 4
uint256 commissionRateLastModifyAt;
}
/**
* @notice This struct represents reward information of a BluwhaleNft token
*
* `initialRewards`: When this tokenID is delegated to a node, the initial amount of delegator's rewards needs to be recorded.
* `totalRewards`: The amount of rewards this tokenID has been confirmed
* `claimedRewards`: The amount of rewards this tokenID has been claimed
*/
struct TokenRewardInfo {
uint256 initialRewards;
uint256 totalRewards;
uint256 claimedRewards;
}
/**
* @notice This struct represents staking information of a tee
*
* `valid`: Whether tee is currently valid, invalid tee cannot report attestation
* `staked`: tee’s current staked amount
* `lastReportAt`: The time when tee last reported attestation, used for unstake check
*/
struct TeeStakeInfo {
bool valid;
uint256 staked;
uint256 lastReportAt;
}
/**
* @notice This struct represents information of an attestation
*
* `reporter`: The address of the tee who reported this attestation
* `valid`: The number of votes that this attestation is considered valid (voted by the verifier node)
* `invalid`: The number of votes that this attestation is considered invalid (voted by the verifier node)
* `vrfChosenID`: ID of the currently randomly selected verification nodes
* `slashed`: Has this attestation been slashed?
* `deadline`: Deadline for collecting verification
*/
struct Attestation {
address reporter;
uint16 valid;
uint16 invalid;
uint16 malicious;
uint32 vrfChosenID;
bool slashed;
uint256 deadline;
}
/**
* @notice This struct represents information of an verification with signature
*
* `result`: result of this attestation
* `index`: index of this node in activeVrfNodeList
* `signer`: address of this node (sign the Data to call contract gasless)
* `v`: v of signature signed by signer
* `r`: r of signature signed by signer
* `s`: s of signature signed by signer
*/
struct VerificationInfo {
AttestationResult result;
uint16 index;
address signer;
uint8 v;
bytes32 r;
bytes32 s;
}
// admin
event UpdateVrfAddress(address vrf);
event UpdateSettingsAddress(address settings);
// tee
event TeeStake(address tee, uint256 amount);
event TeeUnstake(address tee, uint256 amount);
event TeeSlash(address tee, bytes32 attestationID, uint256 amount);
event ClaimMaliciousTeeRewards(address verifer, uint256 amount);
event TeeReportAttestations(address tee, bytes32[] attestationIDs, string[] attestationInfos, uint256 requestID);
event ConfirmVrfNodes(uint256 requestId, uint16[] vrfChosen, uint256 deadline);
// node
event NodeRegister(address node, uint16 id);
event NodeActivate(address node);
event NodeClear(address node);
event NodeSlash(address node, bytes32 attestationID, uint256 rewards);
event NodeModifyCommissionRate(address node, uint32 commissionRate);
event NodeSetClaimer(address node, address claimer);
event NodeClaim(address node, address claimer, uint256 rewards);
event NodeReportVerification(address node, bytes32 attestationID, AttestationResult result);
event NodeReportVerificationBatch(bytes32 attestationID, VerificationInfo[] infos);
// delegation
event Delegate(uint256 tokenID, address to);
event Redelegate(uint256 tokenID, address to);
event Undelegate(uint256 tokenID, address to);
event ClaimRewards(uint256 tokenID, uint256 rewards);
/*----------------------------------------------------------------------------------------------------------------*/
/**
* @notice update address of Settings contract.
*
* @dev Only admin role.
* @dev Emits `UpdateSettingsAddress`.
*
* @param settings_: address of Settings contract.
*/
function updateSettingsAddress(address settings_) external;
/**
* @notice update address of BluwhaleVrf contract.
*
* @dev Only admin role.
* @dev Emits `UpdateVrfAddress`.
*
* @param BluwhaleVrf_: address of BluwhaleVrf contract.
*/
function updateVrfAddress(address BluwhaleVrf_) external;
/**
* @notice Tee needs to stake Bluwhales before reporting attestation,
* @notice and Bluwhales will be converted to veBluwhales stored in Vault.
* @notice which will be used to slash when tee proven to be evil.
*
* @dev Only tee role (granted by admin role).
* @dev Emits `TeeStake`.
*
* @param amount: amount of Bluwhale tee plans to stake.
*/
function teeStake(uint256 amount) external;
/**
* @notice Tee withdraws staked veBluwhales, rules are as follows:
* @notice 1. enough time has passed since the last attestation was submitted.
* @notice 2. veBluwhales that have been slashed cannot be unstaked.
* @notice 3. unable to continue reporting attestation after unstaking.
*
* @dev Only tee role (granted by admin role).
* @dev Emits `TeeUnstake`.
*/
function teeUnstake() external;
/**
* @notice When the attestation reported by a tee is proven to be malicious,
* @notice anyone can initiate a teeSlash, which will slash the veBluwhales staked by the tee.
* @notice Each malicious attestation can only be slashed once.
* @notice When the veBluwhales staked by a tee are slashed below a threshold,
* @notice the tee will be forcibly restricted from continuing to report attestation.
*
* @dev Emits `TeeSlash`.
*
* @param attestationID: id of attestation which is proven malicious.
*/
function teeSlash(bytes32 attestationID) external;
/**
* @notice When an attestation reported by a tee is slashed,
* @notice verifiers that have reported verification for this attestation can claim the reward.
* @notice If a verifier have not reported verification of this attestation, it cannot claim the reward.
*
* @dev Emits `ClaimMaliciousTeeRewards`.
*
* @param attestationID: id of attestation which is slashed.
*/
function claimMaliciousTeeRewards(bytes32 attestationID) external;
/**
* @notice Tee reports attestations. The same attestation can only be reported once.
*
* @dev Only staked tee role.
* @dev Emits `TeeReportAttestations`.
* @dev A request to apply for VRF will be sent to chainlink.
* @dev After receiving the callback from chainlink, emits `ConfirmVrfNodes`.
*
* @param attestationInfos: attestations to be reported.
*/
function teeReportAttestations(string[] memory attestationInfos) external;
/**
* @notice In order to save costs more efficiently when selecting nodes in VRF,
* @notice we designed a data structure `activeVrfNodeList` for this purpose.
* @notice `activeVrfNodeList` stores up to 2000 active nodes with delegation weight,
* @notice and each time VRF will select from these nodes.
*
* @notice Nodes need to activate themselves by calling nodeEnter of the smart contract
* @notice To successfully activate a node, caller need to meet the following conditions:
* @notice 1. The caller needs to hold BluwhaleNft, or be delegated by other holders
* @notice 2. If the current `activeVrfNodeList` is full, you need to choose a node
* @notice in the list with less delegation weight than yourself to replace it.
*
* @dev Emits `NodeActivate`.
* @dev If any node is kicked and replaced by your node. Emits `NodeClear`.
* @dev If any node calls nodeEnter for the first time. Emits `NodeRegister`.
*
* @param replacedNode: address of the node that needs to be replaced by your node
* only works when the `activeVrfNodeList` is full
*/
function nodeEnter(address replacedNode) external;
/**
* @notice Exits by node itself, and verification cannot be reported after exiting.
* @notice When a node exits, it will be punished based on missing counts during the online period.
*
* @dev Emits `NodeClear`.
*/
function nodeExit() external;
/**
* @notice Modify the commission rate of verifier node
*
* @dev Emits `NodeModifyCommissionRate`.
*
* @param commissionRate: rate of commission, which is given by NFT holders to the verifier node
*/
function nodeModifyCommissionRate(uint32 commissionRate) external;
/**
* @notice Similar to `nodeEnter`,
* @notice but the txn can be broadcast to the chain by other address after being authenticated by the node.
* @notice Compared with the `nodeEnter`, it is gasless for the verifier node.
* @notice following the eip-712.
*
* @dev Emits `NodeActivate`.
* @dev If any node is kicked and replaced by your node. Emits `NodeClear`.
* @dev If any node calls nodeEnter for the first time. Emits `NodeRegister`.
*
* @param replacedNode: address of the node that needs to be replaced by your node
* only works when the `activeVrfNodeList` is full
* @param expiredAt: transaction expiration time
* @param signer: address of verifier node ready to enter
*/
function nodeEnterWithSignature(
address replacedNode, uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
) external;
/**
* @notice Similar to `nodeExit`,
* @notice but the txn can be broadcast to the chain by other address after being authenticated by the node.
* @notice Compared with the `nodeExit`, it is gasless for the verifier node.
* @notice following the eip-712.
*
* @dev Emits `NodeClear`.
*
* @param expiredAt: transaction expiration time
* @param signer: address of verifier node ready to exit
*/
function nodeExitWithSignature(
uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
) external;
/**
* @notice Similar to `nodeModifyCommissionRate`,
* @notice but the txn can be broadcast to the chain by other address after being authenticated by the node.
* @notice Compared with the `nodeModifyCommissionRate`, it is gasless for the verifier node.
* @notice following the eip-712.
*
* @dev Emits `NodeModifyCommissionRate`.
*
* @param commissionRate: rate of commission, which is given by NFT holders to the verifier node
* @param expiredAt: transaction expiration time
* @param signer: address of verifier node
*/
function nodeModifyCommissionRateWithSignature(
uint32 commissionRate, uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
) external;
/**
* @notice Specify the address that can claim rewards of this verifier node
* @notice it is gasless for the verifier node.
* @notice following the eip-712.
*
* @dev Emits `NodeSetClaimer`.
*
* @param claimer: address of whom can claim reward of this verifier
* @param expiredAt: transaction expiration time
* @param signer: address of verifier node
*/
function nodeSetRewardClaimerWithSignature(
address claimer, uint256 expiredAt, address signer, uint8 v, bytes32 r, bytes32 s
) external;
/**
* @notice Claim reward of node.
* @notice The reward for reporting verifications is divided into two parts: node's reward and delegators' reward
* @notice Only when the node is online for more than 6 hours/day will there be reward.
*
* @dev Emits `NodeClaim`.
*
* @param node: address of node to claim reward
*/
function nodeClaim(address node) external;
/**
* @notice Anyone can initiate a slash for a miss reporting of a node.
* @notice Each miss reporting of a node can only be slashed once.
* @notice When a node is selected by VRF but fails to report a verification within the specified time,
* @notice the node can be slashed.
*
* @dev Emits `NodeSlash`.
*
* @param node: address of node to be slashed
* @param attestationID: id of attestation that node miss reporting
* @param index: index of this node in vrfChosen list of this attestation
*/
function nodeSlash(address node, bytes32 attestationID, uint16 index) external;
/**
* @notice If the node is online but hasn't reported verification that day,
* @notice the smart contract needs to be notified through this function to update the status of the node today.
*
* @param node: address of the node to report daily active
*/
function nodeReportDailyActive(address node) external;
/**
* @notice After an attestation is reported, a group of nodes will be randomly selected through chainlink's VRF.
* @notice These nodes need to submit the verification within the specified time.
* @notice When a node is selected by VRF, the proof is submitted by calling `nodeReportVerification`.
* @notice The verification cannot be submitted repeatedly.
*
* @dev Only nodes chosen by VRF in this attestation.
* @dev Emits `NodeReportVerification`.
*
* @param attestationID: id of attestation
* @param index: index of this node in vrfChosen list of this attestation
* @param result: Whether the attestation is valid after being checked by the node.
*/
function nodeReportVerification(bytes32 attestationID, uint16 index, AttestationResult result) external;
/**
* @notice Batch reporting verification.
* @notice One address can collect multiple verifications and delegate reporting them in batches,
* @notice following the eip-712.
*
* @dev Emits `NodeReportVerificationBatch`.
*/
function nodeReportVerificationBatch(bytes32 attestationID, VerificationInfo[] calldata infos) external;
/**
* @notice If the NFT holder doesn't want to run the node to report the verification himself,
* @notice he can delegate the authority to others to run by calling `delegate`.
* @notice After delegating, NFT holders will give a proportion of rewards (10%) to the delegatee.
*
* @dev Emits `Delegate`.
*
* @param tokenID: tokenID of BluwhaleNft to be delegated.
* @param to: address of whom you want to delegate to
*/
function delegate(uint256 tokenID, address to) external;
/**
* @notice Specify a new delegatee to replace the current delegatee.
* @notice During the `redelegate`, the node may be triggered to exit.
*
* @dev Emits `Redelegate`.
* @dev When the node's delegation weight is reduced to 0, it will be forced offline. Emits `NodeClear`
*
* @param tokenID: tokenID of BluwhaleNft to be redelegated.
* @param to: address of whom you want to redelegate to
*/
function redelegate(uint256 tokenID, address to) external;
/**
* @notice Cancel current delegation.
* @notice During the `undelegate`, the node may be triggered to exit.
*
* @dev Emits `undelegate`.
* @dev When the node's delegation weight is reduced to 0, it will be forced offline. Emits `NodeClear`
*
* @param tokenID: tokenID of BluwhaleNft to be undelegated.
*/
function undelegate(uint256 tokenID) external;
/**
* @notice Claim rewards corresponding to a certain tokenID owned by you.
* @notice vault.rewardsWithdraw() will be called to transfer veBluwhale to the user.
* @notice After the user claim the reward, this BluwhaleNft cannot be redeemed.
*
* @dev Emits `ClaimRewards`.
*
* @param tokenID: The token ID that needs to be claimed rewards
*/
function claimRewards(uint256 tokenID) external;
/**
* @notice Check whether this tokenID has already claimed rewards.
*
* @param tokenID: token ID
* @return claimed: whether this tokenID has already claimed rewards
*/
function checkClaimed(uint256 tokenID) external view returns (bool);
/**
* @notice Get the index of today.
*/
function todayIndex() external view returns (uint32);
/**
* @notice Get the offset of today.
*/
function todayOffset() external view returns (uint256);
}
Last updated