github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/abci/example/kvstore/persistent_kvstore.go (about)

     1  package kvstore
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"log/slog"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/gnolang/gno/tm2/pkg/amino"
    11  	"github.com/gnolang/gno/tm2/pkg/bft/abci/example/errors"
    12  	abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
    13  	"github.com/gnolang/gno/tm2/pkg/crypto"
    14  	"github.com/gnolang/gno/tm2/pkg/db"
    15  	_ "github.com/gnolang/gno/tm2/pkg/db/goleveldb"
    16  	"github.com/gnolang/gno/tm2/pkg/log"
    17  )
    18  
    19  const (
    20  	ValidatorUpdatePrefix string = "val:"
    21  	ValidatorKeyPrefix    string = "/val/"
    22  )
    23  
    24  const dbBackend = db.GoLevelDBBackend
    25  
    26  // -----------------------------------------
    27  
    28  var _ abci.Application = (*PersistentKVStoreApplication)(nil)
    29  
    30  type PersistentKVStoreApplication struct {
    31  	app *KVStoreApplication
    32  
    33  	// validator set
    34  	ValSetChanges []abci.ValidatorUpdate
    35  
    36  	logger *slog.Logger
    37  }
    38  
    39  func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication {
    40  	name := "kvstore"
    41  	db, err := db.NewDB(name, dbBackend, dbDir)
    42  	if err != nil {
    43  		panic(err)
    44  	}
    45  
    46  	state := loadState(db)
    47  
    48  	return &PersistentKVStoreApplication{
    49  		app:    &KVStoreApplication{state: state},
    50  		logger: log.NewNoopLogger(),
    51  	}
    52  }
    53  
    54  func (app *PersistentKVStoreApplication) SetLogger(l *slog.Logger) {
    55  	app.logger = l
    56  }
    57  
    58  func (app *PersistentKVStoreApplication) Info(req abci.RequestInfo) abci.ResponseInfo {
    59  	res := app.app.Info(req)
    60  	res.LastBlockHeight = app.app.state.Height
    61  	res.LastBlockAppHash = app.app.state.AppHash
    62  	return res
    63  }
    64  
    65  func (app *PersistentKVStoreApplication) SetOption(req abci.RequestSetOption) abci.ResponseSetOption {
    66  	return app.app.SetOption(req)
    67  }
    68  
    69  // tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes
    70  func (app *PersistentKVStoreApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
    71  	// if it starts with "val:", update the validator set
    72  	// format is "val:pubkey!power"
    73  	if isValidatorTx(req.Tx) {
    74  		// update validators in the merkle tree
    75  		// and in app.ValSetChanges
    76  		return app.execValidatorTx(req.Tx)
    77  	}
    78  
    79  	// otherwise, update the key-value store
    80  	return app.app.DeliverTx(req)
    81  }
    82  
    83  func (app *PersistentKVStoreApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
    84  	return app.app.CheckTx(req)
    85  }
    86  
    87  // Commit will panic if InitChain was not called
    88  func (app *PersistentKVStoreApplication) Commit() abci.ResponseCommit {
    89  	return app.app.Commit()
    90  }
    91  
    92  // When path=/val and data={validator address}, returns the validator update (abci.ValidatorUpdate) varint encoded.
    93  // For any other path, returns an associated value or nil if missing.
    94  func (app *PersistentKVStoreApplication) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
    95  	switch reqQuery.Path {
    96  	case "/val":
    97  		key := []byte(ValidatorUpdatePrefix + string(reqQuery.Data))
    98  		value := app.app.state.db.Get(key)
    99  
   100  		resQuery.Key = reqQuery.Data
   101  		resQuery.Value = value
   102  		return
   103  	default:
   104  		return app.app.Query(reqQuery)
   105  	}
   106  }
   107  
   108  // Save the validators in the merkle tree
   109  func (app *PersistentKVStoreApplication) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
   110  	for _, v := range req.Validators {
   111  		r := app.updateValidator(v)
   112  		if r.IsErr() {
   113  			app.logger.Error("Error updating validators", "r", r)
   114  		}
   115  	}
   116  	return abci.ResponseInitChain{}
   117  }
   118  
   119  // Track the block hash and header information
   120  func (app *PersistentKVStoreApplication) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
   121  	// reset valset changes
   122  	app.ValSetChanges = make([]abci.ValidatorUpdate, 0)
   123  
   124  	/* REMOVE
   125  	for _, vio := range req.Violations {
   126  		if _, ok := vio.Evidence.(*tmtypes.DuplicateVoteEvidence); ok {
   127  			for _, val := range vio.Validators {
   128  				// decrease voting power of each by 1
   129  				if val.Power == 0 {
   130  					continue
   131  				}
   132  				app.updateValidator(abci.ValidatorUpdate{
   133  					Address: val.PubKey.Address(),
   134  					PubKey:  val.PubKey,
   135  					Power:   val.Power - 1,
   136  				})
   137  			}
   138  		}
   139  	}
   140  	*/
   141  	return abci.ResponseBeginBlock{}
   142  }
   143  
   144  // Update the validator set
   145  func (app *PersistentKVStoreApplication) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
   146  	return abci.ResponseEndBlock{ValidatorUpdates: app.ValSetChanges}
   147  }
   148  
   149  // ---------------------------------------------
   150  // update validators
   151  
   152  func (app *PersistentKVStoreApplication) Validators() (validators []abci.ValidatorUpdate) {
   153  	itr := app.app.state.db.Iterator(nil, nil)
   154  	for ; itr.Valid(); itr.Next() {
   155  		if isValidatorKey(itr.Key()) {
   156  			validator := new(abci.ValidatorUpdate)
   157  			amino.MustUnmarshal(itr.Value(), validator)
   158  			validators = append(validators, *validator)
   159  		}
   160  	}
   161  	return
   162  }
   163  
   164  func makeValidatorKey(val abci.ValidatorUpdate) []byte {
   165  	return []byte(fmt.Sprintf("%s%X", ValidatorKeyPrefix, val.PubKey.Address()))
   166  }
   167  
   168  func isValidatorKey(tx []byte) bool {
   169  	return strings.HasPrefix(string(tx), ValidatorKeyPrefix)
   170  }
   171  
   172  func MakeValSetChangeTx(pubkey crypto.PubKey, power int64) []byte {
   173  	pubkeyS := base64.StdEncoding.EncodeToString(pubkey.Bytes())
   174  	return []byte(fmt.Sprintf("%s%s!%d", ValidatorUpdatePrefix, pubkeyS, power))
   175  }
   176  
   177  func isValidatorTx(tx []byte) bool {
   178  	return strings.HasPrefix(string(tx), ValidatorUpdatePrefix)
   179  }
   180  
   181  // format is "val:pubkey!power"
   182  // pubkey is a base64-encoded 32-byte ed25519 key
   183  func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) (res abci.ResponseDeliverTx) {
   184  	tx = tx[len(ValidatorUpdatePrefix):]
   185  
   186  	// get the pubkey and power
   187  	pubKeyAndPower := strings.Split(string(tx), "!")
   188  	if len(pubKeyAndPower) != 2 {
   189  		res.Error = errors.EncodingError{}
   190  		res.Log = fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower)
   191  		return
   192  	}
   193  	pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1]
   194  
   195  	// decode the pubkey
   196  	bz, err := base64.StdEncoding.DecodeString(pubkeyS)
   197  	if err != nil {
   198  		res.Error = errors.EncodingError{}
   199  		res.Log = fmt.Sprintf("Pubkey (%s) is invalid base64", pubkeyS)
   200  		return
   201  	}
   202  	var pubkey crypto.PubKey
   203  	amino.MustUnmarshal(bz, &pubkey)
   204  
   205  	// decode the power
   206  	power, err := strconv.ParseInt(powerS, 10, 64)
   207  	if err != nil {
   208  		res.Error = errors.EncodingError{}
   209  		res.Log = fmt.Sprintf("Power (%s) is not an int", powerS)
   210  		return
   211  	}
   212  
   213  	// update
   214  	return app.updateValidator(abci.ValidatorUpdate{pubkey.Address(), pubkey, power})
   215  }
   216  
   217  // add, update, or remove a validator
   218  func (app *PersistentKVStoreApplication) updateValidator(val abci.ValidatorUpdate) (res abci.ResponseDeliverTx) {
   219  	if val.Power == 0 {
   220  		// remove validator
   221  		if !app.app.state.db.Has(makeValidatorKey(val)) {
   222  			res.Error = errors.UnauthorizedError{}
   223  			res.Log = fmt.Sprintf("Cannot remove non-existent validator %s", val.PubKey.String())
   224  			return res
   225  		}
   226  		app.app.state.db.Delete(makeValidatorKey(val))
   227  	} else {
   228  		// add or update validator
   229  		bz := amino.MustMarshal(val)
   230  		app.app.state.db.Set(makeValidatorKey(val), bz)
   231  	}
   232  
   233  	// we only update the changes array if we successfully updated the tree
   234  	app.ValSetChanges = append(app.ValSetChanges, val)
   235  
   236  	return abci.ResponseDeliverTx{}
   237  }
   238  
   239  func (app *PersistentKVStoreApplication) Close() error {
   240  	return app.app.Close()
   241  }