cosmos源码分析之六验证⼈
⼀、简介
在整个cosmos中,验证⼈的⾓⾊是⾮常重要的,它们负责投票决定向区块链提交新的区块。或者可以理解成,没有验证⼈,就没有cosmos的区块,也就没有区块链之说。
验证⼈可以由普通⽤户通过质押Atom来成为验证⼈,当然也可以接受别⼈的委托,这在上⽂已经分析过了,验证⼈由其总股权来决定即质押股权最多的前⼀百⼈会成为Cosmos的验证⼈。在Cosmos⽹络中,验证⼈的上限是⼀百,然后每年增长约在百分之⼗三,最终稳定在三百⼈左右。
如果验证⼈胡作⾮为或者经常不在线,⼜或者没有参与到治理,那么他的相关的抵押的Atom会被Slash掉。Slash的数量根据具体的情况来决定。
做为验证⼈,对硬件的要求有⼀定的限制,其实这和EOS的超级节点有些类似,毕竟做为⼀个验证⼈节点,没事就下线,被罚钱也不是什么好事,所以还是需要有⼀定的环境基础做保证。做为什么⼀个验证⼈的要求的细节,⼤家可以去官⽹查找相关的资料,这玩意⽬前还不能算多靠谱。
⼆、相关的术语
1、验证⼈:Cosmos Hub基于Tendermint,它由⼀组(100~300)个验证⼈来保证⽹络的安全。验证⼈负责去运⾏⼀个全节点,⼴播经过验证⼈私钥签名过的加密信息来参与共识。验证⼈最终根据投票的结果来决定新的区块并因此得到奖励。
2、股权抵押:Cosmos Hub是⼀个POS的区块链,意味着验证⼈的权重由其提供质押的Atom的数量决定。形象的来说,谁抵押的钱多,⼊的股多,权⼒就越⼤,⼤到在top100时,就成了验证⼈。
3、全节点:全节点就是能够完成区块链的所有功能的节点。相对应的还有轻节点,它处理区块头和⼩部分的交易。
三、成为验证⼈的过程
⽹络中的节点均可以发送⼀笔"declare-candidacy"交易⽤来表⽰他们想成为⼀个验证⼈,同时必须填写以下参数: 验证⼈公钥、名称、验证⼈的⽹站(可选)、验证⼈的描述信息(可选)、初始佣⾦⽐例、最⼤佣⾦、佣⾦变化率、最⼩⾃抵押数量、初始⾃抵押数量。 如果某个节点参选,其它Atom持有者可以向其地址委托Atom,从⽽有效地向其股权池⾥增加股权。⼀个地址的所有股权是其⾃抵押的Atom和委托⼈委托的股权的总和。 Top100的候选者被任命为验证⼈。如果某个验证⼈的股权总量跌出了前100名就会失去验证⼈权利,验证⼈的最⼤数量会依照计时按时间逐渐增加: 从第⼀年到最后⼀年的分布为100,113,127,144,163,184,208,235,265,300.
四、源码分析
在x/stake的⽬录中,有验证⼈的源码(x/):
type Validator struct{
Owner sdk.Address&n开学啦手抄报
bsp; `json:"owner"`// nder of BondTx - UnbondTx returns here
PubKey crypto.PubKey `json:"pub_key"`// pubkey of validator
Revoked bool`json:"revoked"`// has the validator been revoked from bonded status?
PoolShares PoolShares `json:"pool_shares"`// total shares for tokens held in the pool
DelegatorShares sdk.Rat `json:"delegator_shares"`// total shares issued to a validator's delegators
Description Description `json:"description"`// description terms for the validator
BondHeight int64`json:"bond_height"`// earliest height as a bonded validator
BondIntraTxCounter int16`json:"bond_intra_tx_counter"`// block-local tx index of validator change
ProporRewardPool sdk.Coins `json:"propor_reward_pool"`// XXX reward pool collected from being the propor
Commission sdk.Rat `json:"commission"`// XXX the commission rate of fees charged to any delegators
CommissionMax sdk.Rat `json:"commission_max"`// XXX maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Rat `json:"commission_change_rate"`// XXX maximum daily increa of the validator commission
CommissionChangeToday sdk.Rat `json:"commission_change_today"`// XXX commission rate change today, ret each day (UTC time)
// fee related
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"`// total shares of a global hold pools
}
// Validators - list of Validators
type Validators []Validator
//委托⼈增加委托
// XXX Audit this function further to make sure it's cor注册微博
rect
// XXX Audit this function further to make sure it's correct
// add tokens to a validator
func(v Validator)addTokensFromDel(pool Pool,
amount int64)(validator2 Validator, p2 Pool, issuedDelegatorShares sdk.Rat){
exRate := v.DelegatorShareExRate(pool)// bshr/delshr
var poolShares PoolShares
var equivalentBondedShares sdk.Rat
switch v.Status(){
ca sdk.Unbonded:
pool, poolShares = pool.addTokensUnbonded(amount)
ca sdk.Unbonding:
pool, poolShares = pool.addTokensUnbonding(amount)
ca sdk.Bonded:
pool, poolShares = pool.addTokensBonded(amount)
}
v.PoolShares.Amount = v.PoolShares.Amount.Add(poolShares.Amount)
equivalentBondedShares = poolShares.ToBonded(pool).Amount
issuedDelegatorShares = equivalentBondedShares.Quo(exRate)// bshr/(bshr/delshr) = delshr
v.DelegatorShares = v.DelegatorShares.Add(issuedDelegatorShares)
return v, pool, issuedDelegatorShares
}
//
// validator for a delegated proof of stake system
//相关的验证⼈的接⼝函数,在上述的验证⼈结构中有体现
type Validator interface{
GetStatus() BondStatus // status of the validator
GetOwner() Address // owner address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey
GetPower() Rat // validation power
GetBondHeight()int64// height in which the validator became active
}
// properties for the t of all validators
type ValidatorSet interface{
// iterate through validator by owner-address, execute func for each validator
IterateValidators(Context,
func(index int64, validator Validator)(stop bool))
// iterate through bonded validator by pubkey-address, execute func for each validator
IterateValidatorsBonded(Context,
func(index int64, validator Validator)(stop bool))
Validator(Context, Address) Validator // get a particular validator by owner address
TotalPower(Context) Rat // total power of the validator t
Slash(Context, crypto.PubKey,int64, Rat)// slash the validator and delegators of the validator, specifying offence height & slash fraction Revoke(Context, crypto.PubKey)// revoke a validator
Unrevoke(Context, crypto.PubKey)// unrevoke a validator
}
创建验证⼈命令的源码:
// create create validator command
func GetCmdCreateValidator(cdc *wire.Codec)*cobra.Command {
cmd :=&cobra.Command{
U:"create-validator",
Short:"create new validator initialized with a lf-delegation to it",
RunE:func(cmd *cobra.Command, args []string)error{
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
amount, err := sdk.ParCoin(viper.GetString(FlagAmount))
if err !=nil{
return err
}
validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err !=nil{
return err
}
pkStr := viper.GetString(FlagPubKey)
if len(pkStr)==0{
return fmt.Errorf("must u --pubkey flag")
}
pk, err := sdk.GetValPubKeyBech32(pkStr)
if err !=nil{
return err
}
if viper.GetString(FlagMoniker)==""{
return fmt.Errorf("plea enter a moniker for the validator using --moniker")
}
description := stake.Description{
Moniker: viper.GetString(FlagMoniker),
Identity: viper.GetString(FlagIdentity),
Website: viper.GetString(FlagWebsite),
Details: viper.GetString(FlagDetails),
}
msg := stake.NewMsgCreateValidator(validatorAddr, pk, amount, description)
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, msg, cdc)
if err !=nil{
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n牵牛花怎么种
", res.Height, res.Hash.String())
return nil
},
}
cmd.Flags().AddFlagSet(fsPk)
cmd.Flags().AddFlagSet(fsAmount)
cmd.Flags().AddFlagSet(fsDescription)
cmd.Flags().AddFlagSet(fsValidator)
return cm夏淑娟
d
}
在Cosmos-SDK中,主要是提供了⼀些数据结构及相关的操作的验证⼈过程,⽽在Tendermint中则是提供了数据的具体的流动和通信接⼝。⽽在ABCI中提供了开发应⽤程序的接⼝和相关的协议。它是区块链和Tendermint的接⼝。只有通过它才可以接⼊相关的应⽤程序。这⾥提到了,在Tendermint中也
包含有相关的验证⼈的部分:
//
// Volatile state for each Validator
// NOTE: The Accum is not included in Validator.Hash();
// make sure to update that method if changes are made here
type Validator struct{
Address Address `json:"address"`
PubKey crypto情态动词表推测
.PubKey `json:"pub_key"`
VotingPower int64`json:"voting_power"`
Accum int64`json:"accum"`
}
/
/ RandValidator returns a randomized valid作文点评
ator, uful for testing.
// UNSTABLE
func RandValidator(randPower bool, minPower int64)(*Validator, PrivValidator){
privVal :=NewMockPV()
votePower := minPower
if randPower {
votePower +=int64(cmn.RandUint32())
}
val :=NewValidator(privVal.GetPubKey(), votePower)
return val, privVal
}
/
/
它有⼀个重要的⼯作-验证块:
// Validate block
//验证块
func validateBlock(stateDB dbm.DB, s State, b *types.Block)error{
// validate internal consistency
if err := b.ValidateBasic(); err !=nil{
return err
}
// validate basic info
if b.ChainID != s.ChainID {
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v", s.ChainID, b.ChainID)
}
if b.Height != s.LastBlockHeight+1{
return fmt.Errorf("Wrong Block.Header.Height. Expected %v, got %v", s.LastBlockHeight+1, b.Height)
}
/* TODO: Determine bounds for Time
See blockchain/reactor "stopSyncingDurationMinutes"
if !b.Time.After(lastBlockTime) {
return errors.New("Invalid Block.Header.Time")
}
*/
// validate prev block info
if!b.LastBlockID.Equals(s.LastBlockID){
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", s.LastBlockID, b.LastBlockID)
}
newTxs :=int64(len(b.Data.Txs))
if b.TotalTxs != s.LastBlockTotalTx+newTxs {
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v", s.LastBlockTotalTx+newTxs, b.TotalTxs)
}
// validate app info
if!bytes.Equal(b.AppHash, s.AppHash){
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v", s.AppHash, b.AppHash)
}
if!bytes.Equal(b.ConnsusHash, s.ConnsusParams.Hash()){
return fmt.Errorf("Wrong Block.Header.ConnsusHash. Expected %X, got %v", s.ConnsusParams.Hash(), b.ConnsusHash) }
}
if!bytes.Equal(b.LastResultsHash, s.LastResultsHash){
return fmt.Errorf("Wrong Block.Header.LastResultsHash. Expected %X, got %v", s.LastResultsHash, b.LastResultsHash) }
if!bytes.Equal(b.ValidatorsHash, s.Validators.Hash()){
return fmt.Errorf("Wrong Block.Header.ValidatorsHash. Expected %X, got %v", s.Validators.Hash(), b.ValidatorsHash) }
/
/ Validate block LastCommit.
if b.Height ==1{
if len(b.LastCommit.Precommits)!=0{
return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
}
}el{
if len(b.LastCommit.Precommits)!= s.LastValidators.Size(){
return fmt.Errorf("Invalid block commit size. Expected %v, got %v",
s.LastValidators.Size(),len(b.LastCommit.Precommits))
}
err := s.LastValidators.VerifyCommit(
s.ChainID, s.LastBlockID, b.Height-1, b.LastCommit)
if err !=nil{
return err
}
}
// TODO: Each check requires loading an old validator t.
// We should cap the amount of evidence per block
// to prevent potential propor DoS.
for_, ev :=range b.Evidence.Evidence {
if err :=VerifyEvidence(stateDB, s, ev); err !=nil{
return types.NewEvidenceInvalidErr(ev, err)
}
}
return nil
}
在ABCI的相关软件中也定义了Validator这个数据结构:
// Validator
type Validator struct{
Address []byte`protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
PubKey PubKey `protobuf:"bytes,2,opt,name=pub_key,json=pubKey" json:"pub_key"`
Power int64`protobuf:"varint,3,opt,name=power,proto3" json:"power,omitempty"`
}
然后再看⼀个出块时调⽤的相关操作,它先是进⾏判断异常特别是双签,然后遍历验证⼈来对块进⾏签名。