Skip to content

Instantly share code, notes, and snippets.

@nnkken
Last active August 6, 2018 03:11
Show Gist options
  • Select an option

  • Save nnkken/e8cc44d155137751bdd7b91eae7919b6 to your computer and use it in GitHub Desktop.

Select an option

Save nnkken/e8cc44d155137751bdd7b91eae7919b6 to your computer and use it in GitHub Desktop.
Example ABCI server

This is an example ABCI server program, which gives you a basic impression about how does an ABCI server works with Tendermint.

You can also try to delete the DB (/tmp/num-adder.db), or reset block info (tendermint unsafe_reset_all) to see what will happen.

Tendermint version

0.22.8-40d6dc2e

Run the node

  1. tendermint node --consensus.create_empty_blocks=false
  2. go run abci.go

Query the state

curl 'localhost:26657/abci_query?path="state"'

Result is base64-encoded.

Add number to the state

Example: add 1337 to the state

curl 'localhost:26657/broadcast_tx_commit?tx="1337"'

package main
import (
"encoding/binary"
"fmt"
"math/big"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tmlibs/db"
"github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/abci/server"
"github.com/ethereum/go-ethereum/common"
ethCrypto "github.com/ethereum/go-ethereum/crypto"
)
type NumberAdderApplication struct {
types.BaseApplication
db db.DB
}
func (app *NumberAdderApplication) rawHeight() []byte {
return app.db.Get([]byte("__BLOCK_HEIGHT__"))
}
func (app *NumberAdderApplication) rawState() []byte {
return app.db.Get([]byte("__STATE__"))
}
func (app *NumberAdderApplication) AppHash() []byte {
if app.Height() == 0 {
return nil
}
rawState := app.rawState()
return ethCrypto.Keccak256(rawState)
}
func (app *NumberAdderApplication) Height() uint64 {
rawHeight := app.rawHeight()
if len(rawHeight) != 8 {
return 0
}
return binary.BigEndian.Uint64(rawHeight)
}
func (app *NumberAdderApplication) State() *big.Int {
rawState := app.rawState()
state := big.NewInt(0)
if len(rawState) == 0 {
return state
}
return state.SetBytes(rawState)
}
func (app *NumberAdderApplication) SetHeight(newHeight uint64) {
rawHeight := make([]byte, 8)
binary.BigEndian.PutUint64(rawHeight, newHeight)
app.db.Set([]byte("__BLOCK_HEIGHT__"), rawHeight)
}
func (app *NumberAdderApplication) SetState(newState *big.Int) {
app.db.Set([]byte("__STATE__"), newState.Bytes())
}
func (app *NumberAdderApplication) CheckTx(tx []byte) types.ResponseCheckTx {
inputInt := big.NewInt(0)
inputInt, succ := inputInt.SetString(string(tx), 10)
if inputInt == nil || !succ {
return types.ResponseCheckTx { Code: 1, Log: "Invalid input number" }
}
return types.ResponseCheckTx { Code: 0 }
}
func (app *NumberAdderApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
inputInt := big.NewInt(0)
inputInt, succ := inputInt.SetString(string(tx), 10)
if inputInt == nil || !succ {
return types.ResponseDeliverTx { Code: 1, Log: "Invalid input number" }
}
newState := app.State()
newState.Add(newState, inputInt)
app.SetState(newState)
return types.ResponseDeliverTx { Code: 0 }
}
func (app *NumberAdderApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
fmt.Printf("EndBlock, Height = %v\n", req.Height)
return types.ResponseEndBlock {}
}
func (app *NumberAdderApplication) Commit() (resp types.ResponseCommit) {
app.SetHeight(app.Height() + 1)
appHash := app.AppHash()
fmt.Printf("Commit, AppHash = %v\n", common.Bytes2Hex(appHash))
return types.ResponseCommit { Data: appHash }
}
func (app *NumberAdderApplication) InitChain(params types.RequestInitChain) types.ResponseInitChain {
fmt.Println("InitChain")
app.SetState(big.NewInt(0))
app.SetHeight(0)
return types.ResponseInitChain {}
}
func (app *NumberAdderApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery {
fmt.Printf("Query, path = %s\n", reqQuery.Path)
switch reqQuery.Path {
case "hash":
return types.ResponseQuery { Value: []byte(fmt.Sprintf("%v", app.AppHash())) }
case "state":
return types.ResponseQuery { Value: []byte(app.State().String()) }
default:
return types.ResponseQuery {
Log: fmt.Sprintf("Invalid query path: %s", reqQuery.Path),
}
}
}
func (app *NumberAdderApplication) Info(req types.RequestInfo) types.ResponseInfo {
fmt.Println("Info")
appHash := app.AppHash()
return types.ResponseInfo {
Data: fmt.Sprintf("{\"hash\":\"%v\"}", app.AppHash()),
Version: "1",
LastBlockHeight: int64(app.Height()),
LastBlockAppHash: appHash,
}
}
func NewNumberAdderApplication(db db.DB) *NumberAdderApplication {
app := &NumberAdderApplication { db: db }
fmt.Println("NewNumberAdderApplication")
fmt.Printf("height: %v\n", app.Height())
return app
}
func main() {
db, err := db.NewGoLevelDB("num-adder", "/tmp")
if err != nil {
panic("Cannot open LevelDB file")
}
defer db.Close()
app := NewNumberAdderApplication(db)
svr, err := server.NewServer("tcp://0.0.0.0:26658", "socket", app)
if err != nil {
fmt.Println("Error when initializing server: ", err)
}
err = svr.Start()
if err != nil {
fmt.Println("Error when starting server: ", err)
}
cmn.TrapSignal(func() {
svr.Stop()
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment