|
|
@ -58,11 +58,11 @@ Inside the example directory create a `main.go` file with the following content: |
|
|
|
package main |
|
|
|
|
|
|
|
import ( |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
) |
|
|
|
|
|
|
|
func main() { |
|
|
|
fmt.Println("Hello, Tendermint Core") |
|
|
|
fmt.Println("Hello, Tendermint Core") |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -87,7 +87,7 @@ Create a file called `app.go` with the following content: |
|
|
|
package main |
|
|
|
|
|
|
|
import ( |
|
|
|
abcitypes "github.com/tendermint/tendermint/abci/types" |
|
|
|
abcitypes "github.com/tendermint/tendermint/abci/types" |
|
|
|
) |
|
|
|
|
|
|
|
type KVStoreApplication struct {} |
|
|
@ -95,55 +95,55 @@ type KVStoreApplication struct {} |
|
|
|
var _ abcitypes.Application = (*KVStoreApplication)(nil) |
|
|
|
|
|
|
|
func NewKVStoreApplication() *KVStoreApplication { |
|
|
|
return &KVStoreApplication{} |
|
|
|
return &KVStoreApplication{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) Info(req abcitypes.RequestInfo) abcitypes.ResponseInfo { |
|
|
|
return abcitypes.ResponseInfo{} |
|
|
|
return abcitypes.ResponseInfo{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) DeliverTx(req abcitypes.RequestDeliverTx) abcitypes.ResponseDeliverTx { |
|
|
|
return abcitypes.ResponseDeliverTx{Code: 0} |
|
|
|
return abcitypes.ResponseDeliverTx{Code: 0} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) CheckTx(req abcitypes.RequestCheckTx) abcitypes.ResponseCheckTx { |
|
|
|
return abcitypes.ResponseCheckTx{Code: 0} |
|
|
|
return abcitypes.ResponseCheckTx{Code: 0} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) Commit() abcitypes.ResponseCommit { |
|
|
|
return abcitypes.ResponseCommit{} |
|
|
|
return abcitypes.ResponseCommit{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) Query(req abcitypes.RequestQuery) abcitypes.ResponseQuery { |
|
|
|
return abcitypes.ResponseQuery{Code: 0} |
|
|
|
return abcitypes.ResponseQuery{Code: 0} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) InitChain(req abcitypes.RequestInitChain) abcitypes.ResponseInitChain { |
|
|
|
return abcitypes.ResponseInitChain{} |
|
|
|
return abcitypes.ResponseInitChain{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) BeginBlock(req abcitypes.RequestBeginBlock) abcitypes.ResponseBeginBlock { |
|
|
|
return abcitypes.ResponseBeginBlock{} |
|
|
|
return abcitypes.ResponseBeginBlock{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) EndBlock(req abcitypes.RequestEndBlock) abcitypes.ResponseEndBlock { |
|
|
|
return abcitypes.ResponseEndBlock{} |
|
|
|
return abcitypes.ResponseEndBlock{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) ListSnapshots(abcitypes.RequestListSnapshots) abcitypes.ResponseListSnapshots { |
|
|
|
return abcitypes.ResponseListSnapshots{} |
|
|
|
return abcitypes.ResponseListSnapshots{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) OfferSnapshot(abcitypes.RequestOfferSnapshot) abcitypes.ResponseOfferSnapshot { |
|
|
|
return abcitypes.ResponseOfferSnapshot{} |
|
|
|
return abcitypes.ResponseOfferSnapshot{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) LoadSnapshotChunk(abcitypes.RequestLoadSnapshotChunk) abcitypes.ResponseLoadSnapshotChunk { |
|
|
|
return abcitypes.ResponseLoadSnapshotChunk{} |
|
|
|
return abcitypes.ResponseLoadSnapshotChunk{} |
|
|
|
} |
|
|
|
|
|
|
|
func (KVStoreApplication) ApplySnapshotChunk(abcitypes.RequestApplySnapshotChunk) abcitypes.ResponseApplySnapshotChunk { |
|
|
|
return abcitypes.ResponseApplySnapshotChunk{} |
|
|
|
return abcitypes.ResponseApplySnapshotChunk{} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -159,40 +159,40 @@ application to check it (validate the format, signatures, etc.). |
|
|
|
import "bytes" |
|
|
|
|
|
|
|
func (app *KVStoreApplication) isValid(tx []byte) (code uint32) { |
|
|
|
// check format |
|
|
|
parts := bytes.Split(tx, []byte("=")) |
|
|
|
if len(parts) != 2 { |
|
|
|
return 1 |
|
|
|
} |
|
|
|
|
|
|
|
key, value := parts[0], parts[1] |
|
|
|
|
|
|
|
// check if the same key=value already exists |
|
|
|
err := app.db.View(func(txn *badger.Txn) error { |
|
|
|
item, err := txn.Get(key) |
|
|
|
if err != nil && err != badger.ErrKeyNotFound { |
|
|
|
return err |
|
|
|
} |
|
|
|
if err == nil { |
|
|
|
return item.Value(func(val []byte) error { |
|
|
|
if bytes.Equal(val, value) { |
|
|
|
code = 2 |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
return code |
|
|
|
// check format |
|
|
|
parts := bytes.Split(tx, []byte("=")) |
|
|
|
if len(parts) != 2 { |
|
|
|
return 1 |
|
|
|
} |
|
|
|
|
|
|
|
key, value := parts[0], parts[1] |
|
|
|
|
|
|
|
// check if the same key=value already exists |
|
|
|
err := app.db.View(func(txn *badger.Txn) error { |
|
|
|
item, err := txn.Get(key) |
|
|
|
if err != nil && err != badger.ErrKeyNotFound { |
|
|
|
return err |
|
|
|
} |
|
|
|
if err == nil { |
|
|
|
return item.Value(func(val []byte) error { |
|
|
|
if bytes.Equal(val, value) { |
|
|
|
code = 2 |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
return code |
|
|
|
} |
|
|
|
|
|
|
|
func (app *KVStoreApplication) CheckTx(req abcitypes.RequestCheckTx) abcitypes.ResponseCheckTx { |
|
|
|
code := app.isValid(req.Tx) |
|
|
|
return abcitypes.ResponseCheckTx{Code: code, GasWanted: 1} |
|
|
|
code := app.isValid(req.Tx) |
|
|
|
return abcitypes.ResponseCheckTx{Code: code, GasWanted: 1} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -217,14 +217,14 @@ persistent and fast key-value (KV) database. |
|
|
|
import "github.com/dgraph-io/badger" |
|
|
|
|
|
|
|
type KVStoreApplication struct { |
|
|
|
db *badger.DB |
|
|
|
currentBatch *badger.Txn |
|
|
|
db *badger.DB |
|
|
|
currentBatch *badger.Txn |
|
|
|
} |
|
|
|
|
|
|
|
func NewKVStoreApplication(db *badger.DB) *KVStoreApplication { |
|
|
|
return &KVStoreApplication{ |
|
|
|
db: db, |
|
|
|
} |
|
|
|
return &KVStoreApplication{ |
|
|
|
db: db, |
|
|
|
} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -237,8 +237,8 @@ responses are expected to come in order. |
|
|
|
|
|
|
|
```go |
|
|
|
func (app *KVStoreApplication) BeginBlock(req abcitypes.RequestBeginBlock) abcitypes.ResponseBeginBlock { |
|
|
|
app.currentBatch = app.db.NewTransaction(true) |
|
|
|
return abcitypes.ResponseBeginBlock{} |
|
|
|
app.currentBatch = app.db.NewTransaction(true) |
|
|
|
return abcitypes.ResponseBeginBlock{} |
|
|
|
} |
|
|
|
|
|
|
|
``` |
|
|
@ -247,20 +247,20 @@ Here we create a batch, which will store block's transactions. |
|
|
|
|
|
|
|
```go |
|
|
|
func (app *KVStoreApplication) DeliverTx(req abcitypes.RequestDeliverTx) abcitypes.ResponseDeliverTx { |
|
|
|
code := app.isValid(req.Tx) |
|
|
|
if code != 0 { |
|
|
|
return abcitypes.ResponseDeliverTx{Code: code} |
|
|
|
} |
|
|
|
code := app.isValid(req.Tx) |
|
|
|
if code != 0 { |
|
|
|
return abcitypes.ResponseDeliverTx{Code: code} |
|
|
|
} |
|
|
|
|
|
|
|
parts := bytes.Split(req.Tx, []byte("=")) |
|
|
|
key, value := parts[0], parts[1] |
|
|
|
parts := bytes.Split(req.Tx, []byte("=")) |
|
|
|
key, value := parts[0], parts[1] |
|
|
|
|
|
|
|
err := app.currentBatch.Set(key, value) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
err := app.currentBatch.Set(key, value) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
return abcitypes.ResponseDeliverTx{Code: 0} |
|
|
|
return abcitypes.ResponseDeliverTx{Code: 0} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -280,8 +280,8 @@ yet committed). |
|
|
|
|
|
|
|
```go |
|
|
|
func (app *KVStoreApplication) Commit() abcitypes.ResponseCommit { |
|
|
|
app.currentBatch.Commit() |
|
|
|
return abcitypes.ResponseCommit{Data: []byte{}} |
|
|
|
app.currentBatch.Commit() |
|
|
|
return abcitypes.ResponseCommit{Data: []byte{}} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -301,27 +301,27 @@ Note we don't include a proof here. |
|
|
|
|
|
|
|
```go |
|
|
|
func (app *KVStoreApplication) Query(reqQuery abcitypes.RequestQuery) (resQuery abcitypes.ResponseQuery) { |
|
|
|
resQuery.Key = reqQuery.Data |
|
|
|
err := app.db.View(func(txn *badger.Txn) error { |
|
|
|
item, err := txn.Get(reqQuery.Data) |
|
|
|
if err != nil && err != badger.ErrKeyNotFound { |
|
|
|
return err |
|
|
|
} |
|
|
|
if err == badger.ErrKeyNotFound { |
|
|
|
resQuery.Log = "does not exist" |
|
|
|
} else { |
|
|
|
return item.Value(func(val []byte) error { |
|
|
|
resQuery.Log = "exists" |
|
|
|
resQuery.Value = val |
|
|
|
return nil |
|
|
|
}) |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
return |
|
|
|
resQuery.Key = reqQuery.Data |
|
|
|
err := app.db.View(func(txn *badger.Txn) error { |
|
|
|
item, err := txn.Get(reqQuery.Data) |
|
|
|
if err != nil && err != badger.ErrKeyNotFound { |
|
|
|
return err |
|
|
|
} |
|
|
|
if err == badger.ErrKeyNotFound { |
|
|
|
resQuery.Log = "does not exist" |
|
|
|
} else { |
|
|
|
return item.Value(func(val []byte) error { |
|
|
|
resQuery.Log = "exists" |
|
|
|
resQuery.Value = val |
|
|
|
return nil |
|
|
|
}) |
|
|
|
} |
|
|
|
return nil |
|
|
|
}) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -336,112 +336,112 @@ Put the following code into the "main.go" file: |
|
|
|
package main |
|
|
|
|
|
|
|
import ( |
|
|
|
"errors" |
|
|
|
"flag" |
|
|
|
"fmt" |
|
|
|
"os" |
|
|
|
"os/signal" |
|
|
|
"path/filepath" |
|
|
|
"syscall" |
|
|
|
|
|
|
|
"github.com/dgraph-io/badger" |
|
|
|
"github.com/spf13/viper" |
|
|
|
|
|
|
|
abci "github.com/tendermint/tendermint/abci/types" |
|
|
|
cfg "github.com/tendermint/tendermint/config" |
|
|
|
tmflags "github.com/tendermint/tendermint/libs/cli/flags" |
|
|
|
"github.com/tendermint/tendermint/libs/log" |
|
|
|
nm "github.com/tendermint/tendermint/node" |
|
|
|
"github.com/tendermint/tendermint/p2p" |
|
|
|
"github.com/tendermint/tendermint/privval" |
|
|
|
"github.com/tendermint/tendermint/proxy" |
|
|
|
"errors" |
|
|
|
"flag" |
|
|
|
"fmt" |
|
|
|
"os" |
|
|
|
"os/signal" |
|
|
|
"path/filepath" |
|
|
|
"syscall" |
|
|
|
|
|
|
|
"github.com/dgraph-io/badger" |
|
|
|
"github.com/spf13/viper" |
|
|
|
|
|
|
|
abci "github.com/tendermint/tendermint/abci/types" |
|
|
|
cfg "github.com/tendermint/tendermint/config" |
|
|
|
tmflags "github.com/tendermint/tendermint/libs/cli/flags" |
|
|
|
"github.com/tendermint/tendermint/libs/log" |
|
|
|
nm "github.com/tendermint/tendermint/node" |
|
|
|
"github.com/tendermint/tendermint/p2p" |
|
|
|
"github.com/tendermint/tendermint/privval" |
|
|
|
"github.com/tendermint/tendermint/proxy" |
|
|
|
) |
|
|
|
|
|
|
|
var configFile string |
|
|
|
|
|
|
|
func init() { |
|
|
|
flag.StringVar(&configFile, "config", "$HOME/.tendermint/config/config.toml", "Path to config.toml") |
|
|
|
flag.StringVar(&configFile, "config", "$HOME/.tendermint/config/config.toml", "Path to config.toml") |
|
|
|
} |
|
|
|
|
|
|
|
func main() { |
|
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger")) |
|
|
|
if err != nil { |
|
|
|
fmt.Fprintf(os.Stderr, "failed to open badger db: %v", err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
defer db.Close() |
|
|
|
app := NewKVStoreApplication(db) |
|
|
|
|
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
node, err := newTendermint(app, configFile) |
|
|
|
if err != nil { |
|
|
|
fmt.Fprintf(os.Stderr, "%v", err) |
|
|
|
os.Exit(2) |
|
|
|
} |
|
|
|
|
|
|
|
node.Start() |
|
|
|
defer func() { |
|
|
|
node.Stop() |
|
|
|
node.Wait() |
|
|
|
}() |
|
|
|
|
|
|
|
c := make(chan os.Signal, 1) |
|
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM) |
|
|
|
<-c |
|
|
|
os.Exit(0) |
|
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger")) |
|
|
|
if err != nil { |
|
|
|
fmt.Fprintf(os.Stderr, "failed to open badger db: %v", err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
defer db.Close() |
|
|
|
app := NewKVStoreApplication(db) |
|
|
|
|
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
node, err := newTendermint(app, configFile) |
|
|
|
if err != nil { |
|
|
|
fmt.Fprintf(os.Stderr, "%v", err) |
|
|
|
os.Exit(2) |
|
|
|
} |
|
|
|
|
|
|
|
node.Start() |
|
|
|
defer func() { |
|
|
|
node.Stop() |
|
|
|
node.Wait() |
|
|
|
}() |
|
|
|
|
|
|
|
c := make(chan os.Signal, 1) |
|
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM) |
|
|
|
<-c |
|
|
|
os.Exit(0) |
|
|
|
} |
|
|
|
|
|
|
|
func newTendermint(app abci.Application, configFile string) (*nm.Node, error) { |
|
|
|
// read config |
|
|
|
config := cfg.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 := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) |
|
|
|
var err error |
|
|
|
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("failed to parse log level: %w", err) |
|
|
|
} |
|
|
|
|
|
|
|
// read private validator |
|
|
|
pv := privval.LoadFilePV( |
|
|
|
config.PrivValidatorKeyFile(), |
|
|
|
config.PrivValidatorStateFile(), |
|
|
|
) |
|
|
|
|
|
|
|
// 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 := nm.NewNode( |
|
|
|
config, |
|
|
|
pv, |
|
|
|
nodeKey, |
|
|
|
proxy.NewLocalClientCreator(app), |
|
|
|
nm.DefaultGenesisDocProviderFunc(config), |
|
|
|
nm.DefaultDBProvider, |
|
|
|
nm.DefaultMetricsProvider(config.Instrumentation), |
|
|
|
logger) |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("failed to create new Tendermint node: %w", err) |
|
|
|
} |
|
|
|
|
|
|
|
return node, nil |
|
|
|
// read config |
|
|
|
config := cfg.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 := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) |
|
|
|
var err error |
|
|
|
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("failed to parse log level: %w", err) |
|
|
|
} |
|
|
|
|
|
|
|
// read private validator |
|
|
|
pv := privval.LoadFilePV( |
|
|
|
config.PrivValidatorKeyFile(), |
|
|
|
config.PrivValidatorStateFile(), |
|
|
|
) |
|
|
|
|
|
|
|
// 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 := nm.NewNode( |
|
|
|
config, |
|
|
|
pv, |
|
|
|
nodeKey, |
|
|
|
proxy.NewLocalClientCreator(app), |
|
|
|
nm.DefaultGenesisDocProviderFunc(config), |
|
|
|
nm.DefaultDBProvider, |
|
|
|
nm.DefaultMetricsProvider(config.Instrumentation), |
|
|
|
logger) |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("failed to create new Tendermint node: %w", err) |
|
|
|
} |
|
|
|
|
|
|
|
return node, nil |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -452,8 +452,8 @@ First, we initialize the Badger database and create an app instance: |
|
|
|
```go |
|
|
|
db, err := badger.Open(badger.DefaultOptions("/tmp/badger")) |
|
|
|
if err != nil { |
|
|
|
fmt.Fprintf(os.Stderr, "failed to open badger db: %v", err) |
|
|
|
os.Exit(1) |
|
|
|
fmt.Fprintf(os.Stderr, "failed to open badger db: %v", err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
defer db.Close() |
|
|
|
app := NewKVStoreApplication(db) |
|
|
@ -473,24 +473,24 @@ flag.Parse() |
|
|
|
|
|
|
|
node, err := newTendermint(app, configFile) |
|
|
|
if err != nil { |
|
|
|
fmt.Fprintf(os.Stderr, "%v", err) |
|
|
|
os.Exit(2) |
|
|
|
fmt.Fprintf(os.Stderr, "%v", err) |
|
|
|
os.Exit(2) |
|
|
|
} |
|
|
|
|
|
|
|
... |
|
|
|
|
|
|
|
// create node |
|
|
|
node, err := nm.NewNode( |
|
|
|
config, |
|
|
|
pv, |
|
|
|
nodeKey, |
|
|
|
proxy.NewLocalClientCreator(app), |
|
|
|
nm.DefaultGenesisDocProviderFunc(config), |
|
|
|
nm.DefaultDBProvider, |
|
|
|
nm.DefaultMetricsProvider(config.Instrumentation), |
|
|
|
logger) |
|
|
|
config, |
|
|
|
pv, |
|
|
|
nodeKey, |
|
|
|
proxy.NewLocalClientCreator(app), |
|
|
|
nm.DefaultGenesisDocProviderFunc(config), |
|
|
|
nm.DefaultDBProvider, |
|
|
|
nm.DefaultMetricsProvider(config.Instrumentation), |
|
|
|
logger) |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("failed to create new Tendermint node: %w", err) |
|
|
|
return nil, fmt.Errorf("failed to create new Tendermint node: %w", err) |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -508,13 +508,13 @@ config := cfg.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) |
|
|
|
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) |
|
|
|
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) |
|
|
|
return nil, fmt.Errorf("config is invalid: %w", err) |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -524,8 +524,8 @@ messages). Normally, you would use `SignerRemote` to connect to an external |
|
|
|
|
|
|
|
```go |
|
|
|
pv := privval.LoadFilePV( |
|
|
|
config.PrivValidatorKeyFile(), |
|
|
|
config.PrivValidatorStateFile(), |
|
|
|
config.PrivValidatorKeyFile(), |
|
|
|
config.PrivValidatorStateFile(), |
|
|
|
) |
|
|
|
|
|
|
|
``` |
|
|
@ -535,7 +535,7 @@ pv := privval.LoadFilePV( |
|
|
|
```go |
|
|
|
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile()) |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("failed to load node's key: %w", err) |
|
|
|
return nil, fmt.Errorf("failed to load node's key: %w", err) |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -548,7 +548,7 @@ logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) |
|
|
|
var err error |
|
|
|
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("failed to parse log level: %w", err) |
|
|
|
return nil, fmt.Errorf("failed to parse log level: %w", err) |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
@ -558,8 +558,8 @@ upon receiving SIGTERM or Ctrl-C. |
|
|
|
```go |
|
|
|
node.Start() |
|
|
|
defer func() { |
|
|
|
node.Stop() |
|
|
|
node.Wait() |
|
|
|
node.Stop() |
|
|
|
node.Wait() |
|
|
|
}() |
|
|
|
|
|
|
|
c := make(chan os.Signal, 1) |
|
|
@ -593,8 +593,8 @@ module github.com/me/example |
|
|
|
go 1.15 |
|
|
|
|
|
|
|
require ( |
|
|
|
github.com/dgraph-io/badger v1.6.2 |
|
|
|
github.com/tendermint/tendermint <vX> |
|
|
|
github.com/dgraph-io/badger v1.6.2 |
|
|
|
github.com/tendermint/tendermint <vX> |
|
|
|
) |
|
|
|
``` |
|
|
|
|
|
|
|