merge
This commit is contained in:
commit
98c19516c5
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -7,9 +7,17 @@
|
|||
BREAKING CHANGES
|
||||
* [x/stake] Specify DelegatorAddress in MsgCreateValidator
|
||||
* [x/auth] NewAccountMapper takes a constructor instead of a prototype
|
||||
* [keys] Keybase.Update function now takes in a function to get the newpass, rather than the password itself
|
||||
|
||||
FEATURES
|
||||
* [baseapp] NewBaseApp now takes option functions as parameters
|
||||
* [store] Added support for tracing multi-store operations via `--trace-store`
|
||||
* [store] Pruning strategy configurable with pruning flag on gaiad start
|
||||
|
||||
BUG FIXES
|
||||
* \#1630 - redelegation nolonger removes tokens from the delegator liquid account
|
||||
* [keys] \#1629 - updating password no longer asks for a new password when the first entered password was incorrect
|
||||
* [lcd] importing an account would create a random account
|
||||
|
||||
## 0.20.0
|
||||
|
||||
|
@ -58,6 +66,8 @@ BREAKING CHANGES
|
|||
* [lcd] Switch key creation output to return bech32
|
||||
* [lcd] Removed shorthand CLI flags (`a`, `c`, `n`, `o`)
|
||||
* [gaiad] genesis transactions now use bech32 addresses / pubkeys
|
||||
* [gov] VoteStatus renamed to ProposalStatus
|
||||
* [gov] VoteOption, ProposalType, and ProposalStatus all marshal to string form in JSON
|
||||
|
||||
DEPRECATED
|
||||
* [cli] Deprecated `--name` flag in commands that send txs, in favor of `--from`
|
||||
|
@ -101,6 +111,7 @@ FEATURES
|
|||
- Auth has its invariants checked within the framework
|
||||
* [tests] Add WaitForNextNBlocksTM helper method
|
||||
* [keys] New keys now have 24 word recovery keys, for heightened security
|
||||
- [keys] Add a temporary method for exporting the private key
|
||||
|
||||
IMPROVEMENTS
|
||||
* [x/bank] Now uses go-wire codec instead of 'encoding/json'
|
||||
|
@ -110,6 +121,7 @@ IMPROVEMENTS
|
|||
* [stake] keeper always loads the store (instead passing around which doesn't really boost efficiency)
|
||||
* [stake] edit-validator changes now can use the keyword [do-not-modify] to not modify unspecified `--flag` (aka won't set them to `""` value)
|
||||
* [stake] offload more generic functionality from the handler into the keeper
|
||||
* [stake] clearer staking logic
|
||||
* [types] added common tag constants
|
||||
* [keys] improve error message when deleting non-existent key
|
||||
* [gaiacli] improve error messages on `send` and `account` commands
|
||||
|
|
|
@ -2,6 +2,7 @@ package baseapp
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
@ -40,7 +42,7 @@ const (
|
|||
runTxModeDeliver runTxMode = iota
|
||||
)
|
||||
|
||||
// The ABCI application
|
||||
// BaseApp reflects the ABCI application implementation.
|
||||
type BaseApp struct {
|
||||
// initialized on creation
|
||||
Logger log.Logger
|
||||
|
@ -74,7 +76,12 @@ type BaseApp struct {
|
|||
|
||||
var _ abci.Application = (*BaseApp)(nil)
|
||||
|
||||
// Create and name new BaseApp
|
||||
// NewBaseApp returns a reference to an initialized BaseApp.
|
||||
//
|
||||
// TODO: Determine how to use a flexible and robust configuration paradigm that
|
||||
// allows for sensible defaults while being highly configurable
|
||||
// (e.g. functional options).
|
||||
//
|
||||
// NOTE: The db is used to store the version number for now.
|
||||
// Accepts variable number of option functions, which act on the BaseApp to set configuration choices
|
||||
// DEPRECATED
|
||||
|
@ -110,7 +117,9 @@ func NewBaseAppNoCodec(name string, logger log.Logger, db dbm.DB, txDecoder sdk.
|
|||
codespacer: sdk.NewCodespacer(),
|
||||
txDecoder: txDecoder,
|
||||
}
|
||||
// Register the undefined & root codespaces, which should not be used by any modules
|
||||
|
||||
// Register the undefined & root codespaces, which should not be used by
|
||||
// any modules.
|
||||
app.codespacer.RegisterOrPanic(sdk.CodespaceRoot)
|
||||
for _, option := range options {
|
||||
option(app)
|
||||
|
@ -123,6 +132,12 @@ func (app *BaseApp) Name() string {
|
|||
return app.name
|
||||
}
|
||||
|
||||
// SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying
|
||||
// CommitMultiStore.
|
||||
func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) {
|
||||
app.cms.WithTracer(w)
|
||||
}
|
||||
|
||||
// Register the next available codespace through the baseapp's codespacer, starting from a default
|
||||
func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.CodespaceType {
|
||||
return app.codespacer.RegisterNext(codespace)
|
||||
|
@ -407,13 +422,18 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc
|
|||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
|
||||
// Implements ABCI
|
||||
// BeginBlock implements the ABCI application interface.
|
||||
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
|
||||
// Initialize the DeliverTx state.
|
||||
// If this is the first block, it should already
|
||||
// be initialized in InitChain.
|
||||
// Otherwise app.deliverState will be nil, since it
|
||||
// is reset on Commit.
|
||||
if app.cms.TracingEnabled() {
|
||||
app.cms.ResetTraceContext()
|
||||
app.cms.WithTracingContext(sdk.TraceContext(
|
||||
map[string]interface{}{"blockHeight": req.Header.Height},
|
||||
))
|
||||
}
|
||||
|
||||
// Initialize the DeliverTx state. If this is the first block, it should
|
||||
// already be initialized in InitChain. Otherwise app.deliverState will be
|
||||
// nil, since it is reset on Commit.
|
||||
if app.deliverState == nil {
|
||||
app.setDeliverState(req.Header)
|
||||
} else {
|
||||
|
@ -421,9 +441,11 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
|
|||
// by InitChain. Context is now updated with Header information.
|
||||
app.deliverState.ctx = app.deliverState.ctx.WithBlockHeader(req.Header)
|
||||
}
|
||||
|
||||
if app.beginBlocker != nil {
|
||||
res = app.beginBlocker(app.deliverState.ctx, req)
|
||||
}
|
||||
|
||||
// set the signed validators for addition to context in deliverTx
|
||||
app.signedValidators = req.Validators
|
||||
return
|
||||
|
@ -563,25 +585,26 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg) (result sdk.Result)
|
|||
return result
|
||||
}
|
||||
|
||||
// Returns deliverState if app is in runTxModeDeliver, otherwhise returns checkstate
|
||||
// Returns the applicantion's deliverState if app is in runTxModeDeliver,
|
||||
// otherwise it returns the application's checkstate.
|
||||
func getState(app *BaseApp, mode runTxMode) *state {
|
||||
if mode == runTxModeCheck || mode == runTxModeSimulate {
|
||||
return app.checkState
|
||||
}
|
||||
|
||||
return app.deliverState
|
||||
}
|
||||
|
||||
// txBytes may be nil in some cases, eg. in tests.
|
||||
// Also, in the future we may support "internal" transactions.
|
||||
// runTx processes a transaction. The transactions is proccessed via an
|
||||
// anteHandler. txBytes may be nil in some cases, eg. in tests. Also, in the
|
||||
// future we may support "internal" transactions.
|
||||
func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) {
|
||||
//NOTE: GasWanted should be returned by the AnteHandler.
|
||||
// GasUsed is determined by the GasMeter.
|
||||
// We need access to the context to get the gas meter so
|
||||
// we initialize upfront
|
||||
// NOTE: GasWanted should be returned by the AnteHandler. GasUsed is
|
||||
// determined by the GasMeter. We need access to the context to get the gas
|
||||
// meter so we initialize upfront.
|
||||
var gasWanted int64
|
||||
ctx := app.getContextForAnte(mode, txBytes)
|
||||
|
||||
// Handle any panics.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch rType := r.(type) {
|
||||
|
@ -593,11 +616,11 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
result = sdk.ErrInternal(log).Result()
|
||||
}
|
||||
}
|
||||
|
||||
result.GasWanted = gasWanted
|
||||
result.GasUsed = ctx.GasMeter().GasConsumed()
|
||||
}()
|
||||
|
||||
// Get the Msg.
|
||||
var msgs = tx.GetMsgs()
|
||||
|
||||
err := validateBasicTxMsgs(msgs)
|
||||
|
@ -605,7 +628,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
return err.Result()
|
||||
}
|
||||
|
||||
// Run the ante handler.
|
||||
// run the ante handler
|
||||
if app.anteHandler != nil {
|
||||
newCtx, anteResult, abort := app.anteHandler(ctx, tx)
|
||||
if abort {
|
||||
|
@ -614,17 +637,24 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
if !newCtx.IsZero() {
|
||||
ctx = newCtx
|
||||
}
|
||||
|
||||
gasWanted = result.GasWanted
|
||||
}
|
||||
|
||||
// CacheWrap the state in case it fails.
|
||||
// Keep the state in a transient CacheWrap in case processing the messages
|
||||
// fails.
|
||||
msCache := getState(app, mode).CacheMultiStore()
|
||||
ctx = ctx.WithMultiStore(msCache)
|
||||
if msCache.TracingEnabled() {
|
||||
msCache = msCache.WithTracingContext(sdk.TraceContext(
|
||||
map[string]interface{}{"txHash": cmn.HexBytes(tmhash.Sum(txBytes)).String()},
|
||||
)).(sdk.CacheMultiStore)
|
||||
}
|
||||
|
||||
ctx = ctx.WithMultiStore(msCache)
|
||||
result = app.runMsgs(ctx, msgs)
|
||||
result.GasWanted = gasWanted
|
||||
|
||||
// Only update state if all messages pass and we're not in a simulation.
|
||||
// only update state if all messages pass and we're not in a simulation
|
||||
if result.IsOK() && mode != runTxModeSimulate {
|
||||
msCache.Write()
|
||||
}
|
||||
|
@ -632,11 +662,16 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
return
|
||||
}
|
||||
|
||||
// Implements ABCI
|
||||
// EndBlock implements the ABCI application interface.
|
||||
func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
||||
if app.deliverState.ms.TracingEnabled() {
|
||||
app.deliverState.ms = app.deliverState.ms.ResetTraceContext().(sdk.CacheMultiStore)
|
||||
}
|
||||
|
||||
if app.endBlocker != nil {
|
||||
res = app.endBlocker(app.deliverState.ctx, req)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package baseapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// File for storing in-package BaseApp optional functions,
|
||||
// for options that need access to non-exported fields of the BaseApp
|
||||
|
||||
// SetPruning sets a pruning option on the multistore associated with the app
|
||||
func SetPruning(pruning string) func(*BaseApp) {
|
||||
var pruningEnum sdk.PruningStrategy
|
||||
switch pruning {
|
||||
case "nothing":
|
||||
pruningEnum = sdk.PruneNothing
|
||||
case "everything":
|
||||
pruningEnum = sdk.PruneEverything
|
||||
case "syncable":
|
||||
pruningEnum = sdk.PruneSyncable
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid pruning strategy: %s", pruning))
|
||||
}
|
||||
return func(bap *BaseApp) {
|
||||
bap.cms.SetPruning(pruningEnum)
|
||||
}
|
||||
}
|
|
@ -161,6 +161,7 @@ func printCreate(info keys.Info, seed string) {
|
|||
type NewKeyBody struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Seed string `json:"seed"`
|
||||
}
|
||||
|
||||
// add new key REST handler
|
||||
|
@ -205,7 +206,11 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// create account
|
||||
info, mnemonic, err := kb.CreateMnemonic(m.Name, keys.English, m.Password, keys.Secp256k1)
|
||||
seed := m.Seed
|
||||
if seed == "" {
|
||||
seed = getSeed(keys.Secp256k1)
|
||||
}
|
||||
info, err := kb.CreateKey(m.Name, seed, m.Password)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -219,7 +224,7 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
keyOutput.Seed = mnemonic
|
||||
keyOutput.Seed = seed
|
||||
|
||||
bz, err := json.Marshal(keyOutput)
|
||||
if err != nil {
|
||||
|
|
|
@ -26,23 +26,23 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error {
|
|||
name := args[0]
|
||||
|
||||
buf := client.BufferStdin()
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldpass, err := client.GetPassword(
|
||||
"Enter the current passphrase:", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newpass, err := client.GetCheckPassword(
|
||||
"Enter the new passphrase:",
|
||||
"Repeat the new passphrase:", buf)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
getNewpass := func() (string, error) {
|
||||
return client.GetCheckPassword(
|
||||
"Enter the new passphrase:",
|
||||
"Repeat the new passphrase:", buf)
|
||||
}
|
||||
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = kb.Update(name, oldpass, newpass)
|
||||
err = kb.Update(name, oldpass, getNewpass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -81,8 +81,10 @@ func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
getNewpass := func() (string, error) { return m.NewPassword, nil }
|
||||
|
||||
// TODO check if account exists and if password is correct
|
||||
err = kb.Update(name, m.OldPassword, m.NewPassword)
|
||||
err = kb.Update(name, m.OldPassword, getNewpass)
|
||||
if err != nil {
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
@ -52,7 +53,7 @@ func TestKeys(t *testing.T) {
|
|||
newPassword := "0987654321"
|
||||
|
||||
// add key
|
||||
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s"}`, newName, newPassword))
|
||||
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed))
|
||||
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
||||
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
@ -64,6 +65,11 @@ func TestKeys(t *testing.T) {
|
|||
_, err = sdk.AccAddressFromBech32(addr2Bech32)
|
||||
require.NoError(t, err, "Failed to return a correct bech32 address")
|
||||
|
||||
// test if created account is the correct account
|
||||
expectedInfo, _ := GetKB(t).CreateKey(newName, seed, newPassword)
|
||||
expectedAccount := sdk.AccAddress(expectedInfo.GetPubKey().Address().Bytes())
|
||||
assert.Equal(t, expectedAccount.String(), addr2Bech32)
|
||||
|
||||
// existing keys
|
||||
res, body = Request(t, port, "GET", "/keys", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
@ -443,7 +449,7 @@ func TestSubmitProposal(t *testing.T) {
|
|||
|
||||
// query proposal
|
||||
proposal := getProposal(t, port, proposalID)
|
||||
require.Equal(t, "Test", proposal.Title)
|
||||
require.Equal(t, "Test", proposal.GetTitle())
|
||||
}
|
||||
|
||||
func TestDeposit(t *testing.T) {
|
||||
|
@ -465,7 +471,7 @@ func TestDeposit(t *testing.T) {
|
|||
|
||||
// query proposal
|
||||
proposal := getProposal(t, port, proposalID)
|
||||
require.Equal(t, "Test", proposal.Title)
|
||||
require.Equal(t, "Test", proposal.GetTitle())
|
||||
|
||||
// create SubmitProposal TX
|
||||
resultTx = doDeposit(t, port, seed, name, password, addr, proposalID)
|
||||
|
@ -473,7 +479,7 @@ func TestDeposit(t *testing.T) {
|
|||
|
||||
// query proposal
|
||||
proposal = getProposal(t, port, proposalID)
|
||||
require.True(t, proposal.TotalDeposit.IsEqual(sdk.Coins{sdk.NewCoin("steak", 10)}))
|
||||
require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{sdk.NewCoin("steak", 10)}))
|
||||
|
||||
// query deposit
|
||||
deposit := getDeposit(t, port, proposalID, addr)
|
||||
|
@ -499,7 +505,7 @@ func TestVote(t *testing.T) {
|
|||
|
||||
// query proposal
|
||||
proposal := getProposal(t, port, proposalID)
|
||||
require.Equal(t, "Test", proposal.Title)
|
||||
require.Equal(t, "Test", proposal.GetTitle())
|
||||
|
||||
// create SubmitProposal TX
|
||||
resultTx = doDeposit(t, port, seed, name, password, addr, proposalID)
|
||||
|
@ -507,7 +513,7 @@ func TestVote(t *testing.T) {
|
|||
|
||||
// query proposal
|
||||
proposal = getProposal(t, port, proposalID)
|
||||
require.Equal(t, gov.StatusToString(gov.StatusVotingPeriod), proposal.Status)
|
||||
require.Equal(t, gov.StatusVotingPeriod, proposal.GetStatus())
|
||||
|
||||
// create SubmitProposal TX
|
||||
resultTx = doVote(t, port, seed, name, password, addr, proposalID)
|
||||
|
@ -515,7 +521,7 @@ func TestVote(t *testing.T) {
|
|||
|
||||
vote := getVote(t, port, proposalID, addr)
|
||||
require.Equal(t, proposalID, vote.ProposalID)
|
||||
require.Equal(t, gov.VoteOptionToString(gov.OptionYes), vote.Option)
|
||||
require.Equal(t, gov.OptionYes, vote.Option)
|
||||
}
|
||||
|
||||
func TestUnrevoke(t *testing.T) {
|
||||
|
@ -576,31 +582,31 @@ func TestProposalsQuery(t *testing.T) {
|
|||
|
||||
// Test query all proposals
|
||||
proposals := getProposalsAll(t, port)
|
||||
require.Equal(t, proposalID1, (proposals[0]).ProposalID)
|
||||
require.Equal(t, proposalID2, (proposals[1]).ProposalID)
|
||||
require.Equal(t, proposalID3, (proposals[2]).ProposalID)
|
||||
require.Equal(t, proposalID1, (proposals[0]).GetProposalID())
|
||||
require.Equal(t, proposalID2, (proposals[1]).GetProposalID())
|
||||
require.Equal(t, proposalID3, (proposals[2]).GetProposalID())
|
||||
|
||||
// Test query deposited by addr1
|
||||
proposals = getProposalsFilterDepositer(t, port, addr)
|
||||
require.Equal(t, proposalID1, (proposals[0]).ProposalID)
|
||||
require.Equal(t, proposalID1, (proposals[0]).GetProposalID())
|
||||
|
||||
// Test query deposited by addr2
|
||||
proposals = getProposalsFilterDepositer(t, port, addr2)
|
||||
require.Equal(t, proposalID2, (proposals[0]).ProposalID)
|
||||
require.Equal(t, proposalID3, (proposals[1]).ProposalID)
|
||||
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
|
||||
require.Equal(t, proposalID3, (proposals[1]).GetProposalID())
|
||||
|
||||
// Test query voted by addr1
|
||||
proposals = getProposalsFilterVoter(t, port, addr)
|
||||
require.Equal(t, proposalID2, (proposals[0]).ProposalID)
|
||||
require.Equal(t, proposalID3, (proposals[1]).ProposalID)
|
||||
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
|
||||
require.Equal(t, proposalID3, (proposals[1]).GetProposalID())
|
||||
|
||||
// Test query voted by addr2
|
||||
proposals = getProposalsFilterVoter(t, port, addr2)
|
||||
require.Equal(t, proposalID3, (proposals[0]).ProposalID)
|
||||
require.Equal(t, proposalID3, (proposals[0]).GetProposalID())
|
||||
|
||||
// Test query voted and deposited by addr1
|
||||
proposals = getProposalsFilterVoterDepositer(t, port, addr, addr)
|
||||
require.Equal(t, proposalID2, (proposals[0]).ProposalID)
|
||||
require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
|
||||
}
|
||||
|
||||
//_____________________________________________________________________________
|
||||
|
@ -838,68 +844,68 @@ func getValidators(t *testing.T, port string) []stakerest.StakeValidatorOutput {
|
|||
return validators
|
||||
}
|
||||
|
||||
func getProposal(t *testing.T, port string, proposalID int64) gov.ProposalRest {
|
||||
func getProposal(t *testing.T, port string, proposalID int64) gov.Proposal {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var proposal gov.ProposalRest
|
||||
var proposal gov.Proposal
|
||||
err := cdc.UnmarshalJSON([]byte(body), &proposal)
|
||||
require.Nil(t, err)
|
||||
return proposal
|
||||
}
|
||||
|
||||
func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.AccAddress) gov.DepositRest {
|
||||
func getDeposit(t *testing.T, port string, proposalID int64, depositerAddr sdk.AccAddress) gov.Deposit {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositerAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var deposit gov.DepositRest
|
||||
var deposit gov.Deposit
|
||||
err := cdc.UnmarshalJSON([]byte(body), &deposit)
|
||||
require.Nil(t, err)
|
||||
return deposit
|
||||
}
|
||||
|
||||
func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.AccAddress) gov.VoteRest {
|
||||
func getVote(t *testing.T, port string, proposalID int64, voterAddr sdk.AccAddress) gov.Vote {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/votes/%s", proposalID, voterAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var vote gov.VoteRest
|
||||
var vote gov.Vote
|
||||
err := cdc.UnmarshalJSON([]byte(body), &vote)
|
||||
require.Nil(t, err)
|
||||
return vote
|
||||
}
|
||||
|
||||
func getProposalsAll(t *testing.T, port string) []gov.ProposalRest {
|
||||
func getProposalsAll(t *testing.T, port string) []gov.Proposal {
|
||||
res, body := Request(t, port, "GET", "/gov/proposals", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var proposals []gov.ProposalRest
|
||||
var proposals []gov.Proposal
|
||||
err := cdc.UnmarshalJSON([]byte(body), &proposals)
|
||||
require.Nil(t, err)
|
||||
return proposals
|
||||
}
|
||||
|
||||
func getProposalsFilterDepositer(t *testing.T, port string, depositerAddr sdk.AccAddress) []gov.ProposalRest {
|
||||
func getProposalsFilterDepositer(t *testing.T, port string, depositerAddr sdk.AccAddress) []gov.Proposal {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s", depositerAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var proposals []gov.ProposalRest
|
||||
var proposals []gov.Proposal
|
||||
err := cdc.UnmarshalJSON([]byte(body), &proposals)
|
||||
require.Nil(t, err)
|
||||
return proposals
|
||||
}
|
||||
|
||||
func getProposalsFilterVoter(t *testing.T, port string, voterAddr sdk.AccAddress) []gov.ProposalRest {
|
||||
func getProposalsFilterVoter(t *testing.T, port string, voterAddr sdk.AccAddress) []gov.Proposal {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?voter=%s", voterAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var proposals []gov.ProposalRest
|
||||
var proposals []gov.Proposal
|
||||
err := cdc.UnmarshalJSON([]byte(body), &proposals)
|
||||
require.Nil(t, err)
|
||||
return proposals
|
||||
}
|
||||
|
||||
func getProposalsFilterVoterDepositer(t *testing.T, port string, voterAddr, depositerAddr sdk.AccAddress) []gov.ProposalRest {
|
||||
func getProposalsFilterVoterDepositer(t *testing.T, port string, voterAddr, depositerAddr sdk.AccAddress) []gov.Proposal {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s&voter=%s", depositerAddr, voterAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var proposals []gov.ProposalRest
|
||||
var proposals []gov.Proposal
|
||||
err := cdc.UnmarshalJSON([]byte(body), &proposals)
|
||||
require.Nil(t, err)
|
||||
return proposals
|
||||
|
|
|
@ -105,7 +105,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
|||
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||
privVal.Reset()
|
||||
db := dbm.NewMemDB()
|
||||
app := gapp.NewGaiaApp(logger, db)
|
||||
app := gapp.NewGaiaApp(logger, db, nil)
|
||||
cdc = gapp.MakeCodec()
|
||||
|
||||
genesisFile := config.GenesisFile()
|
||||
|
|
|
@ -2,6 +2,7 @@ package app
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
@ -55,12 +56,15 @@ type GaiaApp struct {
|
|||
govKeeper gov.Keeper
|
||||
}
|
||||
|
||||
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
|
||||
// NewGaiaApp returns a reference to an initialized GaiaApp.
|
||||
func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
|
||||
cdc := MakeCodec()
|
||||
|
||||
// create your application object
|
||||
bApp := bam.NewBaseAppNoCodec(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
|
||||
bApp.SetCommitMultiStoreTracer(traceStore)
|
||||
|
||||
var app = &GaiaApp{
|
||||
BaseApp: bam.NewBaseAppNoCodec(appName, logger, db, auth.DefaultTxDecoder(cdc)),
|
||||
BaseApp: bApp,
|
||||
cdc: cdc,
|
||||
keyMain: sdk.NewKVStoreKey("main"),
|
||||
keyAccount: sdk.NewKVStoreKey("acc"),
|
||||
|
|
|
@ -49,7 +49,7 @@ func TestGaiaCLISend(t *testing.T) {
|
|||
|
||||
defer proc.Stop(false)
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
|
@ -58,7 +58,7 @@ func TestGaiaCLISend(t *testing.T) {
|
|||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
@ -67,7 +67,7 @@ func TestGaiaCLISend(t *testing.T) {
|
|||
|
||||
// test autosequencing
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
@ -76,7 +76,7 @@ func TestGaiaCLISend(t *testing.T) {
|
|||
|
||||
// test memo
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo --memo 'testmemo'", flags, barAddr), pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(30), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
@ -101,14 +101,14 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
|
||||
defer proc.Stop(false)
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
barAddr, barPubKey := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
|
||||
barCeshPubKey := sdk.MustBech32ifyValPub(barPubKey)
|
||||
|
||||
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
@ -124,7 +124,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
|
||||
|
||||
executeWrite(t, cvStr, pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", barAddr, flags))
|
||||
require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc)
|
||||
|
@ -142,7 +142,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
|
||||
success := executeWrite(t, unbondStr, pass)
|
||||
require.True(t, success)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
/* // this won't be what we expect because we've only started unbonding, haven't completed
|
||||
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
|
||||
|
@ -169,7 +169,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
|
||||
defer proc.Stop(false)
|
||||
tests.WaitForTMStart(port)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome))
|
||||
|
||||
|
@ -177,30 +177,30 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
|
|||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
executeWrite(t, fmt.Sprintf("gaiacli gov submit-proposal %v --proposer=%s --deposit=5steak --type=Text --title=Test --description=test --from=foo", flags, fooAddr), pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
|
||||
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposalID=1 --output=json %v", flags))
|
||||
require.Equal(t, int64(1), proposal1.ProposalID)
|
||||
require.Equal(t, gov.StatusToString(gov.StatusDepositPeriod), proposal1.Status)
|
||||
require.Equal(t, int64(1), proposal1.GetProposalID())
|
||||
require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus())
|
||||
|
||||
executeWrite(t, fmt.Sprintf("gaiacli gov deposit %v --depositer=%s --deposit=10steak --proposalID=1 --from=foo", flags, fooAddr), pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
|
||||
require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf("steak").Int64())
|
||||
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli gov query-proposal --proposalID=1 --output=json %v", flags))
|
||||
require.Equal(t, int64(1), proposal1.ProposalID)
|
||||
require.Equal(t, gov.StatusToString(gov.StatusVotingPeriod), proposal1.Status)
|
||||
require.Equal(t, int64(1), proposal1.GetProposalID())
|
||||
require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus())
|
||||
|
||||
executeWrite(t, fmt.Sprintf("gaiacli gov vote %v --proposalID=1 --voter=%s --option=Yes --from=foo", flags, fooAddr), pass)
|
||||
tests.WaitForNextHeightTM(port)
|
||||
tests.WaitForNextNBlocksTM(2, port)
|
||||
|
||||
vote := executeGetVote(t, fmt.Sprintf("gaiacli gov query-vote --proposalID=1 --voter=%s --output=json %v", fooAddr, flags))
|
||||
require.Equal(t, int64(1), vote.ProposalID)
|
||||
require.Equal(t, gov.VoteOptionToString(gov.OptionYes), vote.Option)
|
||||
require.Equal(t, gov.OptionYes, vote.Option)
|
||||
}
|
||||
|
||||
//___________________________________________________________________________________
|
||||
|
@ -288,18 +288,18 @@ func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
|
|||
return validator
|
||||
}
|
||||
|
||||
func executeGetProposal(t *testing.T, cmdStr string) gov.ProposalRest {
|
||||
func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal {
|
||||
out := tests.ExecuteT(t, cmdStr)
|
||||
var proposal gov.ProposalRest
|
||||
var proposal gov.Proposal
|
||||
cdc := app.MakeCodec()
|
||||
err := cdc.UnmarshalJSON([]byte(out), &proposal)
|
||||
require.NoError(t, err, "out %v\n, err %v", out, err)
|
||||
return proposal
|
||||
}
|
||||
|
||||
func executeGetVote(t *testing.T, cmdStr string) gov.VoteRest {
|
||||
func executeGetVote(t *testing.T, cmdStr string) gov.Vote {
|
||||
out := tests.ExecuteT(t, cmdStr)
|
||||
var vote gov.VoteRest
|
||||
var vote gov.Vote
|
||||
cdc := app.MakeCodec()
|
||||
err := cdc.UnmarshalJSON([]byte(out), &vote)
|
||||
require.NoError(t, err, "out %v\n, err %v", out, err)
|
||||
|
|
|
@ -2,8 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
|
@ -38,11 +42,13 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
||||
return app.NewGaiaApp(logger, db)
|
||||
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||
return app.NewGaiaApp(logger, db, traceStore, baseapp.SetPruning(viper.GetString("pruning")))
|
||||
}
|
||||
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
gapp := app.NewGaiaApp(logger, db)
|
||||
return gapp.ExportAppStateAndValidators()
|
||||
func exportAppStateAndTMValidators(
|
||||
logger log.Logger, db dbm.DB, traceStore io.Writer,
|
||||
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
gApp := app.NewGaiaApp(logger, db, traceStore)
|
||||
return gApp.ExportAppStateAndValidators()
|
||||
}
|
||||
|
|
|
@ -7,7 +7,10 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
@ -44,7 +47,7 @@ func runHackCmd(cmd *cobra.Command, args []string) error {
|
|||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
app := NewGaiaApp(logger, db)
|
||||
app := NewGaiaApp(logger, db, baseapp.SetPruning(viper.GetString("pruning")))
|
||||
|
||||
// print some info
|
||||
id := app.LastCommitID()
|
||||
|
@ -140,12 +143,15 @@ type GaiaApp struct {
|
|||
slashingKeeper slashing.Keeper
|
||||
}
|
||||
|
||||
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
|
||||
func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
|
||||
cdc := MakeCodec()
|
||||
|
||||
bApp := bam.NewBaseApp(appName, cdc, logger, db, baseAppOptions...)
|
||||
bApp.SetCommitMultiStoreTracer(os.Stdout)
|
||||
|
||||
// create your application object
|
||||
var app = &GaiaApp{
|
||||
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
|
||||
BaseApp: bApp,
|
||||
cdc: cdc,
|
||||
keyMain: sdk.NewKVStoreKey("main"),
|
||||
keyAccount: sdk.NewKVStoreKey("acc"),
|
||||
|
|
|
@ -240,6 +240,31 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig tcrypto.Signa
|
|||
return sig, pub, nil
|
||||
}
|
||||
|
||||
func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tcrypto.PrivKey, error) {
|
||||
info, err := kb.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var priv tcrypto.PrivKey
|
||||
switch info.(type) {
|
||||
case localInfo:
|
||||
linfo := info.(localInfo)
|
||||
if linfo.PrivKeyArmor == "" {
|
||||
err = fmt.Errorf("private key not available")
|
||||
return nil, err
|
||||
}
|
||||
priv, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case ledgerInfo:
|
||||
return nil, errors.New("Only works on local private keys")
|
||||
case offlineInfo:
|
||||
return nil, errors.New("Only works on local private keys")
|
||||
}
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
func (kb dbKeybase) Export(name string) (armor string, err error) {
|
||||
bz := kb.db.Get(infoKey(name))
|
||||
if bz == nil {
|
||||
|
@ -330,8 +355,9 @@ func (kb dbKeybase) Delete(name, passphrase string) error {
|
|||
// encrypted.
|
||||
//
|
||||
// oldpass must be the current passphrase used for encryption,
|
||||
// newpass will be the only valid passphrase from this time forward.
|
||||
func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
||||
// getNewpass is a function to get the passphrase to permanently replace
|
||||
// the current passphrase
|
||||
func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
|
||||
info, err := kb.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -343,6 +369,10 @@ func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newpass, err := getNewpass()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kb.writeLocalKey(key, name, newpass)
|
||||
return nil
|
||||
default:
|
||||
|
|
|
@ -167,9 +167,10 @@ func TestSignVerify(t *testing.T) {
|
|||
}
|
||||
|
||||
func assertPassword(t *testing.T, cstore Keybase, name, pass, badpass string) {
|
||||
err := cstore.Update(name, badpass, pass)
|
||||
getNewpass := func() (string, error) { return pass, nil }
|
||||
err := cstore.Update(name, badpass, getNewpass)
|
||||
require.NotNil(t, err)
|
||||
err = cstore.Update(name, pass, pass)
|
||||
err = cstore.Update(name, pass, getNewpass)
|
||||
require.Nil(t, err, "%+v", err)
|
||||
}
|
||||
|
||||
|
@ -265,12 +266,13 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
|||
assertPassword(t, cstore, n1, p1, p2)
|
||||
|
||||
// update password requires the existing password
|
||||
err = cstore.Update(n1, "jkkgkg", p2)
|
||||
getNewpass := func() (string, error) { return p2, nil }
|
||||
err = cstore.Update(n1, "jkkgkg", getNewpass)
|
||||
require.NotNil(t, err)
|
||||
assertPassword(t, cstore, n1, p1, p2)
|
||||
|
||||
// then it changes the password when correct
|
||||
err = cstore.Update(n1, p1, p2)
|
||||
err = cstore.Update(n1, p1, getNewpass)
|
||||
require.NoError(t, err)
|
||||
// p2 is now the proper one!
|
||||
assertPassword(t, cstore, n1, p2, p1)
|
||||
|
|
|
@ -34,11 +34,14 @@ type Keybase interface {
|
|||
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error)
|
||||
|
||||
// The following operations will *only* work on locally-stored keys
|
||||
Update(name, oldpass, newpass string) error
|
||||
Update(name, oldpass string, getNewpass func() (string, error)) error
|
||||
Import(name string, armor string) (err error)
|
||||
ImportPubKey(name string, armor string) (err error)
|
||||
Export(name string) (armor string, err error)
|
||||
ExportPubKey(name string) (armor string, err error)
|
||||
|
||||
// *only* works on locally-stored keys. Temporary method until we redo the exporting API
|
||||
ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error)
|
||||
}
|
||||
|
||||
// Info is the publicly exposed information about a keypair
|
||||
|
|
|
@ -1,10 +1,45 @@
|
|||
# Running a Node
|
||||
|
||||
TODO: document `gaiad`
|
||||
> TODO: Improve documentation of `gaiad`
|
||||
|
||||
|
||||
## Basics
|
||||
|
||||
To start a node:
|
||||
|
||||
```shell
|
||||
$ gaiad start <flags>
|
||||
```
|
||||
|
||||
Options for running the `gaiad` binary are effectively the same as for `tendermint`.
|
||||
See `gaiad --help` and the
|
||||
[guide to using Tendermint](https://github.com/tendermint/tendermint/blob/master/docs/using-tendermint.md)
|
||||
for more details.
|
||||
|
||||
## Debugging
|
||||
|
||||
Optionally, you can run `gaiad` with `--trace-store` to trace all store operations
|
||||
to a specified file.
|
||||
|
||||
```shell
|
||||
$ gaiad start <flags> --trace-store=/path/to/trace.out
|
||||
```
|
||||
|
||||
Key/value pairs will be base64 encoded. Additionally, the block number and any
|
||||
correlated transaction hash will be included as metadata.
|
||||
|
||||
e.g.
|
||||
```json
|
||||
...
|
||||
{"operation":"write","key":"ATW6Bu997eeuUeRBwv1EPGvXRfPR","value":"BggEEBYgFg==","metadata":{"blockHeight":12,"txHash":"5AAC197EC45E6C5DE0798C4A4E2F54BBB695CA9E"}}
|
||||
{"operation":"write","key":"AjW6Bu997eeuUeRBwv1EPGvXRfPRCgAAAAAAAAA=","value":"AQE=","metadata":{"blockHeight":12,"txHash":"5AAC197EC45E6C5DE0798C4A4E2F54BBB695CA9E"}}
|
||||
{"operation":"read","key":"ATW6Bu997eeuUeRBwv1EPGvXRfPR","value":"BggEEBYgFg==","metadata":{"blockHeight":13}}
|
||||
{"operation":"read","key":"AjW6Bu997eeuUeRBwv1EPGvXRfPRCwAAAAAAAAA=","value":"","metadata":{"blockHeight":13}}
|
||||
...
|
||||
```
|
||||
|
||||
You can then query for the various traced operations using a tool like [jq](https://github.com/stedolan/jq).
|
||||
|
||||
```shell
|
||||
$ jq -s '.[] | select((.key=="ATW6Bu997eeuUeRBwv1EPGvXRfPR") and .metadata.blockHeight==14)' /path/to/trace.out
|
||||
```
|
|
@ -46,14 +46,14 @@ type BasecoinApp struct {
|
|||
// In addition, all necessary mappers and keepers are created, routes
|
||||
// registered, and finally the stores being mounted along with any necessary
|
||||
// chain initialization.
|
||||
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||
func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *BasecoinApp {
|
||||
// create and register app-level codec for TXs and accounts
|
||||
cdc := MakeCodec()
|
||||
|
||||
// create your application type
|
||||
var app = &BasecoinApp{
|
||||
cdc: cdc,
|
||||
BaseApp: bam.NewBaseAppNoCodec(appName, logger, db, auth.DefaultTxDecoder(cdc)),
|
||||
BaseApp: bam.NewBaseAppNoCodec(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...),
|
||||
keyMain: sdk.NewKVStoreKey("main"),
|
||||
keyAccount: sdk.NewKVStoreKey("acc"),
|
||||
keyIBC: sdk.NewKVStoreKey("ibc"),
|
||||
|
|
|
@ -2,11 +2,15 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
|
@ -39,11 +43,11 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
||||
return app.NewBasecoinApp(logger, db)
|
||||
func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Application {
|
||||
return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning")))
|
||||
}
|
||||
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
bapp := app.NewBasecoinApp(logger, db)
|
||||
return bapp.ExportAppStateAndValidators()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -50,11 +51,11 @@ func CoolAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso
|
|||
return
|
||||
}
|
||||
|
||||
func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
||||
func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application {
|
||||
return app.NewDemocoinApp(logger, db)
|
||||
}
|
||||
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
dapp := app.NewDemocoinApp(logger, db)
|
||||
return dapp.ExportAppStateAndValidators()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package server
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
@ -10,34 +12,73 @@ import (
|
|||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// AppCreator lets us lazily initialize app, using home dir
|
||||
// and other flags (?) to start
|
||||
type AppCreator func(string, log.Logger) (abci.Application, error)
|
||||
type (
|
||||
// AppCreator reflects a function that allows us to lazily initialize an
|
||||
// application using various configurations.
|
||||
AppCreator func(home string, logger log.Logger, traceStore string) (abci.Application, error)
|
||||
|
||||
// AppExporter dumps all app state to JSON-serializable structure and returns the current validator set
|
||||
type AppExporter func(home string, log log.Logger) (json.RawMessage, []tmtypes.GenesisValidator, error)
|
||||
// AppExporter reflects a function that dumps all app state to
|
||||
// JSON-serializable structure and returns the current validator set.
|
||||
AppExporter func(home string, logger log.Logger, traceStore string) (json.RawMessage, []tmtypes.GenesisValidator, error)
|
||||
|
||||
// ConstructAppCreator returns an application generation function
|
||||
func ConstructAppCreator(appFn func(log.Logger, dbm.DB) abci.Application, name string) AppCreator {
|
||||
return func(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||
// AppCreatorInit reflects a function that performs initialization of an
|
||||
// AppCreator.
|
||||
AppCreatorInit func(log.Logger, dbm.DB, io.Writer) abci.Application
|
||||
|
||||
// AppExporterInit reflects a function that performs initialization of an
|
||||
// AppExporter.
|
||||
AppExporterInit func(log.Logger, dbm.DB, io.Writer) (json.RawMessage, []tmtypes.GenesisValidator, error)
|
||||
)
|
||||
|
||||
// ConstructAppCreator returns an application generation function.
|
||||
func ConstructAppCreator(appFn AppCreatorInit, name string) AppCreator {
|
||||
return func(rootDir string, logger log.Logger, traceStore string) (abci.Application, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
|
||||
db, err := dbm.NewGoLevelDB(name, dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app := appFn(logger, db)
|
||||
|
||||
var traceStoreWriter io.Writer
|
||||
if traceStore != "" {
|
||||
traceStoreWriter, err = os.OpenFile(
|
||||
traceStore,
|
||||
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
||||
0666,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
app := appFn(logger, db, traceStoreWriter)
|
||||
return app, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConstructAppExporter returns an application export function
|
||||
func ConstructAppExporter(appFn func(log.Logger, dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error), name string) AppExporter {
|
||||
return func(rootDir string, logger log.Logger) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
// ConstructAppExporter returns an application export function.
|
||||
func ConstructAppExporter(appFn AppExporterInit, name string) AppExporter {
|
||||
return func(rootDir string, logger log.Logger, traceStore string) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
|
||||
db, err := dbm.NewGoLevelDB(name, dataDir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return appFn(logger, db)
|
||||
|
||||
var traceStoreWriter io.Writer
|
||||
if traceStore != "" {
|
||||
traceStoreWriter, err = os.OpenFile(
|
||||
traceStore,
|
||||
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
||||
0666,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return appFn(logger, db, traceStoreWriter)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,27 +11,33 @@ import (
|
|||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// ExportCmd dumps app state to JSON
|
||||
// ExportCmd dumps app state to JSON.
|
||||
func ExportCmd(ctx *Context, cdc *wire.Codec, appExporter AppExporter) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export state to JSON",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
home := viper.GetString("home")
|
||||
appState, validators, err := appExporter(home, ctx.Logger)
|
||||
traceStore := viper.GetString(flagTraceStore)
|
||||
|
||||
appState, validators, err := appExporter(home, ctx.Logger, traceStore)
|
||||
if err != nil {
|
||||
return errors.Errorf("error exporting state: %v\n", err)
|
||||
}
|
||||
|
||||
doc, err := tmtypes.GenesisDocFromFile(ctx.Config.GenesisFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
doc.AppStateJSON = appState
|
||||
doc.Validators = validators
|
||||
|
||||
encoded, err := wire.MarshalJSONIndent(cdc, doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(encoded))
|
||||
return nil
|
||||
},
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -18,6 +20,26 @@ func (ms multiStore) CacheWrap() sdk.CacheWrap {
|
|||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) CacheWrapWithTrace(_ io.Writer, _ sdk.TraceContext) sdk.CacheWrap {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) ResetTraceContext() sdk.MultiStore {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) TracingEnabled() bool {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) WithTracingContext(tc sdk.TraceContext) sdk.MultiStore {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) WithTracer(w io.Writer) sdk.MultiStore {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) Commit() sdk.CommitID {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
@ -26,6 +48,10 @@ func (ms multiStore) LastCommitID() sdk.CommitID {
|
|||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) SetPruning(s sdk.PruningStrategy) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) GetCommitKVStore(key sdk.StoreKey) sdk.CommitKVStore {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
@ -70,6 +96,10 @@ func (kv kvStore) CacheWrap() sdk.CacheWrap {
|
|||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (kv kvStore) CacheWrapWithTrace(w io.Writer, tc sdk.TraceContext) sdk.CacheWrap {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (kv kvStore) GetStoreType() sdk.StoreType {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ import (
|
|||
const (
|
||||
flagWithTendermint = "with-tendermint"
|
||||
flagAddress = "address"
|
||||
flagTraceStore = "trace-store"
|
||||
flagPruning = "pruning"
|
||||
)
|
||||
|
||||
// StartCmd runs the service passed in, either
|
||||
// stand-alone, or in-process with tendermint
|
||||
// StartCmd runs the service passed in, either stand-alone or in-process with
|
||||
// Tendermint.
|
||||
func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "start",
|
||||
|
@ -30,26 +32,31 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
|
|||
ctx.Logger.Info("Starting ABCI without Tendermint")
|
||||
return startStandAlone(ctx, appCreator)
|
||||
}
|
||||
|
||||
ctx.Logger.Info("Starting ABCI with Tendermint")
|
||||
|
||||
_, err := startInProcess(ctx, appCreator)
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
// basic flags for abci app
|
||||
cmd.Flags().Bool(flagWithTendermint, true, "run abci app embedded in-process with tendermint")
|
||||
// core flags for the ABCI application
|
||||
cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint")
|
||||
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address")
|
||||
cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file")
|
||||
cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything")
|
||||
|
||||
// AddNodeFlags adds support for all tendermint-specific command line options
|
||||
// add support for all Tendermint-specific command line options
|
||||
tcmd.AddNodeFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func startStandAlone(ctx *Context, appCreator AppCreator) error {
|
||||
// Generate the app in the proper dir
|
||||
addr := viper.GetString(flagAddress)
|
||||
home := viper.GetString("home")
|
||||
app, err := appCreator(home, ctx.Logger)
|
||||
traceStore := viper.GetString(flagTraceStore)
|
||||
|
||||
app, err := appCreator(home, ctx.Logger, traceStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -58,15 +65,17 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error {
|
|||
if err != nil {
|
||||
return errors.Errorf("error creating listener: %v\n", err)
|
||||
}
|
||||
|
||||
svr.SetLogger(ctx.Logger.With("module", "abci-server"))
|
||||
|
||||
err = svr.Start()
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
|
||||
// Wait forever
|
||||
// wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
// cleanup
|
||||
err = svr.Stop()
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
|
@ -78,29 +87,33 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error {
|
|||
func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) {
|
||||
cfg := ctx.Config
|
||||
home := cfg.RootDir
|
||||
app, err := appCreator(home, ctx.Logger)
|
||||
traceStore := viper.GetString(flagTraceStore)
|
||||
|
||||
app, err := appCreator(home, ctx.Logger, traceStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create & start tendermint node
|
||||
n, err := node.NewNode(cfg,
|
||||
// create & start tendermint node
|
||||
tmNode, err := node.NewNode(
|
||||
cfg,
|
||||
pvm.LoadOrGenFilePV(cfg.PrivValidatorFile()),
|
||||
proxy.NewLocalClientCreator(app),
|
||||
node.DefaultGenesisDocProviderFunc(cfg),
|
||||
node.DefaultDBProvider,
|
||||
node.DefaultMetricsProvider,
|
||||
ctx.Logger.With("module", "node"))
|
||||
ctx.Logger.With("module", "node"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = n.Start()
|
||||
err = tmNode.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Trap signal, run forever.
|
||||
n.RunForever()
|
||||
return n, nil
|
||||
// trap signal (run forever)
|
||||
tmNode.RunForever()
|
||||
return tmNode, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package store
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
|
@ -27,11 +28,10 @@ var _ CacheKVStore = (*cacheKVStore)(nil)
|
|||
|
||||
// nolint
|
||||
func NewCacheKVStore(parent KVStore) *cacheKVStore {
|
||||
ci := &cacheKVStore{
|
||||
return &cacheKVStore{
|
||||
cache: make(map[string]cValue),
|
||||
parent: parent,
|
||||
}
|
||||
return ci
|
||||
}
|
||||
|
||||
// Implements Store.
|
||||
|
@ -98,6 +98,7 @@ func (ci *cacheKVStore) Write() {
|
|||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
// TODO: Consider allowing usage of Batch, which would allow the write to
|
||||
|
@ -125,6 +126,11 @@ func (ci *cacheKVStore) CacheWrap() CacheWrap {
|
|||
return NewCacheKVStore(ci)
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the CacheWrapper interface.
|
||||
func (ci *cacheKVStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap {
|
||||
return NewCacheKVStore(NewTraceKVStore(ci, w, tc))
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Iteration
|
||||
|
||||
|
@ -140,32 +146,39 @@ func (ci *cacheKVStore) ReverseIterator(start, end []byte) Iterator {
|
|||
|
||||
func (ci *cacheKVStore) iterator(start, end []byte, ascending bool) Iterator {
|
||||
var parent, cache Iterator
|
||||
|
||||
if ascending {
|
||||
parent = ci.parent.Iterator(start, end)
|
||||
} else {
|
||||
parent = ci.parent.ReverseIterator(start, end)
|
||||
}
|
||||
|
||||
items := ci.dirtyItems(ascending)
|
||||
cache = newMemIterator(start, end, items)
|
||||
|
||||
return newCacheMergeIterator(parent, cache, ascending)
|
||||
}
|
||||
|
||||
// Constructs a slice of dirty items, to use w/ memIterator.
|
||||
func (ci *cacheKVStore) dirtyItems(ascending bool) []cmn.KVPair {
|
||||
items := make([]cmn.KVPair, 0, len(ci.cache))
|
||||
|
||||
for key, cacheValue := range ci.cache {
|
||||
if !cacheValue.dirty {
|
||||
continue
|
||||
}
|
||||
items = append(items,
|
||||
cmn.KVPair{[]byte(key), cacheValue.value})
|
||||
|
||||
items = append(items, cmn.KVPair{Key: []byte(key), Value: cacheValue.value})
|
||||
}
|
||||
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
if ascending {
|
||||
return bytes.Compare(items[i].Key, items[j].Key) < 0
|
||||
}
|
||||
|
||||
return bytes.Compare(items[i].Key, items[j].Key) > 0
|
||||
})
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
|
@ -180,10 +193,9 @@ func (ci *cacheKVStore) assertValidKey(key []byte) {
|
|||
|
||||
// Only entrypoint to mutate ci.cache.
|
||||
func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) {
|
||||
cacheValue := cValue{
|
||||
ci.cache[string(key)] = cValue{
|
||||
value: value,
|
||||
deleted: deleted,
|
||||
dirty: dirty,
|
||||
}
|
||||
ci.cache[string(key)] = cacheValue
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
@ -13,33 +15,86 @@ type cacheMultiStore struct {
|
|||
db CacheKVStore
|
||||
stores map[StoreKey]CacheWrap
|
||||
keysByName map[string]StoreKey
|
||||
|
||||
traceWriter io.Writer
|
||||
traceContext TraceContext
|
||||
}
|
||||
|
||||
var _ CacheMultiStore = cacheMultiStore{}
|
||||
|
||||
func newCacheMultiStoreFromRMS(rms *rootMultiStore) cacheMultiStore {
|
||||
cms := cacheMultiStore{
|
||||
db: NewCacheKVStore(dbStoreAdapter{rms.db}),
|
||||
stores: make(map[StoreKey]CacheWrap, len(rms.stores)),
|
||||
keysByName: rms.keysByName,
|
||||
db: NewCacheKVStore(dbStoreAdapter{rms.db}),
|
||||
stores: make(map[StoreKey]CacheWrap, len(rms.stores)),
|
||||
keysByName: rms.keysByName,
|
||||
traceWriter: rms.traceWriter,
|
||||
traceContext: rms.traceContext,
|
||||
}
|
||||
|
||||
for key, store := range rms.stores {
|
||||
cms.stores[key] = store.CacheWrap()
|
||||
if cms.TracingEnabled() {
|
||||
cms.stores[key] = store.CacheWrapWithTrace(cms.traceWriter, cms.traceContext)
|
||||
} else {
|
||||
cms.stores[key] = store.CacheWrap()
|
||||
}
|
||||
}
|
||||
|
||||
return cms
|
||||
}
|
||||
|
||||
func newCacheMultiStoreFromCMS(cms cacheMultiStore) cacheMultiStore {
|
||||
cms2 := cacheMultiStore{
|
||||
db: NewCacheKVStore(cms.db),
|
||||
stores: make(map[StoreKey]CacheWrap, len(cms.stores)),
|
||||
db: NewCacheKVStore(cms.db),
|
||||
stores: make(map[StoreKey]CacheWrap, len(cms.stores)),
|
||||
traceWriter: cms.traceWriter,
|
||||
traceContext: cms.traceContext,
|
||||
}
|
||||
|
||||
for key, store := range cms.stores {
|
||||
cms2.stores[key] = store.CacheWrap()
|
||||
if cms2.TracingEnabled() {
|
||||
cms2.stores[key] = store.CacheWrapWithTrace(cms2.traceWriter, cms2.traceContext)
|
||||
} else {
|
||||
cms2.stores[key] = store.CacheWrap()
|
||||
}
|
||||
}
|
||||
|
||||
return cms2
|
||||
}
|
||||
|
||||
// WithTracer sets the tracer for the MultiStore that the underlying
|
||||
// stores will utilize to trace operations. A MultiStore is returned.
|
||||
func (cms cacheMultiStore) WithTracer(w io.Writer) MultiStore {
|
||||
cms.traceWriter = w
|
||||
return cms
|
||||
}
|
||||
|
||||
// WithTracingContext updates the tracing context for the MultiStore by merging
|
||||
// the given context with the existing context by key. Any existing keys will
|
||||
// be overwritten. It is implied that the caller should update the context when
|
||||
// necessary between tracing operations. It returns a modified MultiStore.
|
||||
func (cms cacheMultiStore) WithTracingContext(tc TraceContext) MultiStore {
|
||||
if cms.traceContext != nil {
|
||||
for k, v := range tc {
|
||||
cms.traceContext[k] = v
|
||||
}
|
||||
} else {
|
||||
cms.traceContext = tc
|
||||
}
|
||||
|
||||
return cms
|
||||
}
|
||||
|
||||
// TracingEnabled returns if tracing is enabled for the MultiStore.
|
||||
func (cms cacheMultiStore) TracingEnabled() bool {
|
||||
return cms.traceWriter != nil
|
||||
}
|
||||
|
||||
// ResetTraceContext resets the current tracing context.
|
||||
func (cms cacheMultiStore) ResetTraceContext() MultiStore {
|
||||
cms.traceContext = nil
|
||||
return cms
|
||||
}
|
||||
|
||||
// Implements Store.
|
||||
func (cms cacheMultiStore) GetStoreType() StoreType {
|
||||
return sdk.StoreTypeMulti
|
||||
|
@ -58,6 +113,11 @@ func (cms cacheMultiStore) CacheWrap() CacheWrap {
|
|||
return cms.CacheMultiStore().(CacheWrap)
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the CacheWrapper interface.
|
||||
func (cms cacheMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap {
|
||||
return cms.CacheWrap()
|
||||
}
|
||||
|
||||
// Implements MultiStore.
|
||||
func (cms cacheMultiStore) CacheMultiStore() CacheMultiStore {
|
||||
return newCacheMultiStoreFromCMS(cms)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
)
|
||||
|
@ -19,6 +21,11 @@ func (dsa dbStoreAdapter) CacheWrap() CacheWrap {
|
|||
return NewCacheKVStore(dsa)
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the KVStore interface.
|
||||
func (dsa dbStoreAdapter) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap {
|
||||
return NewCacheKVStore(NewTraceKVStore(dsa, w, tc))
|
||||
}
|
||||
|
||||
// Implements KVStore
|
||||
func (dsa dbStoreAdapter) Prefix(prefix []byte) KVStore {
|
||||
return prefixStore{dsa, prefix}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
@ -82,7 +84,12 @@ func (gi *gasKVStore) ReverseIterator(start, end []byte) sdk.Iterator {
|
|||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) CacheWrap() sdk.CacheWrap {
|
||||
panic("you cannot CacheWrap a GasKVStore")
|
||||
panic("cannot CacheWrap a GasKVStore")
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the KVStore interface.
|
||||
func (gi *gasKVStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap {
|
||||
panic("cannot CacheWrapWithTrace a GasKVStore")
|
||||
}
|
||||
|
||||
func (gi *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator {
|
||||
|
|
|
@ -2,6 +2,7 @@ package store
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/tendermint/go-amino"
|
||||
|
@ -14,20 +15,19 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
defaultIAVLCacheSize = 10000
|
||||
defaultIAVLNumRecent = 100
|
||||
defaultIAVLStoreEvery = 1
|
||||
defaultIAVLCacheSize = 10000
|
||||
)
|
||||
|
||||
// load the iavl store
|
||||
func LoadIAVLStore(db dbm.DB, id CommitID) (CommitStore, error) {
|
||||
func LoadIAVLStore(db dbm.DB, id CommitID, pruning sdk.PruningStrategy) (CommitStore, error) {
|
||||
tree := iavl.NewVersionedTree(db, defaultIAVLCacheSize)
|
||||
_, err := tree.LoadVersion(id.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store := newIAVLStore(tree, defaultIAVLNumRecent, defaultIAVLStoreEvery)
|
||||
return store, nil
|
||||
iavl := newIAVLStore(tree, int64(0), int64(0))
|
||||
iavl.SetPruning(pruning)
|
||||
return iavl, nil
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@ -43,16 +43,15 @@ type iavlStore struct {
|
|||
tree *iavl.VersionedTree
|
||||
|
||||
// How many old versions we hold onto.
|
||||
// A value of 0 means keep no recent states
|
||||
// A value of 0 means keep no recent states.
|
||||
numRecent int64
|
||||
|
||||
// Distance between state-sync waypoint states to be stored
|
||||
// This is the distance between state-sync waypoint states to be stored.
|
||||
// See https://github.com/tendermint/tendermint/issues/828
|
||||
// A value of 1 means store every state
|
||||
// A value of 0 means store no waypoints (node cannot assist in state-sync)
|
||||
// A value of 1 means store every state.
|
||||
// A value of 0 means store no waypoints. (node cannot assist in state-sync)
|
||||
// By default this value should be set the same across all nodes,
|
||||
// so that nodes can know the waypoints their peers store
|
||||
// TODO if set to non-default, signal to peers that the node is not suitable as a state sync source
|
||||
// so that nodes can know the waypoints their peers store.
|
||||
storeEvery int64
|
||||
}
|
||||
|
||||
|
@ -76,13 +75,13 @@ func (st *iavlStore) Commit() CommitID {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
// Release an old version of history, if not a sync waypoint
|
||||
// Release an old version of history, if not a sync waypoint.
|
||||
previous := version - 1
|
||||
if st.numRecent < previous {
|
||||
toRelease := previous - st.numRecent
|
||||
if st.storeEvery == 0 || toRelease%st.storeEvery != 0 {
|
||||
err := st.tree.DeleteVersion(toRelease)
|
||||
if err != nil {
|
||||
if err != nil && err.(cmn.Error).Data() != iavl.ErrVersionDoesNotExist {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +101,21 @@ func (st *iavlStore) LastCommitID() CommitID {
|
|||
}
|
||||
}
|
||||
|
||||
// VersionExists returns whether or not a given version is stored
|
||||
// Implements Committer.
|
||||
func (st *iavlStore) SetPruning(pruning sdk.PruningStrategy) {
|
||||
switch pruning {
|
||||
case sdk.PruneEverything:
|
||||
st.numRecent = 0
|
||||
st.storeEvery = 0
|
||||
case sdk.PruneNothing:
|
||||
st.storeEvery = 1
|
||||
case sdk.PruneSyncable:
|
||||
st.numRecent = 100
|
||||
st.storeEvery = 10000
|
||||
}
|
||||
}
|
||||
|
||||
// VersionExists returns whether or not a given version is stored.
|
||||
func (st *iavlStore) VersionExists(version int64) bool {
|
||||
return st.tree.VersionExists(version)
|
||||
}
|
||||
|
@ -117,6 +130,11 @@ func (st *iavlStore) CacheWrap() CacheWrap {
|
|||
return NewCacheKVStore(st)
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the Store interface.
|
||||
func (st *iavlStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap {
|
||||
return NewCacheKVStore(NewTraceKVStore(st, w, tc))
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (st *iavlStore) Set(key, value []byte) {
|
||||
st.tree.Set(key, value)
|
||||
|
@ -190,6 +208,10 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
|||
case "/store", "/key": // Get by key
|
||||
key := req.Data // Data holds the key bytes
|
||||
res.Key = key
|
||||
if !st.VersionExists(res.Height) {
|
||||
res.Log = cmn.ErrorWrap(iavl.ErrVersionDoesNotExist, "").Error()
|
||||
break
|
||||
}
|
||||
if req.Prove {
|
||||
value, proof, err := tree.GetVersionedWithProof(key, res.Height)
|
||||
if err != nil {
|
||||
|
|
|
@ -266,13 +266,11 @@ func nextVersion(iavl *iavlStore) {
|
|||
iavl.Set(key, value)
|
||||
iavl.Commit()
|
||||
}
|
||||
|
||||
func TestIAVLDefaultPruning(t *testing.T) {
|
||||
//Expected stored / deleted version numbers for:
|
||||
//numRecent = 5, storeEvery = 3
|
||||
var states = []struct {
|
||||
stored []int64
|
||||
deleted []int64
|
||||
}{
|
||||
var states = []pruneState{
|
||||
{[]int64{}, []int64{}},
|
||||
{[]int64{1}, []int64{}},
|
||||
{[]int64{1, 2}, []int64{}},
|
||||
|
@ -290,6 +288,39 @@ func TestIAVLDefaultPruning(t *testing.T) {
|
|||
{[]int64{3, 6, 9, 10, 11, 12, 13, 14}, []int64{1, 2, 4, 5, 7, 8}},
|
||||
{[]int64{3, 6, 9, 10, 11, 12, 13, 14, 15}, []int64{1, 2, 4, 5, 7, 8}},
|
||||
}
|
||||
testPruning(t, int64(5), int64(3), states)
|
||||
}
|
||||
|
||||
func TestIAVLAlternativePruning(t *testing.T) {
|
||||
//Expected stored / deleted version numbers for:
|
||||
//numRecent = 3, storeEvery = 5
|
||||
var states = []pruneState{
|
||||
{[]int64{}, []int64{}},
|
||||
{[]int64{1}, []int64{}},
|
||||
{[]int64{1, 2}, []int64{}},
|
||||
{[]int64{1, 2, 3}, []int64{}},
|
||||
{[]int64{1, 2, 3, 4}, []int64{}},
|
||||
{[]int64{2, 3, 4, 5}, []int64{1}},
|
||||
{[]int64{3, 4, 5, 6}, []int64{1, 2}},
|
||||
{[]int64{4, 5, 6, 7}, []int64{1, 2, 3}},
|
||||
{[]int64{5, 6, 7, 8}, []int64{1, 2, 3, 4}},
|
||||
{[]int64{5, 6, 7, 8, 9}, []int64{1, 2, 3, 4}},
|
||||
{[]int64{5, 7, 8, 9, 10}, []int64{1, 2, 3, 4, 6}},
|
||||
{[]int64{5, 8, 9, 10, 11}, []int64{1, 2, 3, 4, 6, 7}},
|
||||
{[]int64{5, 9, 10, 11, 12}, []int64{1, 2, 3, 4, 6, 7, 8}},
|
||||
{[]int64{5, 10, 11, 12, 13}, []int64{1, 2, 3, 4, 6, 7, 8, 9}},
|
||||
{[]int64{5, 10, 11, 12, 13, 14}, []int64{1, 2, 3, 4, 6, 7, 8, 9}},
|
||||
{[]int64{5, 10, 12, 13, 14, 15}, []int64{1, 2, 3, 4, 6, 7, 8, 9, 11}},
|
||||
}
|
||||
testPruning(t, int64(3), int64(5), states)
|
||||
}
|
||||
|
||||
type pruneState struct {
|
||||
stored []int64
|
||||
deleted []int64
|
||||
}
|
||||
|
||||
func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []pruneState) {
|
||||
db := dbm.NewMemDB()
|
||||
tree := iavl.NewVersionedTree(db, cacheSize)
|
||||
iavlStore := newIAVLStore(tree, numRecent, storeEvery)
|
||||
|
@ -307,6 +338,7 @@ func TestIAVLDefaultPruning(t *testing.T) {
|
|||
nextVersion(iavlStore)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIAVLNoPrune(t *testing.T) {
|
||||
db := dbm.NewMemDB()
|
||||
tree := iavl.NewVersionedTree(db, cacheSize)
|
||||
|
@ -321,6 +353,7 @@ func TestIAVLNoPrune(t *testing.T) {
|
|||
nextVersion(iavlStore)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIAVLPruneEverything(t *testing.T) {
|
||||
db := dbm.NewMemDB()
|
||||
tree := iavl.NewVersionedTree(db, cacheSize)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
@ -19,6 +21,11 @@ func (s prefixStore) CacheWrap() CacheWrap {
|
|||
return NewCacheKVStore(s)
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the KVStore interface.
|
||||
func (s prefixStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap {
|
||||
return NewCacheKVStore(NewTraceKVStore(s, w, tc))
|
||||
}
|
||||
|
||||
// Implements KVStore
|
||||
func (s prefixStore) Get(key []byte) []byte {
|
||||
return s.store.Get(append(s.prefix, key...))
|
||||
|
|
|
@ -2,6 +2,7 @@ package store
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
|
@ -18,16 +19,19 @@ const (
|
|||
commitInfoKeyFmt = "s/%d" // s/<version>
|
||||
)
|
||||
|
||||
// rootMultiStore is composed of many CommitStores.
|
||||
// Name contrasts with cacheMultiStore which is for cache-wrapping
|
||||
// other MultiStores.
|
||||
// Implements MultiStore.
|
||||
// rootMultiStore is composed of many CommitStores. Name contrasts with
|
||||
// cacheMultiStore which is for cache-wrapping other MultiStores. It implements
|
||||
// the CommitMultiStore interface.
|
||||
type rootMultiStore struct {
|
||||
db dbm.DB
|
||||
lastCommitID CommitID
|
||||
pruning sdk.PruningStrategy
|
||||
storesParams map[StoreKey]storeParams
|
||||
stores map[StoreKey]CommitStore
|
||||
keysByName map[string]StoreKey
|
||||
|
||||
traceWriter io.Writer
|
||||
traceContext TraceContext
|
||||
}
|
||||
|
||||
var _ CommitMultiStore = (*rootMultiStore)(nil)
|
||||
|
@ -43,6 +47,14 @@ func NewCommitMultiStore(db dbm.DB) *rootMultiStore {
|
|||
}
|
||||
}
|
||||
|
||||
// Implements CommitMultiStore
|
||||
func (rs *rootMultiStore) SetPruning(pruning sdk.PruningStrategy) {
|
||||
rs.pruning = pruning
|
||||
for _, substore := range rs.stores {
|
||||
substore.SetPruning(pruning)
|
||||
}
|
||||
}
|
||||
|
||||
// Implements Store.
|
||||
func (rs *rootMultiStore) GetStoreType() StoreType {
|
||||
return sdk.StoreTypeMulti
|
||||
|
@ -130,6 +142,40 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// WithTracer sets the tracer for the MultiStore that the underlying
|
||||
// stores will utilize to trace operations. A MultiStore is returned.
|
||||
func (rs *rootMultiStore) WithTracer(w io.Writer) MultiStore {
|
||||
rs.traceWriter = w
|
||||
return rs
|
||||
}
|
||||
|
||||
// WithTracingContext updates the tracing context for the MultiStore by merging
|
||||
// the given context with the existing context by key. Any existing keys will
|
||||
// be overwritten. It is implied that the caller should update the context when
|
||||
// necessary between tracing operations. It returns a modified MultiStore.
|
||||
func (rs *rootMultiStore) WithTracingContext(tc TraceContext) MultiStore {
|
||||
if rs.traceContext != nil {
|
||||
for k, v := range tc {
|
||||
rs.traceContext[k] = v
|
||||
}
|
||||
} else {
|
||||
rs.traceContext = tc
|
||||
}
|
||||
|
||||
return rs
|
||||
}
|
||||
|
||||
// TracingEnabled returns if tracing is enabled for the MultiStore.
|
||||
func (rs *rootMultiStore) TracingEnabled() bool {
|
||||
return rs.traceWriter != nil
|
||||
}
|
||||
|
||||
// ResetTraceContext resets the current tracing context.
|
||||
func (rs *rootMultiStore) ResetTraceContext() MultiStore {
|
||||
rs.traceContext = nil
|
||||
return rs
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// +CommitStore
|
||||
|
||||
|
@ -165,6 +211,11 @@ func (rs *rootMultiStore) CacheWrap() CacheWrap {
|
|||
return rs.CacheMultiStore().(CacheWrap)
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the CacheWrapper interface.
|
||||
func (rs *rootMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap {
|
||||
return rs.CacheWrap()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// +MultiStore
|
||||
|
||||
|
@ -178,9 +229,17 @@ func (rs *rootMultiStore) GetStore(key StoreKey) Store {
|
|||
return rs.stores[key]
|
||||
}
|
||||
|
||||
// Implements MultiStore.
|
||||
// GetKVStore implements the MultiStore interface. If tracing is enabled on the
|
||||
// rootMultiStore, a wrapped TraceKVStore will be returned with the given
|
||||
// tracer, otherwise, the original KVStore will be returned.
|
||||
func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore {
|
||||
return rs.stores[key].(KVStore)
|
||||
store := rs.stores[key].(KVStore)
|
||||
|
||||
if rs.TracingEnabled() {
|
||||
store = NewTraceKVStore(store, rs.traceWriter, rs.traceContext)
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
// Implements MultiStore.
|
||||
|
@ -263,7 +322,7 @@ func (rs *rootMultiStore) loadCommitStoreFromParams(id CommitID, params storePar
|
|||
// TODO: id?
|
||||
// return NewCommitMultiStore(db, id)
|
||||
case sdk.StoreTypeIAVL:
|
||||
store, err = LoadIAVLStore(db, id)
|
||||
store, err = LoadIAVLStore(db, id, rs.pruning)
|
||||
return
|
||||
case sdk.StoreTypeDB:
|
||||
panic("dbm.DB is not a CommitStore")
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
const (
|
||||
writeOp operation = "write"
|
||||
readOp operation = "read"
|
||||
deleteOp operation = "delete"
|
||||
iterKeyOp operation = "iterKey"
|
||||
iterValueOp operation = "iterValue"
|
||||
)
|
||||
|
||||
type (
|
||||
// TraceKVStore implements the KVStore interface with tracing enabled.
|
||||
// Operations are traced on each core KVStore call and written to the
|
||||
// underlying io.writer.
|
||||
//
|
||||
// TODO: Should we use a buffered writer and implement Commit on
|
||||
// TraceKVStore?
|
||||
TraceKVStore struct {
|
||||
parent sdk.KVStore
|
||||
writer io.Writer
|
||||
context TraceContext
|
||||
}
|
||||
|
||||
// operation represents an IO operation
|
||||
operation string
|
||||
|
||||
// traceOperation implements a traced KVStore operation
|
||||
traceOperation struct {
|
||||
Operation operation `json:"operation"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
)
|
||||
|
||||
// NewTraceKVStore returns a reference to a new traceKVStore given a parent
|
||||
// KVStore implementation and a buffered writer.
|
||||
func NewTraceKVStore(parent sdk.KVStore, writer io.Writer, tc TraceContext) *TraceKVStore {
|
||||
return &TraceKVStore{parent: parent, writer: writer, context: tc}
|
||||
}
|
||||
|
||||
// Get implements the KVStore interface. It traces a read operation and
|
||||
// delegates a Get call to the parent KVStore.
|
||||
func (tkv *TraceKVStore) Get(key []byte) []byte {
|
||||
value := tkv.parent.Get(key)
|
||||
|
||||
writeOperation(tkv.writer, readOp, tkv.context, key, value)
|
||||
return value
|
||||
}
|
||||
|
||||
// Set implements the KVStore interface. It traces a write operation and
|
||||
// delegates the Set call to the parent KVStore.
|
||||
func (tkv *TraceKVStore) Set(key []byte, value []byte) {
|
||||
writeOperation(tkv.writer, writeOp, tkv.context, key, value)
|
||||
tkv.parent.Set(key, value)
|
||||
}
|
||||
|
||||
// Delete implements the KVStore interface. It traces a write operation and
|
||||
// delegates the Delete call to the parent KVStore.
|
||||
func (tkv *TraceKVStore) Delete(key []byte) {
|
||||
writeOperation(tkv.writer, deleteOp, tkv.context, key, nil)
|
||||
tkv.parent.Delete(key)
|
||||
}
|
||||
|
||||
// Has implements the KVStore interface. It delegates the Has call to the
|
||||
// parent KVStore.
|
||||
func (tkv *TraceKVStore) Has(key []byte) bool {
|
||||
return tkv.parent.Has(key)
|
||||
}
|
||||
|
||||
// Prefix implements the KVStore interface.
|
||||
func (tkv *TraceKVStore) Prefix(prefix []byte) KVStore {
|
||||
return prefixStore{tkv, prefix}
|
||||
}
|
||||
|
||||
// Iterator implements the KVStore interface. It delegates the Iterator call
|
||||
// the to the parent KVStore.
|
||||
func (tkv *TraceKVStore) Iterator(start, end []byte) sdk.Iterator {
|
||||
return tkv.iterator(start, end, true)
|
||||
}
|
||||
|
||||
// ReverseIterator implements the KVStore interface. It delegates the
|
||||
// ReverseIterator call the to the parent KVStore.
|
||||
func (tkv *TraceKVStore) ReverseIterator(start, end []byte) sdk.Iterator {
|
||||
return tkv.iterator(start, end, false)
|
||||
}
|
||||
|
||||
// iterator facilitates iteration over a KVStore. It delegates the necessary
|
||||
// calls to it's parent KVStore.
|
||||
func (tkv *TraceKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator {
|
||||
var parent sdk.Iterator
|
||||
|
||||
if ascending {
|
||||
parent = tkv.parent.Iterator(start, end)
|
||||
} else {
|
||||
parent = tkv.parent.ReverseIterator(start, end)
|
||||
}
|
||||
|
||||
return newTraceIterator(tkv.writer, parent, tkv.context)
|
||||
}
|
||||
|
||||
type traceIterator struct {
|
||||
parent sdk.Iterator
|
||||
writer io.Writer
|
||||
context TraceContext
|
||||
}
|
||||
|
||||
func newTraceIterator(w io.Writer, parent sdk.Iterator, tc TraceContext) sdk.Iterator {
|
||||
return &traceIterator{writer: w, parent: parent, context: tc}
|
||||
}
|
||||
|
||||
// Domain implements the Iterator interface.
|
||||
func (ti *traceIterator) Domain() (start []byte, end []byte) {
|
||||
return ti.parent.Domain()
|
||||
}
|
||||
|
||||
// Valid implements the Iterator interface.
|
||||
func (ti *traceIterator) Valid() bool {
|
||||
return ti.parent.Valid()
|
||||
}
|
||||
|
||||
// Next implements the Iterator interface.
|
||||
func (ti *traceIterator) Next() {
|
||||
ti.parent.Next()
|
||||
}
|
||||
|
||||
// Key implements the Iterator interface.
|
||||
func (ti *traceIterator) Key() []byte {
|
||||
key := ti.parent.Key()
|
||||
|
||||
writeOperation(ti.writer, iterKeyOp, ti.context, key, nil)
|
||||
return key
|
||||
}
|
||||
|
||||
// Value implements the Iterator interface.
|
||||
func (ti *traceIterator) Value() []byte {
|
||||
value := ti.parent.Value()
|
||||
|
||||
writeOperation(ti.writer, iterValueOp, ti.context, nil, value)
|
||||
return value
|
||||
}
|
||||
|
||||
// Close implements the Iterator interface.
|
||||
func (ti *traceIterator) Close() {
|
||||
ti.parent.Close()
|
||||
}
|
||||
|
||||
// GetStoreType implements the KVStore interface. It returns the underlying
|
||||
// KVStore type.
|
||||
func (tkv *TraceKVStore) GetStoreType() sdk.StoreType {
|
||||
return tkv.parent.GetStoreType()
|
||||
}
|
||||
|
||||
// CacheWrap implements the KVStore interface. It panics as a TraceKVStore
|
||||
// cannot be cache wrapped.
|
||||
func (tkv *TraceKVStore) CacheWrap() sdk.CacheWrap {
|
||||
panic("cannot CacheWrap a TraceKVStore")
|
||||
}
|
||||
|
||||
// CacheWrapWithTrace implements the KVStore interface. It panics as a
|
||||
// TraceKVStore cannot be cache wrapped.
|
||||
func (tkv *TraceKVStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap {
|
||||
panic("cannot CacheWrapWithTrace a TraceKVStore")
|
||||
}
|
||||
|
||||
// writeOperation writes a KVStore operation to the underlying io.Writer as
|
||||
// JSON-encoded data where the key/value pair is base64 encoded.
|
||||
func writeOperation(w io.Writer, op operation, tc TraceContext, key, value []byte) {
|
||||
traceOp := traceOperation{
|
||||
Operation: op,
|
||||
Key: base64.StdEncoding.EncodeToString(key),
|
||||
Value: base64.StdEncoding.EncodeToString(value),
|
||||
}
|
||||
|
||||
if tc != nil {
|
||||
traceOp.Metadata = tc
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(traceOp)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to serialize trace operation: %v", err))
|
||||
}
|
||||
|
||||
if _, err := w.Write(raw); err != nil {
|
||||
panic(fmt.Sprintf("failed to write trace operation: %v", err))
|
||||
}
|
||||
|
||||
io.WriteString(w, "\n")
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
)
|
||||
|
||||
var kvPairs = []KVPair{
|
||||
KVPair{Key: keyFmt(1), Value: valFmt(1)},
|
||||
KVPair{Key: keyFmt(2), Value: valFmt(2)},
|
||||
KVPair{Key: keyFmt(3), Value: valFmt(3)},
|
||||
}
|
||||
|
||||
func newTraceKVStore(w io.Writer) *TraceKVStore {
|
||||
store := newEmptyTraceKVStore(w)
|
||||
|
||||
for _, kvPair := range kvPairs {
|
||||
store.Set(kvPair.Key, kvPair.Value)
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
func newEmptyTraceKVStore(w io.Writer) *TraceKVStore {
|
||||
memDB := dbStoreAdapter{dbm.NewMemDB()}
|
||||
tc := TraceContext(map[string]interface{}{"blockHeight": 64})
|
||||
|
||||
return NewTraceKVStore(memDB, w, tc)
|
||||
}
|
||||
|
||||
func TestTraceKVStoreGet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
key []byte
|
||||
expectedValue []byte
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
key: []byte{},
|
||||
expectedValue: nil,
|
||||
expectedOut: "{\"operation\":\"read\",\"key\":\"\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
key: kvPairs[0].Key,
|
||||
expectedValue: kvPairs[0].Value,
|
||||
expectedOut: "{\"operation\":\"read\",\"key\":\"a2V5MDAwMDAwMDE=\",\"value\":\"dmFsdWUwMDAwMDAwMQ==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
key: []byte("does-not-exist"),
|
||||
expectedValue: nil,
|
||||
expectedOut: "{\"operation\":\"read\",\"key\":\"ZG9lcy1ub3QtZXhpc3Q=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
var buf bytes.Buffer
|
||||
|
||||
store := newTraceKVStore(&buf)
|
||||
buf.Reset()
|
||||
value := store.Get(tc.key)
|
||||
|
||||
require.Equal(t, tc.expectedValue, value)
|
||||
require.Equal(t, tc.expectedOut, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceKVStoreSet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
key []byte
|
||||
value []byte
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
key: []byte{},
|
||||
value: nil,
|
||||
expectedOut: "{\"operation\":\"write\",\"key\":\"\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
key: kvPairs[0].Key,
|
||||
value: kvPairs[0].Value,
|
||||
expectedOut: "{\"operation\":\"write\",\"key\":\"a2V5MDAwMDAwMDE=\",\"value\":\"dmFsdWUwMDAwMDAwMQ==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
var buf bytes.Buffer
|
||||
|
||||
store := newEmptyTraceKVStore(&buf)
|
||||
buf.Reset()
|
||||
store.Set(tc.key, tc.value)
|
||||
|
||||
require.Equal(t, tc.expectedOut, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceKVStoreDelete(t *testing.T) {
|
||||
testCases := []struct {
|
||||
key []byte
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
key: []byte{},
|
||||
expectedOut: "{\"operation\":\"delete\",\"key\":\"\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
key: kvPairs[0].Key,
|
||||
expectedOut: "{\"operation\":\"delete\",\"key\":\"a2V5MDAwMDAwMDE=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
var buf bytes.Buffer
|
||||
|
||||
store := newTraceKVStore(&buf)
|
||||
buf.Reset()
|
||||
store.Delete(tc.key)
|
||||
|
||||
require.Equal(t, tc.expectedOut, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceKVStoreHas(t *testing.T) {
|
||||
testCases := []struct {
|
||||
key []byte
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
key: []byte{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
key: kvPairs[0].Key,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
var buf bytes.Buffer
|
||||
|
||||
store := newTraceKVStore(&buf)
|
||||
buf.Reset()
|
||||
ok := store.Has(tc.key)
|
||||
|
||||
require.Equal(t, tc.expected, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestTraceKVStoreIterator(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
store := newTraceKVStore(&buf)
|
||||
iterator := store.Iterator(nil, nil)
|
||||
|
||||
s, e := iterator.Domain()
|
||||
require.Equal(t, []uint8([]byte(nil)), s)
|
||||
require.Equal(t, []uint8([]byte(nil)), e)
|
||||
|
||||
testCases := []struct {
|
||||
expectedKey []byte
|
||||
expectedValue []byte
|
||||
expectedKeyOut string
|
||||
expectedvalueOut string
|
||||
}{
|
||||
{
|
||||
expectedKey: kvPairs[0].Key,
|
||||
expectedValue: kvPairs[0].Value,
|
||||
expectedKeyOut: "{\"operation\":\"iterKey\",\"key\":\"a2V5MDAwMDAwMDE=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
expectedvalueOut: "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dmFsdWUwMDAwMDAwMQ==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
expectedKey: kvPairs[1].Key,
|
||||
expectedValue: kvPairs[1].Value,
|
||||
expectedKeyOut: "{\"operation\":\"iterKey\",\"key\":\"a2V5MDAwMDAwMDI=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
expectedvalueOut: "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dmFsdWUwMDAwMDAwMg==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
expectedKey: kvPairs[2].Key,
|
||||
expectedValue: kvPairs[2].Value,
|
||||
expectedKeyOut: "{\"operation\":\"iterKey\",\"key\":\"a2V5MDAwMDAwMDM=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
expectedvalueOut: "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dmFsdWUwMDAwMDAwMw==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
buf.Reset()
|
||||
ka := iterator.Key()
|
||||
require.Equal(t, tc.expectedKeyOut, buf.String())
|
||||
|
||||
buf.Reset()
|
||||
va := iterator.Value()
|
||||
require.Equal(t, tc.expectedvalueOut, buf.String())
|
||||
|
||||
require.Equal(t, tc.expectedKey, ka)
|
||||
require.Equal(t, tc.expectedValue, va)
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
require.False(t, iterator.Valid())
|
||||
require.Panics(t, iterator.Next)
|
||||
require.NotPanics(t, iterator.Close)
|
||||
}
|
||||
|
||||
func TestTestTraceKVStoreReverseIterator(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
store := newTraceKVStore(&buf)
|
||||
iterator := store.ReverseIterator(nil, nil)
|
||||
|
||||
s, e := iterator.Domain()
|
||||
require.Equal(t, []uint8([]byte(nil)), s)
|
||||
require.Equal(t, []uint8([]byte(nil)), e)
|
||||
|
||||
testCases := []struct {
|
||||
expectedKey []byte
|
||||
expectedValue []byte
|
||||
expectedKeyOut string
|
||||
expectedvalueOut string
|
||||
}{
|
||||
{
|
||||
expectedKey: kvPairs[2].Key,
|
||||
expectedValue: kvPairs[2].Value,
|
||||
expectedKeyOut: "{\"operation\":\"iterKey\",\"key\":\"a2V5MDAwMDAwMDM=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
expectedvalueOut: "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dmFsdWUwMDAwMDAwMw==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
expectedKey: kvPairs[1].Key,
|
||||
expectedValue: kvPairs[1].Value,
|
||||
expectedKeyOut: "{\"operation\":\"iterKey\",\"key\":\"a2V5MDAwMDAwMDI=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
expectedvalueOut: "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dmFsdWUwMDAwMDAwMg==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
{
|
||||
expectedKey: kvPairs[0].Key,
|
||||
expectedValue: kvPairs[0].Value,
|
||||
expectedKeyOut: "{\"operation\":\"iterKey\",\"key\":\"a2V5MDAwMDAwMDE=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
expectedvalueOut: "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dmFsdWUwMDAwMDAwMQ==\",\"metadata\":{\"blockHeight\":64}}\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
buf.Reset()
|
||||
ka := iterator.Key()
|
||||
require.Equal(t, tc.expectedKeyOut, buf.String())
|
||||
|
||||
buf.Reset()
|
||||
va := iterator.Value()
|
||||
require.Equal(t, tc.expectedvalueOut, buf.String())
|
||||
|
||||
require.Equal(t, tc.expectedKey, ka)
|
||||
require.Equal(t, tc.expectedValue, va)
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
require.False(t, iterator.Valid())
|
||||
require.Panics(t, iterator.Next)
|
||||
require.NotPanics(t, iterator.Close)
|
||||
}
|
||||
|
||||
func TestTraceKVStorePrefix(t *testing.T) {
|
||||
store := newEmptyTraceKVStore(nil)
|
||||
pStore := store.Prefix([]byte("trace_prefix"))
|
||||
require.IsType(t, prefixStore{}, pStore)
|
||||
}
|
||||
|
||||
func TestTraceKVStoreGetStoreType(t *testing.T) {
|
||||
memDB := dbStoreAdapter{dbm.NewMemDB()}
|
||||
store := newEmptyTraceKVStore(nil)
|
||||
require.Equal(t, memDB.GetStoreType(), store.GetStoreType())
|
||||
}
|
||||
|
||||
func TestTraceKVStoreCacheWrap(t *testing.T) {
|
||||
store := newEmptyTraceKVStore(nil)
|
||||
require.Panics(t, func() { store.CacheWrap() })
|
||||
}
|
||||
func TestTraceKVStoreCacheWrapWithTrace(t *testing.T) {
|
||||
store := newEmptyTraceKVStore(nil)
|
||||
require.Panics(t, func() { store.CacheWrapWithTrace(nil, nil) })
|
||||
}
|
|
@ -6,20 +6,23 @@ import (
|
|||
|
||||
// Import cosmos-sdk/types/store.go for convenience.
|
||||
// nolint
|
||||
type Store = types.Store
|
||||
type Committer = types.Committer
|
||||
type CommitStore = types.CommitStore
|
||||
type MultiStore = types.MultiStore
|
||||
type CacheMultiStore = types.CacheMultiStore
|
||||
type CommitMultiStore = types.CommitMultiStore
|
||||
type KVStore = types.KVStore
|
||||
type KVPair = types.KVPair
|
||||
type Iterator = types.Iterator
|
||||
type CacheKVStore = types.CacheKVStore
|
||||
type CommitKVStore = types.CommitKVStore
|
||||
type CacheWrapper = types.CacheWrapper
|
||||
type CacheWrap = types.CacheWrap
|
||||
type CommitID = types.CommitID
|
||||
type StoreKey = types.StoreKey
|
||||
type StoreType = types.StoreType
|
||||
type Queryable = types.Queryable
|
||||
type (
|
||||
Store = types.Store
|
||||
Committer = types.Committer
|
||||
CommitStore = types.CommitStore
|
||||
MultiStore = types.MultiStore
|
||||
CacheMultiStore = types.CacheMultiStore
|
||||
CommitMultiStore = types.CommitMultiStore
|
||||
KVStore = types.KVStore
|
||||
KVPair = types.KVPair
|
||||
Iterator = types.Iterator
|
||||
CacheKVStore = types.CacheKVStore
|
||||
CommitKVStore = types.CommitKVStore
|
||||
CacheWrapper = types.CacheWrapper
|
||||
CacheWrap = types.CacheWrap
|
||||
CommitID = types.CommitID
|
||||
StoreKey = types.StoreKey
|
||||
StoreType = types.StoreType
|
||||
Queryable = types.Queryable
|
||||
TraceContext = types.TraceContext
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
@ -10,6 +11,20 @@ import (
|
|||
|
||||
// NOTE: These are implemented in cosmos-sdk/store.
|
||||
|
||||
// PruningStrategy specfies how old states will be deleted over time
|
||||
type PruningStrategy uint8
|
||||
|
||||
const (
|
||||
// PruneSyncable means only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th)
|
||||
PruneSyncable PruningStrategy = iota
|
||||
|
||||
// PruneEverything means all saved states will be deleted, storing only the current state
|
||||
PruneEverything PruningStrategy = iota
|
||||
|
||||
// PruneNothing means all historic states will be saved, nothing will be deleted
|
||||
PruneNothing PruningStrategy = iota
|
||||
)
|
||||
|
||||
type Store interface { //nolint
|
||||
GetStoreType() StoreType
|
||||
CacheWrapper
|
||||
|
@ -19,6 +34,7 @@ type Store interface { //nolint
|
|||
type Committer interface {
|
||||
Commit() CommitID
|
||||
LastCommitID() CommitID
|
||||
SetPruning(PruningStrategy)
|
||||
}
|
||||
|
||||
// Stores of MultiStore must implement CommitStore.
|
||||
|
@ -50,6 +66,21 @@ type MultiStore interface { //nolint
|
|||
GetStore(StoreKey) Store
|
||||
GetKVStore(StoreKey) KVStore
|
||||
GetKVStoreWithGas(GasMeter, StoreKey) KVStore
|
||||
|
||||
// TracingEnabled returns if tracing is enabled for the MultiStore.
|
||||
TracingEnabled() bool
|
||||
|
||||
// WithTracer sets the tracer for the MultiStore that the underlying
|
||||
// stores will utilize to trace operations. A MultiStore is returned.
|
||||
WithTracer(w io.Writer) MultiStore
|
||||
|
||||
// WithTracingContext sets the tracing context for a MultiStore. It is
|
||||
// implied that the caller should update the context when necessary between
|
||||
// tracing operations. A MultiStore is returned.
|
||||
WithTracingContext(TraceContext) MultiStore
|
||||
|
||||
// ResetTraceContext resets the current tracing context.
|
||||
ResetTraceContext() MultiStore
|
||||
}
|
||||
|
||||
// From MultiStore.CacheMultiStore()....
|
||||
|
@ -163,25 +194,27 @@ type KVStoreGetter interface {
|
|||
//----------------------------------------
|
||||
// CacheWrap
|
||||
|
||||
/*
|
||||
CacheWrap() makes the most appropriate cache-wrap. For example,
|
||||
IAVLStore.CacheWrap() returns a CacheKVStore.
|
||||
|
||||
CacheWrap() should not return a Committer, since Commit() on
|
||||
cache-wraps make no sense. It can return KVStore, HeapStore,
|
||||
SpaceStore, etc.
|
||||
*/
|
||||
// CacheWrap makes the most appropriate cache-wrap. For example,
|
||||
// IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return
|
||||
// a Committer, since Commit cache-wraps make no sense. It can return KVStore,
|
||||
// HeapStore, SpaceStore, etc.
|
||||
type CacheWrap interface {
|
||||
|
||||
// Write syncs with the underlying store.
|
||||
Write()
|
||||
|
||||
// CacheWrap recursively wraps again.
|
||||
CacheWrap() CacheWrap
|
||||
|
||||
// CacheWrapWithTrace recursively wraps again with tracing enabled.
|
||||
CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap
|
||||
}
|
||||
|
||||
type CacheWrapper interface { //nolint
|
||||
// CacheWrap cache wraps.
|
||||
CacheWrap() CacheWrap
|
||||
|
||||
// CacheWrapWithTrace cache wraps with tracing enabled.
|
||||
CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@ -298,3 +331,7 @@ func (getter PrefixStoreGetter) KVStore(ctx Context) KVStore {
|
|||
type KVPair cmn.KVPair
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// TraceContext contains TraceKVStore context data. It will be written with
|
||||
// every trace operation.
|
||||
type TraceContext map[string]interface{}
|
||||
|
|
|
@ -48,7 +48,7 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
proposalType, err := gov.StringToProposalType(strProposalType)
|
||||
proposalType, err := gov.ProposalTypeFromString(strProposalType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ func GetCmdVote(cdc *wire.Codec) *cobra.Command {
|
|||
|
||||
option := viper.GetString(flagOption)
|
||||
|
||||
byteVoteOption, err := gov.StringToVoteOption(option)
|
||||
byteVoteOption, err := gov.VoteOptionFromString(option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ func GetCmdVote(cdc *wire.Codec) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]", bechVoter, msg.ProposalID, gov.VoteOptionToString(msg.Option))
|
||||
fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]", bechVoter, msg.ProposalID, msg.Option)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
@ -195,8 +195,7 @@ func GetCmdQueryProposal(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
|
||||
var proposal gov.Proposal
|
||||
cdc.MustUnmarshalBinary(res, &proposal)
|
||||
proposalRest := gov.ProposalToRest(proposal)
|
||||
output, err := wire.MarshalJSONIndent(cdc, proposalRest)
|
||||
output, err := wire.MarshalJSONIndent(cdc, proposal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -232,8 +231,7 @@ func GetCmdQueryVote(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
|
||||
var vote gov.Vote
|
||||
cdc.MustUnmarshalBinary(res, &vote)
|
||||
voteRest := gov.VoteToRest(vote)
|
||||
output, err := wire.MarshalJSONIndent(cdc, voteRest)
|
||||
output, err := wire.MarshalJSONIndent(cdc, vote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -36,24 +36,24 @@ func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
|
|||
}
|
||||
|
||||
type postProposalReq struct {
|
||||
BaseReq baseReq `json:"base_req"`
|
||||
Title string `json:"title"` // Title of the proposal
|
||||
Description string `json:"description"` // Description of the proposal
|
||||
ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
|
||||
Proposer string `json:"proposer"` // Address of the proposer
|
||||
InitialDeposit sdk.Coins `json:"initial_deposit"` // Coins to add to the proposal's deposit
|
||||
BaseReq baseReq `json:"base_req"`
|
||||
Title string `json:"title"` // Title of the proposal
|
||||
Description string `json:"description"` // Description of the proposal
|
||||
ProposalType gov.ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
|
||||
Proposer sdk.AccAddress `json:"proposer"` // Address of the proposer
|
||||
InitialDeposit sdk.Coins `json:"initial_deposit"` // Coins to add to the proposal's deposit
|
||||
}
|
||||
|
||||
type depositReq struct {
|
||||
BaseReq baseReq `json:"base_req"`
|
||||
Depositer string `json:"depositer"` // Address of the depositer
|
||||
Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit
|
||||
BaseReq baseReq `json:"base_req"`
|
||||
Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer
|
||||
Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit
|
||||
}
|
||||
|
||||
type voteReq struct {
|
||||
BaseReq baseReq `json:"base_req"`
|
||||
Voter string `json:"voter"` // address of the voter
|
||||
Option string `json:"option"` // option from OptionSet chosen by the voter
|
||||
BaseReq baseReq `json:"base_req"`
|
||||
Voter sdk.AccAddress `json:"voter"` // address of the voter
|
||||
Option gov.VoteOption `json:"option"` // option from OptionSet chosen by the voter
|
||||
}
|
||||
|
||||
func postProposalHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc {
|
||||
|
@ -68,20 +68,8 @@ func postProposalHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.Handle
|
|||
return
|
||||
}
|
||||
|
||||
proposer, err := sdk.AccAddressFromBech32(req.Proposer)
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
proposalTypeByte, err := gov.StringToProposalType(req.ProposalType)
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// create the message
|
||||
msg := gov.NewMsgSubmitProposal(req.Title, req.Description, proposalTypeByte, proposer, req.InitialDeposit)
|
||||
msg := gov.NewMsgSubmitProposal(req.Title, req.Description, req.ProposalType, req.Proposer, req.InitialDeposit)
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
|
@ -117,19 +105,12 @@ func depositHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !req.BaseReq.baseReqValidate(w) {
|
||||
return
|
||||
}
|
||||
|
||||
depositer, err := sdk.AccAddressFromBech32(req.Depositer)
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// create the message
|
||||
msg := gov.NewMsgDeposit(depositer, proposalID, req.Amount)
|
||||
msg := gov.NewMsgDeposit(req.Depositer, proposalID, req.Amount)
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
|
@ -165,25 +146,12 @@ func voteHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !req.BaseReq.baseReqValidate(w) {
|
||||
return
|
||||
}
|
||||
|
||||
voter, err := sdk.AccAddressFromBech32(req.Voter)
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
voteOptionByte, err := gov.StringToVoteOption(req.Option)
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// create the message
|
||||
msg := gov.NewMsgVote(voter, proposalID, voteOptionByte)
|
||||
msg := gov.NewMsgVote(req.Voter, proposalID, req.Option)
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
writeErr(&w, http.StatusBadRequest, err.Error())
|
||||
|
@ -225,8 +193,7 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
var proposal gov.Proposal
|
||||
cdc.MustUnmarshalBinary(res, &proposal)
|
||||
proposalRest := gov.ProposalToRest(proposal)
|
||||
output, err := wire.MarshalJSONIndent(cdc, proposalRest)
|
||||
output, err := wire.MarshalJSONIndent(cdc, proposal)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -291,8 +258,7 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
var deposit gov.Deposit
|
||||
cdc.MustUnmarshalBinary(res, &deposit)
|
||||
depositRest := gov.DepositToRest(deposit)
|
||||
output, err := wire.MarshalJSONIndent(cdc, depositRest)
|
||||
output, err := wire.MarshalJSONIndent(cdc, deposit)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -358,8 +324,7 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
|
||||
var vote gov.Vote
|
||||
cdc.MustUnmarshalBinary(res, &vote)
|
||||
voteRest := gov.VoteToRest(vote)
|
||||
output, err := wire.MarshalJSONIndent(cdc, voteRest)
|
||||
output, err := wire.MarshalJSONIndent(cdc, vote)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -411,7 +376,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
var maxProposalID int64
|
||||
cdc.MustUnmarshalBinary(res, &maxProposalID)
|
||||
|
||||
matchingProposals := []gov.ProposalRest{}
|
||||
matchingProposals := []gov.Proposal{}
|
||||
|
||||
for proposalID := int64(0); proposalID < maxProposalID; proposalID++ {
|
||||
if voterAddr != nil {
|
||||
|
@ -435,7 +400,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
|
|||
var proposal gov.Proposal
|
||||
cdc.MustUnmarshalBinary(res, &proposal)
|
||||
|
||||
matchingProposals = append(matchingProposals, gov.ProposalToRest(proposal))
|
||||
matchingProposals = append(matchingProposals, proposal)
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, matchingProposals)
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
package gov
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Type that represents VoteOption as a byte
|
||||
type VoteOption = byte
|
||||
|
||||
//nolint
|
||||
const (
|
||||
OptionEmpty VoteOption = 0x00
|
||||
OptionYes VoteOption = 0x01
|
||||
OptionAbstain VoteOption = 0x02
|
||||
OptionNo VoteOption = 0x03
|
||||
OptionNoWithVeto VoteOption = 0x04
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Vote
|
||||
|
@ -30,9 +22,80 @@ type Deposit struct {
|
|||
Amount sdk.Coins `json:"amount"` // Deposit amount
|
||||
}
|
||||
|
||||
// ProposalTypeToString for pretty prints of ProposalType
|
||||
func VoteOptionToString(option VoteOption) string {
|
||||
switch option {
|
||||
// Type that represents VoteOption as a byte
|
||||
type VoteOption byte
|
||||
|
||||
//nolint
|
||||
const (
|
||||
OptionEmpty VoteOption = 0x00
|
||||
OptionYes VoteOption = 0x01
|
||||
OptionAbstain VoteOption = 0x02
|
||||
OptionNo VoteOption = 0x03
|
||||
OptionNoWithVeto VoteOption = 0x04
|
||||
)
|
||||
|
||||
// String to proposalType byte. Returns ff if invalid.
|
||||
func VoteOptionFromString(str string) (VoteOption, error) {
|
||||
switch str {
|
||||
case "Yes":
|
||||
return OptionYes, nil
|
||||
case "Abstain":
|
||||
return OptionAbstain, nil
|
||||
case "No":
|
||||
return OptionNo, nil
|
||||
case "NoWithVeto":
|
||||
return OptionNoWithVeto, nil
|
||||
default:
|
||||
return VoteOption(0xff), errors.Errorf("'%s' is not a valid vote option", str)
|
||||
}
|
||||
}
|
||||
|
||||
// Is defined VoteOption
|
||||
func validVoteOption(option VoteOption) bool {
|
||||
if option == OptionYes ||
|
||||
option == OptionAbstain ||
|
||||
option == OptionNo ||
|
||||
option == OptionNoWithVeto {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Marshal needed for protobuf compatibility
|
||||
func (vo VoteOption) Marshal() ([]byte, error) {
|
||||
return []byte{byte(vo)}, nil
|
||||
}
|
||||
|
||||
// Unmarshal needed for protobuf compatibility
|
||||
func (vo *VoteOption) Unmarshal(data []byte) error {
|
||||
*vo = VoteOption(data[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshals to JSON using string
|
||||
func (vo VoteOption) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(vo.String())
|
||||
}
|
||||
|
||||
// Unmarshals from JSON assuming Bech32 encoding
|
||||
func (vo *VoteOption) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
err := json.Unmarshal(data, &s)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bz2, err := VoteOptionFromString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*vo = bz2
|
||||
return nil
|
||||
}
|
||||
|
||||
// Turns VoteOption byte to String
|
||||
func (vo VoteOption) String() string {
|
||||
switch vo {
|
||||
case OptionYes:
|
||||
return "Yes"
|
||||
case OptionAbstain:
|
||||
|
@ -46,63 +109,12 @@ func VoteOptionToString(option VoteOption) string {
|
|||
}
|
||||
}
|
||||
|
||||
func validVoteOption(option VoteOption) bool {
|
||||
if option == OptionYes ||
|
||||
option == OptionAbstain ||
|
||||
option == OptionNo ||
|
||||
option == OptionNoWithVeto {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String to proposalType byte. Returns ff if invalid.
|
||||
func StringToVoteOption(str string) (VoteOption, sdk.Error) {
|
||||
switch str {
|
||||
case "Yes":
|
||||
return OptionYes, nil
|
||||
case "Abstain":
|
||||
return OptionAbstain, nil
|
||||
case "No":
|
||||
return OptionNo, nil
|
||||
case "NoWithVeto":
|
||||
return OptionNoWithVeto, nil
|
||||
// For Printf / Sprintf, returns bech32 when using %s
|
||||
func (vo VoteOption) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
s.Write([]byte(fmt.Sprintf("%s", vo.String())))
|
||||
default:
|
||||
return VoteOption(0xff), ErrInvalidVote(DefaultCodespace, str)
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// REST
|
||||
|
||||
// Rest Deposits
|
||||
type DepositRest struct {
|
||||
Depositer sdk.AccAddress `json:"depositer"` // address of the depositer
|
||||
ProposalID int64 `json:"proposal_id"` // proposalID of the proposal
|
||||
Amount sdk.Coins `json:"option"`
|
||||
}
|
||||
|
||||
// Turn any Deposit to a DepositRest
|
||||
func DepositToRest(deposit Deposit) DepositRest {
|
||||
return DepositRest{
|
||||
Depositer: deposit.Depositer,
|
||||
ProposalID: deposit.ProposalID,
|
||||
Amount: deposit.Amount,
|
||||
}
|
||||
}
|
||||
|
||||
// Rest Votes
|
||||
type VoteRest struct {
|
||||
Voter sdk.AccAddress `json:"voter"` // address of the voter
|
||||
ProposalID int64 `json:"proposal_id"` // proposalID of the proposal
|
||||
Option string `json:"option"`
|
||||
}
|
||||
|
||||
// Turn any Vote to a VoteRest
|
||||
func VoteToRest(vote Vote) VoteRest {
|
||||
return VoteRest{
|
||||
Voter: vote.Voter,
|
||||
ProposalID: vote.ProposalID,
|
||||
Option: VoteOptionToString(vote.Option),
|
||||
s.Write([]byte(fmt.Sprintf("%v", byte(vo))))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ const (
|
|||
CodeInvalidProposalType sdk.CodeType = 8
|
||||
CodeInvalidVote sdk.CodeType = 9
|
||||
CodeInvalidGenesis sdk.CodeType = 10
|
||||
CodeInvalidProposalStatus sdk.CodeType = 11
|
||||
)
|
||||
|
||||
//----------------------------------------
|
||||
|
@ -53,12 +54,12 @@ func ErrInvalidDescription(codespace sdk.CodespaceType, description string) sdk.
|
|||
return sdk.NewError(codespace, CodeInvalidDescription, fmt.Sprintf("Proposal Desciption '%s' is not valid", description))
|
||||
}
|
||||
|
||||
func ErrInvalidProposalType(codespace sdk.CodespaceType, strProposalType string) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidProposalType, fmt.Sprintf("Proposal Type '%s' is not valid", strProposalType))
|
||||
func ErrInvalidProposalType(codespace sdk.CodespaceType, proposalType ProposalKind) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidProposalType, fmt.Sprintf("Proposal Type '%s' is not valid", proposalType))
|
||||
}
|
||||
|
||||
func ErrInvalidVote(codespace sdk.CodespaceType, strOption string) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidVote, fmt.Sprintf("'%s' is not a valid voting option", strOption))
|
||||
func ErrInvalidVote(codespace sdk.CodespaceType, voteOption VoteOption) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidVote, fmt.Sprintf("'%v' is not a valid voting option", voteOption))
|
||||
}
|
||||
|
||||
func ErrInvalidGenesis(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
|
|
|
@ -48,7 +48,7 @@ func (keeper Keeper) WireCodec() *wire.Codec {
|
|||
// Proposals
|
||||
|
||||
// Creates a NewProposal
|
||||
func (keeper Keeper) NewTextProposal(ctx sdk.Context, title string, description string, proposalType byte) Proposal {
|
||||
func (keeper Keeper) NewTextProposal(ctx sdk.Context, title string, description string, proposalType ProposalKind) Proposal {
|
||||
proposalID, err := keeper.getNewProposalID(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
|
@ -165,8 +165,8 @@ func (keeper Keeper) AddVote(ctx sdk.Context, proposalID int64, voterAddr sdk.Ac
|
|||
return ErrInactiveProposal(keeper.codespace, proposalID)
|
||||
}
|
||||
|
||||
if option != OptionYes && option != OptionAbstain && option != OptionNo && option != OptionNoWithVeto {
|
||||
return ErrInvalidVote(keeper.codespace, VoteOptionToString(option))
|
||||
if !validVoteOption(option) {
|
||||
return ErrInvalidVote(keeper.codespace, option)
|
||||
}
|
||||
|
||||
vote := Vote{
|
||||
|
|
|
@ -41,7 +41,7 @@ func (msg MsgSubmitProposal) ValidateBasic() sdk.Error {
|
|||
return ErrInvalidDescription(DefaultCodespace, msg.Description) // TODO: Proper Error
|
||||
}
|
||||
if !validProposalType(msg.ProposalType) {
|
||||
return ErrInvalidProposalType(DefaultCodespace, ProposalTypeToString(msg.ProposalType))
|
||||
return ErrInvalidProposalType(DefaultCodespace, msg.ProposalType)
|
||||
}
|
||||
if len(msg.Proposer) == 0 {
|
||||
return sdk.ErrInvalidAddress(msg.Proposer.String())
|
||||
|
@ -56,7 +56,7 @@ func (msg MsgSubmitProposal) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
func (msg MsgSubmitProposal) String() string {
|
||||
return fmt.Sprintf("MsgSubmitProposal{%v, %v, %v, %v}", msg.Title, msg.Description, ProposalTypeToString(msg.ProposalType), msg.InitialDeposit)
|
||||
return fmt.Sprintf("MsgSubmitProposal{%s, %s, %s, %v}", msg.Title, msg.Description, msg.ProposalType, msg.InitialDeposit)
|
||||
}
|
||||
|
||||
// Implements Msg.
|
||||
|
@ -66,19 +66,7 @@ func (msg MsgSubmitProposal) Get(key interface{}) (value interface{}) {
|
|||
|
||||
// Implements Msg.
|
||||
func (msg MsgSubmitProposal) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
ProposalType string `json:"proposal_type"`
|
||||
Proposer sdk.AccAddress `json:"proposer"`
|
||||
InitialDeposit sdk.Coins `json:"deposit"`
|
||||
}{
|
||||
Title: msg.Title,
|
||||
Description: msg.Description,
|
||||
ProposalType: ProposalTypeToString(msg.ProposalType),
|
||||
Proposer: msg.Proposer,
|
||||
InitialDeposit: msg.InitialDeposit,
|
||||
})
|
||||
b, err := msgCdc.MarshalJSON(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -127,7 +115,7 @@ func (msg MsgDeposit) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
func (msg MsgDeposit) String() string {
|
||||
return fmt.Sprintf("MsgDeposit{%v=>%v: %v}", msg.Depositer, msg.ProposalID, msg.Amount)
|
||||
return fmt.Sprintf("MsgDeposit{%s=>%v: %v}", msg.Depositer, msg.ProposalID, msg.Amount)
|
||||
}
|
||||
|
||||
// Implements Msg.
|
||||
|
@ -137,15 +125,7 @@ func (msg MsgDeposit) Get(key interface{}) (value interface{}) {
|
|||
|
||||
// Implements Msg.
|
||||
func (msg MsgDeposit) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
ProposalID int64 `json:"proposalID"`
|
||||
Depositer sdk.AccAddress `json:"proposer"`
|
||||
Amount sdk.Coins `json:"deposit"`
|
||||
}{
|
||||
ProposalID: msg.ProposalID,
|
||||
Depositer: msg.Depositer,
|
||||
Amount: msg.Amount,
|
||||
})
|
||||
b, err := msgCdc.MarshalJSON(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -185,13 +165,13 @@ func (msg MsgVote) ValidateBasic() sdk.Error {
|
|||
return ErrUnknownProposal(DefaultCodespace, msg.ProposalID)
|
||||
}
|
||||
if !validVoteOption(msg.Option) {
|
||||
return ErrInvalidVote(DefaultCodespace, VoteOptionToString(msg.Option))
|
||||
return ErrInvalidVote(DefaultCodespace, msg.Option)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg MsgVote) String() string {
|
||||
return fmt.Sprintf("MsgVote{%v - %v}", msg.ProposalID, msg.Option)
|
||||
return fmt.Sprintf("MsgVote{%v - %s}", msg.ProposalID, msg.Option)
|
||||
}
|
||||
|
||||
// Implements Msg.
|
||||
|
@ -201,15 +181,7 @@ func (msg MsgVote) Get(key interface{}) (value interface{}) {
|
|||
|
||||
// Implements Msg.
|
||||
func (msg MsgVote) GetSignBytes() []byte {
|
||||
b, err := msgCdc.MarshalJSON(struct {
|
||||
ProposalID int64 `json:"proposalID"`
|
||||
Voter sdk.AccAddress `json:"voter"`
|
||||
Option string `json:"option"`
|
||||
}{
|
||||
ProposalID: msg.ProposalID,
|
||||
Voter: msg.Voter,
|
||||
Option: VoteOptionToString(msg.Option),
|
||||
})
|
||||
b, err := msgCdc.MarshalJSON(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func TestMsgSubmitProposal(t *testing.T) {
|
|||
_, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{})
|
||||
tests := []struct {
|
||||
title, description string
|
||||
proposalType byte
|
||||
proposalType ProposalKind
|
||||
proposerAddr sdk.AccAddress
|
||||
initialDeposit sdk.Coins
|
||||
expectPass bool
|
||||
|
|
|
@ -1,27 +1,14 @@
|
|||
package gov
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Type that represents Status as a byte
|
||||
type VoteStatus = byte
|
||||
|
||||
// Type that represents Proposal Type as a byte
|
||||
type ProposalKind = byte
|
||||
|
||||
//nolint
|
||||
const (
|
||||
StatusDepositPeriod VoteStatus = 0x01
|
||||
StatusVotingPeriod VoteStatus = 0x02
|
||||
StatusPassed VoteStatus = 0x03
|
||||
StatusRejected VoteStatus = 0x04
|
||||
|
||||
ProposalTypeText ProposalKind = 0x01
|
||||
ProposalTypeParameterChange ProposalKind = 0x02
|
||||
ProposalTypeSoftwareUpgrade ProposalKind = 0x03
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Proposal interface
|
||||
type Proposal interface {
|
||||
|
@ -37,8 +24,8 @@ type Proposal interface {
|
|||
GetProposalType() ProposalKind
|
||||
SetProposalType(ProposalKind)
|
||||
|
||||
GetStatus() VoteStatus
|
||||
SetStatus(VoteStatus)
|
||||
GetStatus() ProposalStatus
|
||||
SetStatus(ProposalStatus)
|
||||
|
||||
GetSubmitBlock() int64
|
||||
SetSubmitBlock(int64)
|
||||
|
@ -73,7 +60,7 @@ type TextProposal struct {
|
|||
Description string `json:"description"` // Description of the proposal
|
||||
ProposalType ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
|
||||
|
||||
Status VoteStatus `json:"string"` // Status of the Proposal {Pending, Active, Passed, Rejected}
|
||||
Status ProposalStatus `json:"string"` // Status of the Proposal {Pending, Active, Passed, Rejected}
|
||||
|
||||
SubmitBlock int64 `json:"submit_block"` // Height of the block where TxGovSubmitProposal was included
|
||||
TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit
|
||||
|
@ -93,8 +80,8 @@ func (tp TextProposal) GetDescription() string { return tp.D
|
|||
func (tp *TextProposal) SetDescription(description string) { tp.Description = description }
|
||||
func (tp TextProposal) GetProposalType() ProposalKind { return tp.ProposalType }
|
||||
func (tp *TextProposal) SetProposalType(proposalType ProposalKind) { tp.ProposalType = proposalType }
|
||||
func (tp TextProposal) GetStatus() VoteStatus { return tp.Status }
|
||||
func (tp *TextProposal) SetStatus(status VoteStatus) { tp.Status = status }
|
||||
func (tp TextProposal) GetStatus() ProposalStatus { return tp.Status }
|
||||
func (tp *TextProposal) SetStatus(status ProposalStatus) { tp.Status = status }
|
||||
func (tp TextProposal) GetSubmitBlock() int64 { return tp.SubmitBlock }
|
||||
func (tp *TextProposal) SetSubmitBlock(submitBlock int64) { tp.SubmitBlock = submitBlock }
|
||||
func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit }
|
||||
|
@ -104,12 +91,82 @@ func (tp *TextProposal) SetVotingStartBlock(votingStartBlock int64) {
|
|||
tp.VotingStartBlock = votingStartBlock
|
||||
}
|
||||
|
||||
// Current Active Proposals
|
||||
//-----------------------------------------------------------
|
||||
// ProposalQueue
|
||||
type ProposalQueue []int64
|
||||
|
||||
// ProposalTypeToString for pretty prints of ProposalType
|
||||
func ProposalTypeToString(proposalType ProposalKind) string {
|
||||
switch proposalType {
|
||||
//-----------------------------------------------------------
|
||||
// ProposalKind
|
||||
|
||||
// Type that represents Proposal Type as a byte
|
||||
type ProposalKind byte
|
||||
|
||||
//nolint
|
||||
const (
|
||||
ProposalTypeText ProposalKind = 0x01
|
||||
ProposalTypeParameterChange ProposalKind = 0x02
|
||||
ProposalTypeSoftwareUpgrade ProposalKind = 0x03
|
||||
)
|
||||
|
||||
// String to proposalType byte. Returns ff if invalid.
|
||||
func ProposalTypeFromString(str string) (ProposalKind, error) {
|
||||
switch str {
|
||||
case "Text":
|
||||
return ProposalTypeText, nil
|
||||
case "ParameterChange":
|
||||
return ProposalTypeParameterChange, nil
|
||||
case "SoftwareUpgrade":
|
||||
return ProposalTypeSoftwareUpgrade, nil
|
||||
default:
|
||||
return ProposalKind(0xff), errors.Errorf("'%s' is not a valid proposal type", str)
|
||||
}
|
||||
}
|
||||
|
||||
// is defined ProposalType?
|
||||
func validProposalType(pt ProposalKind) bool {
|
||||
if pt == ProposalTypeText ||
|
||||
pt == ProposalTypeParameterChange ||
|
||||
pt == ProposalTypeSoftwareUpgrade {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Marshal needed for protobuf compatibility
|
||||
func (pt ProposalKind) Marshal() ([]byte, error) {
|
||||
return []byte{byte(pt)}, nil
|
||||
}
|
||||
|
||||
// Unmarshal needed for protobuf compatibility
|
||||
func (pt *ProposalKind) Unmarshal(data []byte) error {
|
||||
*pt = ProposalKind(data[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshals to JSON using string
|
||||
func (pt ProposalKind) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(pt.String())
|
||||
}
|
||||
|
||||
// Unmarshals from JSON assuming Bech32 encoding
|
||||
func (pt *ProposalKind) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
err := json.Unmarshal(data, &s)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bz2, err := ProposalTypeFromString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*pt = bz2
|
||||
return nil
|
||||
}
|
||||
|
||||
// Turns VoteOption byte to String
|
||||
func (pt ProposalKind) String() string {
|
||||
switch pt {
|
||||
case 0x00:
|
||||
return "Text"
|
||||
case 0x01:
|
||||
|
@ -121,31 +178,91 @@ func ProposalTypeToString(proposalType ProposalKind) string {
|
|||
}
|
||||
}
|
||||
|
||||
func validProposalType(proposalType ProposalKind) bool {
|
||||
if proposalType == ProposalTypeText ||
|
||||
proposalType == ProposalTypeParameterChange ||
|
||||
proposalType == ProposalTypeSoftwareUpgrade {
|
||||
// For Printf / Sprintf, returns bech32 when using %s
|
||||
func (pt ProposalKind) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
s.Write([]byte(fmt.Sprintf("%s", pt.String())))
|
||||
default:
|
||||
s.Write([]byte(fmt.Sprintf("%v", pt)))
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// ProposalStatus
|
||||
|
||||
// Type that represents Proposal Status as a byte
|
||||
type ProposalStatus byte
|
||||
|
||||
//nolint
|
||||
const (
|
||||
StatusDepositPeriod ProposalStatus = 0x01
|
||||
StatusVotingPeriod ProposalStatus = 0x02
|
||||
StatusPassed ProposalStatus = 0x03
|
||||
StatusRejected ProposalStatus = 0x04
|
||||
)
|
||||
|
||||
// ProposalStatusToString turns a string into a ProposalStatus
|
||||
func ProposalStatusFromString(str string) (ProposalStatus, error) {
|
||||
switch str {
|
||||
case "DepositPeriod":
|
||||
return StatusDepositPeriod, nil
|
||||
case "VotingPeriod":
|
||||
return StatusVotingPeriod, nil
|
||||
case "Passed":
|
||||
return StatusPassed, nil
|
||||
case "Rejected":
|
||||
return StatusRejected, nil
|
||||
default:
|
||||
return ProposalStatus(0xff), errors.Errorf("'%s' is not a valid proposal status", str)
|
||||
}
|
||||
}
|
||||
|
||||
// is defined ProposalType?
|
||||
func validProposalStatus(status ProposalStatus) bool {
|
||||
if status == StatusDepositPeriod ||
|
||||
status == StatusVotingPeriod ||
|
||||
status == StatusPassed ||
|
||||
status == StatusRejected {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String to proposalType byte. Returns ff if invalid.
|
||||
func StringToProposalType(str string) (ProposalKind, sdk.Error) {
|
||||
switch str {
|
||||
case "Text":
|
||||
return ProposalTypeText, nil
|
||||
case "ParameterChange":
|
||||
return ProposalTypeParameterChange, nil
|
||||
case "SoftwareUpgrade":
|
||||
return ProposalTypeSoftwareUpgrade, nil
|
||||
default:
|
||||
return ProposalKind(0xff), ErrInvalidProposalType(DefaultCodespace, str)
|
||||
}
|
||||
// Marshal needed for protobuf compatibility
|
||||
func (status ProposalStatus) Marshal() ([]byte, error) {
|
||||
return []byte{byte(status)}, nil
|
||||
}
|
||||
|
||||
// StatusToString for pretty prints of Status
|
||||
func StatusToString(status VoteStatus) string {
|
||||
// Unmarshal needed for protobuf compatibility
|
||||
func (status *ProposalStatus) Unmarshal(data []byte) error {
|
||||
*status = ProposalStatus(data[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshals to JSON using string
|
||||
func (status ProposalStatus) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(status.String())
|
||||
}
|
||||
|
||||
// Unmarshals from JSON assuming Bech32 encoding
|
||||
func (status *ProposalStatus) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
err := json.Unmarshal(data, &s)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bz2, err := ProposalStatusFromString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*status = bz2
|
||||
return nil
|
||||
}
|
||||
|
||||
// Turns VoteStatus byte to String
|
||||
func (status ProposalStatus) String() string {
|
||||
switch status {
|
||||
case StatusDepositPeriod:
|
||||
return "DepositPeriod"
|
||||
|
@ -160,45 +277,12 @@ func StatusToString(status VoteStatus) string {
|
|||
}
|
||||
}
|
||||
|
||||
// StatusToString for pretty prints of Status
|
||||
func StringToStatus(status string) VoteStatus {
|
||||
switch status {
|
||||
case "DepositPeriod":
|
||||
return StatusDepositPeriod
|
||||
case "VotingPeriod":
|
||||
return StatusVotingPeriod
|
||||
case "Passed":
|
||||
return StatusPassed
|
||||
case "Rejected":
|
||||
return StatusRejected
|
||||
// For Printf / Sprintf, returns bech32 when using %s
|
||||
func (status ProposalStatus) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
s.Write([]byte(fmt.Sprintf("%s", status.String())))
|
||||
default:
|
||||
return VoteStatus(0xff)
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Rest Proposals
|
||||
type ProposalRest struct {
|
||||
ProposalID int64 `json:"proposal_id"` // ID of the proposal
|
||||
Title string `json:"title"` // Title of the proposal
|
||||
Description string `json:"description"` // Description of the proposal
|
||||
ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
|
||||
Status string `json:"string"` // Status of the Proposal {Pending, Active, Passed, Rejected}
|
||||
SubmitBlock int64 `json:"submit_block"` // Height of the block where TxGovSubmitProposal was included
|
||||
TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit
|
||||
VotingStartBlock int64 `json:"voting_start_block"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached
|
||||
}
|
||||
|
||||
// Turn any Proposal to a ProposalRest
|
||||
func ProposalToRest(proposal Proposal) ProposalRest {
|
||||
return ProposalRest{
|
||||
ProposalID: proposal.GetProposalID(),
|
||||
Title: proposal.GetTitle(),
|
||||
Description: proposal.GetDescription(),
|
||||
ProposalType: ProposalTypeToString(proposal.GetProposalType()),
|
||||
Status: StatusToString(proposal.GetStatus()),
|
||||
SubmitBlock: proposal.GetSubmitBlock(),
|
||||
TotalDeposit: proposal.GetTotalDeposit(),
|
||||
VotingStartBlock: proposal.GetVotingStartBlock(),
|
||||
s.Write([]byte(fmt.Sprintf("%v", status)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
|
|||
|
||||
// move coins from the msg.Address account to a (self-delegation) delegator account
|
||||
// the validator account and global shares are updated within here
|
||||
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator)
|
||||
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
|
|||
if validator.Revoked == true {
|
||||
return ErrValidatorRevoked(k.Codespace()).Result()
|
||||
}
|
||||
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator)
|
||||
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
|
|
@ -268,6 +268,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond)
|
||||
params := setInstantUnbondPeriod(keeper, ctx)
|
||||
denom := params.BondDenom
|
||||
|
||||
// create validator, delegate
|
||||
validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1]
|
||||
|
@ -276,10 +277,17 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
// initial balance
|
||||
amt1 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom)
|
||||
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, initBond)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
|
||||
|
||||
// balance should have been subtracted after delegation
|
||||
amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom)
|
||||
require.Equal(t, amt1.Sub(sdk.NewInt(initBond)).Int64(), amt2.Int64(), "expected coins to be subtracted")
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64())
|
||||
|
@ -528,8 +536,9 @@ func TestUnbondingPeriod(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRedelegationPeriod(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
ctx, AccMapper, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr, validatorAddr2 := keep.Addrs[0], keep.Addrs[1]
|
||||
denom := keeper.GetParams(ctx).BondDenom
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
|
@ -538,18 +547,32 @@ func TestRedelegationPeriod(t *testing.T) {
|
|||
|
||||
// create the validators
|
||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
|
||||
// initial balance
|
||||
amt1 := AccMapper.GetAccount(ctx, validatorAddr).GetCoins().AmountOf(denom)
|
||||
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// balance should have been subtracted after creation
|
||||
amt2 := AccMapper.GetAccount(ctx, validatorAddr).GetCoins().AmountOf(denom)
|
||||
require.Equal(t, amt1.Sub(sdk.NewInt(10)).Int64(), amt2.Int64(), "expected coins to be subtracted")
|
||||
|
||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
bal1 := AccMapper.GetAccount(ctx, validatorAddr).GetCoins()
|
||||
|
||||
// begin redelegate
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(validatorAddr, validatorAddr, validatorAddr2, sdk.NewRat(10))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// origin account should not lose tokens as with a regular delegation
|
||||
bal2 := AccMapper.GetAccount(ctx, validatorAddr).GetCoins()
|
||||
require.Equal(t, bal1, bal2)
|
||||
|
||||
// cannot complete redelegation at same time
|
||||
msgCompleteRedelegate := NewMsgCompleteRedelegate(validatorAddr, validatorAddr, validatorAddr2)
|
||||
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
|
||||
|
|
|
@ -200,9 +200,9 @@ func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
|
|||
|
||||
//_____________________________________________________________________________________
|
||||
|
||||
// Perform a delegation, set/update everything necessary within the store
|
||||
// Perform a delegation, set/update everything necessary within the store.
|
||||
func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt sdk.Coin,
|
||||
validator types.Validator) (newShares sdk.Rat, err sdk.Error) {
|
||||
validator types.Validator, subtractAccount bool) (newShares sdk.Rat, err sdk.Error) {
|
||||
|
||||
// Get or create the delegator delegation
|
||||
delegation, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
|
||||
|
@ -214,12 +214,15 @@ func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt
|
|||
}
|
||||
}
|
||||
|
||||
// Account new shares, save
|
||||
pool := k.GetPool(ctx)
|
||||
_, _, err = k.coinKeeper.SubtractCoins(ctx, delegation.DelegatorAddr, sdk.Coins{bondAmt})
|
||||
if err != nil {
|
||||
return
|
||||
if subtractAccount {
|
||||
// Account new shares, save
|
||||
_, _, err = k.coinKeeper.SubtractCoins(ctx, delegation.DelegatorAddr, sdk.Coins{bondAmt})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount.Int64())
|
||||
delegation.Shares = delegation.Shares.Add(newShares)
|
||||
|
||||
|
@ -358,7 +361,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAd
|
|||
if !found {
|
||||
return types.ErrBadRedelegationDst(k.Codespace())
|
||||
}
|
||||
sharesCreated, err := k.Delegate(ctx, delegatorAddr, returnCoin, dstValidator)
|
||||
sharesCreated, err := k.Delegate(ctx, delegatorAddr, returnCoin, dstValidator, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -191,132 +191,152 @@ func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) {
|
|||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// perfom all the nessisary steps for when a validator changes its power
|
||||
// updates all validator stores as well as tendermint update store
|
||||
// may kick out validators if new validator is entering the bonded validator group
|
||||
// Perfom all the nessisary steps for when a validator changes its power. This
|
||||
// function updates all validator stores as well as tendermint update store.
|
||||
// It may kick out validators if new validator is entering the bonded validator
|
||||
// group.
|
||||
//
|
||||
// nolint: gocyclo
|
||||
// TODO: Remove above nolint, function needs to be simplified
|
||||
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.GetPool(ctx)
|
||||
ownerAddr := validator.Owner
|
||||
oldValidator, oldFound := k.GetValidator(ctx, validator.Owner)
|
||||
|
||||
// always update the main list ordered by owner address before exiting
|
||||
defer func() {
|
||||
k.SetValidator(ctx, validator)
|
||||
}()
|
||||
validator = k.updateForRevoking(ctx, oldFound, oldValidator, validator)
|
||||
powerIncreasing := k.getPowerIncreasing(ctx, oldFound, oldValidator, validator)
|
||||
validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator, validator)
|
||||
valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool)
|
||||
cliffPower := k.GetCliffValidatorPower(ctx)
|
||||
|
||||
// retrieve the old validator record
|
||||
oldValidator, oldFound := k.GetValidator(ctx, ownerAddr)
|
||||
switch {
|
||||
// if already bonded and power increasing only need to update tendermint
|
||||
case powerIncreasing && !validator.Revoked &&
|
||||
(oldFound && oldValidator.Status() == sdk.Bonded):
|
||||
|
||||
if validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
validator = k.unbondValidator(ctx, validator)
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bz)
|
||||
|
||||
// if is a new validator and the new power is less than the cliff validator
|
||||
case cliffPower != nil && !oldFound &&
|
||||
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
|
||||
// skip to completion
|
||||
|
||||
// if was unbonded and the new power is less than the cliff validator
|
||||
case cliffPower != nil &&
|
||||
(oldFound && oldValidator.Status() == sdk.Unbonded) &&
|
||||
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
|
||||
// skip to completion
|
||||
|
||||
// default case - validator was either:
|
||||
// a) not-bonded and now has power-rank greater than cliff validator
|
||||
// b) bonded and now has decreased in power
|
||||
default:
|
||||
|
||||
// update the validator set for this validator
|
||||
updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
|
||||
if updated { // updates to validator occurred to be updated
|
||||
validator = updatedVal
|
||||
} else {
|
||||
|
||||
// if decreased in power but still bonded, update Tendermint validator
|
||||
// (if updatedVal is set, the validator has changed bonding status)
|
||||
stillBonded := oldFound && oldValidator.Status() == sdk.Bonded
|
||||
if stillBonded && oldValidator.PoolShares.Bonded().GT(validator.PoolShares.Bonded()) {
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bz)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
k.SetValidator(ctx, validator)
|
||||
return validator
|
||||
}
|
||||
|
||||
func (k Keeper) updateForRevoking(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
|
||||
if newValidator.Revoked && oldFound && oldValidator.Status() == sdk.Bonded {
|
||||
newValidator = k.unbondValidator(ctx, newValidator)
|
||||
|
||||
// need to also clear the cliff validator spot because the revoke has
|
||||
// opened up a new spot which will be filled when
|
||||
// updateValidatorsBonded is called
|
||||
k.clearCliffValidator(ctx)
|
||||
}
|
||||
return newValidator
|
||||
}
|
||||
|
||||
powerIncreasing := false
|
||||
if oldFound && oldValidator.PoolShares.Bonded().LT(validator.PoolShares.Bonded()) {
|
||||
powerIncreasing = true
|
||||
func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) bool {
|
||||
if oldFound && oldValidator.PoolShares.Bonded().LT(newValidator.PoolShares.Bonded()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// get the bond height and incremented intra-tx counter
|
||||
func (k Keeper) bondIncrement(ctx sdk.Context, oldFound bool, oldValidator,
|
||||
newValidator types.Validator) (height int64, intraTxCounter int16) {
|
||||
|
||||
// if already a validator, copy the old block height and counter, else set them
|
||||
if oldFound && oldValidator.Status() == sdk.Bonded {
|
||||
validator.BondHeight = oldValidator.BondHeight
|
||||
validator.BondIntraTxCounter = oldValidator.BondIntraTxCounter
|
||||
} else {
|
||||
validator.BondHeight = ctx.BlockHeight()
|
||||
counter := k.GetIntraTxCounter(ctx)
|
||||
validator.BondIntraTxCounter = counter
|
||||
k.SetIntraTxCounter(ctx, counter+1)
|
||||
height = oldValidator.BondHeight
|
||||
intraTxCounter = oldValidator.BondIntraTxCounter
|
||||
return
|
||||
}
|
||||
height = ctx.BlockHeight()
|
||||
counter := k.GetIntraTxCounter(ctx)
|
||||
intraTxCounter = counter
|
||||
k.SetIntraTxCounter(ctx, counter+1)
|
||||
return
|
||||
}
|
||||
|
||||
func (k Keeper) updateValidatorPower(ctx sdk.Context, oldFound bool, oldValidator,
|
||||
newValidator types.Validator, pool types.Pool) (valPower []byte) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// update the list ordered by voting power
|
||||
if oldFound {
|
||||
store.Delete(GetValidatorsByPowerIndexKey(oldValidator, pool))
|
||||
}
|
||||
valPower := GetValidatorsByPowerIndexKey(validator, pool)
|
||||
store.Set(valPower, validator.Owner)
|
||||
valPower = GetValidatorsByPowerIndexKey(newValidator, pool)
|
||||
store.Set(valPower, newValidator.Owner)
|
||||
|
||||
// efficiency case:
|
||||
// if already bonded and power increasing only need to update tendermint
|
||||
if powerIncreasing && !validator.Revoked && oldValidator.Status() == sdk.Bonded {
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
||||
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
|
||||
return validator
|
||||
}
|
||||
|
||||
// efficiency case:
|
||||
// if was unbonded/or is a new validator - and the new power is less than the cliff validator
|
||||
cliffPower := k.GetCliffValidatorPower(ctx)
|
||||
if cliffPower != nil &&
|
||||
(!oldFound || (oldFound && oldValidator.Status() == sdk.Unbonded)) &&
|
||||
bytes.Compare(valPower, cliffPower) == -1 { //(valPower < cliffPower
|
||||
return validator
|
||||
}
|
||||
|
||||
// update the validator set for this validator
|
||||
updatedVal := k.UpdateBondedValidators(ctx, validator)
|
||||
if updatedVal.Owner != nil { // updates to validator occurred to be updated
|
||||
validator = updatedVal
|
||||
}
|
||||
// if decreased in power but still bonded, update Tendermint validator
|
||||
// (if updatedVal is set, the validator has changed bonding status)
|
||||
stillBonded := oldFound && oldValidator.Status() == sdk.Bonded && updatedVal.Owner == nil
|
||||
if stillBonded && oldValidator.PoolShares.Bonded().GT(validator.PoolShares.Bonded()) {
|
||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
||||
store.Set(GetTendermintUpdatesKey(ownerAddr), bz)
|
||||
}
|
||||
return validator
|
||||
return valPower
|
||||
}
|
||||
|
||||
// Update the validator group and kick out any old validators. In addition this
|
||||
// function adds (or doesn't add) a validator which has updated its bonded
|
||||
// tokens to the validator group. -> this validator is specified through the
|
||||
// updatedValidatorAddr term.
|
||||
// Update the bonded validator group based on a change to the validator
|
||||
// affectedValidator. This function potentially adds the affectedValidator to
|
||||
// the bonded validator group which kicks out the cliff validator. Under this
|
||||
// situation this function returns the updated affectedValidator.
|
||||
//
|
||||
// The correct subset is retrieved by iterating through an index of the
|
||||
// validators sorted by power, stored using the ValidatorsByPowerIndexKey.
|
||||
// Simultaneously the current validator records are updated in store with the
|
||||
// ValidatorsBondedIndexKey. This store is used to determine if a validator is a
|
||||
// validator without needing to iterate over the subspace as we do in
|
||||
// GetValidators.
|
||||
// The correct bonded subset of validators is retrieved by iterating through an
|
||||
// index of the validators sorted by power, stored using the
|
||||
// ValidatorsByPowerIndexKey. Simultaneously the current validator records are
|
||||
// updated in store with the ValidatorsBondedIndexKey. This store is used to
|
||||
// determine if a validator is a validator without needing to iterate over all
|
||||
// validators.
|
||||
//
|
||||
// Optionally also return the validator from a retrieve address if the validator has been bonded
|
||||
// nolint: gocyclo
|
||||
// TODO: Remove the above golint
|
||||
func (k Keeper) UpdateBondedValidators(ctx sdk.Context,
|
||||
affectedValidator types.Validator) (updatedVal types.Validator) {
|
||||
affectedValidator types.Validator) (updatedVal types.Validator, updated bool) {
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
kickCliffValidator := false
|
||||
oldCliffValidatorAddr := k.GetCliffValidator(ctx)
|
||||
|
||||
// add the actual validator power sorted store
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
var validator types.Validator
|
||||
var validator, validatorToBond types.Validator
|
||||
newValidatorBonded := false
|
||||
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
// TODO benchmark if we should read the current power and not write if it's the same
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
} else if len(oldCliffValidatorAddr) > 0 {
|
||||
k.clearCliffValidator(ctx)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// either retrieve the original validator from the store, or under the
|
||||
// situation that this is the "new validator" just use the validator
|
||||
// provided because it has not yet been updated in the main validator
|
||||
// store
|
||||
// situation that this is the "affected validator" just use the
|
||||
// validator provided because it has not yet been updated in the store
|
||||
ownerAddr := iterator.Value()
|
||||
if bytes.Equal(ownerAddr, affectedValidator.Owner) {
|
||||
validator = affectedValidator
|
||||
|
@ -328,39 +348,50 @@ func (k Keeper) UpdateBondedValidators(ctx sdk.Context,
|
|||
}
|
||||
}
|
||||
|
||||
// if not previously a validator (and unrevoked),
|
||||
// kick the cliff validator / bond this new validator
|
||||
if validator.Status() != sdk.Bonded && !validator.Revoked {
|
||||
kickCliffValidator = true
|
||||
|
||||
validator = k.bondValidator(ctx, validator)
|
||||
if bytes.Equal(ownerAddr, affectedValidator.Owner) {
|
||||
updatedVal = validator
|
||||
}
|
||||
}
|
||||
|
||||
// increment bondedValidatorsCount / get the validator to bond
|
||||
if !validator.Revoked {
|
||||
bondedValidatorsCount++
|
||||
} else {
|
||||
if validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
if validator.Status() != sdk.Bonded {
|
||||
validatorToBond = validator
|
||||
newValidatorBonded = true
|
||||
}
|
||||
bondedValidatorsCount++
|
||||
|
||||
// sanity check
|
||||
} else if validator.Status() == sdk.Bonded {
|
||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||
}
|
||||
|
||||
iterator.Next()
|
||||
}
|
||||
iterator.Close()
|
||||
|
||||
// perform the actual kicks
|
||||
if oldCliffValidatorAddr != nil && kickCliffValidator {
|
||||
validator, found := k.GetValidator(ctx, oldCliffValidatorAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, validator)
|
||||
// clear or set the cliff validator
|
||||
if bondedValidatorsCount == int(maxValidators) {
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
} else if len(oldCliffValidatorAddr) > 0 {
|
||||
k.clearCliffValidator(ctx)
|
||||
}
|
||||
|
||||
return
|
||||
// swap the cliff validator for a new validator if the affected validator was bonded
|
||||
if newValidatorBonded {
|
||||
|
||||
// unbond the cliff validator
|
||||
if oldCliffValidatorAddr != nil {
|
||||
cliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
|
||||
}
|
||||
k.unbondValidator(ctx, cliffVal)
|
||||
|
||||
}
|
||||
|
||||
// bond the new validator
|
||||
validator = k.bondValidator(ctx, validatorToBond)
|
||||
if bytes.Equal(validator.Owner, affectedValidator.Owner) {
|
||||
return validator, true
|
||||
}
|
||||
}
|
||||
return types.Validator{}, false
|
||||
}
|
||||
|
||||
// full update of the bonded validator set, many can be added/kicked
|
||||
|
@ -377,17 +408,14 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
|
|||
}
|
||||
iterator.Close()
|
||||
|
||||
// add the actual validator power sorted store
|
||||
oldCliffValidatorAddr := k.GetCliffValidator(ctx)
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
bondedValidatorsCount := 0
|
||||
|
||||
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest
|
||||
var validator types.Validator
|
||||
for {
|
||||
if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) {
|
||||
|
||||
if bondedValidatorsCount == int(maxValidators) { // is cliff validator
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -425,6 +453,13 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
|
|||
}
|
||||
iterator.Close()
|
||||
|
||||
// clear or set the cliff validator
|
||||
if bondedValidatorsCount == int(maxValidators) {
|
||||
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
|
||||
} else if len(oldCliffValidatorAddr) > 0 {
|
||||
k.clearCliffValidator(ctx)
|
||||
}
|
||||
|
||||
// perform the actual kicks
|
||||
kickOutValidators(k, ctx, toKickOut)
|
||||
return
|
||||
|
|
Loading…
Reference in New Issue