diff --git a/README.md b/README.md index 7817bf17e..fc00366ae 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ All system contracts will be flattened and output into `${workspace}/contracts/f 3. Edit system contracts setting as needed. 4. Run `node scripts/generate-genesis.js` will generate genesis.json -## How to generate mainnet/testnet/QA/local genesis file +## How to generate mainnet/testnet/QA/dev genesis file ```shell poetry run python -m scripts.generate ${network} diff --git a/abi/bscvalidatorset.abi b/abi/bscvalidatorset.abi index 3fc6c6060..5d17be8a7 100644 --- a/abi/bscvalidatorset.abi +++ b/abi/bscvalidatorset.abi @@ -224,6 +224,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -419,6 +432,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -484,6 +510,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/crosschain.abi b/abi/crosschain.abi index daadac17c..b1877a6c9 100644 --- a/abi/crosschain.abi +++ b/abi/crosschain.abi @@ -181,6 +181,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -311,6 +324,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -402,6 +428,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/govhub.abi b/abi/govhub.abi index eceaa2ea2..cc0b71a2e 100644 --- a/abi/govhub.abi +++ b/abi/govhub.abi @@ -129,6 +129,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -207,6 +220,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -259,6 +285,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/relayerhub.abi b/abi/relayerhub.abi index 6ade16145..18892f333 100644 --- a/abi/relayerhub.abi +++ b/abi/relayerhub.abi @@ -103,6 +103,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -194,6 +207,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -246,6 +272,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/relayerincentivize.abi b/abi/relayerincentivize.abi index 99048a69e..2a69bd370 100644 --- a/abi/relayerincentivize.abi +++ b/abi/relayerincentivize.abi @@ -133,6 +133,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "HEADER_RELAYER_REWARD_RATE_DENOMINATOR", @@ -250,6 +263,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -302,6 +328,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/slashindicator.abi b/abi/slashindicator.abi index c1c67c503..56b7f7007 100644 --- a/abi/slashindicator.abi +++ b/abi/slashindicator.abi @@ -129,6 +129,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -144,7 +157,7 @@ }, { "type": "function", - "name": "INIT_FINALITY_SLASH_REWARD_RATIO", + "name": "INIT_FELONY_SLASH_REWARD_RATIO", "inputs": [], "outputs": [ { @@ -233,6 +246,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -285,6 +311,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", @@ -429,7 +468,7 @@ }, { "type": "function", - "name": "felonyThreshold", + "name": "felonySlashRewardRatio", "inputs": [], "outputs": [ { @@ -442,7 +481,7 @@ }, { "type": "function", - "name": "finalitySlashRewardRatio", + "name": "felonyThreshold", "inputs": [], "outputs": [ { diff --git a/abi/stakehub.abi b/abi/stakehub.abi index 96f5c9c07..c8854315f 100644 --- a/abi/stakehub.abi +++ b/abi/stakehub.abi @@ -3,6 +3,19 @@ "type": "receive", "stateMutability": "payable" }, + { + "type": "function", + "name": "BREATH_BLOCK_INTERVAL", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "DEAD_ADDRESS", @@ -105,6 +118,24 @@ "outputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "claimBatch", + "inputs": [ + { + "name": "operatorAddresses", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "requestNumbers", + "type": "uint256[]", + "internalType": "uint256[]" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "createValidator", @@ -424,6 +455,11 @@ "type": "address", "internalType": "address" }, + { + "name": "createdTime", + "type": "uint256", + "internalType": "uint256" + }, { "name": "voteAddress", "type": "bytes", @@ -568,7 +604,7 @@ "internalType": "address" }, { - "name": "dayIndex", + "name": "index", "type": "uint256", "internalType": "uint256" } @@ -592,7 +628,7 @@ "internalType": "address" }, { - "name": "dayIndex", + "name": "index", "type": "uint256", "internalType": "uint256" } @@ -1275,6 +1311,11 @@ "name": "InvalidMoniker", "inputs": [] }, + { + "type": "error", + "name": "InvalidRequest", + "inputs": [] + }, { "type": "error", "name": "InvalidValue", diff --git a/abi/staking.abi b/abi/staking.abi index 2a8788567..f6084f864 100644 --- a/abi/staking.abi +++ b/abi/staking.abi @@ -211,6 +211,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -341,6 +354,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -406,6 +432,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/systemreward.abi b/abi/systemreward.abi index 26403dde4..9ea8b5a65 100644 --- a/abi/systemreward.abi +++ b/abi/systemreward.abi @@ -107,6 +107,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -198,6 +211,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -250,6 +276,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/tendermintlightclient.abi b/abi/tendermintlightclient.abi index a88abab80..861b0329b 100644 --- a/abi/tendermintlightclient.abi +++ b/abi/tendermintlightclient.abi @@ -108,6 +108,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -199,6 +212,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -251,6 +277,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/tokenhub.abi b/abi/tokenhub.abi index 8d29a0677..7eff9d056 100644 --- a/abi/tokenhub.abi +++ b/abi/tokenhub.abi @@ -133,6 +133,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -328,6 +341,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -393,6 +419,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/abi/tokenmanager.abi b/abi/tokenmanager.abi index ff190ff23..a1d0259c9 100644 --- a/abi/tokenmanager.abi +++ b/abi/tokenmanager.abi @@ -225,6 +225,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "GOV_TOKEN_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "INCENTIVIZE_ADDR", @@ -407,6 +420,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "STAKE_CREDIT_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "STAKE_HUB_ADDR", @@ -511,6 +537,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "TIMELOCK_ADDR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "TOKEN_HUB_ADDR", diff --git a/contracts/BC_fusion/BSCGovernor.sol b/contracts/BC_fusion/BSCGovernor.sol index d2c09b0fc..0ed42a93d 100644 --- a/contracts/BC_fusion/BSCGovernor.sol +++ b/contracts/BC_fusion/BSCGovernor.sol @@ -28,10 +28,10 @@ contract BSCGovernor is using Utils for string; /*----------------- constants -----------------*/ - /* - @dev caution: - INIT_VOTING_DELAY, INIT_VOTING_PERIOD and INIT_MIN_PERIOD_AFTER_QUORUM are default in number of blocks, not seconds - */ + /** + * @dev caution: + * INIT_VOTING_DELAY, INIT_VOTING_PERIOD and INIT_MIN_PERIOD_AFTER_QUORUM are default in number of blocks, not seconds + */ uint256 private constant BLOCK_INTERVAL = 3 seconds; uint256 private constant INIT_VOTING_DELAY = 24 hours / BLOCK_INTERVAL; uint256 private constant INIT_VOTING_PERIOD = 14 days / BLOCK_INTERVAL; @@ -88,7 +88,7 @@ contract BSCGovernor is // BSCGovernor => Timelock => GovHub => system contracts whitelistTargets[GOV_HUB_ADDR] = true; - governorProtector = address(0x000000000000000000000000000000000000dEaD); // TODO + governorProtector = address(0xdEaD); // TODO } /*----------------- onlyGovernorProtector -----------------*/ diff --git a/contracts/BC_fusion/StakeCredit.sol b/contracts/BC_fusion/StakeCredit.sol index d7f8c51f8..625e27a68 100644 --- a/contracts/BC_fusion/StakeCredit.sol +++ b/contracts/BC_fusion/StakeCredit.sol @@ -69,9 +69,9 @@ contract StakeCredit is System, Initializable, ReentrancyGuardUpgradeable, ERC20 * @notice only accept BNB from `StakeHub` */ receive() external payable onlyStakeHub { - uint256 dayIndex = block.timestamp / 1 days; - totalPooledBNBRecord[dayIndex] = totalPooledBNB; - rewardRecord[dayIndex] += msg.value; + uint256 index = block.timestamp / IStakeHub(STAKE_HUB_ADDR).BREATH_BLOCK_INTERVAL(); + totalPooledBNBRecord[index] = totalPooledBNB; + rewardRecord[index] += msg.value; totalPooledBNB += msg.value; } @@ -182,9 +182,9 @@ contract StakeCredit is System, Initializable, ReentrancyGuardUpgradeable, ERC20 uint256 _commission = (bnbAmount * uint256(commissionRate)) / COMMISSION_RATE_BASE; uint256 _reward = bnbAmount - _commission; - uint256 dayIndex = block.timestamp / 1 days; - totalPooledBNBRecord[dayIndex] = totalPooledBNB; - rewardRecord[dayIndex] += _reward; + uint256 index = block.timestamp / IStakeHub(STAKE_HUB_ADDR).BREATH_BLOCK_INTERVAL(); + totalPooledBNBRecord[index] = totalPooledBNB; + rewardRecord[index] += _reward; totalPooledBNB += _reward; // mint commission to the validator diff --git a/contracts/BC_fusion/StakeHub.sol b/contracts/BC_fusion/StakeHub.sol index 50ed53151..808b2d8e8 100644 --- a/contracts/BC_fusion/StakeHub.sol +++ b/contracts/BC_fusion/StakeHub.sol @@ -20,10 +20,12 @@ contract StakeHub is System, Initializable { uint256 private constant BLS_PUBKEY_LENGTH = 48; uint256 private constant BLS_SIG_LENGTH = 96; - address public constant DEAD_ADDRESS = address(0xdead); + address public constant DEAD_ADDRESS = address(0xdEaD); uint256 public constant INIT_LOCK_AMOUNT = 1 ether; uint256 public constant REDELEGATE_FEE_RATE_BASE = 10000; // 100% + uint256 public constant BREATH_BLOCK_INTERVAL = 1 days; + //TODO bytes private constant INIT_BC_CONSENSUS_ADDRESSES = hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"; @@ -75,6 +77,8 @@ contract StakeHub is System, Initializable { error AlreadySlashed(); // @notice signature: 0x90b8ec18 error TransferFailed(); + // @notice signature: 0x41abc801 + error InvalidRequest(); /*----------------- storage -----------------*/ bool private _paused; @@ -113,7 +117,7 @@ contract StakeHub is System, Initializable { uint256 public numOfJailed; // max number of jailed validators per day(only for malicious vote and double sign) uint256 private felonyPerDay; - // day index(timestamp / 86400) => number of malicious vote and double sign slash + // index(timestamp / 1 days) => number of malicious vote and double sign slash mapping(uint256 => uint256) private _felonyMap; address public assetProtector; @@ -124,6 +128,7 @@ contract StakeHub is System, Initializable { address consensusAddress; address operatorAddress; address creditContract; + uint256 createdTime; bytes voteAddress; Description description; Commission commission; @@ -285,6 +290,7 @@ contract StakeHub is System, Initializable { valInfo.consensusAddress = consensusAddress; valInfo.operatorAddress = operatorAddress; valInfo.creditContract = creditContract; + valInfo.createdTime = block.timestamp; valInfo.voteAddress = voteAddress; valInfo.description = description; valInfo.commission = commission; @@ -313,7 +319,7 @@ contract StakeHub is System, Initializable { address operatorAddress = msg.sender; Validator storage valInfo = _validators[operatorAddress]; - if (valInfo.updateTime + 1 days > block.timestamp) revert UpdateTooFrequently(); + if (valInfo.updateTime + BREATH_BLOCK_INTERVAL > block.timestamp) revert UpdateTooFrequently(); valInfo.consensusAddress = newConsensusAddress; valInfo.updateTime = block.timestamp; @@ -333,7 +339,7 @@ contract StakeHub is System, Initializable { { address operatorAddress = msg.sender; Validator storage valInfo = _validators[operatorAddress]; - if (valInfo.updateTime + 1 days > block.timestamp) revert UpdateTooFrequently(); + if (valInfo.updateTime + BREATH_BLOCK_INTERVAL > block.timestamp) revert UpdateTooFrequently(); if (commissionRate > valInfo.commission.maxRate) revert InvalidCommission(); uint256 changeRate = commissionRate >= valInfo.commission.rate @@ -360,7 +366,7 @@ contract StakeHub is System, Initializable { address operatorAddress = msg.sender; Validator storage valInfo = _validators[operatorAddress]; - if (valInfo.updateTime + 1 days > block.timestamp) revert UpdateTooFrequently(); + if (valInfo.updateTime + BREATH_BLOCK_INTERVAL > block.timestamp) revert UpdateTooFrequently(); valInfo.description = description; valInfo.updateTime = block.timestamp; @@ -384,7 +390,7 @@ contract StakeHub is System, Initializable { address operatorAddress = msg.sender; Validator storage valInfo = _validators[operatorAddress]; - if (valInfo.updateTime + 1 days > block.timestamp) revert UpdateTooFrequently(); + if (valInfo.updateTime + BREATH_BLOCK_INTERVAL > block.timestamp) revert UpdateTooFrequently(); valInfo.voteAddress = newVoteAddress; valInfo.updateTime = block.timestamp; @@ -512,12 +518,23 @@ contract StakeHub is System, Initializable { * @param operatorAddress the operator address of the validator * @param requestNumber the request number of the undelegation. 0 means claim all */ - function claim( - address operatorAddress, - uint256 requestNumber - ) external whenNotPaused notInBlackList validatorExist(operatorAddress) { - uint256 bnbAmount = IStakeCredit(_validators[operatorAddress].creditContract).claim(msg.sender, requestNumber); - emit Claimed(operatorAddress, msg.sender, bnbAmount); + function claim(address operatorAddress, uint256 requestNumber) external whenNotPaused notInBlackList { + _claim(operatorAddress, requestNumber); + } + + /** + * @dev Claim the undelegated BNB from the pools after unbondPeriod + * @param operatorAddresses the operator addresses of the validator + * @param requestNumbers numbers of the undelegation requests. 0 means claim all + */ + function claimBatch( + address[] calldata operatorAddresses, + uint256[] calldata requestNumbers + ) external whenNotPaused notInBlackList { + if (operatorAddresses.length != requestNumbers.length) revert InvalidRequest(); + for (uint256 i; i < operatorAddresses.length; ++i) { + _claim(operatorAddresses[i], requestNumbers[i]); + } } /** @@ -583,10 +600,10 @@ contract StakeHub is System, Initializable { if (!_validatorSet.contains(operatorAddress)) revert ValidatorNotExist(); // should never happen Validator storage valInfo = _validators[operatorAddress]; - uint256 dayIndex = block.timestamp / 1 days; + uint256 index = block.timestamp / BREATH_BLOCK_INTERVAL; // This is to prevent many honest validators being slashed at the same time because of implementation bugs - if (_felonyMap[dayIndex] >= felonyPerDay) revert NoMoreFelonyToday(); - _felonyMap[dayIndex] += 1; + if (_felonyMap[index] >= felonyPerDay) revert NoMoreFelonyToday(); + _felonyMap[index] += 1; // slash (bool canSlash, uint256 jailUntil) = _checkFelonyRecord(operatorAddress, SlashType.MaliciousVote); @@ -607,10 +624,10 @@ contract StakeHub is System, Initializable { if (!_validatorSet.contains(operatorAddress)) revert ValidatorNotExist(); // should never happen Validator storage valInfo = _validators[operatorAddress]; - uint256 dayIndex = block.timestamp / 1 days; + uint256 index = block.timestamp / BREATH_BLOCK_INTERVAL; // This is to prevent many honest validators being slashed at the same time because of implementation bugs - if (_felonyMap[dayIndex] >= felonyPerDay) revert NoMoreFelonyToday(); - _felonyMap[dayIndex] += 1; + if (_felonyMap[index] >= felonyPerDay) revert NoMoreFelonyToday(); + _felonyMap[index] += 1; // slash (bool canSlash, uint256 jailUntil) = _checkFelonyRecord(operatorAddress, SlashType.DoubleSign); @@ -741,25 +758,22 @@ contract StakeHub is System, Initializable { /** * @return the validator's reward of the day */ - function getValidatorRewardRecord(address operatorAddress, uint256 dayIndex) external view returns (uint256) { + function getValidatorRewardRecord(address operatorAddress, uint256 index) external view returns (uint256) { if (!_validatorSet.contains(operatorAddress)) revert ValidatorNotExist(); - return IStakeCredit(_validators[operatorAddress].creditContract).rewardRecord(dayIndex); + return IStakeCredit(_validators[operatorAddress].creditContract).rewardRecord(index); } /** * @return the validator's total pooled BNB of the day */ - function getValidatorTotalPooledBNBRecord( - address operatorAddress, - uint256 dayIndex - ) external view returns (uint256) { + function getValidatorTotalPooledBNBRecord(address operatorAddress, uint256 index) external view returns (uint256) { if (!_validatorSet.contains(operatorAddress)) revert ValidatorNotExist(); - return IStakeCredit(_validators[operatorAddress].creditContract).totalPooledBNBRecord(dayIndex); + return IStakeCredit(_validators[operatorAddress].creditContract).totalPooledBNBRecord(index); } /** * @notice get the basic info of a validator - * including consensus address, credit contract, vote address, jailed and jailUntil + * including consensus address, credit contract, created time, vote address, jailed and jailUntil */ function getValidatorBasicInfo(address operatorAddress) external @@ -768,6 +782,7 @@ contract StakeHub is System, Initializable { returns ( address consensusAddress, address creditContract, + uint256 createdTime, bytes memory voteAddress, bool jailed, uint256 jailUntil @@ -776,6 +791,7 @@ contract StakeHub is System, Initializable { Validator memory valInfo = _validators[operatorAddress]; consensusAddress = valInfo.consensusAddress; creditContract = valInfo.creditContract; + createdTime = valInfo.createdTime; voteAddress = valInfo.voteAddress; jailed = valInfo.jailed; jailUntil = valInfo.jailUntil; @@ -966,4 +982,9 @@ contract StakeHub is System, Initializable { emit ValidatorJailed(valInfo.operatorAddress); } } + + function _claim(address operatorAddress, uint256 requestNumber) internal validatorExist(operatorAddress) { + uint256 bnbAmount = IStakeCredit(_validators[operatorAddress].creditContract).claim(msg.sender, requestNumber); + emit Claimed(operatorAddress, msg.sender, bnbAmount); + } } diff --git a/contracts/BC_fusion/interface/IStakeHub.sol b/contracts/BC_fusion/interface/IStakeHub.sol index a84c8073f..cd833235c 100644 --- a/contracts/BC_fusion/interface/IStakeHub.sol +++ b/contracts/BC_fusion/interface/IStakeHub.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.17; interface IStakeHub { function DEAD_ADDRESS() external view returns (address); function INIT_LOCK_AMOUNT() external view returns (uint256); + function BREATH_BLOCK_INTERVAL() external view returns (uint256); function unbondPeriod() external view returns (uint256); function transferGasLimit() external view returns (uint256); } diff --git a/contracts/SlashIndicator.sol b/contracts/SlashIndicator.sol index bf5e8a554..2e89169a2 100644 --- a/contracts/SlashIndicator.sol +++ b/contracts/SlashIndicator.sol @@ -37,9 +37,11 @@ contract SlashIndicator is ISlashIndicator,System,IParamSubscriber, IApplication uint256 public felonyThreshold; // BEP-126 Fast Finality - uint256 public constant INIT_FINALITY_SLASH_REWARD_RATIO = 20; + // @notice change name from `INIT_FINALITY_SLASH_REWARD_RATIO` to `INIT_FELONY_SLASH_REWARD_RATIO` after BC-fusion + uint256 public constant INIT_FELONY_SLASH_REWARD_RATIO = 20; - uint256 public finalitySlashRewardRatio; + // @notice change name from `finalitySlashRewardRatio` to `felonySlashRewardRatio` after BC-fusion + uint256 public felonySlashRewardRatio; bool public enableMaliciousVoteSlash; uint256 public constant INIT_MALICIOUS_VOTE_SLASH_SCOPE = 86400; // 3 days @@ -217,8 +219,8 @@ contract SlashIndicator is ISlashIndicator,System,IParamSubscriber, IApplication function submitFinalityViolationEvidence(FinalityEvidence memory _evidence) public onlyInit { require(enableMaliciousVoteSlash, "malicious vote slash not enabled"); - if (finalitySlashRewardRatio == 0) { - finalitySlashRewardRatio = INIT_FINALITY_SLASH_REWARD_RATIO; + if (felonySlashRewardRatio == 0) { + felonySlashRewardRatio = INIT_FELONY_SLASH_REWARD_RATIO; } if (maliciousVoteSlashScope == 0) { maliciousVoteSlashScope = INIT_MALICIOUS_VOTE_SLASH_SCOPE; @@ -248,7 +250,7 @@ contract SlashIndicator is ISlashIndicator,System,IParamSubscriber, IApplication (address[] memory vals, bytes[] memory voteAddrs) = IBSCValidatorSet(VALIDATOR_CONTRACT_ADDR).getLivingValidators(); for (uint i; i < voteAddrs.length; ++i) { if (BytesLib.equal(voteAddrs[i], _evidence.voteAddr)) { - uint256 amount = (address(SYSTEM_REWARD_ADDR).balance * finalitySlashRewardRatio) / 100; + uint256 amount = (address(SYSTEM_REWARD_ADDR).balance * felonySlashRewardRatio) / 100; ISystemReward(SYSTEM_REWARD_ADDR).claimRewardsforFinality(msg.sender, amount); IBSCValidatorSet(VALIDATOR_CONTRACT_ADDR).felony(vals[i]); break; @@ -269,6 +271,10 @@ contract SlashIndicator is ISlashIndicator,System,IParamSubscriber, IApplication } function submitDoubleSignEvidence(bytes memory header1, bytes memory header2) public onlyInit { + if (felonySlashRewardRatio == 0) { + felonySlashRewardRatio = INIT_FELONY_SLASH_REWARD_RATIO; + } + require(header1.length != 0 && header2.length != 0, "empty header"); bytes[] memory elements = new bytes[](3); @@ -299,7 +305,7 @@ contract SlashIndicator is ISlashIndicator,System,IParamSubscriber, IApplication (address[] memory vals, ) = IBSCValidatorSet(VALIDATOR_CONTRACT_ADDR).getLivingValidators(); for (uint i; i < vals.length; ++i) { if (signer == vals[i]) { - uint256 amount = (address(SYSTEM_REWARD_ADDR).balance * finalitySlashRewardRatio) / 100; + uint256 amount = (address(SYSTEM_REWARD_ADDR).balance * felonySlashRewardRatio) / 100; ISystemReward(SYSTEM_REWARD_ADDR).claimRewards(msg.sender, amount); IBSCValidatorSet(VALIDATOR_CONTRACT_ADDR).felony(vals[i]); break; @@ -369,11 +375,11 @@ contract SlashIndicator is ISlashIndicator,System,IParamSubscriber, IApplication uint256 newFelonyThreshold = BytesToTypes.bytesToUint256(32, value); require(newFelonyThreshold <= 1000 && newFelonyThreshold > misdemeanorThreshold, "the felonyThreshold out of range"); felonyThreshold = newFelonyThreshold; - } else if (Memory.compareStrings(key, "finalitySlashRewardRatio")) { - require(value.length == 32, "length of finalitySlashRewardRatio mismatch"); - uint256 newFinalitySlashRewardRatio = BytesToTypes.bytesToUint256(32, value); - require(newFinalitySlashRewardRatio >= 10 && newFinalitySlashRewardRatio < 100, "the finality slash reward ratio out of range"); - finalitySlashRewardRatio = newFinalitySlashRewardRatio; + } else if (Memory.compareStrings(key, "felonySlashRewardRatio")) { + require(value.length == 32, "length of felonySlashRewardRatio mismatch"); + uint256 newFelonySlashRewardRatio = BytesToTypes.bytesToUint256(32, value); + require(newFelonySlashRewardRatio >= 10 && newFelonySlashRewardRatio < 100, "the felony slash reward ratio out of range"); + felonySlashRewardRatio = newFelonySlashRewardRatio; } else if (Memory.compareStrings(key, "enableMaliciousVoteSlash")) { require(value.length == 32, "length of enableMaliciousVoteSlash mismatch"); enableMaliciousVoteSlash = BytesToTypes.bytesToBool(32, value); diff --git a/scripts/generate.py b/scripts/generate.py index 04f50cb8c..155152701 100644 --- a/scripts/generate.py +++ b/scripts/generate.py @@ -47,24 +47,24 @@ def insert(contract, pattern, ins): raise Exception(f"{pattern} not found") -def replace(contract, pattern, repl): +def replace(contract, pattern, repl, count=1): pattern = re.compile(pattern) filepath = os.path.join(work_dir, "contracts", contract) - found = False - with fileinput.FileInput(filepath, inplace=True) as file: - for line in file: - if not found and pattern.search(line): - line = pattern.sub(repl, line) - found = True - print(line, end="") + with open(filepath, "r") as f: + content = f.read() - if not found: + if pattern.search(content): + content = pattern.sub(repl, content, count=count) + else: raise Exception(f"{pattern} not found") + with open(filepath, "w") as f: + f.write(content) + def replace_parameter(contract, parameter, value): - pattern = rf"{parameter} = .*;" + pattern = f"{parameter} =[^;]*;" repl = f"{parameter} = {value};" replace(contract, pattern, repl) @@ -111,13 +111,13 @@ def generate_relayer_hub(whitelist_1, whitelist_2): replace_parameter(contract, "address public constant WHITELIST_1", f"{whitelist_1}") replace_parameter(contract, "address public constant WHITELIST_2", f"{whitelist_2}") - if network == "local": + if network == "dev": replace(contract, r"function whitelistInit\(\) external", "function whitelistInit() public") insert(contract, "alreadyInit = true;", "\t\twhitelistInit();") def generate_slash_indicator(): - if network == "local": + if network == "dev": contract = "SlashIndicator.sol" backup_file( os.path.join(work_dir, "contracts", contract), os.path.join(work_dir, "contracts", contract[:-4] + ".bak") @@ -126,6 +126,51 @@ def generate_slash_indicator(): insert(contract, "alreadyInit = true;", "\t\tenableMaliciousVoteSlash = true;") +def generate_stake_hub( + breath_block_interval, init_bc_consensus_addresses, init_bc_vote_addresses, unbond_period, downtime_jail_time, + felony_jail_time, asset_protector +): + contract = "BC_fusion/StakeHub.sol" + backup_file( + os.path.join(work_dir, "contracts", contract), os.path.join(work_dir, "contracts", contract[:-4] + ".bak") + ) + + replace_parameter(contract, "uint256 public constant BREATH_BLOCK_INTERVAL", f"{breath_block_interval}") + replace_parameter(contract, "bytes private constant INIT_BC_CONSENSUS_ADDRESSES", f"{init_bc_consensus_addresses}") + replace_parameter(contract, "bytes private constant INIT_BC_VOTE_ADDRESSES", f"{init_bc_vote_addresses}") + + replace(contract, r"unbondPeriod = .*;", f"unbondPeriod = {unbond_period};") + replace(contract, r"downtimeJailTime = .*;", f"downtimeJailTime = {downtime_jail_time};") + replace(contract, r"felonyJailTime = .*;", f"felonyJailTime = {felony_jail_time};") + replace(contract, r"assetProtector = .*;", f"assetProtector = {asset_protector};") + + +def generate_governor( + block_interval, init_voting_delay, init_voting_period, init_min_period_after_quorum, governor_protector +): + contract = "BC_fusion/BSCGovernor.sol" + backup_file( + os.path.join(work_dir, "contracts", contract), os.path.join(work_dir, "contracts", contract[:-4] + ".bak") + ) + + replace_parameter(contract, "uint256 private constant BLOCK_INTERVAL", f"{block_interval}") + replace_parameter(contract, "uint256 private constant INIT_VOTING_DELAY", f"{init_voting_delay}") + replace_parameter(contract, "uint256 private constant INIT_VOTING_PERIOD", f"{init_voting_period}") + replace_parameter( + contract, "uint64 private constant INIT_MIN_PERIOD_AFTER_QUORUM", f"{init_min_period_after_quorum}" + ) + replace(contract, r"governorProtector = .*;", f"governorProtector = {governor_protector};") + + +def generate_timelock(init_minimal_delay): + contract = "BC_fusion/BSCTimelock.sol" + backup_file( + os.path.join(work_dir, "contracts", contract), os.path.join(work_dir, "contracts", contract[:-4] + ".bak") + ) + + replace_parameter(contract, "uint256 private constant INIT_MINIMAL_DELAY", f"{init_minimal_delay}") + + def generate_system(): contract = "System.sol" backup_file( @@ -136,7 +181,7 @@ def generate_system(): def generate_system_reward(): - if network == "local": + if network == "dev": contract = "SystemReward.sol" backup_file( os.path.join(work_dir, "contracts", contract), os.path.join(work_dir, "contracts", contract[:-4] + ".bak") @@ -192,7 +237,7 @@ def generate_validator_set(init_burn_ratio, init_validatorset_bytes): replace_parameter(contract, "uint256 public constant INIT_BURN_RATIO", f"{init_burn_ratio}") replace_parameter(contract, "bytes public constant INIT_VALIDATORSET_BYTES", f"hex\"{init_validatorset_bytes}\"") - if network == "local": + if network == "dev": insert( contract, r"for \(uint i; i