Skip to content

Instantly share code, notes, and snippets.

@enriquefynn
Created October 2, 2020 16:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enriquefynn/f261edee578eba1b80fa4c26e408ca2a to your computer and use it in GitHub Desktop.
Save enriquefynn/f261edee578eba1b80fa4c26e408ca2a to your computer and use it in GitHub Desktop.
package main
import (
"encoding/binary"
"errors"
"fmt"
"github.com/sirupsen/logrus"
"github.com/tendermint/iavl"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/abci/example/code"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/version"
)
var _ abci.Application = (*Application)(nil)
const (
snapshotFrequency = 10
cacheSize = 4294967296 // 4Gb
prepopulateLimit = 10000000
)
type Application struct {
abci.BaseApplication
store *iavl.MutableTree
}
func NewApplication(dataDir, logPath string, prepopulate int, snapshotChunkSize uint64) *Application {
// Set up IAVL store
// db, err := dbm.NewGoLevelDB("iavlapp", dataDir)
db := dbm.NewDB("marolapp", dbm.RocksDBBackend, dataDir)
store, err := iavl.NewMutableTree(db, cacheSize)
if err != nil {
panic(err)
}
_, err = store.Load()
if err != nil {
panic(err)
}
return &Application{
store: store,
}
}
func (app *Application) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
return abci.ResponseInfo{
Data: fmt.Sprintf(`{"size":%v}`, app.store.Size()),
Version: version.ABCIVersion,
AppVersion: 1,
LastBlockHeight: app.store.Version(),
LastBlockAppHash: app.store.Hash(),
}
}
// parseTx parses a tx in 'key=value' format into a key and value.
func parseTx(tx []byte) ([]byte, []byte, error) {
keySize := binary.LittleEndian.Uint16(tx[:2])
if len(tx) < int(keySize) {
return nil, nil, errors.New("key cannot be empty")
}
key, value := tx[2:keySize+2], tx[keySize+2:]
return key, value, nil
}
func (app *Application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
_, _, err := parseTx(req.Tx)
if err != nil {
logrus.Infof("ERROR: %v", err)
panic(err)
}
return abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
}
func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
key, value, err := parseTx(req.Tx)
// logrus.Infof("KEY: %x, VALUE: %x", key, value)
if err != nil {
panic(err)
}
app.store.Set(key, value)
return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
}
func (app *Application) Commit() abci.ResponseCommit {
hash, _, _ := app.store.SaveVersion()
return abci.ResponseCommit{Data: hash}
}
func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery {
_, value := app.store.Get(req.Data)
return abci.ResponseQuery{
Height: app.store.Version(),
Key: req.Data,
Value: value,
}
}
func (app *Application) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
// reset valset changes
return abci.ResponseBeginBlock{}
}
func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
return abci.ResponseEndBlock{}
}
func (app *Application) InitChain(abci.RequestInitChain) abci.ResponseInitChain {
return abci.ResponseInitChain{}
}
package main
import (
"fmt"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/apex/log"
"github.com/spf13/viper"
abci "github.com/tendermint/tendermint/abci/types"
tmCfg "github.com/tendermint/tendermint/config"
tmflags "github.com/tendermint/tendermint/libs/cli/flags"
tmLogger "github.com/tendermint/tendermint/libs/log"
tmNode "github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
)
var storagePath string
var tmConfigFile string
var treeTypeArg string
var validatorKeyPath string
var validatorStatePath string
var rpcServers string
var trustHeight int64
var trustHash string
var prepopulate int
var byzantineBehaviourString string
var byzantineBehaviour int
var chunkCapacity int
func init() {
}
func main() {
app := NewApplication(storagePath, "/tmp/tree_logs_"+treeTypeArg, prepopulate, uint64(100*chunkCapacity))
// logger := tmLogger.NewTMLogger(tmLogger.NewSyncWriter(os.Stdout))
node, err := newTendermint(app, tmConfigFile)
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(2)
}
node.Start()
// Handle Ctr-c
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
node.Stop()
node.Wait()
log.Infof("Exiting app")
os.Exit(0)
}
func newTendermint(app abci.Application, configFile string) (*tmNode.Node, error) {
// read config
config := tmCfg.DefaultConfig()
config.RootDir = filepath.Dir(filepath.Dir(configFile))
viper.SetConfigFile(configFile)
if err := viper.ReadInConfig(); err != nil {
return nil, fmt.Errorf("viper failed to read config file: %w", err)
}
if err := viper.Unmarshal(config); err != nil {
return nil, fmt.Errorf("viper failed to unmarshal config: %w", err)
}
if err := config.ValidateBasic(); err != nil {
return nil, fmt.Errorf("config is invalid: %w", err)
}
// create logger
logger := tmLogger.NewTMLogger(tmLogger.NewSyncWriter(os.Stdout))
var err error
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, tmCfg.DefaultLogLevel())
if err != nil {
return nil, fmt.Errorf("failed to parse log level: %w", err)
}
// read private validator
pv := privval.LoadFilePV(validatorKeyPath, validatorStatePath)
// read node key
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
if err != nil {
return nil, fmt.Errorf("failed to load node's key: %w", err)
}
// create node
node, err := tmNode.NewNode(
config,
pv,
nodeKey,
proxy.NewLocalClientCreator(app),
tmNode.DefaultGenesisDocProviderFunc(config),
tmNode.DefaultDBProvider,
tmNode.DefaultMetricsProvider(config.Instrumentation),
logger)
if err != nil {
return nil, fmt.Errorf("failed to create new Tendermint node: %w", err)
}
return node, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment