使用Tendermint開發一條獨立的區塊鏈(0)

根據下面的內容安裝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()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章