Merge branch 'develop' of https://github.com/cosmos/cosmos-sdk into develop

This commit is contained in:
David Kajpust 2018-05-18 22:05:07 -03:00
commit c0c31629c0
38 changed files with 954 additions and 127 deletions

View File

@ -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)

35
Gopkg.lock generated
View File

@ -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

View File

@ -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

View File

@ -16,7 +16,7 @@ master | [![CircleCI](https://circleci.com/gh/cosmos/cosmos-sdk/tree/master.s
**WARNING**: the libraries are still undergoing breaking changes as we get better ideas and start building out the Apps.
**Note**: Requires [Go 1.9+](https://golang.org/dl/)
**Note**: Requires [Go 1.10+](https://golang.org/dl/)
## Overview

View File

@ -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()
}

View File

@ -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

View File

@ -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()

View File

@ -40,7 +40,7 @@ var (
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
fee = sdk.StdFee{
sdk.Coins{{"foocoin", 0}},
0,
100000,
}
sendMsg1 = bank.MsgSend{

View File

@ -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,

View File

@ -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:

View File

@ -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))

View File

@ -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()

View File

@ -33,7 +33,7 @@ var (
coins = sdk.Coins{{"foocoin", 10}}
fee = sdk.StdFee{
sdk.Coins{{"foocoin", 0}},
0,
1000000,
}
sendMsg = bank.MsgSend{

View File

@ -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)

View File

@ -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")
}

View File

@ -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)

View File

@ -79,7 +79,6 @@ func AddCommands(
ShowNodeIDCmd(ctx),
ShowValidatorCmd(ctx),
ExportCmd(ctx, cdc, appExport),
UnsafeResetAllCmd(ctx),
version.VersionCmd,
)
}

View File

@ -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))
}

148
store/gaskvstore.go Normal file
View File

@ -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()
}

68
store/gaskvstore_test.go Normal file
View File

@ -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")
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

58
types/gas.go Normal file
View File

@ -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
}

View File

@ -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()....

View File

@ -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 {

View File

@ -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 = ""

View File

@ -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()
}

View File

@ -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
}

View File

@ -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())
//------------------------------------

34
x/auth/handler.go Normal file
View File

@ -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()),
}
}

View File

@ -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)

44
x/auth/msgs.go Normal file
View File

@ -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}
}

47
x/auth/msgs_test.go Normal file
View File

@ -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())
}

View File

@ -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() {

View File

@ -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}})

View File

@ -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"`