根據下面的內容安裝Tendermint:
https://github.com/tendermint/tendermint/blob/master/docs/introduction/install.md
Tendermint
什麼是Tendermint?
Tendermint是具有拜占庭容錯機制的可定製協議區塊鏈。
Tendermint暴露了自身的abci(Application BlockChain Interface)接口,允許用戶定製屬於他們自己的協議。
兩個Tendermint終端互相承認,如果他們的協議是一致的.這將使得開發者很容易完成一條可以在多臺服務器上部署的符合實驗需求的區塊鏈.
Minimum Functional
獲取一個能夠啓動的區塊鏈,只需要實現一個接口abci, 自定義一個application,讓它內聯types.BaseApplication
,任意重載自己想重載的函數,就能啓動。
State
先定義存儲到database中的狀態結構
type NSBState struct {
db dbm.DB
ActionRoot trie.MerkleHash `json:"action_root"`
MerkleProofRoot trie.MerkleHash `json:"merkle_proof_root"`
ActiveISCRoot trie.MerkleHash `json:"active_isc_root"`
Height int64 `json:"height"`
AppHash []byte `json:"app_hash"`
}
func loadState(db dbm.DB) NSBState {
stateBytes := db.Get(stateKey)
var state NSBState
if len(stateBytes) != 0 {
err := json.Unmarshal(stateBytes, &state)
if err != nil {
panic(err)
}
}
state.db = db
return state
}
func saveState(state NSBState) {
stateBytes, err := json.Marshal(state)
if err != nil {
panic(err)
}
state.db.Set(stateKey, stateBytes)
}
然後自定義application.
type NSBApplication struct {
types.BaseApplication
state NSBState
ValUpdates []types.ValidatorUpdate
logger log.Logger
}
func NewNSBApplication(dbDir string) (*NSBApplication, error) {
name := "nsb"
db, err := dbm.NewGoLevelDB(name, dbDir)
if err != nil {
return nil, err
}
state := loadState(db)
return &NSBApplication{
state: state,
logger: log.NewNopLogger(),
}, nil
}
實現abci接口,這裏直接複製kvstore的內容,以後有需要可以再改。
func (nsb *NSBApplication) Info(req types.RequestInfo) types.ResponseInfo {
return types.ResponseInfo{
Data: fmt.Sprintf(
"{\"action_root\":%v, \"merkle_proof_root\":%v, \"active_isc_root\":%v, }",
hex.EncodeToString(nsb.state.ActionRoot),
hex.EncodeToString(nsb.state.MerkleProofRoot),
hex.EncodeToString(nsb.state.ActiveISCRoot)),
Version: version.ABCIVersion,
AppVersion: NSBVersion.Uint64(),
}
}
// Save the validators in the merkle tree
func (nsb *NSBApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain {
for _, v := range req.Validators {
r := nsb.updateValidator(v)
if r.IsErr() {
nsb.logger.Error("Error updating validators", "r", r)
}
}
return types.ResponseInitChain{}
}
// Track the block hash and header information
func (nsb *NSBApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
// reset valset changes
nsb.ValUpdates = make([]types.ValidatorUpdate, 0)
return types.ResponseBeginBlock{}
}
// Update the validator set
func (nsb *NSBApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
return types.ResponseEndBlock{ValidatorUpdates: nsb.ValUpdates}
}
func (nsb *NSBApplication) CheckTx(tx []byte) types.ResponseCheckTx {
return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
}
func (nsb *NSBApplication) SetLogger(l log.Logger) {
nsb.logger = l
}
func (nsb *NSBApplication) deliverTx(tx []byte) types.ResponseDeliverTx {
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}
func (nsb *NSBApplication) 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 nsb.execValidatorTx(tx)
}
// otherwise, update the key-value store
return nsb.deliverTx(tx)
}
func (nsb *NSBApplication) Validators() (validators []types.ValidatorUpdate) {
itr := nsb.state.db.Iterator(nil, nil)
for ; itr.Valid(); itr.Next() {
if isValidatorTx(itr.Key()) {
validator := new(types.ValidatorUpdate)
err := types.ReadMessage(bytes.NewBuffer(itr.Value()), validator)
if err != nil {
panic(err)
}
validators = append(validators, *validator)
}
}
return
}
func MakeValSetChangeTx(pubkey types.PubKey, power int64) []byte {
return []byte(fmt.Sprintf("val:%X/%d", pubkey.Data, power))
}
func isValidatorTx(tx []byte) bool {
return true// strings.HasPrefix(string(tx), ValidatorSetChangePrefix)
}
func (nsb *NSBApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx {
// tx = tx[len(ValidatorSetChangePrefix):]
//get the pubkey and power
pubKeyAndPower := strings.Split(string(tx), "/")
if len(pubKeyAndPower) != 2 {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Expected 'pubkey/power'. Got %v", pubKeyAndPower)}
}
pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1]
// decode the pubkey
pubkey, err := hex.DecodeString(pubkeyS)
if err != nil {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Pubkey (%s) is invalid hex", pubkeyS)}
}
// decode the power
power, err := strconv.ParseInt(powerS, 10, 64)
if err != nil {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Power (%s) is not an int", powerS)}
}
// update
return nsb.updateValidator(types.Ed25519ValidatorUpdate(pubkey, int64(power)))
}
func (nsb *NSBApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx {
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}
利用官方提供的庫,可以迅速寫出符合要求的rpc-server,這裏我簡單封裝了一下,NSB就是我們最終成型的區塊鏈邏輯集成.
const (
nsb_port = ":27667"
nsb_tcp = "tcp://0.0.0.0:27667"
nsb_net_type = "socket"
nsb_db_dir = "./data/"
nsb_must_connected = false
)
type NSB struct {
app types.Application
srv cmn.Service
cli abcicli.Client
}
func NewNSBClient() (cli abcicli.Client, err error) {
cli, err = abcicli.NewClient(nsb_port, nsb_net_type, nsb_must_connected)
return
}
func NewNSBServer(app abcinsb.NSBApplication) (srv cmn.Service, err error) {
srv, err = abcisrv.NewServer(nsb_tcp, nsb_net_type, app)
return
}
func NewNSB() (nsb NSB, err error) {
nsb.app, err = abcinsb.NewNSBApplication(nsb_db_dir)
if err != nil {
return
}
nsb.srv, err = NewNSBServer(nsb.app)
if err != nil {
return
}
nsb.cli, err = NewNSBClient()
return
}
func (nsb *NSB) Start() (err error) {
if err = nsb.srv.Start(); err != nil {
fmt.Println(err)
fmt.Println("start error")
}
return
}
func (nsb *NSB) Loop() {
cmn.TrapSignal(
nsb.app.logger, func() {
// Cleanup
nsb.srv.Stop()
})
}
來寫一個入口文件,在命令行鍵入go run nsbcli.go
,abci-server就啓動了。
package main
import (
nsbnet "github.com/Myriad-Dreamin/NetworkStatusBlockChain/nsb_abci/net"
)
func main() {
nsb := nsbnet.NewNSB()
nsb.Start()
nsb.Loop()
}