type Application interface {
// Info/Query Connection
Info(RequestInfo) ResponseInfo // Return application info
SetOption(RequestSetOption) ResponseSetOption // Set application option
Query(RequestQuery) ResponseQuery // Query for state
// Mempool Connection
CheckTx(tx []byte) ResponseCheckTx // Validate a tx for the mempool
// Consensus Connection
InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore
BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block
DeliverTx(tx []byte) ResponseDeliverTx // Deliver a tx for full processing
EndBlock(RequestEndBlock) ResponseEndBlock // Signals the end of a block, returns changes to the validator set
Commit() ResponseCommit // Commit the state and return the application Merkle root hash
}
它可以用来在每个区块结束时运行一些代码,此外,应答可以包含验证人列表,可以用来更新验证人集合。要添加新验证人或更新现有验证人,只需将它们包括在 EndBlock 应答返回的列表中即可。要移除一个验证人,将其 power 设为 0 并放入此列表。Tendermint 将负责更新验证人集合。
// update the validator set with the latest abciResponses
lastHeightValsChanged := state.LastHeightValidatorsChanged
if len(abciResponses.EndBlock.ValidatorUpdates) > 0 {
err := updateValidators(nextValSet, abciResponses.EndBlock.ValidatorUpdates)
if err != nil {
return state, fmt.Errorf("Error changing validator set: %v", err)
}
// change results from this height but only applies to the next height
lastHeightValsChanged = header.Height + 1
}
// tx is either "val:pubkey/power" or "key=value" or just arbitrary bytes
func (app *PersistentKVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
// if it starts with "val:", update the validator set
// format is "val:pubkey/power"
if isValidatorTx(tx) {
// update validators in the merkle tree
// and in app.ValUpdates
return app.execValidatorTx(tx)
}
// otherwise, update the key-value store
return app.app.DeliverTx(tx)
}
添加验证人的两种方式
添加 Tendermint 验证人有两种方式:
genesis.json
中进行操作。可以创建一个新的priv_validator.json
文件,然后把里面的pub_key
拷贝到genesis.json
文件中。EndBlock
方法添加验证人。前面的文档已经对第一种方式进行了说明,这里讨论第二种方式,即如何在运行中的 Tendermint 网络中添加验证人节点。
启动 Tendermint 前添加
最简单的方式是在启动 Tendermint 网络前,在
genesis.json
中进行操作。可以创建一个新的priv_validator.json
文件,然后把里面的pub_key
拷贝到genesis.json
文件中。执行这条命令来生成
priv_validator.json
:现在可以更新
genesis
文件了。比如新的priv_validator.json
长这样:然后新的
genesis.json
将长这样:更新本机
~/.tendermint/config
目录中的genesis.json
。把genesis
文件和新的priv_validator.json
拷贝到新机器上的~/.tendermint/config
目录中。现在在所有机器上执行
tendermint node
,使用--p2p.persistent_peers
或/dial_peers
来让它们互为 peer。他们应该开始生成区块,只要他们都在线就会继续生成区块。要让 Tendermint 网络可以容忍其中一个验证人失败,至少需要四个验证人节点(> 2/3)。
启动 Tendermint 后添加
EndBlock 方法的定义
EndBlock
是 abci 中Application
接口中定义的一个方法:它可以用来在每个区块结束时运行一些代码,此外,应答可以包含验证人列表,可以用来更新验证人集合。要添加新验证人或更新现有验证人,只需将它们包括在
EndBlock
应答返回的列表中即可。要移除一个验证人,将其power
设为 0 并放入此列表。Tendermint 将负责更新验证人集合。注意,如果希望轻客户端能够从外部证明状态的转换,则投票权重的变化必须严格小于每个区块的 1/3。参考 这篇文档 来查看它如何追踪验证人。
Tendermint 源码中如何进行验证人的更新
相关代码在
tendermint/state/execution.go
第 315 行。执行
ApplyBlock
方法时会执行updateState
方法,它会根据执行execBlockOnProxyApp
返回的应答来更新状态:execBlockOnProxyApp
方法内部会调用开发者定义的 ABCI 应用的BeginBlock
、DeliverTx
和EndBlock
方法。ABCI 应用中如何实现验证人的更新
处理逻辑就是在由客户端向 ABCI 应用提交交易时,在
DeliverTx
方法中进行验证人的更新。当 Tendermint 在ApplyBlock
方法中应用区块时,会调用此方法。这里更新验证人的交易格式为
val:pubkey/power
,看一下它的具体实现:execValidatorTx
方法会在数据库及 app 的ValUpdates
字段中更新验证人:References
Adding a Validator
EndBlock
package lite doc
issues 1291
issues 1313