From 5da41a6c88edb834848da38296b3cc0ab2120553 Mon Sep 17 00:00:00 2001 From: LLLeon Date: Sun, 13 May 2018 17:34:56 +0800 Subject: [PATCH 01/50] types: modify bad syntax for struct tag value --- types/tx_msg.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/tx_msg.go b/types/tx_msg.go index 25d35512d..d4c568e51 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -80,8 +80,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 { From f1e441346a05df46617a153a609b0c7727515a8f Mon Sep 17 00:00:00 2001 From: LLLeon Date: Sun, 13 May 2018 20:47:26 +0800 Subject: [PATCH 02/50] x/bank: fix typo --- x/bank/msgs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/bank/msgs.go b/x/bank/msgs.go index 15822eed7..c8fcb5925 100644 --- a/x/bank/msgs.go +++ b/x/bank/msgs.go @@ -138,7 +138,7 @@ func (msg IssueMsg) GetSigners() []sdk.Address { //---------------------------------------- // Input -// Transaction Output +// Transaction Input type Input struct { Address sdk.Address `json:"address"` Coins sdk.Coins `json:"coins"` From cd24244308a0e2afc9511c3f2a767e21c1d05140 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Sun, 13 May 2018 18:19:42 -0400 Subject: [PATCH 03/50] Sort all genesis transaction by node id This ensures that users can rename the genesis transactions and they will still be in the same order. --- cmd/gaia/app/genesis.go | 5 +++-- server/init.go | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 9d88953cf..7cb7564dd 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -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, diff --git a/server/init.go b/server/init.go index 2d8be85f8..ffb84f27c 100644 --- a/server/init.go +++ b/server/init.go @@ -7,6 +7,7 @@ import ( "os" "path" "path/filepath" + "sort" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -210,12 +211,14 @@ 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") { @@ -234,16 +237,24 @@ 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 { // combine some stuff - validators = append(validators, genTx.Validator) - appGenTxs = append(appGenTxs, genTx.AppGenTx) + validators = append(validators, genTxs[nodeID].Validator) + appGenTxs = append(appGenTxs, genTxs[nodeID].AppGenTx) // Add a persistent peer comma := "," if len(persistentPeers) == 0 { comma = "" } - persistentPeers += fmt.Sprintf("%s%s@%s:46656", comma, genTx.NodeID, genTx.IP) + persistentPeers += fmt.Sprintf("%s%s@%s:46656", comma, genTxs[nodeID].NodeID, + genTxs[nodeID].IP) } return From 878a53bf0def7c4aa1d76145383dfda204514b5b Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Sun, 13 May 2018 18:24:48 -0400 Subject: [PATCH 04/50] Prevent --gen-txs from failing silently Instead of just failing silently if there is a non .json file or folder in the gentx/ directory it now just skips that file or directory. ref #940 --- server/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/init.go b/server/init.go index ffb84f27c..8912d2a6f 100644 --- a/server/init.go +++ b/server/init.go @@ -222,7 +222,7 @@ func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) ( for _, fo := range fos { filename := path.Join(genTxsDir, fo.Name()) if !fo.IsDir() && (path.Ext(filename) != ".json") { - return + continue } // get the genTx From d2163017ceed3a6b8b5ef5e0c907f9f9957991df Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 14 May 2018 09:39:29 -0400 Subject: [PATCH 05/50] small efficiency increase --- server/init.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/init.go b/server/init.go index 8912d2a6f..68a1709bc 100644 --- a/server/init.go +++ b/server/init.go @@ -244,17 +244,18 @@ func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) ( sort.Strings(nodeIDs) for _, nodeID := range nodeIDs { + genTx := genTxs[nodeID] + // combine some stuff - validators = append(validators, genTxs[nodeID].Validator) - appGenTxs = append(appGenTxs, genTxs[nodeID].AppGenTx) + validators = append(validators, genTx.Validator) + appGenTxs = append(appGenTxs, genTx.AppGenTx) // Add a persistent peer comma := "," if len(persistentPeers) == 0 { comma = "" } - persistentPeers += fmt.Sprintf("%s%s@%s:46656", comma, genTxs[nodeID].NodeID, - genTxs[nodeID].IP) + persistentPeers += fmt.Sprintf("%s%s@%s:46656", comma, genTx.NodeID, genTx.IP) } return From 27085cf75acdc1c619a7607df84f19ece415460c Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 14 May 2018 17:52:52 +0200 Subject: [PATCH 06/50] updated rest spec --- docs/sdk/lcd-rest-api.yaml | 66 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/docs/sdk/lcd-rest-api.yaml b/docs/sdk/lcd-rest-api.yaml index 7d38274c0..56ec21ea6 100644 --- a/docs/sdk/lcd-rest-api.yaml +++ b/docs/sdk/lcd-rest-api.yaml @@ -2,7 +2,7 @@ openapi: 3.0.0 servers: - url: 'http://localhost:8998' info: - version: "1.0.0-oas3" + version: "1.1.0" title: Light client daemon to interface with Cosmos baseserver via REST description: Specification for the LCD provided by `gaia rest-server` @@ -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: From 249c5508ef61d3abb96e9e47da3a76f793deaac7 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 14 May 2018 17:05:27 -0400 Subject: [PATCH 07/50] changelog and version --- CHANGELOG.md | 2 +- version/version.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6fd48959..7acdc4ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.16.0 (TBD) +## 0.16.0 (May 14th, 2018) BREAKING CHANGES diff --git a/version/version.go b/version/version.go index d315bfea1..b19a56018 100644 --- a/version/version.go +++ b/version/version.go @@ -9,7 +9,7 @@ const Maj = "0" const Min = "16" const Fix = "0" -const Version = "0.16.0-dev" +const Version = "0.16.0" // GitCommit set by build flags var GitCommit = "" From 69b6522410845ce065a01b21bfd0f373e6a73b38 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 14 May 2018 17:20:00 -0400 Subject: [PATCH 08/50] update for tendermint v0.19.3 --- Gopkg.lock | 35 ++++++++++++++++------------------- Gopkg.toml | 9 +++++++-- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index f474e7756..9aff8fe56 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -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 = "03f6a29a64fc4d26322a90839b892014d2cb90a5" + version = "v0.19.3-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 = "30cd9c49b9e87f62f40a02fd0ba35a2d67a519ef781e06316246b307e6be638d" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index d2d4ee2ff..5fe068df6 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -70,11 +70,16 @@ [[constraint]] name = "github.com/tendermint/tendermint" - version = "0.19.1" + version = "0.19.3-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 From a2ba50718e8156a73740a56bf53a150addf516f5 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 15 May 2018 12:43:13 -0400 Subject: [PATCH 09/50] changelog and version --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++------ version/version.go | 4 ++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a88e09c4..01180ade9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.17.0 (TBD) + +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 @@ -18,7 +45,6 @@ BREAKING CHANGES 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 @@ -28,14 +54,10 @@ FEATURES: * New genesis account keys are automatically added to the client keybase (introduce `--client-home` flag) * Initialize with genesis txs using `--gen-txs` flag * 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) diff --git a/version/version.go b/version/version.go index b19a56018..ec2b70f3a 100644 --- a/version/version.go +++ b/version/version.go @@ -6,10 +6,10 @@ package version // TODO improve const Maj = "0" -const Min = "16" +const Min = "17" const Fix = "0" -const Version = "0.16.0" +const Version = "0.17.0-dev" // GitCommit set by build flags var GitCommit = "" From 20abeb3dcf5d7d692f5dd93263bc785fdba7a9ec Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 15 May 2018 12:45:36 -0400 Subject: [PATCH 10/50] version and changelog --- CHANGELOG.md | 2 +- version/version.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01180ade9..486bd3476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.17.0 (TBD) +## 0.17.0 (May 15, 2018) BREAKING CHANGES diff --git a/version/version.go b/version/version.go index ec2b70f3a..b8b59827d 100644 --- a/version/version.go +++ b/version/version.go @@ -9,7 +9,7 @@ const Maj = "0" const Min = "17" const Fix = "0" -const Version = "0.17.0-dev" +const Version = "0.17.0" // GitCommit set by build flags var GitCommit = "" From ea6d3e8efc2c7172775659ebfcb870626e6ab841 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 7 May 2018 01:13:32 -0400 Subject: [PATCH 11/50] should work maybe? --- CHANGELOG.md | 1 + examples/basecoin/app/app.go | 1 + examples/basecoin/app/app_test.go | 55 +++++++++++++++++++++++++++ x/auth/ante.go | 7 ++-- x/auth/baseaccount.go | 3 -- x/auth/baseaccount_test.go | 6 +-- x/auth/handler.go | 33 ++++++++++++++++ x/auth/mapper.go | 63 +++++++++++++++++++++++++------ x/auth/msgs.go | 44 +++++++++++++++++++++ x/auth/msgs_test.go | 47 +++++++++++++++++++++++ 10 files changed, 238 insertions(+), 22 deletions(-) create mode 100644 x/auth/handler.go create mode 100644 x/auth/msgs.go create mode 100644 x/auth/msgs_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 01180ade9..b5c302d52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ BREAKING CHANGES * gaiad init now requires use of `--name` flag * Removed Get from Msg interface * types/rational now extends big.Rat +* added ability to change pubkey to auth module FEATURES: diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index d3eae85c0..b1a434fa2 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -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)) diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index b502aae46..034e19860 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -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() diff --git a/x/auth/ante.go b/x/auth/ante.go index c7af7e2d9..4be1c6674 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -11,7 +11,7 @@ import ( // 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 +24,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 +61,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,7 +81,7 @@ func NewAnteHandler(accountMapper sdk.AccountMapper, feeHandler sdk.FeeHandler) } // Save the account. - accountMapper.SetAccount(ctx, signerAcc) + am.SetAccount(ctx, signerAcc) signerAccs[i] = signerAcc } diff --git a/x/auth/baseaccount.go b/x/auth/baseaccount.go index 6a612689d..ff907fc38 100644 --- a/x/auth/baseaccount.go +++ b/x/auth/baseaccount.go @@ -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 } diff --git a/x/auth/baseaccount_test.go b/x/auth/baseaccount_test.go index 8b69b6dfc..d3363e4fb 100644 --- a/x/auth/baseaccount_test.go +++ b/x/auth/baseaccount_test.go @@ -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()) //------------------------------------ diff --git a/x/auth/handler.go b/x/auth/handler.go new file mode 100644 index 000000000..bba03c56b --- /dev/null +++ b/x/auth/handler.go @@ -0,0 +1,33 @@ +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() + } + + // TODO: add some tags so we can search it! + return sdk.Result{} // TODO +} diff --git a/x/auth/mapper.go b/x/auth/mapper.go index f9e202c8a..3666f13b6 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -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) diff --git a/x/auth/msgs.go b/x/auth/msgs.go new file mode 100644 index 000000000..545b296e5 --- /dev/null +++ b/x/auth/msgs.go @@ -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} +} diff --git a/x/auth/msgs_test.go b/x/auth/msgs_test.go new file mode 100644 index 000000000..30c98b073 --- /dev/null +++ b/x/auth/msgs_test.go @@ -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()) +} From 2c6b414098bbfeaf48085362486a1cb822267552 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 23:36:44 +0200 Subject: [PATCH 12/50] Move changelog entry to version 0.17 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c302d52..b4bd85670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ FEATURES * [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 +* [x/auth] Added ability to change pubkey to auth module IMPROVEMENTS @@ -42,7 +43,6 @@ BREAKING CHANGES * gaiad init now requires use of `--name` flag * Removed Get from Msg interface * types/rational now extends big.Rat -* added ability to change pubkey to auth module FEATURES: From c81e37ce7a75b3a43c9ab9f48cdf68f068424f52 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 23:40:01 +0200 Subject: [PATCH 13/50] Return tags on changePubkey --- x/auth/handler.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/auth/handler.go b/x/auth/handler.go index bba03c56b..8a0e1061a 100644 --- a/x/auth/handler.go +++ b/x/auth/handler.go @@ -28,6 +28,7 @@ func handleMsgChangeKey(ctx sdk.Context, am AccountMapper, msg MsgChangeKey) sdk return err.Result() } - // TODO: add some tags so we can search it! - return sdk.Result{} // TODO + return sdk.Result{ + Tags: sdk.NewTags("action", []byte("changePubkey"), "address", msg.Address.Bytes(), "pubkey", msg.NewPubKey.Bytes()), + } } From d6708a40e85748b8d5b4c876b9952c2f07e51eb8 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Tue, 15 May 2018 18:30:06 -0400 Subject: [PATCH 14/50] gaiad: don't print command twice --- server/util.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/util.go b/server/util.go index 313d4fc5b..f44cc2d72 100644 --- a/server/util.go +++ b/server/util.go @@ -79,7 +79,6 @@ func AddCommands( ShowNodeIDCmd(ctx), ShowValidatorCmd(ctx), ExportCmd(ctx, cdc, appExport), - UnsafeResetAllCmd(ctx), version.VersionCmd, ) } From 46f9445f06c72582660512070d451d6e4b3878f5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 19:13:46 +0200 Subject: [PATCH 15/50] Add gas limit / gas consumed to context --- types/context.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/types/context.go b/types/context.go index e28523ebe..b3f30a2e0 100644 --- a/types/context.go +++ b/types/context.go @@ -43,6 +43,8 @@ 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.WithGasLimit(0) + c = c.WithGasConsumed(0) return c } @@ -127,6 +129,8 @@ const ( contextKeyIsCheckTx contextKeyTxBytes contextKeyLogger + contextKeyGasLimit + contextKeyGasConsumed ) // NOTE: Do not expose MultiStore. @@ -155,6 +159,12 @@ func (c Context) TxBytes() []byte { func (c Context) Logger() log.Logger { return c.Value(contextKeyLogger).(log.Logger) } +func (c Context) GasLimit() uint64 { + return c.Value(contextKeyGasLimit).(uint64) +} +func (c Context) GasConsumed() uint64 { + return c.Value(contextKeyGasConsumed).(uint64) +} func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) } @@ -177,6 +187,12 @@ func (c Context) WithTxBytes(txBytes []byte) Context { func (c Context) WithLogger(logger log.Logger) Context { return c.withValue(contextKeyLogger, logger) } +func (c Context) WithGasLimit(limit uint64) Context { + return c.withValue(contextKeyGasLimit, limit) +} +func (c Context) WithGasConsumed(consumed uint64) Context { + return c.withValue(contextKeyGasConsumed, consumed) +} // Cache the multistore and return a new cached context. The cached context is // written to the context when writeCache is called. From 26991803ee50456719def16c3284437bdcf90200 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 19:48:12 +0200 Subject: [PATCH 16/50] GasMeter & context updates --- baseapp/baseapp.go | 10 +++--- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/handler_test.go | 2 +- examples/democoin/x/pow/keeper_test.go | 2 +- .../democoin/x/simplestake/keeper_test.go | 4 +-- types/context.go | 22 ++++-------- types/context_test.go | 4 +-- types/gas.go | 36 +++++++++++++++++++ types/lib/mapper_test.go | 2 +- x/auth/ante_test.go | 10 +++--- x/auth/context_test.go | 2 +- x/auth/mapper_test.go | 2 +- x/bank/keeper_test.go | 6 ++-- x/ibc/ibc_test.go | 2 +- x/stake/test_common.go | 2 +- 15 files changed, 69 insertions(+), 39 deletions(-) create mode 100644 types/gas.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index d3bf85fe8..36843b776 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -35,6 +35,7 @@ type BaseApp struct { // must be set txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth + txGasLimit sdk.Gas // per-transaction gas limit // may be nil initChainer sdk.InitChainer // initialize state with validators and state blob @@ -66,6 +67,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *Bas router: NewRouter(), codespacer: sdk.NewCodespacer(), txDecoder: defaultTxDecoder(cdc), + txGasLimit: sdk.Gas(10000), } // Register the undefined & root codespaces, which should not be used by any modules app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined) @@ -210,9 +212,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger) + return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, app.txGasLimit) } - return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger) + return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, app.txGasLimit) } type state struct { @@ -228,7 +230,7 @@ func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, true, nil, app.Logger), + ctx: sdk.NewContext(ms, header, true, nil, app.Logger, app.txGasLimit), } } @@ -236,7 +238,7 @@ func (app *BaseApp) setDeliverState(header abci.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, false, nil, app.Logger), + ctx: sdk.NewContext(ms, header, false, nil, app.Logger, app.txGasLimit), } } diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index d497dee69..10e958ce7 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -30,7 +30,7 @@ func TestCoolKeeper(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 0) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index 30aeafa2a..48e9c53e7 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -20,7 +20,7 @@ func TestPowHandler(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index a4afb876a..324cdcdce 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -33,7 +33,7 @@ func TestPowKeeperGetSet(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index 302a2e58b..1d19a61d7 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -33,7 +33,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func TestKeeperGetSet(t *testing.T) { ms, _, capKey := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace) addr := sdk.Address([]byte("some-address")) @@ -60,7 +60,7 @@ func TestBonding(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := bank.NewKeeper(accountMapper) diff --git a/types/context.go b/types/context.go index b3f30a2e0..33713d353 100644 --- a/types/context.go +++ b/types/context.go @@ -30,7 +30,7 @@ type Context struct { } // create a new context -func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger) Context { +func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger, gasLimit Gas) Context { c := Context{ Context: context.Background(), pst: newThePast(), @@ -43,8 +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.WithGasLimit(0) - c = c.WithGasConsumed(0) + c = c.WithGasMeter(NewGasMeter(gasLimit)) return c } @@ -129,8 +128,7 @@ const ( contextKeyIsCheckTx contextKeyTxBytes contextKeyLogger - contextKeyGasLimit - contextKeyGasConsumed + contextKeyGasMeter ) // NOTE: Do not expose MultiStore. @@ -159,11 +157,8 @@ func (c Context) TxBytes() []byte { func (c Context) Logger() log.Logger { return c.Value(contextKeyLogger).(log.Logger) } -func (c Context) GasLimit() uint64 { - return c.Value(contextKeyGasLimit).(uint64) -} -func (c Context) GasConsumed() uint64 { - return c.Value(contextKeyGasConsumed).(uint64) +func (c Context) GasMeter() GasMeter { + return c.Value(contextKeyGasMeter).(GasMeter) } func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) @@ -187,11 +182,8 @@ func (c Context) WithTxBytes(txBytes []byte) Context { func (c Context) WithLogger(logger log.Logger) Context { return c.withValue(contextKeyLogger, logger) } -func (c Context) WithGasLimit(limit uint64) Context { - return c.withValue(contextKeyGasLimit, limit) -} -func (c Context) WithGasConsumed(consumed uint64) Context { - return c.withValue(contextKeyGasConsumed, consumed) +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 diff --git a/types/context_test.go b/types/context_test.go index ec5faab44..9eafed625 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -43,7 +43,7 @@ func (l MockLogger) With(kvs ...interface{}) log.Logger { func TestContextGetOpShouldNeverPanic(t *testing.T) { var ms types.MultiStore - ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) indices := []int64{ -10, 1, 0, 10, 20, } @@ -58,7 +58,7 @@ func defaultContext(key types.StoreKey) types.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) return ctx } diff --git a/types/gas.go b/types/gas.go new file mode 100644 index 000000000..63aa71c1b --- /dev/null +++ b/types/gas.go @@ -0,0 +1,36 @@ +package types + +import () + +type Gas uint64 + +type GasMeter interface { + GasExceeded() bool + ConsumeGas(amount Gas) + ConsumeGasOrFail(amount Gas) bool +} + +type basicGasMeter struct { + limit Gas + consumed Gas +} + +func NewGasMeter(limit Gas) GasMeter { + return &basicGasMeter{ + limit: limit, + consumed: 0, + } +} + +func (g *basicGasMeter) GasExceeded() bool { + return g.consumed > g.limit +} + +func (g *basicGasMeter) ConsumeGas(amount Gas) { + g.consumed += amount +} + +func (g *basicGasMeter) ConsumeGasOrFail(amount Gas) bool { + g.ConsumeGas(amount) + return g.GasExceeded() +} diff --git a/types/lib/mapper_test.go b/types/lib/mapper_test.go index e1759b06a..ab6fb605a 100644 --- a/types/lib/mapper_test.go +++ b/types/lib/mapper_test.go @@ -25,7 +25,7 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *wire.Codec) { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) cdc := wire.NewCodec() return ctx, cdc } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index ec296b12b..8310d3069 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -74,7 +74,7 @@ func TestAnteHandlerSigErrors(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -115,7 +115,7 @@ func TestAnteHandlerSequences(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -181,7 +181,7 @@ func TestAnteHandlerFees(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -218,7 +218,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() @@ -293,7 +293,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) // keys and addresses priv1, addr1 := privAndAddr() diff --git a/x/auth/context_test.go b/x/auth/context_test.go index 89e318e0a..996a06889 100644 --- a/x/auth/context_test.go +++ b/x/auth/context_test.go @@ -13,7 +13,7 @@ import ( func TestContextWithSigners(t *testing.T) { ms, _ := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) _, _, addr1 := keyPubAddr() _, _, addr2 := keyPubAddr() diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go index cdd418990..80f673f99 100644 --- a/x/auth/mapper_test.go +++ b/x/auth/mapper_test.go @@ -29,7 +29,7 @@ func TestAccountMapperGetSet(t *testing.T) { RegisterBaseAccount(cdc) // make context and mapper - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) addr := sdk.Address([]byte("some-address")) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 3db16c5f9..324152df2 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -31,7 +31,7 @@ func TestKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) @@ -117,7 +117,7 @@ func TestSendKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) sendKeeper := NewSendKeeper(accountMapper) @@ -186,7 +186,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 60cc59bad..298b5b462 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -24,7 +24,7 @@ func defaultContext(key sdk.StoreKey) sdk.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) return ctx } diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 27acebe08..15c300d41 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger()) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 0) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec From efc7843fb926f88180c2297b0eb854e988d97b4e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 20:28:53 +0200 Subject: [PATCH 17/50] Changes to bank keeper for gas --- types/errors.go | 6 ++ x/bank/keeper.go | 32 ++++++--- x/bank/keeper_test.go | 149 +++++++++++++++++++++++++++-------------- x/ibc/ibc_test.go | 2 +- x/stake/test_common.go | 2 +- 5 files changed, 128 insertions(+), 63 deletions(-) diff --git a/types/errors.go b/types/errors.go index 059a0dd74..20d452464 100644 --- a/types/errors.go +++ b/types/errors.go @@ -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 diff --git a/x/bank/keeper.go b/x/bank/keeper.go index d23167c3c..8bcbb2c86 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -17,7 +17,7 @@ func NewKeeper(am sdk.AccountMapper) Keeper { } // GetCoins returns the coins at the addr. -func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { +func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { return getCoins(ctx, keeper.am, addr) } @@ -27,7 +27,7 @@ func (keeper Keeper) SetCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { +func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { return hasCoins(ctx, keeper.am, addr, amt) } @@ -64,12 +64,12 @@ func NewSendKeeper(am sdk.AccountMapper) SendKeeper { } // GetCoins returns the coins at the addr. -func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { +func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { +func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { return hasCoins(ctx, keeper.am, addr, amt) } @@ -96,23 +96,26 @@ func NewViewKeeper(am sdk.AccountMapper) ViewKeeper { } // GetCoins returns the coins at the addr. -func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { +func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { +func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { return hasCoins(ctx, keeper.am, addr, amt) } //______________________________________________________________________________________________ -func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins { +func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) (sdk.Coins, sdk.Error) { + if ctx.GasMeter().ConsumeGasOrFail(10) { + return sdk.Coins{}, sdk.ErrOutOfGas("out of gas in getCoins") + } acc := am.GetAccount(ctx, addr) if acc == nil { - return sdk.Coins{} + return sdk.Coins{}, nil } - return acc.GetCoins() + return acc.GetCoins(), nil } func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { @@ -126,8 +129,15 @@ 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 { - return getCoins(ctx, am, addr).IsGTE(amt) +func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { + if ctx.GasMeter().ConsumeGasOrFail(10) { + return false, sdk.ErrOutOfGas("out of gas in hasCoins") + } + coins, err := getCoins(ctx, am, addr) + if err != nil { + return false, err + } + return coins.IsGTE(amt), nil } // SubtractCoins subtracts amt from the coins at the addr. diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 324152df2..430b87c73 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -31,7 +31,7 @@ func TestKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) @@ -42,58 +42,79 @@ func TestKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + coins, err := coinKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) - assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) - assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) - assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + foo, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, foo) + foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) + assert.True(t, foo) + foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + assert.False(t, foo) + bar, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) + assert.False(t, bar) // Test AddCoins coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 25}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 25}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 15}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) // Test SubtractCoins coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) - _, _, err := coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) + _, err = coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) assert.Implements(t, (*sdk.Error)(nil), err) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 15}})) - assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 15}})) + bar, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}}) + assert.False(t, bar) // Test SendCoins coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) coinKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -105,9 +126,12 @@ func TestKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } coinKeeper.InputOutputCoins(ctx, inputs, outputs) - assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - assert.True(t, coinKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + coins, err = coinKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + coins, err = coinKeeper.GetCoins(ctx, addr3) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } @@ -117,7 +141,7 @@ func TestSendKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) sendKeeper := NewSendKeeper(accountMapper) @@ -129,40 +153,55 @@ func TestSendKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + coins, err := sendKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) - assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) - assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) - assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + foo, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, foo) + foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) + assert.True(t, foo) + foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + assert.False(t, foo) + bar, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) + assert.False(t, bar) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) // Test SendCoins sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) sendKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -174,9 +213,12 @@ func TestSendKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } sendKeeper.InputOutputCoins(ctx, inputs, outputs) - assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - assert.True(t, sendKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + coins, err = sendKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + coins, err = sendKeeper.GetCoins(ctx, addr2) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + coins, err = sendKeeper.GetCoins(ctx, addr3) + assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } @@ -186,7 +228,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) @@ -196,14 +238,21 @@ func TestViewKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) + coins, err := viewKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + coins, err = viewKeeper.GetCoins(ctx, addr) + assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) - assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) - assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) - assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) + foo, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + assert.True(t, foo) + foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) + assert.True(t, foo) + foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) + assert.False(t, foo) + bar, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) + assert.False(t, bar) } diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 298b5b462..e05245ff3 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -24,7 +24,7 @@ func defaultContext(key sdk.StoreKey) sdk.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) return ctx } diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 15c300d41..5c1c38723 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 10000) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec From f0e4d24ea31a6def10387b40e1651f3a9a63e566 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 20:40:33 +0200 Subject: [PATCH 18/50] Basic gas impl, quick testcase --- examples/democoin/x/pow/handler_test.go | 2 +- x/bank/keeper.go | 3 +++ x/bank/keeper_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index 48e9c53e7..df8312502 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -20,7 +20,7 @@ func TestPowHandler(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 8bcbb2c86..efb2f0109 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -119,6 +119,9 @@ func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) (sdk.Coin } func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { + if ctx.GasMeter().ConsumeGasOrFail(100) { + return sdk.ErrOutOfGas("out of gas in setCoins") + } acc := am.GetAccount(ctx, addr) if acc == nil { acc = am.NewAccountWithAddress(ctx, addr) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 430b87c73..a711a893c 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -135,6 +135,31 @@ func TestKeeper(t *testing.T) { } +func TestKeeperGas(t *testing.T) { + ms, authKey := setupMultiStore() + + cdc := wire.NewCodec() + auth.RegisterBaseAccount(cdc) + + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10) + accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) + coinKeeper := NewKeeper(accountMapper) + + addr := sdk.Address([]byte("addr1")) + acc := accountMapper.NewAccountWithAddress(ctx, addr) + + // Test GetCoins/SetCoins + accountMapper.SetAccount(ctx, acc) + coins, err := coinKeeper.GetCoins(ctx, addr) + assert.Nil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) + + coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) + coins, err = coinKeeper.GetCoins(ctx, addr) + assert.NotNil(t, err) + assert.True(t, coins.IsEqual(sdk.Coins{})) +} + func TestSendKeeper(t *testing.T) { ms, authKey := setupMultiStore() From ddb3b36b7b2c513c284267845d2cdadba1327306 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 20:49:34 +0200 Subject: [PATCH 19/50] Pass gas consumed back in result struct --- baseapp/baseapp.go | 4 ++++ types/gas.go | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 36843b776..aeb534d43 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -322,6 +322,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, @@ -433,6 +434,9 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk result = handler(ctx, msg) + // Set gas utilized + result.GasUsed = ctx.GasMeter().GasConsumed() + // If result was successful, write to app.checkState.ms or app.deliverState.ms if result.IsOK() { msCache.Write() diff --git a/types/gas.go b/types/gas.go index 63aa71c1b..d4b63597c 100644 --- a/types/gas.go +++ b/types/gas.go @@ -2,10 +2,11 @@ package types import () -type Gas uint64 +type Gas = int64 type GasMeter interface { GasExceeded() bool + GasConsumed() Gas ConsumeGas(amount Gas) ConsumeGasOrFail(amount Gas) bool } @@ -26,6 +27,10 @@ func (g *basicGasMeter) GasExceeded() bool { return g.consumed > g.limit } +func (g *basicGasMeter) GasConsumed() Gas { + return g.consumed +} + func (g *basicGasMeter) ConsumeGas(amount Gas) { g.consumed += amount } From af379b6cf660d5e7359a73eff233846fd55ced9c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 7 May 2018 21:49:11 +0200 Subject: [PATCH 20/50] Linter fixes --- types/gas.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/gas.go b/types/gas.go index d4b63597c..24be99f3f 100644 --- a/types/gas.go +++ b/types/gas.go @@ -2,8 +2,10 @@ package types import () +// Gas measured by the SDK type Gas = int64 +// GasMeter interface to track gas consumption type GasMeter interface { GasExceeded() bool GasConsumed() Gas From 1f8ef62d28b444bb39f1b7d24199bda77a2ca68a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 17:34:09 +0200 Subject: [PATCH 21/50] Swap to panic/recover version --- types/gas.go | 23 +++--- x/bank/keeper.go | 40 ++++------ x/bank/keeper_test.go | 170 ++++++++++++------------------------------ 3 files changed, 74 insertions(+), 159 deletions(-) diff --git a/types/gas.go b/types/gas.go index 24be99f3f..78246be2d 100644 --- a/types/gas.go +++ b/types/gas.go @@ -5,12 +5,15 @@ 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 { - GasExceeded() bool GasConsumed() Gas - ConsumeGas(amount Gas) - ConsumeGasOrFail(amount Gas) bool + ConsumeGas(amount Gas, descriptor string) } type basicGasMeter struct { @@ -25,19 +28,13 @@ func NewGasMeter(limit Gas) GasMeter { } } -func (g *basicGasMeter) GasExceeded() bool { - return g.consumed > g.limit -} - func (g *basicGasMeter) GasConsumed() Gas { return g.consumed } -func (g *basicGasMeter) ConsumeGas(amount Gas) { +func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { g.consumed += amount -} - -func (g *basicGasMeter) ConsumeGasOrFail(amount Gas) bool { - g.ConsumeGas(amount) - return g.GasExceeded() + if g.consumed > g.limit { + panic(ErrorOutOfGas{descriptor}) + } } diff --git a/x/bank/keeper.go b/x/bank/keeper.go index efb2f0109..129ffa792 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -17,7 +17,7 @@ func NewKeeper(am sdk.AccountMapper) Keeper { } // GetCoins returns the coins at the addr. -func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { +func (keeper Keeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { return getCoins(ctx, keeper.am, addr) } @@ -27,7 +27,7 @@ func (keeper Keeper) SetCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { +func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { return hasCoins(ctx, keeper.am, addr, amt) } @@ -64,12 +64,12 @@ func NewSendKeeper(am sdk.AccountMapper) SendKeeper { } // GetCoins returns the coins at the addr. -func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { +func (keeper SendKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { +func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { return hasCoins(ctx, keeper.am, addr, amt) } @@ -96,32 +96,28 @@ func NewViewKeeper(am sdk.AccountMapper) ViewKeeper { } // GetCoins returns the coins at the addr. -func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) (sdk.Coins, sdk.Error) { +func (keeper ViewKeeper) GetCoins(ctx sdk.Context, addr sdk.Address) sdk.Coins { return getCoins(ctx, keeper.am, addr) } // HasCoins returns whether or not an account has at least amt coins. -func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (bool, sdk.Error) { +func (keeper ViewKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) bool { return hasCoins(ctx, keeper.am, addr, amt) } //______________________________________________________________________________________________ -func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) (sdk.Coins, sdk.Error) { - if ctx.GasMeter().ConsumeGasOrFail(10) { - return sdk.Coins{}, sdk.ErrOutOfGas("out of gas in getCoins") - } +func getCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address) sdk.Coins { + ctx.GasMeter().ConsumeGas(10, "getCoins") acc := am.GetAccount(ctx, addr) if acc == nil { - return sdk.Coins{}, nil + return sdk.Coins{} } - return acc.GetCoins(), nil + return acc.GetCoins() } func setCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) sdk.Error { - if ctx.GasMeter().ConsumeGasOrFail(100) { - return sdk.ErrOutOfGas("out of gas in setCoins") - } + ctx.GasMeter().ConsumeGas(100, "setCoins") acc := am.GetAccount(ctx, addr) if acc == nil { acc = am.NewAccountWithAddress(ctx, addr) @@ -132,19 +128,14 @@ 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, sdk.Error) { - if ctx.GasMeter().ConsumeGasOrFail(10) { - return false, sdk.ErrOutOfGas("out of gas in hasCoins") - } - coins, err := getCoins(ctx, am, addr) - if err != nil { - return false, err - } - return coins.IsGTE(amt), nil +func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) bool { + ctx.GasMeter().ConsumeGas(10, "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(10, "subtractCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Minus(amt) if !newCoins.IsNotNegative() { @@ -157,6 +148,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(10, "addCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Plus(amt) if !newCoins.IsNotNegative() { diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index a711a893c..38c9ad576 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -42,79 +42,58 @@ func TestKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - coins, err := coinKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - foo, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, foo) - foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) - assert.True(t, foo) - foo, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.False(t, foo) - bar, _ := coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.False(t, bar) + assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) // Test AddCoins coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 25}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 25}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 15}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 15}, {"foocoin", 25}})) // Test SubtractCoins coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) - _, err = coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) + _, err := coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 11}}) assert.Implements(t, (*sdk.Error)(nil), err) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 15}})) coinKeeper.SubtractCoins(ctx, addr, sdk.Coins{{"barcoin", 10}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 15}})) - bar, _ = coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}}) - assert.False(t, bar) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 15}})) + assert.False(t, coinKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 1}})) // Test SendCoins coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) coinKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) coinKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -126,40 +105,12 @@ func TestKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } coinKeeper.InputOutputCoins(ctx, inputs, outputs) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - coins, err = coinKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - coins, err = coinKeeper.GetCoins(ctx, addr3) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + assert.True(t, coinKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } -func TestKeeperGas(t *testing.T) { - ms, authKey := setupMultiStore() - - cdc := wire.NewCodec() - auth.RegisterBaseAccount(cdc) - - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10) - accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) - coinKeeper := NewKeeper(accountMapper) - - addr := sdk.Address([]byte("addr1")) - acc := accountMapper.NewAccountWithAddress(ctx, addr) - - // Test GetCoins/SetCoins - accountMapper.SetAccount(ctx, acc) - coins, err := coinKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) - - coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = coinKeeper.GetCoins(ctx, addr) - assert.NotNil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) -} - func TestSendKeeper(t *testing.T) { ms, authKey := setupMultiStore() @@ -178,55 +129,40 @@ func TestSendKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - coins, err := sendKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - foo, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, foo) - foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) - assert.True(t, foo) - foo, _ = sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.False(t, foo) - bar, _ := sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.False(t, bar) + assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, sendKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) // Test SendCoins sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 5}}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) _, err2 := sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"foocoin", 50}}) assert.Implements(t, (*sdk.Error)(nil), err2) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"foocoin", 5}})) coinKeeper.AddCoins(ctx, addr, sdk.Coins{{"barcoin", 30}}) sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{{"barcoin", 10}, {"foocoin", 5}}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 10}})) // Test InputOutputCoins input1 := NewInput(addr2, sdk.Coins{{"foocoin", 2}}) output1 := NewOutput(addr, sdk.Coins{{"foocoin", 2}}) sendKeeper.InputOutputCoins(ctx, []Input{input1}, []Output{output1}) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 20}, {"foocoin", 7}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 10}, {"foocoin", 8}})) inputs := []Input{ NewInput(addr, sdk.Coins{{"foocoin", 3}}), @@ -238,12 +174,9 @@ func TestSendKeeper(t *testing.T) { NewOutput(addr3, sdk.Coins{{"barcoin", 2}, {"foocoin", 5}}), } sendKeeper.InputOutputCoins(ctx, inputs, outputs) - coins, err = sendKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) - coins, err = sendKeeper.GetCoins(ctx, addr2) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) - coins, err = sendKeeper.GetCoins(ctx, addr3) - assert.True(t, coins.IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"barcoin", 21}, {"foocoin", 4}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.Coins{{"barcoin", 7}, {"foocoin", 6}})) + assert.True(t, sendKeeper.GetCoins(ctx, addr3).IsEqual(sdk.Coins{{"barcoin", 2}, {"foocoin", 5}})) } @@ -253,7 +186,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) @@ -263,21 +196,14 @@ func TestViewKeeper(t *testing.T) { // Test GetCoins/SetCoins accountMapper.SetAccount(ctx, acc) - coins, err := viewKeeper.GetCoins(ctx, addr) - assert.Nil(t, err) - assert.True(t, coins.IsEqual(sdk.Coins{})) + assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{})) coinKeeper.SetCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - coins, err = viewKeeper.GetCoins(ctx, addr) - assert.True(t, coins.IsEqual(sdk.Coins{{"foocoin", 10}})) + assert.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.Coins{{"foocoin", 10}})) // Test HasCoins - foo, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}}) - assert.True(t, foo) - foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}}) - assert.True(t, foo) - foo, _ = viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}}) - assert.False(t, foo) - bar, _ := viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}}) - assert.False(t, bar) + assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 10}})) + assert.True(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 5}})) + assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"foocoin", 15}})) + assert.False(t, viewKeeper.HasCoins(ctx, addr, sdk.Coins{{"barcoin", 5}})) } From 09517056b07dbd1ba55c7ebffe9b537b11a5907d Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 17:46:02 +0200 Subject: [PATCH 22/50] Catch out-of-gas panics --- baseapp/baseapp.go | 14 ++++++++++---- baseapp/baseapp_test.go | 10 +++++----- cmd/gaia/app/app.go | 2 +- examples/basecoin/app/app.go | 2 +- examples/democoin/app/app.go | 2 +- examples/kvstore/main.go | 2 +- mock/app.go | 2 +- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index aeb534d43..e1198c3c8 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -58,7 +58,7 @@ var _ abci.Application = (*BaseApp)(nil) // Create and name new BaseApp // NOTE: The db is used to store the version number for now. -func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *BaseApp { +func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGasLimit sdk.Gas) *BaseApp { app := &BaseApp{ Logger: logger, name: name, @@ -67,7 +67,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *Bas router: NewRouter(), codespacer: sdk.NewCodespacer(), txDecoder: defaultTxDecoder(cdc), - txGasLimit: sdk.Gas(10000), + txGasLimit: txGasLimit, } // Register the undefined & root codespaces, which should not be used by any modules app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined) @@ -375,8 +375,14 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // 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() + } } }() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index aab64fc20..3c07e4ac0 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -25,7 +25,7 @@ func defaultLogger() log.Logger { func newBaseApp(name string) *BaseApp { logger := defaultLogger() db := dbm.NewMemDB() - return NewBaseApp(name, nil, logger, db) + return NewBaseApp(name, nil, logger, db, 10000) } func TestMountStores(t *testing.T) { @@ -59,7 +59,7 @@ func TestLoadVersion(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() name := t.Name() - app := NewBaseApp(name, nil, logger, db) + app := NewBaseApp(name, nil, logger, db, 10000) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("main") @@ -81,7 +81,7 @@ func TestLoadVersion(t *testing.T) { commitID := sdk.CommitID{1, res.Data} // reload - app = NewBaseApp(name, nil, logger, db) + app = NewBaseApp(name, nil, logger, db, 10000) app.MountStoresIAVL(capKey) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -147,7 +147,7 @@ func TestInitChainer(t *testing.T) { name := t.Name() db := dbm.NewMemDB() logger := defaultLogger() - app := NewBaseApp(name, nil, logger, db) + app := NewBaseApp(name, nil, logger, db, 10000) // make cap keys and mount the stores // NOTE/TODO: mounting multiple stores is broken // see https://github.com/cosmos/cosmos-sdk/issues/532 @@ -184,7 +184,7 @@ func TestInitChainer(t *testing.T) { assert.Equal(t, value, res.Value) // reload app - app = NewBaseApp(name, nil, logger, db) + app = NewBaseApp(name, nil, logger, db, 10000) app.MountStoresIAVL(capKey, capKey2) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 5ff532bff..672fd5b76 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -51,7 +51,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // create your application object var app = &GaiaApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index b1a434fa2..d5994f55b 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -48,7 +48,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // Create your application object. var app = &BasecoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 7c8250b18..bf48348b4 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -56,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Create your application object. var app = &DemocoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), cdc: cdc, capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyAccountStore: sdk.NewKVStoreKey("acc"), diff --git a/examples/kvstore/main.go b/examples/kvstore/main.go index 856538f63..bd10d31e9 100644 --- a/examples/kvstore/main.go +++ b/examples/kvstore/main.go @@ -32,7 +32,7 @@ func main() { var capKeyMainStore = sdk.NewKVStoreKey("main") // Create BaseApp. - var baseApp = bam.NewBaseApp("kvstore", nil, logger, db) + var baseApp = bam.NewBaseApp("kvstore", nil, logger, db, 10000) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) diff --git a/mock/app.go b/mock/app.go index ab1a8447a..4799b726a 100644 --- a/mock/app.go +++ b/mock/app.go @@ -29,7 +29,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { capKeyMainStore := sdk.NewKVStoreKey("main") // Create BaseApp. - baseApp := bam.NewBaseApp("kvstore", nil, logger, db) + baseApp := bam.NewBaseApp("kvstore", nil, logger, db, 10000) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) From ca4ef9a2fca6a5a6175c86820ecb7ef3a2aedc08 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 17:54:00 +0200 Subject: [PATCH 23/50] Add baseapp test for out-of-gas handling --- baseapp/baseapp_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 3c07e4ac0..913ae1aaa 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -260,6 +260,34 @@ func TestDeliverTx(t *testing.T) { } } +// Test that transactions exceeding gas limits fail +func TestTxGasLimits(t *testing.T) { + logger := defaultLogger() + db := dbm.NewMemDB() + app := NewBaseApp(t.Name(), nil, logger, db, 0) + + // 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) { 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()) From c410ceb1552e5dba3a68f3e47acfe86e62a4f2b8 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 8 May 2018 22:03:42 +0200 Subject: [PATCH 24/50] GasKVStore WIP --- store/gaskvstore.go | 80 ++++++++++++++++++++++++++++++++++++++++ store/gaskvstore_test.go | 27 ++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 store/gaskvstore.go create mode 100644 store/gaskvstore_test.go diff --git a/store/gaskvstore.go b/store/gaskvstore.go new file mode 100644 index 000000000..b28bfdba9 --- /dev/null +++ b/store/gaskvstore.go @@ -0,0 +1,80 @@ +package store + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// gasKVStore applies gas tracking to an underlying kvstore +type gasKVStore struct { + gasMeter sdk.GasMeter + parent KVStore +} + +// nolint +func NewGasKVStore(gasMeter sdk.GasMeter, parent KVStore) *gasKVStore { + kvs := &gasKVStore{ + gasMeter: gasMeter, + parent: parent, + } + return kvs +} + +// Implements Store. +func (gi *gasKVStore) GetStoreType() StoreType { + return gi.parent.GetStoreType() +} + +// Implements KVStore. +func (gi *gasKVStore) Get(key []byte) (value []byte) { + return gi.parent.Get(key) +} + +// Implements KVStore. +func (gi *gasKVStore) Set(key []byte, value []byte) { + gi.parent.Set(key, value) +} + +// Implements KVStore. +func (gi *gasKVStore) Has(key []byte) bool { + return gi.parent.Has(key) +} + +// Implements KVStore. +func (gi *gasKVStore) Delete(key []byte) { + gi.parent.Delete(key) +} + +// Implements KVStore. +func (gi *gasKVStore) Iterator(start, end []byte) Iterator { + return gi.iterator(start, end, true) +} + +// Implements KVStore. +func (gi *gasKVStore) ReverseIterator(start, end []byte) Iterator { + return gi.iterator(start, end, false) +} + +// Implements KVStore. +func (gi *gasKVStore) SubspaceIterator(prefix []byte) Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true) +} + +// Implements KVStore. +func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false) +} + +// Implements KVStore. +func (gi *gasKVStore) CacheWrap() CacheWrap { + return gi.parent.CacheWrap() // TODO +} + +func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { + var parent Iterator + if ascending { + parent = gi.parent.Iterator(start, end) + } else { + parent = gi.parent.ReverseIterator(start, end) + } + return parent // TODO +} diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go new file mode 100644 index 000000000..dab32dfa1 --- /dev/null +++ b/store/gaskvstore_test.go @@ -0,0 +1,27 @@ +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 TestGasKVStore(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") + + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) +} From ef1923f6605f797f1c85d668b79da135bef6c06e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 00:27:41 +0200 Subject: [PATCH 25/50] Add GasIterator --- store/gaskvstore.go | 75 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/store/gaskvstore.go b/store/gaskvstore.go index b28bfdba9..84faab66e 100644 --- a/store/gaskvstore.go +++ b/store/gaskvstore.go @@ -4,6 +4,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +const ( + HasCost = 10 + ReadCostFlat = 10 + ReadCostPerByte = 1 + WriteCostFlat = 10 + WriteCostPerByte = 10 +) + // gasKVStore applies gas tracking to an underlying kvstore type gasKVStore struct { gasMeter sdk.GasMeter @@ -26,21 +34,30 @@ func (gi *gasKVStore) GetStoreType() StoreType { // Implements KVStore. func (gi *gasKVStore) Get(key []byte) (value []byte) { - return gi.parent.Get(key) + 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) } @@ -66,7 +83,7 @@ func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { // Implements KVStore. func (gi *gasKVStore) CacheWrap() CacheWrap { - return gi.parent.CacheWrap() // TODO + panic("you cannot CacheWrap a GasKVStore") } func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { @@ -76,5 +93,57 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { } else { parent = gi.parent.ReverseIterator(start, end) } - return parent // TODO + return newGasIterator(gi.gasMeter, parent) +} + +type gasIterator struct { + gasMeter sdk.GasMeter + parent Iterator +} + +func newGasIterator(gasMeter sdk.GasMeter, parent Iterator) 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() +} + +/* + TODO + + Not quite sure what to charge for here. Depends on underlying retrieval model. + Could charge for Next(), and Key()/Value() are free, but want to have value-size-proportional gas. +*/ + +// Implements Iterator. +func (g *gasIterator) Next() { + g.parent.Next() +} + +// Implements Iterator. +func (g *gasIterator) Key() (key []byte) { + return g.parent.Key() +} + +// Implements Iterator. +func (g *gasIterator) Value() (value []byte) { + value = g.parent.Value() + g.gasMeter.ConsumeGas(ReadCostFlat, "ValueFlat") + g.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ValuePerByte") + return value +} + +// Implements Iterator. +func (g *gasIterator) Close() { + g.parent.Close() } From 9dfccb1cfd6b6d4c96a69eb797dda23f60a06ffa Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 01:11:22 +0200 Subject: [PATCH 26/50] Update iterator gas pricing model --- store/gaskvstore.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/store/gaskvstore.go b/store/gaskvstore.go index 84faab66e..1bc014229 100644 --- a/store/gaskvstore.go +++ b/store/gaskvstore.go @@ -4,12 +4,16 @@ 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 @@ -118,13 +122,6 @@ func (g *gasIterator) Valid() bool { return g.parent.Valid() } -/* - TODO - - Not quite sure what to charge for here. Depends on underlying retrieval model. - Could charge for Next(), and Key()/Value() are free, but want to have value-size-proportional gas. -*/ - // Implements Iterator. func (g *gasIterator) Next() { g.parent.Next() @@ -132,14 +129,16 @@ func (g *gasIterator) Next() { // Implements Iterator. func (g *gasIterator) Key() (key []byte) { - return g.parent.Key() + 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(ReadCostFlat, "ValueFlat") - g.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ValuePerByte") + g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat") + g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte") return value } From 1c4ed7b833354a21fc36ebe91980b6558eb113fa Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 01:38:32 +0200 Subject: [PATCH 27/50] Gas-wrap ctx.KVStore --- cmd/gaia/app/app.go | 2 +- examples/basecoin/app/app.go | 2 +- examples/democoin/app/app.go | 2 +- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/keeper_test.go | 2 +- .../democoin/x/simplestake/keeper_test.go | 4 +- store/gaskvstore_test.go | 27 -------- types/context.go | 2 +- types/context_test.go | 4 +- {store => types}/gaskvstore.go | 24 +++---- types/gaskvstore_test.go | 63 +++++++++++++++++++ types/lib/mapper_test.go | 2 +- x/auth/ante_test.go | 10 +-- x/auth/context_test.go | 2 +- x/auth/mapper_test.go | 2 +- x/stake/test_common.go | 2 +- 16 files changed, 92 insertions(+), 60 deletions(-) delete mode 100644 store/gaskvstore_test.go rename {store => types}/gaskvstore.go (81%) create mode 100644 types/gaskvstore_test.go diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 672fd5b76..eac94bf5e 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -51,7 +51,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // create your application object var app = &GaiaApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index d5994f55b..06c493d91 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -48,7 +48,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // Create your application object. var app = &BasecoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index bf48348b4..7f6c5fd4d 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -56,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Create your application object. var app = &DemocoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 10000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), cdc: cdc, capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyAccountStore: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index 10e958ce7..c0b9f3e0a 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -30,7 +30,7 @@ func TestCoolKeeper(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 100000) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index 324cdcdce..17a40d0da 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -33,7 +33,7 @@ func TestPowKeeperGetSet(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index 1d19a61d7..8f790344f 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -33,7 +33,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func TestKeeperGetSet(t *testing.T) { ms, _, capKey := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace) addr := sdk.Address([]byte("some-address")) @@ -60,7 +60,7 @@ func TestBonding(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := bank.NewKeeper(accountMapper) diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go deleted file mode 100644 index dab32dfa1..000000000 --- a/store/gaskvstore_test.go +++ /dev/null @@ -1,27 +0,0 @@ -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 TestGasKVStore(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") - - mem.Set(keyFmt(1), valFmt(1)) - st.Set(keyFmt(1), valFmt(1)) - require.Equal(t, valFmt(1), st.Get(keyFmt(1))) -} diff --git a/types/context.go b/types/context.go index 33713d353..91c14373c 100644 --- a/types/context.go +++ b/types/context.go @@ -69,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 NewGasKVStore(c.GasMeter(), c.multiStore().GetKVStore(key)) } //---------------------------------------- diff --git a/types/context_test.go b/types/context_test.go index 9eafed625..6b1ea1f48 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -43,7 +43,7 @@ func (l MockLogger) With(kvs ...interface{}) log.Logger { func TestContextGetOpShouldNeverPanic(t *testing.T) { var ms types.MultiStore - ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) indices := []int64{ -10, 1, 0, 10, 20, } @@ -58,7 +58,7 @@ func defaultContext(key types.StoreKey) types.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) return ctx } diff --git a/store/gaskvstore.go b/types/gaskvstore.go similarity index 81% rename from store/gaskvstore.go rename to types/gaskvstore.go index 1bc014229..ed8fc0ea9 100644 --- a/store/gaskvstore.go +++ b/types/gaskvstore.go @@ -1,8 +1,4 @@ -package store - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) +package types // nolint const ( @@ -18,12 +14,12 @@ const ( // gasKVStore applies gas tracking to an underlying kvstore type gasKVStore struct { - gasMeter sdk.GasMeter + gasMeter GasMeter parent KVStore } // nolint -func NewGasKVStore(gasMeter sdk.GasMeter, parent KVStore) *gasKVStore { +func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { kvs := &gasKVStore{ gasMeter: gasMeter, parent: parent, @@ -41,7 +37,7 @@ 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") + gi.gasMeter.ConsumeGas(ReadCostPerByte*Gas(len(value)), "ReadPerByte") return value } @@ -49,7 +45,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { 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.gasMeter.ConsumeGas(WriteCostPerByte*Gas(len(value)), "SetPerByte") gi.parent.Set(key, value) } @@ -77,12 +73,12 @@ func (gi *gasKVStore) ReverseIterator(start, end []byte) Iterator { // Implements KVStore. func (gi *gasKVStore) SubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true) + return gi.iterator(prefix, PrefixEndBytes(prefix), true) } // Implements KVStore. func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false) + return gi.iterator(prefix, PrefixEndBytes(prefix), false) } // Implements KVStore. @@ -101,11 +97,11 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { } type gasIterator struct { - gasMeter sdk.GasMeter + gasMeter GasMeter parent Iterator } -func newGasIterator(gasMeter sdk.GasMeter, parent Iterator) Iterator { +func newGasIterator(gasMeter GasMeter, parent Iterator) Iterator { return &gasIterator{ gasMeter: gasMeter, parent: parent, @@ -138,7 +134,7 @@ func (g *gasIterator) Key() (key []byte) { func (g *gasIterator) Value() (value []byte) { value = g.parent.Value() g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat") - g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte") + g.gasMeter.ConsumeGas(ValueCostPerByte*Gas(len(value)), "ValuePerByte") return value } diff --git a/types/gaskvstore_test.go b/types/gaskvstore_test.go new file mode 100644 index 000000000..4ab9c15a1 --- /dev/null +++ b/types/gaskvstore_test.go @@ -0,0 +1,63 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" +) + +func newGasKVStore() KVStore { + meter := NewGasMeter(1000) + mem := dbStoreAdapter{dbm.NewMemDB()} + return NewGasKVStore(meter, mem) +} + +func TestGasKVStoreBasic(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := NewGasMeter(1000) + st := NewGasKVStore(meter, mem) + + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) +} + +func TestGasKVStoreOutOfGas(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := NewGasMeter(0) + st := NewGasKVStore(meter, mem) + require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") +} + +func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) } +func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) } +func bz(s string) []byte { return []byte(s) } + +type dbStoreAdapter struct { + dbm.DB +} + +// Implements Store. +func (dbStoreAdapter) GetStoreType() StoreType { + return StoreTypeDB +} + +// Implements KVStore. +func (dsa dbStoreAdapter) CacheWrap() CacheWrap { + panic("unsupported") +} + +func (dsa dbStoreAdapter) SubspaceIterator(prefix []byte) Iterator { + return dsa.Iterator(prefix, PrefixEndBytes(prefix)) +} + +func (dsa dbStoreAdapter) ReverseSubspaceIterator(prefix []byte) Iterator { + return dsa.ReverseIterator(prefix, PrefixEndBytes(prefix)) +} + +// dbm.DB implements KVStore so we can CacheKVStore it. +var _ KVStore = dbStoreAdapter{dbm.DB(nil)} diff --git a/types/lib/mapper_test.go b/types/lib/mapper_test.go index ab6fb605a..c29b55a93 100644 --- a/types/lib/mapper_test.go +++ b/types/lib/mapper_test.go @@ -25,7 +25,7 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *wire.Codec) { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) cdc := wire.NewCodec() return ctx, cdc } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 8310d3069..2307a3561 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -74,7 +74,7 @@ func TestAnteHandlerSigErrors(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) // keys and addresses priv1, addr1 := privAndAddr() @@ -115,7 +115,7 @@ func TestAnteHandlerSequences(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) // keys and addresses priv1, addr1 := privAndAddr() @@ -181,7 +181,7 @@ func TestAnteHandlerFees(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) // keys and addresses priv1, addr1 := privAndAddr() @@ -218,7 +218,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) // keys and addresses priv1, addr1 := privAndAddr() @@ -293,7 +293,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) // keys and addresses priv1, addr1 := privAndAddr() diff --git a/x/auth/context_test.go b/x/auth/context_test.go index 996a06889..b7a6003cb 100644 --- a/x/auth/context_test.go +++ b/x/auth/context_test.go @@ -13,7 +13,7 @@ import ( func TestContextWithSigners(t *testing.T) { ms, _ := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) _, _, addr1 := keyPubAddr() _, _, addr2 := keyPubAddr() diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go index 80f673f99..a4b24f1ab 100644 --- a/x/auth/mapper_test.go +++ b/x/auth/mapper_test.go @@ -29,7 +29,7 @@ func TestAccountMapperGetSet(t *testing.T) { RegisterBaseAccount(cdc) // make context and mapper - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 0) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000000) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) addr := sdk.Address([]byte("some-address")) diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 5c1c38723..29a91d499 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 100000000) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec From da5fe2ef130c488563842ce189f62b618c4b08d0 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 17:54:04 +0200 Subject: [PATCH 28/50] Add baseapp.CheckFull --- baseapp/baseapp.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e1198c3c8..32ee0eec6 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -314,7 +314,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(true, false, txBytes, tx) } return abci.ResponseCheckTx{ @@ -339,7 +339,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(false, false, txBytes, tx) } // After-handler hooks. @@ -361,17 +361,24 @@ 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(true, false, nil, tx) } + +// nolint - full tx execution +func (app *BaseApp) CheckFull(tx sdk.Tx) (result sdk.Result) { + return app.runTx(true, true, nil, tx) +} + +// nolint func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { - return app.runTx(false, nil, tx) + return app.runTx(false, false, 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(isCheckTx bool, fullRun bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // Handle any panics. defer func() { if r := recover(); r != nil { @@ -407,6 +414,14 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk ctx = app.deliverState.ctx.WithTxBytes(txBytes) } + // Create a new zeroed gas meter + ctx = ctx.WithGasMeter(sdk.NewGasMeter(app.txGasLimit)) + + // Simulate a DeliverTx for gas calculation + if isCheckTx && fullRun { + ctx = ctx.WithIsCheckTx(false) + } + // Run the ante handler. if app.anteHandler != nil { newCtx, result, abort := app.anteHandler(ctx, tx) @@ -427,7 +442,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 isCheckTx { // CacheWrap app.checkState.ms in case it fails. msCache = app.checkState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) @@ -435,7 +450,6 @@ 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) From 097646e6dfac43dc1fc4f638d75993257f4955cb Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 21:57:30 +0200 Subject: [PATCH 29/50] Correct semantics for simulateDeliver --- baseapp/baseapp.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 32ee0eec6..a6e2dfdf2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -378,7 +378,7 @@ func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { // 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, fullRun bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { +func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // Handle any panics. defer func() { if r := recover(); r != nil { @@ -418,7 +418,7 @@ func (app *BaseApp) runTx(isCheckTx bool, fullRun bool, txBytes []byte, tx sdk.T ctx = ctx.WithGasMeter(sdk.NewGasMeter(app.txGasLimit)) // Simulate a DeliverTx for gas calculation - if isCheckTx && fullRun { + if isCheckTx && simulateDeliver { ctx = ctx.WithIsCheckTx(false) } @@ -457,8 +457,8 @@ func (app *BaseApp) runTx(isCheckTx bool, fullRun bool, txBytes []byte, tx sdk.T // Set gas utilized result.GasUsed = ctx.GasMeter().GasConsumed() - // If result was successful, write to app.checkState.ms or app.deliverState.ms - if result.IsOK() { + // If not a simulated run and result was successful, write to app.checkState.ms or app.deliverState.ms + if !simulateDeliver && result.IsOK() { msCache.Write() } From a2405546959c342df5648b9533f3ebbeb1f01f07 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 9 May 2018 22:25:13 +0200 Subject: [PATCH 30/50] SimulateTx through Query --- baseapp/baseapp.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index a6e2dfdf2..01b6bf272 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -3,6 +3,7 @@ package baseapp import ( "fmt" "runtime/debug" + "strings" "github.com/pkg/errors" @@ -27,6 +28,7 @@ type BaseApp struct { // initialized on creation Logger log.Logger name string // application name from abci.Info + codec *wire.Codec // Amino codec db dbm.DB // common DB backend cms sdk.CommitMultiStore // Main (uncached) state router Router // handle any kind of message @@ -62,6 +64,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGa app := &BaseApp{ Logger: logger, name: name, + codec: cdc, db: db, cms: store.NewCommitMultiStore(db), router: NewRouter(), @@ -287,6 +290,28 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { msg := "application doesn't support queries" return sdk.ErrUnknownRequest(msg).QueryResult() } + // Special prefix backslash for special queries + path := req.Path + if strings.HasPrefix(path, "\\") { + query := path[1:] + var result sdk.Result + switch query { + case "simulate": + txBytes := req.Data + tx, err := app.txDecoder(txBytes) + if err != nil { + result = err.Result() + } + result = app.runTx(true, true, txBytes, tx) + default: + result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() + } + value := app.codec.MustMarshalBinary(result) + return abci.ResponseQuery{ + Code: uint32(sdk.ABCICodeOK), + Value: value, + } + } return queryable.Query(req) } From 8c1c40b89a2ac5431e51948ec0b0652f6d73920a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 10 May 2018 20:20:34 +0200 Subject: [PATCH 31/50] New store query prefixes (ref #979) --- baseapp/baseapp.go | 24 +++++++++++++++--------- baseapp/baseapp_test.go | 4 ++-- client/context/helpers.go | 3 +-- mock/app_test.go | 5 ++--- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 01b6bf272..680635b49 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -285,15 +285,10 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // 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() - } - // Special prefix backslash for special queries path := req.Path - if strings.HasPrefix(path, "\\") { - query := path[1:] + // "/app" prefix for special application queries + if strings.HasPrefix(path, "/app") { + query := path[4:] var result sdk.Result switch query { case "simulate": @@ -312,7 +307,18 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { Value: value, } } - return queryable.Query(req) + // "/store" prefix for store queries + if strings.HasPrefix(path, "/store") { + queryable, ok := app.cms.(sdk.Queryable) + if !ok { + msg := "multistore doesn't support queries" + return sdk.ErrUnknownRequest(msg).QueryResult() + } + req.Path = req.Path[6:] // slice off "/store" + return queryable.Query(req) + } + msg := "unknown query path" + return sdk.ErrUnknownRequest(msg).QueryResult() } // Implements ABCI diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 913ae1aaa..dbfd9f5a8 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -167,7 +167,7 @@ func TestInitChainer(t *testing.T) { } query := abci.RequestQuery{ - Path: "/main/key", + Path: "/store/main/key", Data: key, } @@ -308,7 +308,7 @@ func TestQuery(t *testing.T) { }) query := abci.RequestQuery{ - Path: "/main/key", + Path: "/store/main/key", Data: key, } diff --git a/client/context/helpers.go b/client/context/helpers.go index c3dc0a4ab..47c94c98e 100644 --- a/client/context/helpers.go +++ b/client/context/helpers.go @@ -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 diff --git a/mock/app_test.go b/mock/app_test.go index 7c84f9a1d..be1d77829 100644 --- a/mock/app_test.go +++ b/mock/app_test.go @@ -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) From 214720318fed7d108bda108137eca3628b901706 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 10 May 2018 20:34:18 +0200 Subject: [PATCH 32/50] Update changelog --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4bd85670..4420a17d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,12 @@ IMPROVEMENTS auto-unbonding * [spec/governance] Fixup some names and pseudocode * NOTE: specs are still a work-in-progress ... +* 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 +* 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 @@ -43,6 +49,7 @@ 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: @@ -55,7 +62,8 @@ FEATURES: * New genesis account keys are automatically added to the client keybase (introduce `--client-home` flag) * Initialize with genesis txs using `--gen-txs` flag * Context now has access to the application-configured logger - +* Add (non-proof) subspace query helper functions +* Add more staking query functions: candidates, delegator-bonds BUG FIXES * Gaia now uses stake, ported from github.com/cosmos/gaia From 702ffafa061baebc62237012a77881d5e8a716e3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 17:34:24 +0200 Subject: [PATCH 33/50] Rebase --- x/bank/keeper_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 38c9ad576..c65f9528b 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -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}}) From 147cf9f8975ce9436388944bf2ff8918829b6ca4 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 17:46:50 +0200 Subject: [PATCH 34/50] Move GasKVStore to /store --- mock/store.go | 4 +++ store/cachemultistore.go | 5 +++ {types => store}/gaskvstore.go | 44 +++++++++++++----------- store/gaskvstore_test.go | 34 ++++++++++++++++++ store/rootmultistore.go | 5 +++ types/context.go | 2 +- types/gaskvstore_test.go | 63 ---------------------------------- types/store.go | 1 + 8 files changed, 74 insertions(+), 84 deletions(-) rename {types => store}/gaskvstore.go (68%) create mode 100644 store/gaskvstore_test.go delete mode 100644 types/gaskvstore_test.go diff --git a/mock/store.go b/mock/store.go index 329eb250b..7f62234ea 100644 --- a/mock/store.go +++ b/mock/store.go @@ -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") } diff --git a/store/cachemultistore.go b/store/cachemultistore.go index b1a754881..47878fb15 100644 --- a/store/cachemultistore.go +++ b/store/cachemultistore.go @@ -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)) +} diff --git a/types/gaskvstore.go b/store/gaskvstore.go similarity index 68% rename from types/gaskvstore.go rename to store/gaskvstore.go index ed8fc0ea9..9f50f3444 100644 --- a/types/gaskvstore.go +++ b/store/gaskvstore.go @@ -1,4 +1,8 @@ -package types +package store + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) // nolint const ( @@ -14,12 +18,12 @@ const ( // gasKVStore applies gas tracking to an underlying kvstore type gasKVStore struct { - gasMeter GasMeter - parent KVStore + gasMeter sdk.GasMeter + parent sdk.KVStore } // nolint -func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { +func NewGasKVStore(gasMeter sdk.GasMeter, parent sdk.KVStore) *gasKVStore { kvs := &gasKVStore{ gasMeter: gasMeter, parent: parent, @@ -28,7 +32,7 @@ func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { } // Implements Store. -func (gi *gasKVStore) GetStoreType() StoreType { +func (gi *gasKVStore) GetStoreType() sdk.StoreType { return gi.parent.GetStoreType() } @@ -37,7 +41,7 @@ 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*Gas(len(value)), "ReadPerByte") + gi.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte") return value } @@ -45,7 +49,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { func (gi *gasKVStore) Set(key []byte, value []byte) { gi.gasMeter.ConsumeGas(WriteCostFlat, "SetFlat") // TODO overflow-safe math? - gi.gasMeter.ConsumeGas(WriteCostPerByte*Gas(len(value)), "SetPerByte") + gi.gasMeter.ConsumeGas(WriteCostPerByte*sdk.Gas(len(value)), "SetPerByte") gi.parent.Set(key, value) } @@ -62,32 +66,32 @@ func (gi *gasKVStore) Delete(key []byte) { } // Implements KVStore. -func (gi *gasKVStore) Iterator(start, end []byte) Iterator { +func (gi *gasKVStore) Iterator(start, end []byte) sdk.Iterator { return gi.iterator(start, end, true) } // Implements KVStore. -func (gi *gasKVStore) ReverseIterator(start, end []byte) Iterator { +func (gi *gasKVStore) ReverseIterator(start, end []byte) sdk.Iterator { return gi.iterator(start, end, false) } // Implements KVStore. -func (gi *gasKVStore) SubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, PrefixEndBytes(prefix), true) +func (gi *gasKVStore) SubspaceIterator(prefix []byte) sdk.Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true) } // Implements KVStore. -func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, PrefixEndBytes(prefix), false) +func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) sdk.Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false) } // Implements KVStore. -func (gi *gasKVStore) CacheWrap() CacheWrap { +func (gi *gasKVStore) CacheWrap() sdk.CacheWrap { panic("you cannot CacheWrap a GasKVStore") } -func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { - var parent Iterator +func (gi *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { + var parent sdk.Iterator if ascending { parent = gi.parent.Iterator(start, end) } else { @@ -97,11 +101,11 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { } type gasIterator struct { - gasMeter GasMeter - parent Iterator + gasMeter sdk.GasMeter + parent sdk.Iterator } -func newGasIterator(gasMeter GasMeter, parent Iterator) Iterator { +func newGasIterator(gasMeter sdk.GasMeter, parent sdk.Iterator) sdk.Iterator { return &gasIterator{ gasMeter: gasMeter, parent: parent, @@ -134,7 +138,7 @@ func (g *gasIterator) Key() (key []byte) { func (g *gasIterator) Value() (value []byte) { value = g.parent.Value() g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat") - g.gasMeter.ConsumeGas(ValueCostPerByte*Gas(len(value)), "ValuePerByte") + g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte") return value } diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go new file mode 100644 index 000000000..4879c327c --- /dev/null +++ b/store/gaskvstore_test.go @@ -0,0 +1,34 @@ +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") + + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) +} + +func TestGasKVStoreOutOfGas(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") +} diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 217e8eb14..11cebc22e 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -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 diff --git a/types/context.go b/types/context.go index 91c14373c..50619f9b7 100644 --- a/types/context.go +++ b/types/context.go @@ -69,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 NewGasKVStore(c.GasMeter(), c.multiStore().GetKVStore(key)) + return c.multiStore().GetKVStoreWithGas(c.GasMeter(), key) } //---------------------------------------- diff --git a/types/gaskvstore_test.go b/types/gaskvstore_test.go deleted file mode 100644 index 4ab9c15a1..000000000 --- a/types/gaskvstore_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tmlibs/common" - dbm "github.com/tendermint/tmlibs/db" -) - -func newGasKVStore() KVStore { - meter := NewGasMeter(1000) - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewGasKVStore(meter, mem) -} - -func TestGasKVStoreBasic(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := NewGasMeter(1000) - st := NewGasKVStore(meter, mem) - - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - - mem.Set(keyFmt(1), valFmt(1)) - st.Set(keyFmt(1), valFmt(1)) - require.Equal(t, valFmt(1), st.Get(keyFmt(1))) -} - -func TestGasKVStoreOutOfGas(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := NewGasMeter(0) - st := NewGasKVStore(meter, mem) - require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") -} - -func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) } -func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) } -func bz(s string) []byte { return []byte(s) } - -type dbStoreAdapter struct { - dbm.DB -} - -// Implements Store. -func (dbStoreAdapter) GetStoreType() StoreType { - return StoreTypeDB -} - -// Implements KVStore. -func (dsa dbStoreAdapter) CacheWrap() CacheWrap { - panic("unsupported") -} - -func (dsa dbStoreAdapter) SubspaceIterator(prefix []byte) Iterator { - return dsa.Iterator(prefix, PrefixEndBytes(prefix)) -} - -func (dsa dbStoreAdapter) ReverseSubspaceIterator(prefix []byte) Iterator { - return dsa.ReverseIterator(prefix, PrefixEndBytes(prefix)) -} - -// dbm.DB implements KVStore so we can CacheKVStore it. -var _ KVStore = dbStoreAdapter{dbm.DB(nil)} diff --git a/types/store.go b/types/store.go index f8367a126..abf02ec07 100644 --- a/types/store.go +++ b/types/store.go @@ -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().... From ce38d8f423cf0af9f7a8332bdf2e0fd72339fad9 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 20:02:05 +0200 Subject: [PATCH 35/50] Minor fix, testcases --- baseapp/baseapp.go | 7 +++-- baseapp/baseapp_test.go | 66 +++++++++++++++++++++++++++++++++++++++- store/gaskvstore_test.go | 42 ++++++++++++++++++++++--- 3 files changed, 107 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 680635b49..50b915dcb 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -291,13 +291,14 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { query := path[4:] var result sdk.Result switch query { - case "simulate": + case "/simulate": txBytes := req.Data tx, err := app.txDecoder(txBytes) if err != nil { result = err.Result() + } else { + result = app.Simulate(tx) } - result = app.runTx(true, true, txBytes, tx) default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() } @@ -398,7 +399,7 @@ func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { } // nolint - full tx execution -func (app *BaseApp) CheckFull(tx sdk.Tx) (result sdk.Result) { +func (app *BaseApp) Simulate(tx sdk.Tx) (result sdk.Result) { return app.runTx(true, true, nil, tx) } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index dbfd9f5a8..7a5d77ea5 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -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, 10000) + codec := wire.NewCodec() + wire.RegisterCrypto(codec) + return NewBaseApp(name, codec, logger, db, 10000) } func TestMountStores(t *testing.T) { @@ -260,6 +264,66 @@ 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, result.GasUsed, int64(80)) + 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.codec.MustUnmarshalBinary(queryResult.Value, &res) + require.Equal(t, res.Code, sdk.ABCICodeOK) + require.Equal(t, res.GasUsed, int64(80)) + app.EndBlock(abci.RequestEndBlock{}) + app.Commit() + } +} + // Test that transactions exceeding gas limits fail func TestTxGasLimits(t *testing.T) { logger := defaultLogger() diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go index 4879c327c..524dc5323 100644 --- a/store/gaskvstore_test.go +++ b/store/gaskvstore_test.go @@ -18,17 +18,51 @@ 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") - - mem.Set(keyFmt(1), valFmt(1)) 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 TestGasKVStoreOutOfGas(t *testing.T) { +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") +} From a801874aba1c0cc55fe0f579fff7310341c00249 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 11 May 2018 22:06:53 +0200 Subject: [PATCH 36/50] PR comment: codec => cdc --- baseapp/baseapp.go | 6 +++--- baseapp/baseapp_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 50b915dcb..cbe69d1b7 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -28,7 +28,7 @@ type BaseApp struct { // initialized on creation Logger log.Logger name string // application name from abci.Info - codec *wire.Codec // Amino codec + cdc *wire.Codec // Amino codec db dbm.DB // common DB backend cms sdk.CommitMultiStore // Main (uncached) state router Router // handle any kind of message @@ -64,7 +64,7 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGa app := &BaseApp{ Logger: logger, name: name, - codec: cdc, + cdc: cdc, db: db, cms: store.NewCommitMultiStore(db), router: NewRouter(), @@ -302,7 +302,7 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() } - value := app.codec.MustMarshalBinary(result) + value := app.cdc.MustMarshalBinary(result) return abci.ResponseQuery{ Code: uint32(sdk.ABCICodeOK), Value: value, diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 7a5d77ea5..a605935c7 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -316,7 +316,7 @@ func TestSimulateTx(t *testing.T) { queryResult := app.Query(query) require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK)) var res sdk.Result - app.codec.MustUnmarshalBinary(queryResult.Value, &res) + app.cdc.MustUnmarshalBinary(queryResult.Value, &res) require.Equal(t, res.Code, sdk.ABCICodeOK) require.Equal(t, res.GasUsed, int64(80)) app.EndBlock(abci.RequestEndBlock{}) From 38716d5edc422896610e28f5b8f154058d89300f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 15:02:54 +0200 Subject: [PATCH 37/50] ConsumeGas for pubkey.VerifyBytes --- x/auth/ante.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x/auth/ante.go b/x/auth/ante.go index 4be1c6674..bf70b3e9b 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -8,6 +8,10 @@ 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. @@ -134,6 +138,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() } From 4cfa99e21b0f8ed37e8d71415fb4339f52fb769e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 15:11:26 +0200 Subject: [PATCH 38/50] Move to new version in changelog --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4420a17d6..82c29584e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ FEATURES * [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 * [x/auth] Added ability to change pubkey to auth module +* 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 +* 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 IMPROVEMENTS @@ -23,12 +29,6 @@ IMPROVEMENTS auto-unbonding * [spec/governance] Fixup some names and pseudocode * NOTE: specs are still a work-in-progress ... -* 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 -* 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 From d55ba2ca7d95731ce49166fc4115ce52229cbe64 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 16:00:17 +0200 Subject: [PATCH 39/50] Add p2p filter functions & tests --- baseapp/baseapp.go | 45 ++++++++++++++++++++++++++++++++++++++--- baseapp/baseapp_test.go | 33 ++++++++++++++++++++++++++++++ types/abci.go | 3 +++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cbe69d1b7..8de849200 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -40,9 +40,11 @@ type BaseApp struct { txGasLimit sdk.Gas // per-transaction gas limit // 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 @@ -142,6 +144,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 @@ -282,6 +290,22 @@ 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) { @@ -318,6 +342,21 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { req.Path = req.Path[6:] // slice off "/store" return queryable.Query(req) } + // "/p2p" prefix for p2p queries + if strings.HasPrefix(path, "/p2p") { + path = path[4:] + if strings.HasPrefix(path, "/filter") { + path = path[7:] + if strings.HasPrefix(path, "/addr") { + path = path[6:] + return app.FilterPeerByAddrPort(path) + } + if strings.HasPrefix(path, "/pubkey") { + path = path[8:] + return app.FilterPeerByPubKey(path) + } + } + } msg := "unknown query path" return sdk.ErrUnknownRequest(msg).QueryResult() } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index a605935c7..e54993648 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -399,6 +399,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 diff --git a/types/abci.go b/types/abci.go index 40651163c..a46e797eb 100644 --- a/types/abci.go +++ b/types/abci.go @@ -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 From 396bf74b9fd45ddb2d8e6b83e514a39e4df879ac Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 16:00:49 +0200 Subject: [PATCH 40/50] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c29584e..389f61b43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ FEATURES * [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 * [x/auth] Added ability to change pubkey to auth module +* baseapp now has settable functions for filtering peers by address/port & public key * 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 From 4775437426d7a2bb70cb3b593cb9a81ff82d4624 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 15 May 2018 16:23:57 +0200 Subject: [PATCH 41/50] Unexport verifyCost --- x/auth/ante.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/auth/ante.go b/x/auth/ante.go index bf70b3e9b..69b867e17 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -9,7 +9,7 @@ import ( ) const ( - VerifyCost = 100 + verifyCost = 100 ) // NewAnteHandler returns an AnteHandler that checks @@ -138,7 +138,7 @@ func processSig( } // Check sig. - ctx.GasMeter().ConsumeGas(VerifyCost, "ante verify") + ctx.GasMeter().ConsumeGas(verifyCost, "ante verify") if !pubKey.VerifyBytes(signBytes, sig.Signature) { return nil, sdk.ErrUnauthorized("signature verification failed").Result() } From 0cc1c52077353dcb98b8e061145ce70d233a4fd3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 01:15:49 +0200 Subject: [PATCH 42/50] Rebase changelog --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 389f61b43..3361c4361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,13 +13,13 @@ FEATURES * [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 * [x/auth] Added ability to change pubkey to auth module -* baseapp now has settable functions for filtering peers by address/port & public key -* Gas consumption is now measured as transactions are executed +* [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 -* 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 +* [x/bank] Bank module now tags transactions with sender/recipient for indexing & later retrieval +* [x/stake] Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy IMPROVEMENTS From 4134bf922c6c4c93b3a178a36d818457b44c3d25 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 02:06:17 +0200 Subject: [PATCH 43/50] Address PR comments --- baseapp/baseapp.go | 68 +++++++++++++++++-------------- baseapp/baseapp_test.go | 6 +-- client/context/helpers.go | 1 + cmd/gaia/app/app_test.go | 2 +- examples/basecoin/app/app_test.go | 2 +- examples/democoin/app/app_test.go | 2 +- x/auth/ante.go | 3 ++ x/bank/keeper.go | 18 +++++--- 8 files changed, 61 insertions(+), 41 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 8de849200..60e32afa4 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -23,6 +23,17 @@ import ( // and to avoid affecting the Merkle root. var dbHeaderKey = []byte("header") +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 @@ -309,13 +320,17 @@ func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery { // Implements ABCI. // Delegates to CommitMultiStore if it implements Queryable func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { - path := req.Path + path := strings.Split(req.Path, "/") + // first element is empty string + if len(path) > 0 && path[0] == "" { + path = path[1:] + } + fmt.Sprintf("Path: %v\n", path) // "/app" prefix for special application queries - if strings.HasPrefix(path, "/app") { - query := path[4:] + if len(path) >= 2 && path[0] == "app" { var result sdk.Result - switch query { - case "/simulate": + switch path[1] { + case "simulate": txBytes := req.Data tx, err := app.txDecoder(txBytes) if err != nil { @@ -333,27 +348,23 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { } } // "/store" prefix for store queries - if strings.HasPrefix(path, "/store") { + 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 = req.Path[6:] // slice off "/store" + req.Path = "/" + strings.Join(path[1:], "/") return queryable.Query(req) } // "/p2p" prefix for p2p queries - if strings.HasPrefix(path, "/p2p") { - path = path[4:] - if strings.HasPrefix(path, "/filter") { - path = path[7:] - if strings.HasPrefix(path, "/addr") { - path = path[6:] - return app.FilterPeerByAddrPort(path) + if len(path) >= 4 && path[0] == "p2p" { + if path[1] == "filter" { + if path[2] == "addr" { + return app.FilterPeerByAddrPort(path[3]) } - if strings.HasPrefix(path, "/pubkey") { - path = path[8:] - return app.FilterPeerByPubKey(path) + if path[2] == "pubkey" { + return app.FilterPeerByPubKey(path[3]) } } } @@ -385,7 +396,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { if err != nil { result = err.Result() } else { - result = app.runTx(true, false, txBytes, tx) + result = app.runTx(RunTxModeCheck, txBytes, tx) } return abci.ResponseCheckTx{ @@ -410,7 +421,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { if err != nil { result = err.Result() } else { - result = app.runTx(false, false, txBytes, tx) + result = app.runTx(RunTxModeDeliver, txBytes, tx) } // After-handler hooks. @@ -434,22 +445,22 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { // nolint - Mostly for testing func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { - return app.runTx(true, false, 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(true, true, nil, tx) + return app.runTx(RunTxModeSimulate, nil, tx) } // nolint func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { - return app.runTx(false, 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, simulateDeliver 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 { @@ -479,17 +490,14 @@ func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, // 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) } - // Create a new zeroed gas meter - ctx = ctx.WithGasMeter(sdk.NewGasMeter(app.txGasLimit)) - // Simulate a DeliverTx for gas calculation - if isCheckTx && simulateDeliver { + if mode == RunTxModeSimulate { ctx = ctx.WithIsCheckTx(false) } @@ -513,7 +521,7 @@ func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, // Get the correct cache var msCache sdk.CacheMultiStore - if isCheckTx { + if mode == RunTxModeCheck || mode == RunTxModeSimulate { // CacheWrap app.checkState.ms in case it fails. msCache = app.checkState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) @@ -529,7 +537,7 @@ func (app *BaseApp) runTx(isCheckTx bool, simulateDeliver bool, txBytes []byte, result.GasUsed = ctx.GasMeter().GasConsumed() // If not a simulated run and result was successful, write to app.checkState.ms or app.deliverState.ms - if !simulateDeliver && result.IsOK() { + if mode != RunTxModeSimulate && result.IsOK() { msCache.Write() } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index e54993648..25de7ef7e 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -305,7 +305,7 @@ func TestSimulateTx(t *testing.T) { app.BeginBlock(abci.RequestBeginBlock{Header: header}) result := app.Simulate(tx) require.Equal(t, result.Code, sdk.ABCICodeOK) - require.Equal(t, result.GasUsed, int64(80)) + require.Equal(t, int64(80), result.GasUsed) counter-- encoded, err := json.Marshal(tx) require.Nil(t, err) @@ -317,8 +317,8 @@ func TestSimulateTx(t *testing.T) { require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK)) var res sdk.Result app.cdc.MustUnmarshalBinary(queryResult.Value, &res) - require.Equal(t, res.Code, sdk.ABCICodeOK) - require.Equal(t, res.GasUsed, int64(80)) + require.Equal(t, sdk.ABCICodeOK, res.Code) + require.Equal(t, int64(160), res.GasUsed) app.EndBlock(abci.RequestEndBlock{}) app.Commit() } diff --git a/client/context/helpers.go b/client/context/helpers.go index 47c94c98e..562bde9b4 100644 --- a/client/context/helpers.go +++ b/client/context/helpers.go @@ -113,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() diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 3bca2654b..fd26ce27a 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -40,7 +40,7 @@ var ( manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}} fee = sdk.StdFee{ sdk.Coins{{"foocoin", 0}}, - 0, + 100000, } sendMsg1 = bank.MsgSend{ diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index 034e19860..d0b59f331 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -39,7 +39,7 @@ var ( manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}} fee = sdk.StdFee{ sdk.Coins{{"foocoin", 0}}, - 0, + 100000, } sendMsg1 = bank.MsgSend{ diff --git a/examples/democoin/app/app_test.go b/examples/democoin/app/app_test.go index c67782d92..b0f188f10 100644 --- a/examples/democoin/app/app_test.go +++ b/examples/democoin/app/app_test.go @@ -33,7 +33,7 @@ var ( coins = sdk.Coins{{"foocoin", 10}} fee = sdk.StdFee{ sdk.Coins{{"foocoin", 0}}, - 0, + 1000000, } sendMsg = bank.MsgSend{ diff --git a/x/auth/ante.go b/x/auth/ante.go index 69b867e17..248083206 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -92,6 +92,9 @@ func NewAnteHandler(am sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHan // 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... diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 129ffa792..6ef73c68b 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -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,7 +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(10, "getCoins") + ctx.GasMeter().ConsumeGas(costGetCoins, "getCoins") acc := am.GetAccount(ctx, addr) if acc == nil { return sdk.Coins{} @@ -117,7 +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(100, "setCoins") + ctx.GasMeter().ConsumeGas(costSetCoins, "setCoins") acc := am.GetAccount(ctx, addr) if acc == nil { acc = am.NewAccountWithAddress(ctx, addr) @@ -129,13 +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(10, "hasCoins") + 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(10, "subtractCoins") + ctx.GasMeter().ConsumeGas(costSubtractCoins, "subtractCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Minus(amt) if !newCoins.IsNotNegative() { @@ -148,7 +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(10, "addCoins") + ctx.GasMeter().ConsumeGas(costAddCoins, "addCoins") oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Plus(amt) if !newCoins.IsNotNegative() { From 03e220700e73c1dc7d30d0d49e60bf83523f0e92 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 02:18:25 +0200 Subject: [PATCH 44/50] Unexport RunTxMode (fix linter) --- baseapp/baseapp.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 60e32afa4..223da0ea5 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -23,15 +23,16 @@ import ( // and to avoid affecting the Merkle root. var dbHeaderKey = []byte("header") -type RunTxMode uint8 +// Enum mode for app.runTx +type runTxMode uint8 const ( // Check a transaction - RunTxModeCheck RunTxMode = iota + runTxModeCheck runTxMode = iota // Simulate a transaction - RunTxModeSimulate RunTxMode = iota + runTxModeSimulate runTxMode = iota // Deliver a transaction - RunTxModeDeliver RunTxMode = iota + runTxModeDeliver runTxMode = iota ) // The ABCI application @@ -396,7 +397,7 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { if err != nil { result = err.Result() } else { - result = app.runTx(RunTxModeCheck, txBytes, tx) + result = app.runTx(runTxModeCheck, txBytes, tx) } return abci.ResponseCheckTx{ @@ -421,7 +422,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { if err != nil { result = err.Result() } else { - result = app.runTx(RunTxModeDeliver, txBytes, tx) + result = app.runTx(runTxModeDeliver, txBytes, tx) } // After-handler hooks. @@ -445,22 +446,22 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { // nolint - Mostly for testing func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { - return app.runTx(RunTxModeCheck, 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) + return app.runTx(runTxModeSimulate, nil, tx) } // nolint func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { - return app.runTx(RunTxModeDeliver, 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(mode RunTxMode, 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 { @@ -490,14 +491,14 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk // Get the context var ctx sdk.Context - if mode == RunTxModeCheck || mode == RunTxModeSimulate { + 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 { + if mode == runTxModeSimulate { ctx = ctx.WithIsCheckTx(false) } @@ -521,7 +522,7 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk // Get the correct cache var msCache sdk.CacheMultiStore - if mode == RunTxModeCheck || mode == RunTxModeSimulate { + if mode == runTxModeCheck || mode == runTxModeSimulate { // CacheWrap app.checkState.ms in case it fails. msCache = app.checkState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) @@ -537,7 +538,7 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk 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() { + if mode != runTxModeSimulate && result.IsOK() { msCache.Write() } From 3d5b0484441490cabac73f4c0a142f57c7d63fcf Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 16 May 2018 02:31:52 +0200 Subject: [PATCH 45/50] Remove txGasLimit, update tests --- baseapp/baseapp.go | 12 +++++------- baseapp/baseapp_test.go | 17 ++++++++++------- cmd/gaia/app/app.go | 2 +- examples/basecoin/app/app.go | 2 +- examples/democoin/app/app.go | 2 +- examples/kvstore/main.go | 2 +- mock/app.go | 2 +- types/context.go | 2 +- types/gas.go | 18 ++++++++++++++++++ 9 files changed, 39 insertions(+), 20 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 223da0ea5..1da26fe3f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -49,7 +49,6 @@ type BaseApp struct { // must be set txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth - txGasLimit sdk.Gas // per-transaction gas limit // may be nil initChainer sdk.InitChainer // initialize state with validators and state blob @@ -74,7 +73,7 @@ var _ abci.Application = (*BaseApp)(nil) // Create and name new BaseApp // NOTE: The db is used to store the version number for now. -func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGasLimit sdk.Gas) *BaseApp { +func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *BaseApp { app := &BaseApp{ Logger: logger, name: name, @@ -84,7 +83,6 @@ func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB, txGa router: NewRouter(), codespacer: sdk.NewCodespacer(), txDecoder: defaultTxDecoder(cdc), - txGasLimit: txGasLimit, } // Register the undefined & root codespaces, which should not be used by any modules app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined) @@ -235,9 +233,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, app.txGasLimit) + return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, 0) } - return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, app.txGasLimit) + return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, 0) } type state struct { @@ -253,7 +251,7 @@ func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, true, nil, app.Logger, app.txGasLimit), + ctx: sdk.NewContext(ms, header, true, nil, app.Logger, 0), } } @@ -261,7 +259,7 @@ func (app *BaseApp) setDeliverState(header abci.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, false, nil, app.Logger, app.txGasLimit), + ctx: sdk.NewContext(ms, header, false, nil, app.Logger, 0), } } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 25de7ef7e..969a9f9ad 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -29,7 +29,7 @@ func newBaseApp(name string) *BaseApp { db := dbm.NewMemDB() codec := wire.NewCodec() wire.RegisterCrypto(codec) - return NewBaseApp(name, codec, logger, db, 10000) + return NewBaseApp(name, codec, logger, db) } func TestMountStores(t *testing.T) { @@ -63,7 +63,7 @@ func TestLoadVersion(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() name := t.Name() - app := NewBaseApp(name, nil, logger, db, 10000) + app := NewBaseApp(name, nil, logger, db) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("main") @@ -85,7 +85,7 @@ func TestLoadVersion(t *testing.T) { commitID := sdk.CommitID{1, res.Data} // reload - app = NewBaseApp(name, nil, logger, db, 10000) + app = NewBaseApp(name, nil, logger, db) app.MountStoresIAVL(capKey) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -151,7 +151,7 @@ func TestInitChainer(t *testing.T) { name := t.Name() db := dbm.NewMemDB() logger := defaultLogger() - app := NewBaseApp(name, nil, logger, db, 10000) + app := NewBaseApp(name, nil, logger, db) // make cap keys and mount the stores // NOTE/TODO: mounting multiple stores is broken // see https://github.com/cosmos/cosmos-sdk/issues/532 @@ -188,7 +188,7 @@ func TestInitChainer(t *testing.T) { assert.Equal(t, value, res.Value) // reload app - app = NewBaseApp(name, nil, logger, db, 10000) + app = NewBaseApp(name, nil, logger, db) app.MountStoresIAVL(capKey, capKey2) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -328,7 +328,7 @@ func TestSimulateTx(t *testing.T) { func TestTxGasLimits(t *testing.T) { logger := defaultLogger() db := dbm.NewMemDB() - app := NewBaseApp(t.Name(), nil, logger, db, 0) + app := NewBaseApp(t.Name(), nil, logger, db) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("main") @@ -336,7 +336,10 @@ func TestTxGasLimits(t *testing.T) { 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) { return }) + 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{} diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index eac94bf5e..5ff532bff 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -51,7 +51,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { // create your application object var app = &GaiaApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 06c493d91..b1a434fa2 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -48,7 +48,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // Create your application object. var app = &BasecoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), diff --git a/examples/democoin/app/app.go b/examples/democoin/app/app.go index 7f6c5fd4d..7c8250b18 100644 --- a/examples/democoin/app/app.go +++ b/examples/democoin/app/app.go @@ -56,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Create your application object. var app = &DemocoinApp{ - BaseApp: bam.NewBaseApp(appName, cdc, logger, db, 1000000), + BaseApp: bam.NewBaseApp(appName, cdc, logger, db), cdc: cdc, capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyAccountStore: sdk.NewKVStoreKey("acc"), diff --git a/examples/kvstore/main.go b/examples/kvstore/main.go index bd10d31e9..856538f63 100644 --- a/examples/kvstore/main.go +++ b/examples/kvstore/main.go @@ -32,7 +32,7 @@ func main() { var capKeyMainStore = sdk.NewKVStoreKey("main") // Create BaseApp. - var baseApp = bam.NewBaseApp("kvstore", nil, logger, db, 10000) + var baseApp = bam.NewBaseApp("kvstore", nil, logger, db) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) diff --git a/mock/app.go b/mock/app.go index 4799b726a..ab1a8447a 100644 --- a/mock/app.go +++ b/mock/app.go @@ -29,7 +29,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { capKeyMainStore := sdk.NewKVStoreKey("main") // Create BaseApp. - baseApp := bam.NewBaseApp("kvstore", nil, logger, db, 10000) + baseApp := bam.NewBaseApp("kvstore", nil, logger, db) // Set mounts for BaseApp's MultiStore. baseApp.MountStoresIAVL(capKeyMainStore) diff --git a/types/context.go b/types/context.go index 50619f9b7..4ffd88160 100644 --- a/types/context.go +++ b/types/context.go @@ -43,7 +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(NewGasMeter(gasLimit)) + c = c.WithGasMeter(NewInfiniteGasMeter()) return c } diff --git a/types/gas.go b/types/gas.go index 78246be2d..49bfa27ec 100644 --- a/types/gas.go +++ b/types/gas.go @@ -38,3 +38,21 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { 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 +} From 4bdcad572be8de20614ff0606c4ebf7d4727b547 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 15 May 2018 22:19:09 -0400 Subject: [PATCH 46/50] remove gasLimit from NewContext --- baseapp/baseapp.go | 9 ++++----- examples/democoin/x/cool/keeper_test.go | 2 +- examples/democoin/x/pow/handler_test.go | 2 +- examples/democoin/x/pow/keeper_test.go | 2 +- examples/democoin/x/simplestake/keeper_test.go | 4 ++-- types/context.go | 2 +- types/context_test.go | 4 ++-- types/lib/mapper_test.go | 2 +- x/auth/ante_test.go | 10 +++++----- x/auth/context_test.go | 2 +- x/auth/mapper_test.go | 2 +- x/bank/keeper_test.go | 6 +++--- x/ibc/ibc_test.go | 2 +- x/stake/test_common.go | 2 +- 14 files changed, 25 insertions(+), 26 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 1da26fe3f..ef3bbc3c7 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -233,9 +233,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger, 0) + return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger) } - return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger, 0) + return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger) } type state struct { @@ -251,7 +251,7 @@ func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, true, nil, app.Logger, 0), + ctx: sdk.NewContext(ms, header, true, nil, app.Logger), } } @@ -259,7 +259,7 @@ func (app *BaseApp) setDeliverState(header abci.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, - ctx: sdk.NewContext(ms, header, false, nil, app.Logger, 0), + ctx: sdk.NewContext(ms, header, false, nil, app.Logger), } } @@ -324,7 +324,6 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { if len(path) > 0 && path[0] == "" { path = path[1:] } - fmt.Sprintf("Path: %v\n", path) // "/app" prefix for special application queries if len(path) >= 2 && path[0] == "app" { var result sdk.Result diff --git a/examples/democoin/x/cool/keeper_test.go b/examples/democoin/x/cool/keeper_test.go index c0b9f3e0a..d497dee69 100644 --- a/examples/democoin/x/cool/keeper_test.go +++ b/examples/democoin/x/cool/keeper_test.go @@ -30,7 +30,7 @@ func TestCoolKeeper(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil, 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/handler_test.go b/examples/democoin/x/pow/handler_test.go index df8312502..30aeafa2a 100644 --- a/examples/democoin/x/pow/handler_test.go +++ b/examples/democoin/x/pow/handler_test.go @@ -20,7 +20,7 @@ func TestPowHandler(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/pow/keeper_test.go b/examples/democoin/x/pow/keeper_test.go index 17a40d0da..a4afb876a 100644 --- a/examples/democoin/x/pow/keeper_test.go +++ b/examples/democoin/x/pow/keeper_test.go @@ -33,7 +33,7 @@ func TestPowKeeperGetSet(t *testing.T) { auth.RegisterBaseAccount(cdc) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) config := NewConfig("pow", int64(1)) ck := bank.NewKeeper(am) keeper := NewKeeper(capKey, config, ck, DefaultCodespace) diff --git a/examples/democoin/x/simplestake/keeper_test.go b/examples/democoin/x/simplestake/keeper_test.go index 8f790344f..302a2e58b 100644 --- a/examples/democoin/x/simplestake/keeper_test.go +++ b/examples/democoin/x/simplestake/keeper_test.go @@ -33,7 +33,7 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) { func TestKeeperGetSet(t *testing.T) { ms, _, capKey := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace) addr := sdk.Address([]byte("some-address")) @@ -60,7 +60,7 @@ func TestBonding(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := bank.NewKeeper(accountMapper) diff --git a/types/context.go b/types/context.go index 4ffd88160..4ab0a5d09 100644 --- a/types/context.go +++ b/types/context.go @@ -30,7 +30,7 @@ type Context struct { } // create a new context -func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger, gasLimit Gas) Context { +func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger) Context { c := Context{ Context: context.Background(), pst: newThePast(), diff --git a/types/context_test.go b/types/context_test.go index 6b1ea1f48..ec5faab44 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -43,7 +43,7 @@ func (l MockLogger) With(kvs ...interface{}) log.Logger { func TestContextGetOpShouldNeverPanic(t *testing.T) { var ms types.MultiStore - ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := types.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) indices := []int64{ -10, 1, 0, 10, 20, } @@ -58,7 +58,7 @@ func defaultContext(key types.StoreKey) types.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := types.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) return ctx } diff --git a/types/lib/mapper_test.go b/types/lib/mapper_test.go index c29b55a93..e1759b06a 100644 --- a/types/lib/mapper_test.go +++ b/types/lib/mapper_test.go @@ -25,7 +25,7 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *wire.Codec) { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) cdc := wire.NewCodec() return ctx, cdc } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 2307a3561..ec296b12b 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -74,7 +74,7 @@ func TestAnteHandlerSigErrors(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -115,7 +115,7 @@ func TestAnteHandlerSequences(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -181,7 +181,7 @@ func TestAnteHandlerFees(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -218,7 +218,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() @@ -293,7 +293,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { RegisterBaseAccount(cdc) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) anteHandler := NewAnteHandler(mapper, BurnFeeHandler) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) // keys and addresses priv1, addr1 := privAndAddr() diff --git a/x/auth/context_test.go b/x/auth/context_test.go index b7a6003cb..89e318e0a 100644 --- a/x/auth/context_test.go +++ b/x/auth/context_test.go @@ -13,7 +13,7 @@ import ( func TestContextWithSigners(t *testing.T) { ms, _ := setupMultiStore() - ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger(), 1000000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger()) _, _, addr1 := keyPubAddr() _, _, addr2 := keyPubAddr() diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go index a4b24f1ab..cdd418990 100644 --- a/x/auth/mapper_test.go +++ b/x/auth/mapper_test.go @@ -29,7 +29,7 @@ func TestAccountMapperGetSet(t *testing.T) { RegisterBaseAccount(cdc) // make context and mapper - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 1000000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) mapper := NewAccountMapper(cdc, capKey, &BaseAccount{}) addr := sdk.Address([]byte("some-address")) diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index c65f9528b..117c69e7a 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -31,7 +31,7 @@ func TestKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) @@ -116,7 +116,7 @@ func TestSendKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 100000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) sendKeeper := NewSendKeeper(accountMapper) @@ -185,7 +185,7 @@ func TestViewKeeper(t *testing.T) { cdc := wire.NewCodec() auth.RegisterBaseAccount(cdc) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger()) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) coinKeeper := NewKeeper(accountMapper) viewKeeper := NewViewKeeper(accountMapper) diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index e05245ff3..60cc59bad 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -24,7 +24,7 @@ func defaultContext(key sdk.StoreKey) sdk.Context { cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger(), 10000) + ctx := sdk.NewContext(cms, abci.Header{}, false, nil, log.NewNopLogger()) return ctx } diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 29a91d499..27acebe08 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -158,7 +158,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger(), 100000000) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, nil, log.NewNopLogger()) cdc := makeTestCodec() accountMapper := auth.NewAccountMapper( cdc, // amino codec From 2b6a3fc8e314f2e6e6ac6fd95aef1d560afcd749 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 15 May 2018 22:30:20 -0400 Subject: [PATCH 47/50] changelog and version --- CHANGELOG.md | 19 +++++++++++-------- version/version.go | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29375a44f..ce684911a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 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.0 (May 15, 2018) BREAKING CHANGES @@ -12,14 +23,6 @@ FEATURES * [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 -* [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 -* [x/bank] Bank module now tags transactions with sender/recipient for indexing & later retrieval -* [x/stake] Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy IMPROVEMENTS diff --git a/version/version.go b/version/version.go index b8b59827d..116f1ff28 100644 --- a/version/version.go +++ b/version/version.go @@ -6,10 +6,10 @@ package version // TODO improve const Maj = "0" -const Min = "17" +const Min = "18" const Fix = "0" -const Version = "0.17.0" +const Version = "0.18.0-dev" // GitCommit set by build flags var GitCommit = "" From a6ac950efe2637692eb51a3c764c10526b65b506 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 16 May 2018 10:32:25 -0400 Subject: [PATCH 48/50] require go1.10 minimum #1009 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 640cd2874..3faf2c8af 100644 --- a/README.md +++ b/README.md @@ -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 From 13d1667ac84b83dd91715903b811729fd5f4be3e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 17 May 2018 16:10:16 -0400 Subject: [PATCH 49/50] update tm to 0.19.4. update changelog and version --- CHANGELOG.md | 4 ++++ Gopkg.lock | 6 +++--- Gopkg.toml | 2 +- version/version.go | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 486bd3476..65c0bb7fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 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 diff --git a/Gopkg.lock b/Gopkg.lock index 9aff8fe56..1bb4ec31a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -341,8 +341,8 @@ "types/priv_validator", "version" ] - revision = "03f6a29a64fc4d26322a90839b892014d2cb90a5" - version = "v0.19.3-rc0" + revision = "19ccd1842fe5efffcc2ff32e6cfc127ca0cd1f9b" + version = "v0.19.4-rc0" [[projects]] name = "github.com/tendermint/tmlibs" @@ -457,6 +457,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "30cd9c49b9e87f62f40a02fd0ba35a2d67a519ef781e06316246b307e6be638d" + inputs-digest = "8c37fee3e6d3034b865c68185f4796223fe53b1b0ee4a305adbee5f53c50bfce" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 5fe068df6..13060b061 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -70,7 +70,7 @@ [[constraint]] name = "github.com/tendermint/tendermint" - version = "0.19.3-rc0" + version = "0.19.4-rc0" [[override]] name = "github.com/tendermint/tmlibs" diff --git a/version/version.go b/version/version.go index b8b59827d..76b0370a4 100644 --- a/version/version.go +++ b/version/version.go @@ -7,9 +7,9 @@ package version const Maj = "0" const Min = "17" -const Fix = "0" +const Fix = "1" -const Version = "0.17.0" +const Version = "0.17.1" // GitCommit set by build flags var GitCommit = "" From d47fdc1667ad8df2dd0991c36e3e1740850a1140 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 19 May 2018 00:07:33 +0200 Subject: [PATCH 50/50] baseserver => Gaia --- docs/sdk/lcd-rest-api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk/lcd-rest-api.yaml b/docs/sdk/lcd-rest-api.yaml index 56ec21ea6..f88d4380f 100644 --- a/docs/sdk/lcd-rest-api.yaml +++ b/docs/sdk/lcd-rest-api.yaml @@ -3,7 +3,7 @@ servers: - url: 'http://localhost:8998' info: version: "1.1.0" - title: Light client daemon to interface with Cosmos baseserver via REST + title: Light client daemon to interface with full Gaia node via REST description: Specification for the LCD provided by `gaia rest-server` paths: