mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-10 05:50:19 +00:00
The code in the Tendermint repository makes heavy use of import aliasing. This is made necessary by our extensive reuse of common base package names, and by repetition of similar names across different subdirectories. Unfortunately we have not been very consistent about which packages we alias in various circumstances, and the aliases we use vary. In the spirit of the advice in the style guide and https://github.com/golang/go/wiki/CodeReviewComments#imports, his change makes an effort to clean up and normalize import aliasing. This change makes no API or behavioral changes. It is a pure cleanup intended o help make the code more readable to developers (including myself) trying to understand what is being imported where. Only unexported names have been modified, and the changes were generated and applied mechanically with gofmt -r and comby, respecting the lexical and syntactic rules of Go. Even so, I did not fix every inconsistency. Where the changes would be too disruptive, I left it alone. The principles I followed in this cleanup are: - Remove aliases that restate the package name. - Remove aliases where the base package name is unambiguous. - Move overly-terse abbreviations from the import to the usage site. - Fix lexical issues (remove underscores, remove capitalization). - Fix import groupings to more closely match the style guide. - Group blank (side-effecting) imports and ensure they are commented. - Add aliases to multiple imports with the same base package name.
287 lines
8.2 KiB
Go
287 lines
8.2 KiB
Go
package kvstore
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
dbm "github.com/tendermint/tm-db"
|
|
|
|
"github.com/tendermint/tendermint/abci/example/code"
|
|
"github.com/tendermint/tendermint/abci/types"
|
|
"github.com/tendermint/tendermint/crypto/encoding"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
cryptoproto "github.com/tendermint/tendermint/proto/tendermint/crypto"
|
|
)
|
|
|
|
const (
|
|
ValidatorSetChangePrefix string = "val:"
|
|
)
|
|
|
|
//-----------------------------------------
|
|
|
|
var _ types.Application = (*PersistentKVStoreApplication)(nil)
|
|
|
|
type PersistentKVStoreApplication struct {
|
|
app *Application
|
|
|
|
// validator set
|
|
ValUpdates []types.ValidatorUpdate
|
|
|
|
valAddrToPubKeyMap map[string]cryptoproto.PublicKey
|
|
|
|
logger log.Logger
|
|
}
|
|
|
|
func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication {
|
|
name := "kvstore"
|
|
db, err := dbm.NewGoLevelDB(name, dbDir)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
state := loadState(db)
|
|
|
|
return &PersistentKVStoreApplication{
|
|
app: &Application{state: state},
|
|
valAddrToPubKeyMap: make(map[string]cryptoproto.PublicKey),
|
|
logger: log.NewNopLogger(),
|
|
}
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) Close() error {
|
|
return app.app.state.db.Close()
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) SetLogger(l log.Logger) {
|
|
app.logger = l
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) Info(req types.RequestInfo) types.ResponseInfo {
|
|
res := app.app.Info(req)
|
|
res.LastBlockHeight = app.app.state.Height
|
|
res.LastBlockAppHash = app.app.state.AppHash
|
|
return res
|
|
}
|
|
|
|
// tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes
|
|
func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
|
|
// if it starts with "val:", update the validator set
|
|
// format is "val:pubkey!power"
|
|
if isValidatorTx(req.Tx) {
|
|
// update validators in the merkle tree
|
|
// and in app.ValUpdates
|
|
return app.execValidatorTx(req.Tx)
|
|
}
|
|
|
|
// otherwise, update the key-value store
|
|
return app.app.DeliverTx(req)
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
|
|
return app.app.CheckTx(req)
|
|
}
|
|
|
|
// Commit will panic if InitChain was not called
|
|
func (app *PersistentKVStoreApplication) Commit() types.ResponseCommit {
|
|
return app.app.Commit()
|
|
}
|
|
|
|
// When path=/val and data={validator address}, returns the validator update (types.ValidatorUpdate) varint encoded.
|
|
// For any other path, returns an associated value or nil if missing.
|
|
func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
|
|
switch reqQuery.Path {
|
|
case "/val":
|
|
key := []byte("val:" + string(reqQuery.Data))
|
|
value, err := app.app.state.db.Get(key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
resQuery.Key = reqQuery.Data
|
|
resQuery.Value = value
|
|
return
|
|
default:
|
|
return app.app.Query(reqQuery)
|
|
}
|
|
}
|
|
|
|
// Save the validators in the merkle tree
|
|
func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain {
|
|
for _, v := range req.Validators {
|
|
r := app.updateValidator(v)
|
|
if r.IsErr() {
|
|
app.logger.Error("Error updating validators", "r", r)
|
|
}
|
|
}
|
|
return types.ResponseInitChain{}
|
|
}
|
|
|
|
// Track the block hash and header information
|
|
func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
|
|
// reset valset changes
|
|
app.ValUpdates = make([]types.ValidatorUpdate, 0)
|
|
|
|
// Punish validators who committed equivocation.
|
|
for _, ev := range req.ByzantineValidators {
|
|
if ev.Type == types.EvidenceType_DUPLICATE_VOTE {
|
|
addr := string(ev.Validator.Address)
|
|
if pubKey, ok := app.valAddrToPubKeyMap[addr]; ok {
|
|
app.updateValidator(types.ValidatorUpdate{
|
|
PubKey: pubKey,
|
|
Power: ev.Validator.Power - 1,
|
|
})
|
|
app.logger.Info("Decreased val power by 1 because of the equivocation",
|
|
"val", addr)
|
|
} else {
|
|
app.logger.Error("Wanted to punish val, but can't find it",
|
|
"val", addr)
|
|
}
|
|
}
|
|
}
|
|
|
|
return types.ResponseBeginBlock{}
|
|
}
|
|
|
|
// Update the validator set
|
|
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
|
|
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) ListSnapshots(
|
|
req types.RequestListSnapshots) types.ResponseListSnapshots {
|
|
return types.ResponseListSnapshots{}
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) LoadSnapshotChunk(
|
|
req types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk {
|
|
return types.ResponseLoadSnapshotChunk{}
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) OfferSnapshot(
|
|
req types.RequestOfferSnapshot) types.ResponseOfferSnapshot {
|
|
return types.ResponseOfferSnapshot{Result: types.ResponseOfferSnapshot_ABORT}
|
|
}
|
|
|
|
func (app *PersistentKVStoreApplication) ApplySnapshotChunk(
|
|
req types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk {
|
|
return types.ResponseApplySnapshotChunk{Result: types.ResponseApplySnapshotChunk_ABORT}
|
|
}
|
|
|
|
//---------------------------------------------
|
|
// update validators
|
|
|
|
func (app *PersistentKVStoreApplication) Validators() (validators []types.ValidatorUpdate) {
|
|
itr, err := app.app.state.db.Iterator(nil, nil)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
if err = itr.Error(); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
func MakeValSetChangeTx(pubkey cryptoproto.PublicKey, power int64) []byte {
|
|
pk, err := encoding.PubKeyFromProto(pubkey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
pubStr := base64.StdEncoding.EncodeToString(pk.Bytes())
|
|
return []byte(fmt.Sprintf("val:%s!%d", pubStr, power))
|
|
}
|
|
|
|
func isValidatorTx(tx []byte) bool {
|
|
return strings.HasPrefix(string(tx), ValidatorSetChangePrefix)
|
|
}
|
|
|
|
// format is "val:pubkey!power"
|
|
// pubkey is a base64-encoded 32-byte ed25519 key
|
|
func (app *PersistentKVStoreApplication) 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 := base64.StdEncoding.DecodeString(pubkeyS)
|
|
if err != nil {
|
|
return types.ResponseDeliverTx{
|
|
Code: code.CodeTypeEncodingError,
|
|
Log: fmt.Sprintf("Pubkey (%s) is invalid base64", 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 app.updateValidator(types.UpdateValidator(pubkey, power, ""))
|
|
}
|
|
|
|
// add, update, or remove a validator
|
|
func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx {
|
|
pubkey, err := encoding.PubKeyFromProto(v.PubKey)
|
|
if err != nil {
|
|
panic(fmt.Errorf("can't decode public key: %w", err))
|
|
}
|
|
key := []byte("val:" + string(pubkey.Bytes()))
|
|
|
|
if v.Power == 0 {
|
|
// remove validator
|
|
hasKey, err := app.app.state.db.Has(key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if !hasKey {
|
|
pubStr := base64.StdEncoding.EncodeToString(pubkey.Bytes())
|
|
return types.ResponseDeliverTx{
|
|
Code: code.CodeTypeUnauthorized,
|
|
Log: fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)}
|
|
}
|
|
if err = app.app.state.db.Delete(key); err != nil {
|
|
panic(err)
|
|
}
|
|
delete(app.valAddrToPubKeyMap, string(pubkey.Address()))
|
|
} else {
|
|
// add or update validator
|
|
value := bytes.NewBuffer(make([]byte, 0))
|
|
if err := types.WriteMessage(&v, value); err != nil {
|
|
return types.ResponseDeliverTx{
|
|
Code: code.CodeTypeEncodingError,
|
|
Log: fmt.Sprintf("Error encoding validator: %v", err)}
|
|
}
|
|
if err = app.app.state.db.Set(key, value.Bytes()); err != nil {
|
|
panic(err)
|
|
}
|
|
app.valAddrToPubKeyMap[string(pubkey.Address())] = v.PubKey
|
|
}
|
|
|
|
// we only update the changes array if we successfully updated the tree
|
|
app.ValUpdates = append(app.ValUpdates, v)
|
|
|
|
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
|
|
}
|