Merge branch 'develop' of https://github.com/cosmos/cosmos-sdk into develop
This commit is contained in:
commit
c0c31629c0
49
CHANGELOG.md
49
CHANGELOG.md
|
@ -1,6 +1,48 @@
|
|||
# Changelog
|
||||
|
||||
## 0.16.0 (TBD)
|
||||
## 0.18.0 (TBD)
|
||||
|
||||
FEATURES
|
||||
|
||||
* [x/auth] Added ability to change pubkey to auth module
|
||||
* [baseapp] baseapp now has settable functions for filtering peers by address/port & public key
|
||||
* [sdk] Gas consumption is now measured as transactions are executed
|
||||
* Transactions which run out of gas stop execution and revert state changes
|
||||
* A "simulate" query has been added to determine how much gas a transaction will need
|
||||
* Modules can include their own gas costs for execution of particular message types
|
||||
|
||||
## 0.17.1 (May 17, 2018)
|
||||
|
||||
Update to Tendermint v0.19.4 (fixes a consensus bug and improves logging)
|
||||
|
||||
## 0.17.0 (May 15, 2018)
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
* [stake] MarshalJSON -> MarshalBinary
|
||||
|
||||
FEATURES
|
||||
|
||||
* [gaiacli] Support queries for candidates, delegator-bonds
|
||||
* [gaiad] Added `gaiad export` command to export current state to JSON
|
||||
* [x/bank] Tx tags with sender/recipient for indexing & later retrieval
|
||||
* [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy
|
||||
|
||||
IMPROVEMENTS
|
||||
|
||||
* [gaiad] Update for Tendermint v0.19.3 (improve `/dump_consensus_state` and add
|
||||
`/consensus_state`)
|
||||
* [spec/ibc] Added spec!
|
||||
* [spec/stake] Cleanup structure, include details about slashing and
|
||||
auto-unbonding
|
||||
* [spec/governance] Fixup some names and pseudocode
|
||||
* NOTE: specs are still a work-in-progress ...
|
||||
|
||||
BUG FIXES
|
||||
|
||||
* Auto-sequencing now works correctly
|
||||
|
||||
## 0.16.0 (May 14th, 2018)
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
|
@ -15,10 +57,10 @@ BREAKING CHANGES
|
|||
* gaiad init now requires use of `--name` flag
|
||||
* Removed Get from Msg interface
|
||||
* types/rational now extends big.Rat
|
||||
* Queries against the store must be prefixed with the path "/store"
|
||||
|
||||
FEATURES:
|
||||
|
||||
* Added `gaiad export` command, which exports genesis information & current state
|
||||
* Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond
|
||||
* MountStoreWithDB without providing a custom store works.
|
||||
* Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI
|
||||
|
@ -30,12 +72,9 @@ FEATURES:
|
|||
* Context now has access to the application-configured logger
|
||||
* Add (non-proof) subspace query helper functions
|
||||
* Add more staking query functions: candidates, delegator-bonds
|
||||
* Bank module now tags transactions with sender/recipient for indexing & later retrieval
|
||||
* Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy
|
||||
|
||||
BUG FIXES
|
||||
* Gaia now uses stake, ported from github.com/cosmos/gaia
|
||||
* Auto-sequencing now works correctly
|
||||
|
||||
## 0.15.1 (April 29, 2018)
|
||||
|
||||
|
|
|
@ -87,14 +87,14 @@
|
|||
[[projects]]
|
||||
name = "github.com/gorilla/context"
|
||||
packages = ["."]
|
||||
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
|
||||
version = "v1.1"
|
||||
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/mux"
|
||||
packages = ["."]
|
||||
revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"
|
||||
version = "v1.6.1"
|
||||
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
|
||||
version = "v1.6.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
|
@ -159,7 +159,7 @@
|
|||
branch = "master"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
|
||||
revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pelletier/go-toml"
|
||||
|
@ -183,7 +183,7 @@
|
|||
branch = "master"
|
||||
name = "github.com/rcrowley/go-metrics"
|
||||
packages = ["."]
|
||||
revision = "d932a24a8ccb8fcadc993e5c6c58f93dac168294"
|
||||
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/afero"
|
||||
|
@ -250,7 +250,7 @@
|
|||
"leveldb/table",
|
||||
"leveldb/util"
|
||||
]
|
||||
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
|
||||
revision = "9637fa0b2f0db13c99d899b91007edb7df4610b7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tendermint/abci"
|
||||
|
@ -323,7 +323,6 @@
|
|||
"p2p",
|
||||
"p2p/conn",
|
||||
"p2p/pex",
|
||||
"p2p/trust",
|
||||
"p2p/upnp",
|
||||
"proxy",
|
||||
"rpc/client",
|
||||
|
@ -342,8 +341,8 @@
|
|||
"types/priv_validator",
|
||||
"version"
|
||||
]
|
||||
revision = "26f633ed48441f72895b710f0e87b7b6c6791066"
|
||||
version = "v0.19.1"
|
||||
revision = "19ccd1842fe5efffcc2ff32e6cfc127ca0cd1f9b"
|
||||
version = "v0.19.4-rc0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
|
@ -360,8 +359,8 @@
|
|||
"pubsub",
|
||||
"pubsub/query"
|
||||
]
|
||||
revision = "d94e312673e16a11ea55d742cefb3e331228f898"
|
||||
version = "v0.8.2"
|
||||
revision = "cc5f287c4798ffe88c04d02df219ecb6932080fd"
|
||||
version = "v0.8.3-rc0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -377,7 +376,7 @@
|
|||
"ripemd160",
|
||||
"salsa20/salsa"
|
||||
]
|
||||
revision = "b49d69b5da943f7ef3c9cf91c8777c1f78a0cc3c"
|
||||
revision = "2fc4c88bf43f0ea5ea305eae2b7af24b2cc93287"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -389,16 +388,15 @@
|
|||
"http2/hpack",
|
||||
"idna",
|
||||
"internal/timeseries",
|
||||
"lex/httplex",
|
||||
"trace"
|
||||
]
|
||||
revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23"
|
||||
revision = "2491c5de3490fced2f6cff376127c667efeed857"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "cbbc999da32df943dac6cd71eb3ee39e1d7838b9"
|
||||
revision = "7c87d13f8e835d2fb3a70a2912c811ed0c1d241b"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
|
@ -422,10 +420,9 @@
|
|||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
revision = "86e600f69ee4704c6efbf6a2a40a5c10700e76c2"
|
||||
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/grpc"
|
||||
|
@ -460,6 +457,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "fad966346d3b6042faf2bf793168b6ce9a8ff59ae207b7ad57008ead0f3ff7d4"
|
||||
inputs-digest = "8c37fee3e6d3034b865c68185f4796223fe53b1b0ee4a305adbee5f53c50bfce"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -70,11 +70,16 @@
|
|||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/tendermint"
|
||||
version = "0.19.1"
|
||||
version = "0.19.4-rc0"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
version = "~0.8.2-rc1"
|
||||
version = "~0.8.3-rc0"
|
||||
|
||||
# this got updated and broke, so locked to an old working commit ...
|
||||
[[override]]
|
||||
name = "google.golang.org/genproto"
|
||||
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
|
|
|
@ -16,7 +16,7 @@ master | [
|
||||
**Note**: Requires [Go 1.10+](https://golang.org/dl/)
|
||||
|
||||
|
||||
## Overview
|
||||
|
|
|
@ -3,6 +3,7 @@ package baseapp
|
|||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
@ -22,11 +23,24 @@ import (
|
|||
// and to avoid affecting the Merkle root.
|
||||
var dbHeaderKey = []byte("header")
|
||||
|
||||
// Enum mode for app.runTx
|
||||
type runTxMode uint8
|
||||
|
||||
const (
|
||||
// Check a transaction
|
||||
runTxModeCheck runTxMode = iota
|
||||
// Simulate a transaction
|
||||
runTxModeSimulate runTxMode = iota
|
||||
// Deliver a transaction
|
||||
runTxModeDeliver runTxMode = iota
|
||||
)
|
||||
|
||||
// The ABCI application
|
||||
type BaseApp struct {
|
||||
// initialized on creation
|
||||
Logger log.Logger
|
||||
name string // application name from abci.Info
|
||||
cdc *wire.Codec // Amino codec
|
||||
db dbm.DB // common DB backend
|
||||
cms sdk.CommitMultiStore // Main (uncached) state
|
||||
router Router // handle any kind of message
|
||||
|
@ -37,9 +51,11 @@ type BaseApp struct {
|
|||
anteHandler sdk.AnteHandler // ante handler for fee and auth
|
||||
|
||||
// may be nil
|
||||
initChainer sdk.InitChainer // initialize state with validators and state blob
|
||||
beginBlocker sdk.BeginBlocker // logic to run before any txs
|
||||
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes
|
||||
initChainer sdk.InitChainer // initialize state with validators and state blob
|
||||
beginBlocker sdk.BeginBlocker // logic to run before any txs
|
||||
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes
|
||||
addrPeerFilter sdk.PeerFilter // filter peers by address and port
|
||||
pubkeyPeerFilter sdk.PeerFilter // filter peers by public key
|
||||
|
||||
//--------------------
|
||||
// Volatile
|
||||
|
@ -61,6 +77,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *Bas
|
|||
app := &BaseApp{
|
||||
Logger: logger,
|
||||
name: name,
|
||||
cdc: cdc,
|
||||
db: db,
|
||||
cms: store.NewCommitMultiStore(db),
|
||||
router: NewRouter(),
|
||||
|
@ -137,6 +154,12 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
|
|||
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
|
||||
app.anteHandler = ah
|
||||
}
|
||||
func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) {
|
||||
app.addrPeerFilter = pf
|
||||
}
|
||||
func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) {
|
||||
app.pubkeyPeerFilter = pf
|
||||
}
|
||||
func (app *BaseApp) Router() Router { return app.router }
|
||||
|
||||
// load latest application version
|
||||
|
@ -277,15 +300,74 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
|
|||
return
|
||||
}
|
||||
|
||||
// Filter peers by address / port
|
||||
func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery {
|
||||
if app.addrPeerFilter != nil {
|
||||
return app.addrPeerFilter(info)
|
||||
}
|
||||
return abci.ResponseQuery{}
|
||||
}
|
||||
|
||||
// Filter peers by public key
|
||||
func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery {
|
||||
if app.pubkeyPeerFilter != nil {
|
||||
return app.pubkeyPeerFilter(info)
|
||||
}
|
||||
return abci.ResponseQuery{}
|
||||
}
|
||||
|
||||
// Implements ABCI.
|
||||
// Delegates to CommitMultiStore if it implements Queryable
|
||||
func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
queryable, ok := app.cms.(sdk.Queryable)
|
||||
if !ok {
|
||||
msg := "application doesn't support queries"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
path := strings.Split(req.Path, "/")
|
||||
// first element is empty string
|
||||
if len(path) > 0 && path[0] == "" {
|
||||
path = path[1:]
|
||||
}
|
||||
return queryable.Query(req)
|
||||
// "/app" prefix for special application queries
|
||||
if len(path) >= 2 && path[0] == "app" {
|
||||
var result sdk.Result
|
||||
switch path[1] {
|
||||
case "simulate":
|
||||
txBytes := req.Data
|
||||
tx, err := app.txDecoder(txBytes)
|
||||
if err != nil {
|
||||
result = err.Result()
|
||||
} else {
|
||||
result = app.Simulate(tx)
|
||||
}
|
||||
default:
|
||||
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result()
|
||||
}
|
||||
value := app.cdc.MustMarshalBinary(result)
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.ABCICodeOK),
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
// "/store" prefix for store queries
|
||||
if len(path) >= 1 && path[0] == "store" {
|
||||
queryable, ok := app.cms.(sdk.Queryable)
|
||||
if !ok {
|
||||
msg := "multistore doesn't support queries"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
req.Path = "/" + strings.Join(path[1:], "/")
|
||||
return queryable.Query(req)
|
||||
}
|
||||
// "/p2p" prefix for p2p queries
|
||||
if len(path) >= 4 && path[0] == "p2p" {
|
||||
if path[1] == "filter" {
|
||||
if path[2] == "addr" {
|
||||
return app.FilterPeerByAddrPort(path[3])
|
||||
}
|
||||
if path[2] == "pubkey" {
|
||||
return app.FilterPeerByPubKey(path[3])
|
||||
}
|
||||
}
|
||||
}
|
||||
msg := "unknown query path"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
|
||||
// Implements ABCI
|
||||
|
@ -312,7 +394,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
|
|||
if err != nil {
|
||||
result = err.Result()
|
||||
} else {
|
||||
result = app.runTx(true, txBytes, tx)
|
||||
result = app.runTx(runTxModeCheck, txBytes, tx)
|
||||
}
|
||||
|
||||
return abci.ResponseCheckTx{
|
||||
|
@ -320,6 +402,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
|
|||
Data: result.Data,
|
||||
Log: result.Log,
|
||||
GasWanted: result.GasWanted,
|
||||
GasUsed: result.GasUsed,
|
||||
Fee: cmn.KI64Pair{
|
||||
[]byte(result.FeeDenom),
|
||||
result.FeeAmount,
|
||||
|
@ -336,7 +419,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
|
|||
if err != nil {
|
||||
result = err.Result()
|
||||
} else {
|
||||
result = app.runTx(false, txBytes, tx)
|
||||
result = app.runTx(runTxModeDeliver, txBytes, tx)
|
||||
}
|
||||
|
||||
// After-handler hooks.
|
||||
|
@ -358,22 +441,35 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
|
|||
}
|
||||
}
|
||||
|
||||
// nolint- Mostly for testing
|
||||
// nolint - Mostly for testing
|
||||
func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) {
|
||||
return app.runTx(true, nil, tx)
|
||||
return app.runTx(runTxModeCheck, nil, tx)
|
||||
}
|
||||
|
||||
// nolint - full tx execution
|
||||
func (app *BaseApp) Simulate(tx sdk.Tx) (result sdk.Result) {
|
||||
return app.runTx(runTxModeSimulate, nil, tx)
|
||||
}
|
||||
|
||||
// nolint
|
||||
func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) {
|
||||
return app.runTx(false, nil, tx)
|
||||
return app.runTx(runTxModeDeliver, nil, tx)
|
||||
}
|
||||
|
||||
// txBytes may be nil in some cases, eg. in tests.
|
||||
// Also, in the future we may support "internal" transactions.
|
||||
func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) {
|
||||
func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) {
|
||||
// Handle any panics.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log := fmt.Sprintf("Recovered: %v\nstack:\n%v", r, string(debug.Stack()))
|
||||
result = sdk.ErrInternal(log).Result()
|
||||
switch r.(type) {
|
||||
case sdk.ErrorOutOfGas:
|
||||
log := fmt.Sprintf("Out of gas in location: %v", r.(sdk.ErrorOutOfGas).Descriptor)
|
||||
result = sdk.ErrOutOfGas(log).Result()
|
||||
default:
|
||||
log := fmt.Sprintf("Recovered: %v\nstack:\n%v", r, string(debug.Stack()))
|
||||
result = sdk.ErrInternal(log).Result()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -392,12 +488,17 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
|
||||
// Get the context
|
||||
var ctx sdk.Context
|
||||
if isCheckTx {
|
||||
if mode == runTxModeCheck || mode == runTxModeSimulate {
|
||||
ctx = app.checkState.ctx.WithTxBytes(txBytes)
|
||||
} else {
|
||||
ctx = app.deliverState.ctx.WithTxBytes(txBytes)
|
||||
}
|
||||
|
||||
// Simulate a DeliverTx for gas calculation
|
||||
if mode == runTxModeSimulate {
|
||||
ctx = ctx.WithIsCheckTx(false)
|
||||
}
|
||||
|
||||
// Run the ante handler.
|
||||
if app.anteHandler != nil {
|
||||
newCtx, result, abort := app.anteHandler(ctx, tx)
|
||||
|
@ -418,7 +519,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
|
||||
// Get the correct cache
|
||||
var msCache sdk.CacheMultiStore
|
||||
if isCheckTx == true {
|
||||
if mode == runTxModeCheck || mode == runTxModeSimulate {
|
||||
// CacheWrap app.checkState.ms in case it fails.
|
||||
msCache = app.checkState.CacheMultiStore()
|
||||
ctx = ctx.WithMultiStore(msCache)
|
||||
|
@ -426,13 +527,15 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
// CacheWrap app.deliverState.ms in case it fails.
|
||||
msCache = app.deliverState.CacheMultiStore()
|
||||
ctx = ctx.WithMultiStore(msCache)
|
||||
|
||||
}
|
||||
|
||||
result = handler(ctx, msg)
|
||||
|
||||
// If result was successful, write to app.checkState.ms or app.deliverState.ms
|
||||
if result.IsOK() {
|
||||
// Set gas utilized
|
||||
result.GasUsed = ctx.GasMeter().GasConsumed()
|
||||
|
||||
// If not a simulated run and result was successful, write to app.checkState.ms or app.deliverState.ms
|
||||
if mode != runTxModeSimulate && result.IsOK() {
|
||||
msCache.Write()
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/go-crypto"
|
||||
|
@ -16,6 +17,7 @@ import (
|
|||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
func defaultLogger() log.Logger {
|
||||
|
@ -25,7 +27,9 @@ func defaultLogger() log.Logger {
|
|||
func newBaseApp(name string) *BaseApp {
|
||||
logger := defaultLogger()
|
||||
db := dbm.NewMemDB()
|
||||
return NewBaseApp(name, nil, logger, db)
|
||||
codec := wire.NewCodec()
|
||||
wire.RegisterCrypto(codec)
|
||||
return NewBaseApp(name, codec, logger, db)
|
||||
}
|
||||
|
||||
func TestMountStores(t *testing.T) {
|
||||
|
@ -167,7 +171,7 @@ func TestInitChainer(t *testing.T) {
|
|||
}
|
||||
|
||||
query := abci.RequestQuery{
|
||||
Path: "/main/key",
|
||||
Path: "/store/main/key",
|
||||
Data: key,
|
||||
}
|
||||
|
||||
|
@ -260,6 +264,97 @@ func TestDeliverTx(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSimulateTx(t *testing.T) {
|
||||
app := newBaseApp(t.Name())
|
||||
|
||||
// make a cap key and mount the store
|
||||
capKey := sdk.NewKVStoreKey("main")
|
||||
app.MountStoresIAVL(capKey)
|
||||
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
|
||||
assert.Nil(t, err)
|
||||
|
||||
counter := 0
|
||||
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
|
||||
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
ctx.GasMeter().ConsumeGas(10, "test")
|
||||
store := ctx.KVStore(capKey)
|
||||
// ensure store is never written
|
||||
require.Nil(t, store.Get([]byte("key")))
|
||||
store.Set([]byte("key"), []byte("value"))
|
||||
// check we can see the current header
|
||||
thisHeader := ctx.BlockHeader()
|
||||
height := int64(counter)
|
||||
assert.Equal(t, height, thisHeader.Height)
|
||||
counter++
|
||||
return sdk.Result{}
|
||||
})
|
||||
|
||||
tx := testUpdatePowerTx{} // doesn't matter
|
||||
header := abci.Header{AppHash: []byte("apphash")}
|
||||
|
||||
app.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var ttx testUpdatePowerTx
|
||||
fromJSON(txBytes, &ttx)
|
||||
return ttx, nil
|
||||
})
|
||||
|
||||
nBlocks := 3
|
||||
for blockN := 0; blockN < nBlocks; blockN++ {
|
||||
// block1
|
||||
header.Height = int64(blockN + 1)
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
result := app.Simulate(tx)
|
||||
require.Equal(t, result.Code, sdk.ABCICodeOK)
|
||||
require.Equal(t, int64(80), result.GasUsed)
|
||||
counter--
|
||||
encoded, err := json.Marshal(tx)
|
||||
require.Nil(t, err)
|
||||
query := abci.RequestQuery{
|
||||
Path: "/app/simulate",
|
||||
Data: encoded,
|
||||
}
|
||||
queryResult := app.Query(query)
|
||||
require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK))
|
||||
var res sdk.Result
|
||||
app.cdc.MustUnmarshalBinary(queryResult.Value, &res)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code)
|
||||
require.Equal(t, int64(160), res.GasUsed)
|
||||
app.EndBlock(abci.RequestEndBlock{})
|
||||
app.Commit()
|
||||
}
|
||||
}
|
||||
|
||||
// Test that transactions exceeding gas limits fail
|
||||
func TestTxGasLimits(t *testing.T) {
|
||||
logger := defaultLogger()
|
||||
db := dbm.NewMemDB()
|
||||
app := NewBaseApp(t.Name(), nil, logger, db)
|
||||
|
||||
// make a cap key and mount the store
|
||||
capKey := sdk.NewKVStoreKey("main")
|
||||
app.MountStoresIAVL(capKey)
|
||||
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
|
||||
assert.Nil(t, err)
|
||||
|
||||
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(0))
|
||||
return
|
||||
})
|
||||
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
ctx.GasMeter().ConsumeGas(10, "counter")
|
||||
return sdk.Result{}
|
||||
})
|
||||
|
||||
tx := testUpdatePowerTx{} // doesn't matter
|
||||
header := abci.Header{AppHash: []byte("apphash")}
|
||||
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
res := app.Deliver(tx)
|
||||
assert.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), "Expected transaction to run out of gas")
|
||||
app.EndBlock(abci.RequestEndBlock{})
|
||||
app.Commit()
|
||||
}
|
||||
|
||||
// Test that we can only query from the latest committed state.
|
||||
func TestQuery(t *testing.T) {
|
||||
app := newBaseApp(t.Name())
|
||||
|
@ -280,7 +375,7 @@ func TestQuery(t *testing.T) {
|
|||
})
|
||||
|
||||
query := abci.RequestQuery{
|
||||
Path: "/main/key",
|
||||
Path: "/store/main/key",
|
||||
Data: key,
|
||||
}
|
||||
|
||||
|
@ -307,6 +402,39 @@ func TestQuery(t *testing.T) {
|
|||
assert.Equal(t, value, res.Value)
|
||||
}
|
||||
|
||||
// Test p2p filter queries
|
||||
func TestP2PQuery(t *testing.T) {
|
||||
app := newBaseApp(t.Name())
|
||||
|
||||
// make a cap key and mount the store
|
||||
capKey := sdk.NewKVStoreKey("main")
|
||||
app.MountStoresIAVL(capKey)
|
||||
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
|
||||
assert.Nil(t, err)
|
||||
|
||||
app.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
|
||||
require.Equal(t, "1.1.1.1:8000", addrport)
|
||||
return abci.ResponseQuery{Code: uint32(3)}
|
||||
})
|
||||
|
||||
app.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
|
||||
require.Equal(t, "testpubkey", pubkey)
|
||||
return abci.ResponseQuery{Code: uint32(4)}
|
||||
})
|
||||
|
||||
addrQuery := abci.RequestQuery{
|
||||
Path: "/p2p/filter/addr/1.1.1.1:8000",
|
||||
}
|
||||
res := app.Query(addrQuery)
|
||||
require.Equal(t, uint32(3), res.Code)
|
||||
|
||||
pubkeyQuery := abci.RequestQuery{
|
||||
Path: "/p2p/filter/pubkey/testpubkey",
|
||||
}
|
||||
res = app.Query(pubkeyQuery)
|
||||
require.Equal(t, uint32(4), res.Code)
|
||||
}
|
||||
|
||||
//----------------------
|
||||
// TODO: clean this up
|
||||
|
||||
|
|
|
@ -58,8 +58,7 @@ func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName
|
|||
|
||||
// Query from Tendermint with the provided storename and path
|
||||
func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) {
|
||||
|
||||
path := fmt.Sprintf("/%s/%s", storeName, endPath)
|
||||
path := fmt.Sprintf("/store/%s/key", storeName)
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
@ -114,6 +113,7 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w
|
|||
ChainID: chainID,
|
||||
Sequences: []int64{sequence},
|
||||
Msg: msg,
|
||||
Fee: sdk.NewStdFee(10000, sdk.Coin{}), // TODO run simulate to estimate gas?
|
||||
}
|
||||
|
||||
keybase, err := keys.GetKeyBase()
|
||||
|
|
|
@ -40,7 +40,7 @@ var (
|
|||
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
|
||||
fee = sdk.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
0,
|
||||
100000,
|
||||
}
|
||||
|
||||
sendMsg1 = bank.MsgSend{
|
||||
|
|
|
@ -66,8 +66,9 @@ func GaiaAppInit() server.AppInit {
|
|||
|
||||
fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError)
|
||||
fsAppGenTx.String(flagName, "", "validator moniker, if left blank, do not add validator")
|
||||
fsAppGenTx.String(flagClientHome, DefaultCLIHome, "home directory for the client, used for key generation")
|
||||
fsAppGenTx.Bool(flagOWK, false, "overwrite the for the accounts created")
|
||||
fsAppGenTx.String(flagClientHome, DefaultCLIHome,
|
||||
"home directory for the client, used for key generation")
|
||||
fsAppGenTx.Bool(flagOWK, false, "overwrite the accounts created")
|
||||
|
||||
return server.AppInit{
|
||||
FlagsAppGenState: fsAppGenState,
|
||||
|
|
|
@ -2,8 +2,8 @@ openapi: 3.0.0
|
|||
servers:
|
||||
- url: 'http://localhost:8998'
|
||||
info:
|
||||
version: "1.0.0-oas3"
|
||||
title: Light client daemon to interface with Cosmos baseserver via REST
|
||||
version: "1.1.0"
|
||||
title: Light client daemon to interface with full Gaia node via REST
|
||||
description: Specification for the LCD provided by `gaia rest-server`
|
||||
|
||||
paths:
|
||||
|
@ -13,7 +13,7 @@ paths:
|
|||
description: Get the version of the LCD running locally to compare against expected
|
||||
responses:
|
||||
200:
|
||||
description: Plaintext version i.e. "v0.5.0"
|
||||
description: Plaintext version i.e. "0.16.0-dev-26440095"
|
||||
/node_info:
|
||||
description: Only the node info. Block information can be queried via /block/latest
|
||||
get:
|
||||
|
@ -26,25 +26,31 @@ paths:
|
|||
schema:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
description: ???
|
||||
type: string
|
||||
listen_addr:
|
||||
type: string
|
||||
example: 192.168.56.1:46656
|
||||
network:
|
||||
type: string
|
||||
example: gaia-5000
|
||||
version:
|
||||
description: Tendermint version
|
||||
type: string
|
||||
example: 0.19.1
|
||||
channels:
|
||||
description: ???
|
||||
type: string
|
||||
pub_key:
|
||||
$ref: '#/components/schemas/PubKey'
|
||||
moniker:
|
||||
type: string
|
||||
example: 159.89.198.221
|
||||
network:
|
||||
type: string
|
||||
example: gaia-2
|
||||
remote_addr:
|
||||
type: string
|
||||
listen_addr:
|
||||
type: string
|
||||
example: 192.168.56.1:46656
|
||||
version:
|
||||
description: Tendermint version
|
||||
type: string
|
||||
example: 0.15.0
|
||||
other:
|
||||
description: more information on versions
|
||||
description: more information on versions and options for the node
|
||||
type: array
|
||||
/syncing:
|
||||
get:
|
||||
|
@ -204,7 +210,11 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Balance"
|
||||
type:
|
||||
description: "???"
|
||||
type: string
|
||||
value:
|
||||
$ref: "#/components/schemas/Balance"
|
||||
204:
|
||||
description: There is no data for the requested account. This is not a 404 as the account might exist, just does not hold data.
|
||||
/accounts/{address}/send:
|
||||
|
@ -226,6 +236,7 @@ paths:
|
|||
type: object
|
||||
properties:
|
||||
name:
|
||||
description: Name of locally stored key
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
|
@ -234,6 +245,9 @@ paths:
|
|||
items:
|
||||
$ref: "#/components/schemas/Coins"
|
||||
chain_id:
|
||||
description: Target chain
|
||||
type: string
|
||||
src_chain_id:
|
||||
type: string
|
||||
squence:
|
||||
type: number
|
||||
|
@ -242,19 +256,6 @@ paths:
|
|||
description: Tx was send and will probably be added to the next block
|
||||
400:
|
||||
description: The Tx was malformated
|
||||
/accounts/{address}/nonce:
|
||||
parameters:
|
||||
- in: path
|
||||
name: address
|
||||
description: Account address
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/components/schemas/Address"
|
||||
get:
|
||||
summary: Get the nonce for a certain account
|
||||
responses:
|
||||
200:
|
||||
description: Plaintext nonce i.e. "4" defaults to "0"
|
||||
/blocks/latest:
|
||||
get:
|
||||
summary: Get the latest block
|
||||
|
@ -667,15 +668,16 @@ components:
|
|||
Balance:
|
||||
type: object
|
||||
properties:
|
||||
height:
|
||||
type: number
|
||||
example: 123456
|
||||
address:
|
||||
type: string
|
||||
coins:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Coins"
|
||||
credit:
|
||||
type: array
|
||||
public_key:
|
||||
$ref: "#/components/schemas/PubKey"
|
||||
sequence:
|
||||
type: number
|
||||
BlockID:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -70,6 +70,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
|||
|
||||
// register message routes
|
||||
app.Router().
|
||||
AddRoute("auth", auth.NewHandler(app.accountMapper.(auth.AccountMapper))).
|
||||
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
||||
|
|
|
@ -39,7 +39,7 @@ var (
|
|||
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
|
||||
fee = sdk.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
0,
|
||||
100000,
|
||||
}
|
||||
|
||||
sendMsg1 = bank.MsgSend{
|
||||
|
@ -208,6 +208,61 @@ func TestGenesis(t *testing.T) {
|
|||
assert.Equal(t, acc, res1)
|
||||
}
|
||||
|
||||
func TestMsgChangePubKey(t *testing.T) {
|
||||
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
// Give 77 foocoin to the first key
|
||||
coins, err := sdk.ParseCoins("77foocoin")
|
||||
require.Nil(t, err)
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
|
||||
// Construct genesis state
|
||||
err = setGenesisAccounts(bapp, baseAcc)
|
||||
assert.Nil(t, err)
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, baseAcc, res1.(*types.AppAccount).BaseAccount)
|
||||
|
||||
// Run a CheckDeliver
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "67foocoin")
|
||||
CheckBalance(t, bapp, addr2, "10foocoin")
|
||||
|
||||
changePubKeyMsg := auth.MsgChangeKey{
|
||||
Address: addr1,
|
||||
NewPubKey: priv2.PubKey(),
|
||||
}
|
||||
|
||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||
acc := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
|
||||
// send a MsgChangePubKey
|
||||
SignCheckDeliver(t, bapp, changePubKeyMsg, []int64{1}, true, priv1)
|
||||
acc = bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
|
||||
assert.True(t, priv2.PubKey().Equals(acc.GetPubKey()))
|
||||
|
||||
// signing a SendMsg with the old privKey should be an auth error
|
||||
tx := genTx(sendMsg1, []int64{2}, priv1)
|
||||
res := bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the new correct priv key should work
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{2}, true, priv2)
|
||||
|
||||
// Check balances
|
||||
CheckBalance(t, bapp, addr1, "57foocoin")
|
||||
CheckBalance(t, bapp, addr2, "20foocoin")
|
||||
}
|
||||
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ var (
|
|||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
fee = sdk.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
0,
|
||||
1000000,
|
||||
}
|
||||
|
||||
sendMsg = bank.MsgSend{
|
||||
|
|
|
@ -31,10 +31,9 @@ func TestInitApp(t *testing.T) {
|
|||
app.InitChain(req)
|
||||
app.Commit()
|
||||
|
||||
// XXX test failing
|
||||
// make sure we can query these values
|
||||
query := abci.RequestQuery{
|
||||
Path: "/main/key",
|
||||
Path: "/store/main/key",
|
||||
Data: []byte("foo"),
|
||||
}
|
||||
qres := app.Query(query)
|
||||
|
@ -70,7 +69,7 @@ func TestDeliverTx(t *testing.T) {
|
|||
|
||||
// make sure we can query these values
|
||||
query := abci.RequestQuery{
|
||||
Path: "/main/key",
|
||||
Path: "/store/main/key",
|
||||
Data: []byte(key),
|
||||
}
|
||||
qres := app.Query(query)
|
||||
|
|
|
@ -50,6 +50,10 @@ func (ms multiStore) GetKVStore(key sdk.StoreKey) sdk.KVStore {
|
|||
return ms.kv[key]
|
||||
}
|
||||
|
||||
func (ms multiStore) GetKVStoreWithGas(meter sdk.GasMeter, key sdk.StoreKey) sdk.KVStore {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms multiStore) GetStore(key sdk.StoreKey) sdk.Store {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -210,16 +211,18 @@ func InitCmd(ctx *Context, cdc *wire.Codec, appInit AppInit) *cobra.Command {
|
|||
func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) (
|
||||
validators []tmtypes.GenesisValidator, appGenTxs []json.RawMessage, persistentPeers string, err error) {
|
||||
|
||||
// XXX sort the files by contents just incase people renamed their files
|
||||
var fos []os.FileInfo
|
||||
fos, err = ioutil.ReadDir(genTxsDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
genTxs := make(map[string]GenesisTx)
|
||||
var nodeIDs []string
|
||||
for _, fo := range fos {
|
||||
filename := path.Join(genTxsDir, fo.Name())
|
||||
if !fo.IsDir() && (path.Ext(filename) != ".json") {
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
// get the genTx
|
||||
|
@ -234,6 +237,15 @@ func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) (
|
|||
return
|
||||
}
|
||||
|
||||
genTxs[genTx.NodeID] = genTx
|
||||
nodeIDs = append(nodeIDs, genTx.NodeID)
|
||||
}
|
||||
|
||||
sort.Strings(nodeIDs)
|
||||
|
||||
for _, nodeID := range nodeIDs {
|
||||
genTx := genTxs[nodeID]
|
||||
|
||||
// combine some stuff
|
||||
validators = append(validators, genTx.Validator)
|
||||
appGenTxs = append(appGenTxs, genTx.AppGenTx)
|
||||
|
|
|
@ -79,7 +79,6 @@ func AddCommands(
|
|||
ShowNodeIDCmd(ctx),
|
||||
ShowValidatorCmd(ctx),
|
||||
ExportCmd(ctx, cdc, appExport),
|
||||
UnsafeResetAllCmd(ctx),
|
||||
version.VersionCmd,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -72,3 +72,8 @@ func (cms cacheMultiStore) GetStore(key StoreKey) Store {
|
|||
func (cms cacheMultiStore) GetKVStore(key StoreKey) KVStore {
|
||||
return cms.stores[key].(KVStore)
|
||||
}
|
||||
|
||||
// Implements MultiStore.
|
||||
func (cms cacheMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, key StoreKey) KVStore {
|
||||
return NewGasKVStore(meter, cms.GetKVStore(key))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// nolint
|
||||
const (
|
||||
HasCost = 10
|
||||
ReadCostFlat = 10
|
||||
ReadCostPerByte = 1
|
||||
WriteCostFlat = 10
|
||||
WriteCostPerByte = 10
|
||||
KeyCostFlat = 5
|
||||
ValueCostFlat = 10
|
||||
ValueCostPerByte = 1
|
||||
)
|
||||
|
||||
// gasKVStore applies gas tracking to an underlying kvstore
|
||||
type gasKVStore struct {
|
||||
gasMeter sdk.GasMeter
|
||||
parent sdk.KVStore
|
||||
}
|
||||
|
||||
// nolint
|
||||
func NewGasKVStore(gasMeter sdk.GasMeter, parent sdk.KVStore) *gasKVStore {
|
||||
kvs := &gasKVStore{
|
||||
gasMeter: gasMeter,
|
||||
parent: parent,
|
||||
}
|
||||
return kvs
|
||||
}
|
||||
|
||||
// Implements Store.
|
||||
func (gi *gasKVStore) GetStoreType() sdk.StoreType {
|
||||
return gi.parent.GetStoreType()
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) Get(key []byte) (value []byte) {
|
||||
gi.gasMeter.ConsumeGas(ReadCostFlat, "GetFlat")
|
||||
value = gi.parent.Get(key)
|
||||
// TODO overflow-safe math?
|
||||
gi.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte")
|
||||
return value
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) Set(key []byte, value []byte) {
|
||||
gi.gasMeter.ConsumeGas(WriteCostFlat, "SetFlat")
|
||||
// TODO overflow-safe math?
|
||||
gi.gasMeter.ConsumeGas(WriteCostPerByte*sdk.Gas(len(value)), "SetPerByte")
|
||||
gi.parent.Set(key, value)
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) Has(key []byte) bool {
|
||||
gi.gasMeter.ConsumeGas(HasCost, "Has")
|
||||
return gi.parent.Has(key)
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) Delete(key []byte) {
|
||||
// No gas costs for deletion
|
||||
gi.parent.Delete(key)
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) Iterator(start, end []byte) sdk.Iterator {
|
||||
return gi.iterator(start, end, true)
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) ReverseIterator(start, end []byte) sdk.Iterator {
|
||||
return gi.iterator(start, end, false)
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) SubspaceIterator(prefix []byte) sdk.Iterator {
|
||||
return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true)
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) sdk.Iterator {
|
||||
return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false)
|
||||
}
|
||||
|
||||
// Implements KVStore.
|
||||
func (gi *gasKVStore) CacheWrap() sdk.CacheWrap {
|
||||
panic("you cannot CacheWrap a GasKVStore")
|
||||
}
|
||||
|
||||
func (gi *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator {
|
||||
var parent sdk.Iterator
|
||||
if ascending {
|
||||
parent = gi.parent.Iterator(start, end)
|
||||
} else {
|
||||
parent = gi.parent.ReverseIterator(start, end)
|
||||
}
|
||||
return newGasIterator(gi.gasMeter, parent)
|
||||
}
|
||||
|
||||
type gasIterator struct {
|
||||
gasMeter sdk.GasMeter
|
||||
parent sdk.Iterator
|
||||
}
|
||||
|
||||
func newGasIterator(gasMeter sdk.GasMeter, parent sdk.Iterator) sdk.Iterator {
|
||||
return &gasIterator{
|
||||
gasMeter: gasMeter,
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (g *gasIterator) Domain() (start []byte, end []byte) {
|
||||
return g.parent.Domain()
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (g *gasIterator) Valid() bool {
|
||||
return g.parent.Valid()
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (g *gasIterator) Next() {
|
||||
g.parent.Next()
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (g *gasIterator) Key() (key []byte) {
|
||||
g.gasMeter.ConsumeGas(KeyCostFlat, "KeyFlat")
|
||||
key = g.parent.Key()
|
||||
return key
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (g *gasIterator) Value() (value []byte) {
|
||||
value = g.parent.Value()
|
||||
g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat")
|
||||
g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte")
|
||||
return value
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (g *gasIterator) Close() {
|
||||
g.parent.Close()
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
)
|
||||
|
||||
func newGasKVStore() KVStore {
|
||||
meter := sdk.NewGasMeter(1000)
|
||||
mem := dbStoreAdapter{dbm.NewMemDB()}
|
||||
return NewGasKVStore(meter, mem)
|
||||
}
|
||||
|
||||
func TestGasKVStoreBasic(t *testing.T) {
|
||||
mem := dbStoreAdapter{dbm.NewMemDB()}
|
||||
meter := sdk.NewGasMeter(1000)
|
||||
st := NewGasKVStore(meter, mem)
|
||||
require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty")
|
||||
st.Set(keyFmt(1), valFmt(1))
|
||||
require.Equal(t, valFmt(1), st.Get(keyFmt(1)))
|
||||
st.Delete(keyFmt(1))
|
||||
require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty")
|
||||
require.Equal(t, meter.GasConsumed(), sdk.Gas(183))
|
||||
}
|
||||
|
||||
func TestGasKVStoreIterator(t *testing.T) {
|
||||
mem := dbStoreAdapter{dbm.NewMemDB()}
|
||||
meter := sdk.NewGasMeter(1000)
|
||||
st := NewGasKVStore(meter, mem)
|
||||
require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty")
|
||||
require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty")
|
||||
st.Set(keyFmt(1), valFmt(1))
|
||||
st.Set(keyFmt(2), valFmt(2))
|
||||
iterator := st.Iterator(nil, nil)
|
||||
ka := iterator.Key()
|
||||
require.Equal(t, ka, keyFmt(1))
|
||||
va := iterator.Value()
|
||||
require.Equal(t, va, valFmt(1))
|
||||
iterator.Next()
|
||||
kb := iterator.Key()
|
||||
require.Equal(t, kb, keyFmt(2))
|
||||
vb := iterator.Value()
|
||||
require.Equal(t, vb, valFmt(2))
|
||||
iterator.Next()
|
||||
require.False(t, iterator.Valid())
|
||||
require.Panics(t, iterator.Next)
|
||||
require.Equal(t, meter.GasConsumed(), sdk.Gas(356))
|
||||
}
|
||||
|
||||
func TestGasKVStoreOutOfGasSet(t *testing.T) {
|
||||
mem := dbStoreAdapter{dbm.NewMemDB()}
|
||||
meter := sdk.NewGasMeter(0)
|
||||
st := NewGasKVStore(meter, mem)
|
||||
require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas")
|
||||
}
|
||||
|
||||
func TestGasKVStoreOutOfGasIterator(t *testing.T) {
|
||||
mem := dbStoreAdapter{dbm.NewMemDB()}
|
||||
meter := sdk.NewGasMeter(200)
|
||||
st := NewGasKVStore(meter, mem)
|
||||
st.Set(keyFmt(1), valFmt(1))
|
||||
iterator := st.Iterator(nil, nil)
|
||||
iterator.Next()
|
||||
require.Panics(t, func() { iterator.Value() }, "Expected out-of-gas")
|
||||
}
|
|
@ -183,6 +183,11 @@ func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore {
|
|||
return rs.stores[key].(KVStore)
|
||||
}
|
||||
|
||||
// Implements MultiStore.
|
||||
func (rs *rootMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, key StoreKey) KVStore {
|
||||
return NewGasKVStore(meter, rs.GetKVStore(key))
|
||||
}
|
||||
|
||||
// getStoreByName will first convert the original name to
|
||||
// a special key, before looking up the CommitStore.
|
||||
// This is not exposed to the extensions (which will need the
|
||||
|
|
|
@ -10,3 +10,6 @@ type BeginBlocker func(ctx Context, req abci.RequestBeginBlock) abci.ResponseBeg
|
|||
|
||||
// run code after the transactions in a block and return updates to the validator set
|
||||
type EndBlocker func(ctx Context, req abci.RequestEndBlock) abci.ResponseEndBlock
|
||||
|
||||
// respond to p2p filtering queries from Tendermint
|
||||
type PeerFilter func(info string) abci.ResponseQuery
|
||||
|
|
|
@ -43,6 +43,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byt
|
|||
c = c.WithIsCheckTx(isCheckTx)
|
||||
c = c.WithTxBytes(txBytes)
|
||||
c = c.WithLogger(logger)
|
||||
c = c.WithGasMeter(NewInfiniteGasMeter())
|
||||
return c
|
||||
}
|
||||
|
||||
|
@ -68,7 +69,7 @@ func (c Context) Value(key interface{}) interface{} {
|
|||
|
||||
// KVStore fetches a KVStore from the MultiStore.
|
||||
func (c Context) KVStore(key StoreKey) KVStore {
|
||||
return c.multiStore().GetKVStore(key)
|
||||
return c.multiStore().GetKVStoreWithGas(c.GasMeter(), key)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@ -127,6 +128,7 @@ const (
|
|||
contextKeyIsCheckTx
|
||||
contextKeyTxBytes
|
||||
contextKeyLogger
|
||||
contextKeyGasMeter
|
||||
)
|
||||
|
||||
// NOTE: Do not expose MultiStore.
|
||||
|
@ -155,6 +157,9 @@ func (c Context) TxBytes() []byte {
|
|||
func (c Context) Logger() log.Logger {
|
||||
return c.Value(contextKeyLogger).(log.Logger)
|
||||
}
|
||||
func (c Context) GasMeter() GasMeter {
|
||||
return c.Value(contextKeyGasMeter).(GasMeter)
|
||||
}
|
||||
func (c Context) WithMultiStore(ms MultiStore) Context {
|
||||
return c.withValue(contextKeyMultiStore, ms)
|
||||
}
|
||||
|
@ -177,6 +182,9 @@ func (c Context) WithTxBytes(txBytes []byte) Context {
|
|||
func (c Context) WithLogger(logger log.Logger) Context {
|
||||
return c.withValue(contextKeyLogger, logger)
|
||||
}
|
||||
func (c Context) WithGasMeter(meter GasMeter) Context {
|
||||
return c.withValue(contextKeyGasMeter, meter)
|
||||
}
|
||||
|
||||
// Cache the multistore and return a new cached context. The cached context is
|
||||
// written to the context when writeCache is called.
|
||||
|
|
|
@ -52,6 +52,7 @@ const (
|
|||
CodeUnknownAddress CodeType = 9
|
||||
CodeInsufficientCoins CodeType = 10
|
||||
CodeInvalidCoins CodeType = 11
|
||||
CodeOutOfGas CodeType = 12
|
||||
|
||||
// CodespaceRoot is a codespace for error codes in this file only.
|
||||
// Notice that 0 is an "unset" codespace, which can be overridden with
|
||||
|
@ -88,6 +89,8 @@ func CodeToDefaultMsg(code CodeType) string {
|
|||
return "Insufficient coins"
|
||||
case CodeInvalidCoins:
|
||||
return "Invalid coins"
|
||||
case CodeOutOfGas:
|
||||
return "Out of gas"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown code %d", code)
|
||||
}
|
||||
|
@ -131,6 +134,9 @@ func ErrInsufficientCoins(msg string) Error {
|
|||
func ErrInvalidCoins(msg string) Error {
|
||||
return newErrorWithRootCodespace(CodeInvalidCoins, msg)
|
||||
}
|
||||
func ErrOutOfGas(msg string) Error {
|
||||
return newErrorWithRootCodespace(CodeOutOfGas, msg)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Error & sdkError
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package types
|
||||
|
||||
import ()
|
||||
|
||||
// Gas measured by the SDK
|
||||
type Gas = int64
|
||||
|
||||
// Error thrown when out of gas
|
||||
type ErrorOutOfGas struct {
|
||||
Descriptor string
|
||||
}
|
||||
|
||||
// GasMeter interface to track gas consumption
|
||||
type GasMeter interface {
|
||||
GasConsumed() Gas
|
||||
ConsumeGas(amount Gas, descriptor string)
|
||||
}
|
||||
|
||||
type basicGasMeter struct {
|
||||
limit Gas
|
||||
consumed Gas
|
||||
}
|
||||
|
||||
func NewGasMeter(limit Gas) GasMeter {
|
||||
return &basicGasMeter{
|
||||
limit: limit,
|
||||
consumed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *basicGasMeter) GasConsumed() Gas {
|
||||
return g.consumed
|
||||
}
|
||||
|
||||
func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) {
|
||||
g.consumed += amount
|
||||
if g.consumed > g.limit {
|
||||
panic(ErrorOutOfGas{descriptor})
|
||||
}
|
||||
}
|
||||
|
||||
type infiniteGasMeter struct {
|
||||
consumed Gas
|
||||
}
|
||||
|
||||
func NewInfiniteGasMeter() GasMeter {
|
||||
return &infiniteGasMeter{
|
||||
consumed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *infiniteGasMeter) GasConsumed() Gas {
|
||||
return g.consumed
|
||||
}
|
||||
|
||||
func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) {
|
||||
g.consumed += amount
|
||||
}
|
|
@ -49,6 +49,7 @@ type MultiStore interface { //nolint
|
|||
// Convenience for fetching substores.
|
||||
GetStore(StoreKey) Store
|
||||
GetKVStore(StoreKey) KVStore
|
||||
GetKVStoreWithGas(GasMeter, StoreKey) KVStore
|
||||
}
|
||||
|
||||
// From MultiStore.CacheMultiStore()....
|
||||
|
|
|
@ -77,8 +77,8 @@ func FeePayer(tx Tx) Address {
|
|||
// gas to be used by the transaction. The ratio yields an effective "gasprice",
|
||||
// which must be above some miminum to be accepted into the mempool.
|
||||
type StdFee struct {
|
||||
Amount Coins `json"amount"`
|
||||
Gas int64 `json"gas"`
|
||||
Amount Coins `json:"amount"`
|
||||
Gas int64 `json:"gas"`
|
||||
}
|
||||
|
||||
func NewStdFee(gas int64, amount ...Coin) StdFee {
|
||||
|
|
|
@ -6,10 +6,10 @@ package version
|
|||
// TODO improve
|
||||
|
||||
const Maj = "0"
|
||||
const Min = "16"
|
||||
const Min = "18"
|
||||
const Fix = "0"
|
||||
|
||||
const Version = "0.16.0-dev"
|
||||
const Version = "0.18.0-dev"
|
||||
|
||||
// GitCommit set by build flags
|
||||
var GitCommit = ""
|
||||
|
|
|
@ -8,10 +8,14 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
verifyCost = 100
|
||||
)
|
||||
|
||||
// NewAnteHandler returns an AnteHandler that checks
|
||||
// and increments sequence numbers, checks signatures,
|
||||
// and deducts fees from the first signer.
|
||||
func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHandler {
|
||||
func NewAnteHandler(am sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHandler {
|
||||
return func(
|
||||
ctx sdk.Context, tx sdk.Tx,
|
||||
) (_ sdk.Context, _ sdk.Result, abort bool) {
|
||||
|
@ -24,7 +28,6 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler)
|
|||
true
|
||||
}
|
||||
|
||||
// TODO: can tx just implement message?
|
||||
msg := tx.GetMsg()
|
||||
|
||||
// TODO: will this always be a stdtx? should that be used in the function signature?
|
||||
|
@ -62,7 +65,7 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler)
|
|||
|
||||
// check signature, return account with incremented nonce
|
||||
signerAcc, res := processSig(
|
||||
ctx, accountMapper,
|
||||
ctx, am,
|
||||
signerAddr, sig, signBytes,
|
||||
)
|
||||
if !res.IsOK() {
|
||||
|
@ -82,13 +85,16 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler)
|
|||
}
|
||||
|
||||
// Save the account.
|
||||
accountMapper.SetAccount(ctx, signerAcc)
|
||||
am.SetAccount(ctx, signerAcc)
|
||||
signerAccs[i] = signerAcc
|
||||
}
|
||||
|
||||
// cache the signer accounts in the context
|
||||
ctx = WithSigners(ctx, signerAccs)
|
||||
|
||||
// set the gas meter
|
||||
ctx = ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas))
|
||||
|
||||
// TODO: tx tags (?)
|
||||
|
||||
return ctx, sdk.Result{}, false // continue...
|
||||
|
@ -135,6 +141,7 @@ func processSig(
|
|||
}
|
||||
|
||||
// Check sig.
|
||||
ctx.GasMeter().ConsumeGas(verifyCost, "ante verify")
|
||||
if !pubKey.VerifyBytes(signBytes, sig.Signature) {
|
||||
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
|
||||
}
|
||||
|
|
|
@ -51,9 +51,6 @@ func (acc BaseAccount) GetPubKey() crypto.PubKey {
|
|||
|
||||
// Implements sdk.Account.
|
||||
func (acc *BaseAccount) SetPubKey(pubKey crypto.PubKey) error {
|
||||
if acc.PubKey != nil {
|
||||
return errors.New("cannot override BaseAccount pubkey")
|
||||
}
|
||||
acc.PubKey = pubKey
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -37,10 +37,10 @@ func TestBaseAccountAddressPubKey(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
assert.Equal(t, pub1, acc.GetPubKey())
|
||||
|
||||
// can't override pubkey
|
||||
// can override pubkey
|
||||
err = acc.SetPubKey(pub2)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, pub1, acc.GetPubKey())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, pub2, acc.GetPubKey())
|
||||
|
||||
//------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// NewHandler returns a handler for "auth" type messages.
|
||||
func NewHandler(am AccountMapper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MsgChangeKey:
|
||||
return handleMsgChangeKey(ctx, am, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized auth Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle MsgChangeKey
|
||||
// Should be very expensive, because once this happens, an account is un-prunable
|
||||
func handleMsgChangeKey(ctx sdk.Context, am AccountMapper, msg MsgChangeKey) sdk.Result {
|
||||
|
||||
err := am.setPubKey(ctx, msg.Address, msg.NewPubKey)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
return sdk.Result{
|
||||
Tags: sdk.NewTags("action", []byte("changePubkey"), "address", msg.Address.Bytes(), "pubkey", msg.NewPubKey.Bytes()),
|
||||
}
|
||||
}
|
|
@ -6,14 +6,15 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
var _ sdk.AccountMapper = (*accountMapper)(nil)
|
||||
var _ sdk.AccountMapper = (*AccountMapper)(nil)
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
// This AccountMapper encodes/decodes accounts using the
|
||||
// go-amino (binary) encoding/decoding library.
|
||||
type accountMapper struct {
|
||||
type AccountMapper struct {
|
||||
|
||||
// The (unexposed) key used to access the store from the Context.
|
||||
key sdk.StoreKey
|
||||
|
@ -28,23 +29,23 @@ type accountMapper struct {
|
|||
// NewAccountMapper returns a new sdk.AccountMapper that
|
||||
// uses go-amino to (binary) encode and decode concrete sdk.Accounts.
|
||||
// nolint
|
||||
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) accountMapper {
|
||||
return accountMapper{
|
||||
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) AccountMapper {
|
||||
return AccountMapper{
|
||||
key: key,
|
||||
proto: proto,
|
||||
cdc: cdc,
|
||||
}
|
||||
}
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
func (am accountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||
// Implaements sdk.AccountMapper.
|
||||
func (am AccountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||
acc := am.clonePrototype()
|
||||
acc.SetAddress(addr)
|
||||
return acc
|
||||
}
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
func (am accountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||
func (am AccountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||
store := ctx.KVStore(am.key)
|
||||
bz := store.Get(addr)
|
||||
if bz == nil {
|
||||
|
@ -55,7 +56,7 @@ func (am accountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) sdk.Accoun
|
|||
}
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
||||
func (am AccountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
||||
addr := acc.GetAddress()
|
||||
store := ctx.KVStore(am.key)
|
||||
bz := am.encodeAccount(acc)
|
||||
|
@ -63,7 +64,7 @@ func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
|||
}
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
func (am accountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Account) (stop bool)) {
|
||||
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Account) (stop bool)) {
|
||||
store := ctx.KVStore(am.key)
|
||||
iter := store.Iterator(nil, nil)
|
||||
for {
|
||||
|
@ -79,11 +80,49 @@ func (am accountMapper) IterateAccounts(ctx sdk.Context, process func(sdk.Accoun
|
|||
}
|
||||
}
|
||||
|
||||
// Returns the PubKey of the account at address
|
||||
func (am AccountMapper) GetPubKey(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, sdk.Error) {
|
||||
acc := am.GetAccount(ctx, addr)
|
||||
if acc == nil {
|
||||
return nil, sdk.ErrUnknownAddress(addr.String())
|
||||
}
|
||||
return acc.GetPubKey(), nil
|
||||
}
|
||||
|
||||
func (am AccountMapper) setPubKey(ctx sdk.Context, addr sdk.Address, newPubKey crypto.PubKey) sdk.Error {
|
||||
acc := am.GetAccount(ctx, addr)
|
||||
if acc == nil {
|
||||
return sdk.ErrUnknownAddress(addr.String())
|
||||
}
|
||||
acc.SetPubKey(newPubKey)
|
||||
am.SetAccount(ctx, acc)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the Sequence of the account at address
|
||||
func (am AccountMapper) GetSequence(ctx sdk.Context, addr sdk.Address) (int64, sdk.Error) {
|
||||
acc := am.GetAccount(ctx, addr)
|
||||
if acc == nil {
|
||||
return 0, sdk.ErrUnknownAddress(addr.String())
|
||||
}
|
||||
return acc.GetSequence(), nil
|
||||
}
|
||||
|
||||
func (am AccountMapper) setSequence(ctx sdk.Context, addr sdk.Address, newSequence int64) sdk.Error {
|
||||
acc := am.GetAccount(ctx, addr)
|
||||
if acc == nil {
|
||||
return sdk.ErrUnknownAddress(addr.String())
|
||||
}
|
||||
acc.SetSequence(newSequence)
|
||||
am.SetAccount(ctx, acc)
|
||||
return nil
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// misc.
|
||||
|
||||
// Creates a new struct (or pointer to struct) from am.proto.
|
||||
func (am accountMapper) clonePrototype() sdk.Account {
|
||||
func (am AccountMapper) clonePrototype() sdk.Account {
|
||||
protoRt := reflect.TypeOf(am.proto)
|
||||
if protoRt.Kind() == reflect.Ptr {
|
||||
protoCrt := protoRt.Elem()
|
||||
|
@ -106,7 +145,7 @@ func (am accountMapper) clonePrototype() sdk.Account {
|
|||
return clone
|
||||
}
|
||||
|
||||
func (am accountMapper) encodeAccount(acc sdk.Account) []byte {
|
||||
func (am AccountMapper) encodeAccount(acc sdk.Account) []byte {
|
||||
bz, err := am.cdc.MarshalBinaryBare(acc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -114,7 +153,7 @@ func (am accountMapper) encodeAccount(acc sdk.Account) []byte {
|
|||
return bz
|
||||
}
|
||||
|
||||
func (am accountMapper) decodeAccount(bz []byte) (acc sdk.Account) {
|
||||
func (am AccountMapper) decodeAccount(bz []byte) (acc sdk.Account) {
|
||||
err := am.cdc.UnmarshalBinaryBare(bz, &acc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// MsgChangeKey - high level transaction of the auth module
|
||||
type MsgChangeKey struct {
|
||||
Address sdk.Address `json:"address"`
|
||||
NewPubKey crypto.PubKey `json:"public_key"`
|
||||
}
|
||||
|
||||
var _ sdk.Msg = MsgChangeKey{}
|
||||
|
||||
// NewMsgChangeKey - msg to claim an account and set the PubKey
|
||||
func NewMsgChangeKey(addr sdk.Address, pubkey crypto.PubKey) MsgChangeKey {
|
||||
return MsgChangeKey{Address: addr, NewPubKey: pubkey}
|
||||
}
|
||||
|
||||
// Implements Msg.
|
||||
func (msg MsgChangeKey) Type() string { return "auth" }
|
||||
|
||||
// Implements Msg.
|
||||
func (msg MsgChangeKey) ValidateBasic() sdk.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements Msg.
|
||||
func (msg MsgChangeKey) GetSignBytes() []byte {
|
||||
b, err := json.Marshal(msg) // XXX: ensure some canonical form
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Implements Msg.
|
||||
func (msg MsgChangeKey) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.Address}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestNewMsgChangeKey(t *testing.T) {}
|
||||
|
||||
func TestMsgChangeKeyType(t *testing.T) {
|
||||
addr1 := sdk.Address([]byte("input"))
|
||||
newPubKey := crypto.GenPrivKeyEd25519().PubKey()
|
||||
|
||||
var msg = MsgChangeKey{
|
||||
Address: addr1,
|
||||
NewPubKey: newPubKey,
|
||||
}
|
||||
|
||||
assert.Equal(t, msg.Type(), "auth")
|
||||
}
|
||||
|
||||
func TestMsgChangeKeyValidation(t *testing.T) {
|
||||
|
||||
addr1 := sdk.Address([]byte("input"))
|
||||
|
||||
// emptyPubKey := crypto.PubKeyEd25519{}
|
||||
// var msg = MsgChangeKey{
|
||||
// Address: addr1,
|
||||
// NewPubKey: emptyPubKey,
|
||||
// }
|
||||
|
||||
// // fmt.Println(msg.NewPubKey.Empty())
|
||||
// fmt.Println(msg.NewPubKey.Bytes())
|
||||
|
||||
// assert.NotNil(t, msg.ValidateBasic())
|
||||
|
||||
newPubKey := crypto.GenPrivKeyEd25519().PubKey()
|
||||
msg := MsgChangeKey{
|
||||
Address: addr1,
|
||||
NewPubKey: newPubKey,
|
||||
}
|
||||
assert.Nil(t, msg.ValidateBasic())
|
||||
}
|
|
@ -6,6 +6,14 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
const (
|
||||
costGetCoins sdk.Gas = 10
|
||||
costHasCoins sdk.Gas = 10
|
||||
costSetCoins sdk.Gas = 100
|
||||
costSubtractCoins sdk.Gas = 10
|
||||
costAddCoins sdk.Gas = 10
|
||||
)
|
||||
|
||||
// Keeper manages transfers between accounts
|
||||
type Keeper struct {
|
||||
am sdk.AccountMapper
|
||||
|
@ -108,6 +116,7 @@ func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coi
|
|||
//______________________________________________________________________________________________
|
||||
|
||||
func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins {
|
||||
ctx.GasMeter().ConsumeGas(costGetCoins, "getCoins")
|
||||
acc := am.GetAccount(ctx, addr)
|
||||
if acc == nil {
|
||||
return sdk.Coins{}
|
||||
|
@ -116,6 +125,7 @@ func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins
|
|||
}
|
||||
|
||||
func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error {
|
||||
ctx.GasMeter().ConsumeGas(costSetCoins, "setCoins")
|
||||
acc := am.GetAccount(ctx, addr)
|
||||
if acc == nil {
|
||||
acc = am.NewAccountWithAddress(ctx, addr)
|
||||
|
@ -127,11 +137,13 @@ func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.C
|
|||
|
||||
// HasCoins returns whether or not an account has at least amt coins.
|
||||
func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) bool {
|
||||
ctx.GasMeter().ConsumeGas(costHasCoins, "hasCoins")
|
||||
return getCoins(ctx, am, addr).IsGTE(amt)
|
||||
}
|
||||
|
||||
// SubtractCoins subtracts amt from the coins at the addr.
|
||||
func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
||||
ctx.GasMeter().ConsumeGas(costSubtractCoins, "subtractCoins")
|
||||
oldCoins := getCoins(ctx, am, addr)
|
||||
newCoins := oldCoins.Minus(amt)
|
||||
if !newCoins.IsNotNegative() {
|
||||
|
@ -144,6 +156,7 @@ func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt
|
|||
|
||||
// AddCoins adds amt to the coins at the addr.
|
||||
func addCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) {
|
||||
ctx.GasMeter().ConsumeGas(costAddCoins, "addCoins")
|
||||
oldCoins := getCoins(ctx, am, addr)
|
||||
newCoins := oldCoins.Plus(amt)
|
||||
if !newCoins.IsNotNegative() {
|
||||
|
|
|
@ -65,8 +65,7 @@ func TestKeeper(t *testing.T) {
|
|||
coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})
|
||||
assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}}))
|
||||
|
||||
_, _, err := coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}})
|
||||
assert.Implements(t, (*sdk.Error)(nil), err)
|
||||
coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}})
|
||||
assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}}))
|
||||
|
||||
coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}})
|
||||
|
|
|
@ -119,7 +119,7 @@ func (msg MsgIssue) GetSigners() []sdk.Address {
|
|||
//----------------------------------------
|
||||
// Input
|
||||
|
||||
// Transaction Output
|
||||
// Transaction Input
|
||||
type Input struct {
|
||||
Address sdk.Address `json:"address"`
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
|
|
Loading…
Reference in New Issue