文章作用
本片文章主要是为了满足没有接触过Ethereum合约的同学,熟悉当前系统合约,以及了解Ethereum合约测试的交互流程。
基于文中所对应的系统合约,主要是测试阶段会尽量详细以及给出预计的测试结果做对比。
在相关系统合约未开源之前,不建议提前浏览此文章。
POS + POA 系统合约开源地址
待开源
合约组成
Validators 验证者合约
主要数据结构如下
enum Status {
Inactivated,
Activated,
Disabled
}
struct Description {
string nickname;
string location;
string website;
string contact;
string remarks;
}
struct Validator {
address payable rewardReceiveAddress;
address claimRewardAddress;
Status status;
uint256 totalStakeCoins;
uint256 totalPendingStakeCoins;
Description description;
uint256 shareRatioBase;
uint256 bpPendingClaimCoins;
uint256 issueTotalRewardsCoins;
uint256 issueBalanceRewardsCoins;
uint256 totalDisabledCoins;
uint256 bpLastClaimRewardTime;
uint256 lastEditValidatorInfoTime;
address[] stakers;
}
struct StakingInfo {
uint256 pendingStakeCoins;
uint256 stakeBalanceCoins;
uint256 lastClaimRewardTime;
}
struct UserCoreData {
address payable rewardReceiveAddress;
address claimRewardAddress;
uint256 totalUnstakeBalanceCoins;
uint256 lastUnstakeClaimTime;
}
mapping(address => Validator) validatorInfo;
mapping(address => mapping(address => StakingInfo)) staked;
mapping(address => UserCoreData) coreStaked;
address[] public workingValidators;
address[] public topValidators;
address[] public activedValidators;
uint256 public totalStake;
uint256 public totalPendingStake;
uint256 public totalDisabledCoins;
合约对外接口
合约初始化
initialize(address[] calldata vals, address proposalAddr, address punishContractAddr, address configContractAddr, uint256 epoch)
参数
- vals 初始化出块验证者地址
- proposalAddr 提案合约地址
- punishContractAddr 惩罚治理合约地址
- configContractAddr 全局配置合约地址
- epoch 出块周期
设置用户奖励相关地址
setUserCoreRewardAddress(address payable rewardReceiveAddress, address claimRewardAddress)
参数
- rewardReceiveAddress 用户投票奖励接收地址
- claimRewardAddress 用户奖励待领取地址
抵押投票
stake(address validator, uint256 stakeCoins)
参数
- validator 将要投票的节点
- stakeCoins 投票代币数
如果当前抵押总量大于转账金额,则消费已取消抵押待领取的代币数。
对于抵押成功后,抵押代币量,会进去待投票数据,等待下一领取周期后,才会进入真正的抵押投票数据
取消投票
unstake(address validator, uint256 unstakeCoins)
参数
- validator 将要赎回的验证者
- unstakeCoins 取消投票的代币数
赎回时,优先扣除待投票数据
赎回成功后,对应的代币数,将进入待领取(实时)
领取赎回
refund()
领取待赎回的代币,时间间隔默认3day,每次执行unstake都会刷新,已最后一次赎回时间为开始计算时间。
领取出块验证奖励
claimBpReward(address validator)
参数
- validator 对应的验证者地址
执行操作的账户地址需要为创建节点信息时执行的claimRewardAddress账户地址
执行期间会根据创建节点时,设置的shareRatioBase分配比例,进行期间的奖励划分,该验证者会直接收到自己应得的奖励,接收奖励的账户地址为创建节点时配置的rewardReceiveAddress奖励接收账户(对于创世验证节点,初始化为该账户地址)。
同时记录支持该节点的抵押用户部分的奖励,等待用户方主动过来领取。(如果领取周期内未及时领取的用户奖励,将会合到下一周期统一继续分分配)
领取投票奖励
claimReward(address voter, address validator)
参数
- voter 投票者账户地址
- validator 验证者账户地址
操作的执行权限地址可以为当前投票账户地址,也可以为setUserCoreRewardAddress设置中的claimRewardAddress代为领取地址,同理奖励接收地址如果未设置,则为当前投票地址。
更新当前工作验证者地址 (链程序调用)
updateWorkingValidators(address[] memory newSet, uint256 epoch)
参数
- newSet 当前工作验证者地址组,及为链程序执行getTopValidators()获取的头部地址,
- epoch 出块周期
上缴出块奖励
distributeBlockReward()
验证节点上缴出块奖励,奖励上缴后,根据各正在工作的验证节点所抵押的比例进行分配。
创建出块验证节点信息
function createOrEditValidator(
address payable rewardReceiveAddress, // 奖励接收地址
address claimRewardAddress, // 奖励代为领取地址
string calldata nickname, // 昵称
string calldata location, // 位置
string calldata website, // 网址
string calldata contact, // 联系方式
uint256 shareRatioBase, // 奖励分配比例(1/N)
string calldata remarks // 备注
)
重新激活节点
tryReactive(address validator)
当节点处于非Active时,置为激活状态
更新头部出块节点
updateTopValidators()
重新计算头部出块节点地址,为下一周期链程序获取做准备
其他数据获取类接口省略
Proposal 提案合约
struct ProposalInfo {
address proposer;
address dst;
string details;
uint256 createTime;
uint16 agree;
uint16 reject;
bool isEnd;
}
struct VoteInfo {
address voter;
uint256 voteTime;
uint16 votedTimes;
bool isAgree;
}
合约接口
合约初始化
initialize(address validatorContractAddr, address configContractAddr)
参数
- validatorContractAddr 验证者合约地址
- configContractAddr 全局合约配置地址
创建提案
createProposal(address val, string calldata details)
参数
- val 申请出块的验证者地址
- details 备注信息
通过监听event获取创建完的提案id
取消提案
cancelProposal(bytes32 id)
创建者可取消创建的提案
对提案投票
voteProposal(bytes32 id, bool isAgree)
对于提案,只有正在出块的节点才有资格投票,经过 1/2+1
通过,通过后会把提案中的地址添加到验证者列表,状态为Active
默认全局配置,同一个提案,有最多三次的投票修改机会
Punish 惩罚治理合约
稍后补充
PublicBase 通用基类合约
稍后补充
Config 全局配置合约
稍后补充
Ownable 合约权限合约
稍后补充
为模拟测试修改合约
为了测试方便,我们直接使用普通账户模拟出块账户进行合约的调用
需要注释掉下PublicBase
合约中的两个判断条件
modifier onlyMiner() {
_;
}
modifier onlyBlockEpoch(uint256 epoch) {
_;
}
合约部署步骤
先将各个合约编译好,通过 myetherwallet工具将以下合约部署到链上
- Validators
- Proposal
- Punish
- Config
各个合约部署完后的地址
合约名 |
地址 |
Validators |
0x113D0CD580edB0a70e8De5F92C15145f83211318 |
Proposal |
0x00C8D6d9736347f66c069C9285a5b99B8ddEFDC7 |
Punish |
0x8049F22032C2F7b7fD0981014953bFe5CE891373 |
Config |
0xB3bB2C011277DB3CcB66250617437C42Df69455B |
初始化各个系统合约
此处是为了模拟出块验证者测试,生产中当前初始化过程将在链程序打第一个块时做相应的初始化,各个初始化参数配置在genesis.json
中
"config": {
....
"congress": {
"period": 3,
"epoch": 200,
"validatorsContractName": "validators",
"punishContractName": "punish",
"proposalContractName": "proposal",
"configContractName": "config",
"validatorsContractAddr": "0x113D0CD580edB0a70e8De5F92C15145f83211318",
"punishContractAddr": "0x8049F22032C2F7b7fD0981014953bFe5CE891373",
"proposalContractAddr": "0x00C8D6d9736347f66c069C9285a5b99B8ddEFDC7",
"configContractAddr": "0xB3bB2C011277DB3CcB66250617437C42Df69455B"
}
....
}
相应的合约bytecode 也会初始化加到genesis.json
中的alloc
里的各个对应地址。
此篇文章我们只讲模拟测试,更灵活控制测试的步骤。
初始化Validators
对于后面的合约交互统一使用myetherwallet工具
合约测试步骤
稍后补充
备注
文中所涉及的系统合约暂时并没有开源,等后面计划开源后,再做补充
参考
https://github.com/HuobiGroup/huobi-eco-chain
https://github.com/HuobiGroup/huobi-eco-contracts