github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/abci/example/kvstore/persistent_kvstore.go (about)

     1  package kvstore
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/tendermint/tendermint/abci/types"
    11  	pc "github.com/tendermint/tendermint/proto/tendermint/crypto"
    12  	dbm "github.com/tendermint/tm-db"
    13  
    14  	"github.com/line/ostracon/abci/example/code"
    15  	ocabci "github.com/line/ostracon/abci/types"
    16  	cryptoenc "github.com/line/ostracon/crypto/encoding"
    17  	"github.com/line/ostracon/libs/log"
    18  )
    19  
    20  const (
    21  	ValidatorSetChangePrefix string = "val:"
    22  )
    23  
    24  //-----------------------------------------
    25  
    26  var _ ocabci.Application = (*PersistentKVStoreApplication)(nil)
    27  
    28  type PersistentKVStoreApplication struct {
    29  	app *Application
    30  
    31  	// validator set
    32  	ValUpdates []types.ValidatorUpdate
    33  
    34  	valAddrToPubKeyMap map[string]pc.PublicKey
    35  
    36  	logger log.Logger
    37  }
    38  
    39  func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication {
    40  	name := "kvstore"
    41  	db, err := dbm.NewGoLevelDB(name, dbDir)
    42  	if err != nil {
    43  		panic(err)
    44  	}
    45  
    46  	state := loadState(db)
    47  
    48  	return &PersistentKVStoreApplication{
    49  		app:                &Application{state: state},
    50  		valAddrToPubKeyMap: make(map[string]pc.PublicKey),
    51  		logger:             log.NewNopLogger(),
    52  	}
    53  }
    54  
    55  func (app *PersistentKVStoreApplication) SetLogger(l log.Logger) {
    56  	app.logger = l
    57  }
    58  
    59  func (app *PersistentKVStoreApplication) Info(req types.RequestInfo) types.ResponseInfo {
    60  	res := app.app.Info(req)
    61  	res.LastBlockHeight = app.app.state.Height
    62  	res.LastBlockAppHash = app.app.state.AppHash
    63  	return res
    64  }
    65  
    66  func (app *PersistentKVStoreApplication) SetOption(req types.RequestSetOption) types.ResponseSetOption {
    67  	return app.app.SetOption(req)
    68  }
    69  
    70  // tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes
    71  func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
    72  	// if it starts with "val:", update the validator set
    73  	// format is "val:pubkey!power"
    74  	if isValidatorTx(req.Tx) {
    75  		// update validators in the merkle tree
    76  		// and in app.ValUpdates
    77  		return app.execValidatorTx(req.Tx)
    78  	}
    79  
    80  	// otherwise, update the key-value store
    81  	return app.app.DeliverTx(req)
    82  }
    83  
    84  func (app *PersistentKVStoreApplication) CheckTxSync(req types.RequestCheckTx) ocabci.ResponseCheckTx {
    85  	return app.app.CheckTxSync(req)
    86  }
    87  
    88  func (app *PersistentKVStoreApplication) CheckTxAsync(req types.RequestCheckTx, callback ocabci.CheckTxCallback) {
    89  	app.app.CheckTxAsync(req, callback)
    90  }
    91  
    92  func (app *PersistentKVStoreApplication) BeginRecheckTx(req ocabci.RequestBeginRecheckTx) ocabci.ResponseBeginRecheckTx {
    93  	return app.app.BeginRecheckTx(req)
    94  }
    95  
    96  func (app *PersistentKVStoreApplication) EndRecheckTx(req ocabci.RequestEndRecheckTx) ocabci.ResponseEndRecheckTx {
    97  	return app.app.EndRecheckTx(req)
    98  }
    99  
   100  // Commit will panic if InitChain was not called
   101  func (app *PersistentKVStoreApplication) Commit() types.ResponseCommit {
   102  	return app.app.Commit()
   103  }
   104  
   105  // When path=/val and data={validator address}, returns the validator update (types.ValidatorUpdate) varint encoded.
   106  // For any other path, returns an associated value or nil if missing.
   107  func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
   108  	switch reqQuery.Path {
   109  	case "/val":
   110  		key := []byte("val:" + string(reqQuery.Data))
   111  		value, err := app.app.state.db.Get(key)
   112  		if err != nil {
   113  			panic(err)
   114  		}
   115  
   116  		resQuery.Key = reqQuery.Data
   117  		resQuery.Value = value
   118  		return
   119  	default:
   120  		return app.app.Query(reqQuery)
   121  	}
   122  }
   123  
   124  // Save the validators in the merkle tree
   125  func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain {
   126  	for _, v := range req.Validators {
   127  		r := app.updateValidator(v)
   128  		if r.IsErr() {
   129  			app.logger.Error("Error updating validators", "r", r)
   130  		}
   131  	}
   132  	return types.ResponseInitChain{}
   133  }
   134  
   135  // Track the block hash and header information
   136  func (app *PersistentKVStoreApplication) BeginBlock(req ocabci.RequestBeginBlock) types.ResponseBeginBlock {
   137  	// reset valset changes
   138  	app.ValUpdates = make([]types.ValidatorUpdate, 0)
   139  
   140  	// Punish validators who committed equivocation.
   141  	for _, ev := range req.ByzantineValidators {
   142  		if ev.Type == types.EvidenceType_DUPLICATE_VOTE {
   143  			addr := string(ev.Validator.Address)
   144  			if pubKey, ok := app.valAddrToPubKeyMap[addr]; ok {
   145  				app.updateValidator(types.ValidatorUpdate{
   146  					PubKey: pubKey,
   147  					Power:  ev.Validator.Power - 1,
   148  				})
   149  				app.logger.Info("Decreased val power by 1 because of the equivocation",
   150  					"val", addr)
   151  			} else {
   152  				app.logger.Error("Wanted to punish val, but can't find it",
   153  					"val", addr)
   154  			}
   155  		}
   156  	}
   157  
   158  	return types.ResponseBeginBlock{}
   159  }
   160  
   161  // Update the validator set
   162  func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
   163  	return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
   164  }
   165  
   166  func (app *PersistentKVStoreApplication) ListSnapshots(
   167  	req types.RequestListSnapshots) types.ResponseListSnapshots {
   168  	return types.ResponseListSnapshots{}
   169  }
   170  
   171  func (app *PersistentKVStoreApplication) LoadSnapshotChunk(
   172  	req types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk {
   173  	return types.ResponseLoadSnapshotChunk{}
   174  }
   175  
   176  func (app *PersistentKVStoreApplication) OfferSnapshot(
   177  	req types.RequestOfferSnapshot) types.ResponseOfferSnapshot {
   178  	return types.ResponseOfferSnapshot{Result: types.ResponseOfferSnapshot_ABORT}
   179  }
   180  
   181  func (app *PersistentKVStoreApplication) ApplySnapshotChunk(
   182  	req types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk {
   183  	return types.ResponseApplySnapshotChunk{Result: types.ResponseApplySnapshotChunk_ABORT}
   184  }
   185  
   186  //---------------------------------------------
   187  // update validators
   188  
   189  func (app *PersistentKVStoreApplication) Validators() (validators []types.ValidatorUpdate) {
   190  	itr, err := app.app.state.db.Iterator(nil, nil)
   191  	if err != nil {
   192  		panic(err)
   193  	}
   194  	for ; itr.Valid(); itr.Next() {
   195  		if isValidatorTx(itr.Key()) {
   196  			validator := new(types.ValidatorUpdate)
   197  			err := ocabci.ReadMessage(bytes.NewBuffer(itr.Value()), validator)
   198  			if err != nil {
   199  				panic(err)
   200  			}
   201  			validators = append(validators, *validator)
   202  		}
   203  	}
   204  	if err = itr.Error(); err != nil {
   205  		panic(err)
   206  	}
   207  	return
   208  }
   209  
   210  func MakeValSetChangeTx(pubkey pc.PublicKey, power int64) []byte {
   211  	_, tx := MakeValSetChangeTxAndMore(pubkey, power)
   212  	return []byte(tx)
   213  }
   214  
   215  func MakeValSetChangeTxAndMore(pubkey pc.PublicKey, power int64) (string, string) {
   216  	pkBytes, err := pubkey.Marshal()
   217  	if err != nil {
   218  		panic(err)
   219  	}
   220  	pubStr := base64.StdEncoding.EncodeToString(pkBytes)
   221  	return pubStr, fmt.Sprintf("val:%s!%d", pubStr, power)
   222  }
   223  
   224  func isValidatorTx(tx []byte) bool {
   225  	return strings.HasPrefix(string(tx), ValidatorSetChangePrefix)
   226  }
   227  
   228  // format is "val:pubkey!power"
   229  // pubkey is a base64-encoded proto.ostracon.crypto.PublicKey bytes
   230  // See MakeValSetChangeTx
   231  func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx {
   232  	tx = tx[len(ValidatorSetChangePrefix):]
   233  
   234  	// get the pubkey and power
   235  	pubKeyAndPower := strings.Split(string(tx), "!")
   236  	if len(pubKeyAndPower) != 2 {
   237  		return types.ResponseDeliverTx{
   238  			Code: code.CodeTypeEncodingError,
   239  			Log:  fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower)}
   240  	}
   241  	pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1]
   242  
   243  	// decode the pubkey
   244  	pkBytes, err := base64.StdEncoding.DecodeString(pubkeyS)
   245  	if err != nil {
   246  		return types.ResponseDeliverTx{
   247  			Code: code.CodeTypeEncodingError,
   248  			Log:  fmt.Sprintf("pubkeyS (%s) is invalid base64", pubkeyS)}
   249  	}
   250  	var pkProto pc.PublicKey
   251  	err = pkProto.Unmarshal(pkBytes)
   252  	if err != nil {
   253  		return types.ResponseDeliverTx{
   254  			Code: code.CodeTypeEncodingError,
   255  			Log:  fmt.Sprintf("pkBytes (%x) is invalid binary", pkBytes)}
   256  	}
   257  	pubkey, err := cryptoenc.PubKeyFromProto(&pkProto)
   258  	if err != nil {
   259  		return types.ResponseDeliverTx{
   260  			Code: code.CodeTypeEncodingError,
   261  			Log:  fmt.Sprintf("pkProto (%s) is invalid binary", pkProto)}
   262  	}
   263  
   264  	// decode the power
   265  	power, err := strconv.ParseInt(powerS, 10, 64)
   266  	if err != nil {
   267  		return types.ResponseDeliverTx{
   268  			Code: code.CodeTypeEncodingError,
   269  			Log:  fmt.Sprintf("Power (%s) is not an int", powerS)}
   270  	}
   271  
   272  	// update
   273  	return app.updateValidator(ocabci.NewValidatorUpdate(pubkey, power))
   274  }
   275  
   276  // add, update, or remove a validator
   277  // See MakeValSetChangeTx
   278  func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx {
   279  	pubkey, err := cryptoenc.PubKeyFromProto(&v.PubKey)
   280  	if err != nil {
   281  		return types.ResponseDeliverTx{
   282  			Code: code.CodeTypeEncodingError,
   283  			Log:  fmt.Sprintf("Error encoding Public Key: %s", err)}
   284  	}
   285  	pubStr, _ := MakeValSetChangeTxAndMore(v.PubKey, v.Power)
   286  	key := []byte("val:" + pubStr)
   287  
   288  	if v.Power == 0 {
   289  		// remove validator
   290  		hasKey, err := app.app.state.db.Has(key)
   291  		if err != nil {
   292  			panic(err)
   293  		}
   294  		if !hasKey {
   295  			return types.ResponseDeliverTx{
   296  				Code: code.CodeTypeUnauthorized,
   297  				Log:  fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)}
   298  		}
   299  		if err = app.app.state.db.Delete(key); err != nil {
   300  			panic(err)
   301  		}
   302  		delete(app.valAddrToPubKeyMap, string(pubkey.Address()))
   303  	} else {
   304  		// add or update validator
   305  		value := bytes.NewBuffer(make([]byte, 0))
   306  		if err := types.WriteMessage(&v, value); err != nil {
   307  			return types.ResponseDeliverTx{
   308  				Code: code.CodeTypeEncodingError,
   309  				Log:  fmt.Sprintf("Error encoding validator: %v", err)}
   310  		}
   311  		if err = app.app.state.db.Set(key, value.Bytes()); err != nil {
   312  			panic(err)
   313  		}
   314  		app.valAddrToPubKeyMap[string(pubkey.Address())] = v.PubKey
   315  	}
   316  
   317  	// we only update the changes array if we successfully updated the tree
   318  	app.ValUpdates = append(app.ValUpdates, v)
   319  
   320  	return types.ResponseDeliverTx{Code: code.CodeTypeOK}
   321  }