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

     1  package kvstore
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/json"
     7  	"fmt"
     8  
     9  	abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
    10  	abciver "github.com/gnolang/gno/tm2/pkg/bft/abci/version"
    11  	dbm "github.com/gnolang/gno/tm2/pkg/db"
    12  	"github.com/gnolang/gno/tm2/pkg/db/memdb"
    13  )
    14  
    15  var (
    16  	stateKey        = []byte("stateKey")
    17  	kvPairPrefixKey = []byte("kvPairKey:")
    18  	AppVersion      = "v0.0.0"
    19  )
    20  
    21  type State struct {
    22  	db      dbm.DB
    23  	Size    int64  `json:"size"`
    24  	Height  int64  `json:"height"`
    25  	AppHash []byte `json:"app_hash"`
    26  }
    27  
    28  func loadState(db dbm.DB) State {
    29  	stateBytes := db.Get(stateKey)
    30  	var state State
    31  	if len(stateBytes) != 0 {
    32  		err := json.Unmarshal(stateBytes, &state)
    33  		if err != nil {
    34  			panic(err)
    35  		}
    36  	}
    37  	state.db = db
    38  	return state
    39  }
    40  
    41  func saveState(state State) {
    42  	stateBytes, err := json.Marshal(state)
    43  	if err != nil {
    44  		panic(err)
    45  	}
    46  	state.db.Set(stateKey, stateBytes)
    47  }
    48  
    49  func prefixKey(key []byte) []byte {
    50  	return append(kvPairPrefixKey, key...)
    51  }
    52  
    53  //---------------------------------------------------
    54  
    55  var _ abci.Application = (*KVStoreApplication)(nil)
    56  
    57  type KVStoreApplication struct {
    58  	abci.BaseApplication
    59  
    60  	state State
    61  }
    62  
    63  func NewKVStoreApplication() *KVStoreApplication {
    64  	state := loadState(memdb.NewMemDB())
    65  	return &KVStoreApplication{state: state}
    66  }
    67  
    68  func (app *KVStoreApplication) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
    69  	return abci.ResponseInfo{
    70  		ResponseBase: abci.ResponseBase{
    71  			Data: []byte(fmt.Sprintf("{\"size\":%v}", app.state.Size)),
    72  		},
    73  		ABCIVersion: abciver.Version,
    74  		AppVersion:  AppVersion,
    75  	}
    76  }
    77  
    78  // tx is either "key=value" or just arbitrary bytes
    79  func (app *KVStoreApplication) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) {
    80  	var key, value []byte
    81  	parts := bytes.Split(req.Tx, []byte("="))
    82  	if len(parts) == 2 {
    83  		key, value = parts[0], parts[1]
    84  	} else {
    85  		key, value = req.Tx, req.Tx
    86  	}
    87  
    88  	app.state.db.Set(prefixKey(key), value)
    89  	app.state.Size += 1
    90  
    91  	events := []abci.Event{abci.EventString(`{"creator":"Cosmoshi Netowoko"}`)}
    92  
    93  	res.Events = events
    94  	return res
    95  }
    96  
    97  func (app *KVStoreApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
    98  	return abci.ResponseCheckTx{GasWanted: 1}
    99  }
   100  
   101  func (app *KVStoreApplication) Commit() (res abci.ResponseCommit) {
   102  	// Using a memdb - just return the big endian size of the db
   103  	appHash := make([]byte, 8)
   104  	binary.PutVarint(appHash, app.state.Size)
   105  	app.state.AppHash = appHash
   106  	app.state.Height += 1
   107  	saveState(app.state)
   108  
   109  	res.Data = appHash
   110  	return res
   111  }
   112  
   113  // Returns an associated value or nil if missing.
   114  func (app *KVStoreApplication) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
   115  	if reqQuery.Prove {
   116  		value := app.state.db.Get(prefixKey(reqQuery.Data))
   117  		// resQuery.Index = -1 // TODO make Proof return index
   118  		resQuery.Key = reqQuery.Data
   119  		resQuery.Value = value
   120  		if value != nil {
   121  			resQuery.Log = "exists"
   122  		} else {
   123  			resQuery.Log = "does not exist"
   124  		}
   125  		return
   126  	} else {
   127  		resQuery.Key = reqQuery.Data
   128  		value := app.state.db.Get(prefixKey(reqQuery.Data))
   129  		resQuery.Value = value
   130  		if value != nil {
   131  			resQuery.Log = "exists"
   132  		} else {
   133  			resQuery.Log = "does not exist"
   134  		}
   135  		return
   136  	}
   137  }
   138  
   139  func (app *KVStoreApplication) Close() error {
   140  	app.state.db.Close()
   141  	return nil
   142  }