From bef7e44f6dcb2b1c41aa33d291c072567c00454e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 26 Apr 2018 16:14:51 +0200 Subject: [PATCH] Example tag implementation for CoinKeeper --- examples/democoin/x/cool/handler.go | 2 +- examples/democoin/x/pow/keeper.go | 2 +- examples/democoin/x/simplestake/keeper.go | 4 +- types/result.go | 3 +- types/tags.go | 31 +++++++++++++ types/tags_test.go | 30 +++++++++++++ x/bank/handler.go | 7 +-- x/bank/keeper.go | 54 +++++++++++++---------- x/bank/keeper_test.go | 6 +-- x/ibc/handler.go | 4 +- x/ibc/ibc_test.go | 5 ++- x/stake/handler.go | 2 +- 12 files changed, 109 insertions(+), 41 deletions(-) create mode 100644 types/tags.go create mode 100644 types/tags_test.go diff --git a/examples/democoin/x/cool/handler.go b/examples/democoin/x/cool/handler.go index c82fa4ae43..b4375c5dcd 100644 --- a/examples/democoin/x/cool/handler.go +++ b/examples/democoin/x/cool/handler.go @@ -53,7 +53,7 @@ func handleMsgQuiz(ctx sdk.Context, k Keeper, msg MsgQuiz) sdk.Result { bonusCoins := sdk.Coins{{msg.CoolAnswer, 69}} - _, err := k.ck.AddCoins(ctx, msg.Sender, bonusCoins) + _, _, err := k.ck.AddCoins(ctx, msg.Sender, bonusCoins) if err != nil { return err.Result() } diff --git a/examples/democoin/x/pow/keeper.go b/examples/democoin/x/pow/keeper.go index 931e41a320..35fccf7424 100644 --- a/examples/democoin/x/pow/keeper.go +++ b/examples/democoin/x/pow/keeper.go @@ -125,7 +125,7 @@ func (k Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (ui // Add some coins for a POW well done func (k Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error { - _, ckErr := k.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{k.config.Denomination, k.config.Reward}}) + _, _, ckErr := k.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{k.config.Denomination, k.config.Reward}}) if ckErr != nil { return ckErr } diff --git a/examples/democoin/x/simplestake/keeper.go b/examples/democoin/x/simplestake/keeper.go index 7b61c36236..5bd2639610 100644 --- a/examples/democoin/x/simplestake/keeper.go +++ b/examples/democoin/x/simplestake/keeper.go @@ -66,7 +66,7 @@ func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, st return 0, ErrIncorrectStakingToken(k.codespace) } - _, err := k.ck.SubtractCoins(ctx, addr, []sdk.Coin{stake}) + _, _, err := k.ck.SubtractCoins(ctx, addr, []sdk.Coin{stake}) if err != nil { return 0, err } @@ -95,7 +95,7 @@ func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, returnedBond := sdk.Coin{stakingToken, bi.Power} - _, err := k.ck.AddCoins(ctx, addr, []sdk.Coin{returnedBond}) + _, _, err := k.ck.AddCoins(ctx, addr, []sdk.Coin{returnedBond}) if err != nil { return bi.PubKey, bi.Power, err } diff --git a/types/result.go b/types/result.go index f4f7454e2c..65f87400d2 100644 --- a/types/result.go +++ b/types/result.go @@ -2,7 +2,6 @@ package types import ( abci "github.com/tendermint/abci/types" - cmn "github.com/tendermint/tmlibs/common" ) // Result is the union of ResponseDeliverTx and ResponseCheckTx. @@ -31,7 +30,7 @@ type Result struct { ValidatorUpdates []abci.Validator // Tags are used for transaction indexing and pubsub. - Tags []cmn.KVPair + Tags Tags } // TODO: In the future, more codes may be OK. diff --git a/types/tags.go b/types/tags.go new file mode 100644 index 0000000000..82e9bc9963 --- /dev/null +++ b/types/tags.go @@ -0,0 +1,31 @@ +package types + +import ( + cmn "github.com/tendermint/tmlibs/common" +) + +type Tag = cmn.KVPair + +type Tags = cmn.KVPairs + +// Append two lists of tags +func AppendTags(a, b Tags) Tags { + return append(a, b...) +} + +// New empty tags +func EmptyTags() Tags { + return make(Tags, 0) +} + +// Single tag to tags +func SingleTag(t Tag) Tags { + return append(EmptyTags(), t) +} + +// Make a tag from a key and a value +func MakeTag(k string, v []byte) Tag { + return Tag{Key: []byte(k), Value: v} +} + +// TODO: Deduplication? diff --git a/types/tags_test.go b/types/tags_test.go new file mode 100644 index 0000000000..ae00ffdd91 --- /dev/null +++ b/types/tags_test.go @@ -0,0 +1,30 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAppendTags(t *testing.T) { + a := SingleTag(MakeTag("a", []byte("1"))) + b := SingleTag(MakeTag("b", []byte("2"))) + c := AppendTags(a, b) + require.Equal(t, c, Tags{MakeTag("a", []byte("1")), MakeTag("b", []byte("2"))}) +} + +func TestEmptyTags(t *testing.T) { + a := EmptyTags() + require.Equal(t, a, Tags{}) +} + +func TestSingleTag(t *testing.T) { + a := MakeTag("a", []byte("1")) + b := SingleTag(a) + require.Equal(t, b, Tags{MakeTag("a", []byte("1"))}) +} + +func TestMakeTag(t *testing.T) { + a := MakeTag("a", []byte("1")) + require.Equal(t, a, Tag{[]byte("a"), []byte("1")}) +} diff --git a/x/bank/handler.go b/x/bank/handler.go index a50b0afcf3..ec56d05b42 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -25,13 +25,14 @@ func NewHandler(k Keeper) sdk.Handler { func handleMsgSend(ctx sdk.Context, k Keeper, msg MsgSend) sdk.Result { // NOTE: totalIn == totalOut should already have been checked - err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) + tags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) if err != nil { return err.Result() } - // TODO: add some tags so we can search it! - return sdk.Result{} // TODO + return sdk.Result{ + Tags: tags, + } } // Handle MsgIssue. diff --git a/x/bank/keeper.go b/x/bank/keeper.go index d1fdeaea06..869caeb259 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -32,22 +32,22 @@ func (keeper Keeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) } // SubtractCoins subtracts amt from the coins at the addr. -func (keeper Keeper) SubtractCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { +func (keeper Keeper) SubtractCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { return subtractCoins(ctx, keeper.am, addr, amt) } // AddCoins adds amt to the coins at the addr. -func (keeper Keeper) AddCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Error) { +func (keeper Keeper) AddCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { return addCoins(ctx, keeper.am, addr, amt) } // SendCoins moves coins from one account to another -func (keeper Keeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) sdk.Error { +func (keeper Keeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) (sdk.Tags, sdk.Error) { return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt) } // InputOutputCoins handles a list of inputs and outputs -func (keeper Keeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) sdk.Error { +func (keeper Keeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) { return inputOutputCoins(ctx, keeper.am, inputs, outputs) } @@ -74,12 +74,12 @@ func (keeper SendKeeper) HasCoins(ctx sdk.Context, addr sdk.Address, amt sdk.Coi } // SendCoins moves coins from one account to another -func (keeper SendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) sdk.Error { +func (keeper SendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) (sdk.Tags, sdk.Error) { return sendCoins(ctx, keeper.am, fromAddr, toAddr, amt) } // InputOutputCoins handles a list of inputs and outputs -func (keeper SendKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) sdk.Error { +func (keeper SendKeeper) InputOutputCoins(ctx sdk.Context, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) { return inputOutputCoins(ctx, keeper.am, inputs, outputs) } @@ -131,59 +131,65 @@ func hasCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.C } // 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.Error) { +func subtractCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Minus(amt) if !newCoins.IsNotNegative() { - return amt, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) + return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) } err := setCoins(ctx, am, addr, newCoins) - return newCoins, err + tags := sdk.SingleTag(sdk.MakeTag("sender", addr.Bytes())) + return newCoins, tags, err } // 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.Error) { +func addCoins(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { oldCoins := getCoins(ctx, am, addr) newCoins := oldCoins.Plus(amt) if !newCoins.IsNotNegative() { - return amt, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) + return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) } err := setCoins(ctx, am, addr, newCoins) - return newCoins, err + tags := sdk.SingleTag(sdk.MakeTag("recipient", addr.Bytes())) + return newCoins, tags, err } // SendCoins moves coins from one account to another // NOTE: Make sure to revert state changes from tx on error -func sendCoins(ctx sdk.Context, am sdk.AccountMapper, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) sdk.Error { - _, err := subtractCoins(ctx, am, fromAddr, amt) +func sendCoins(ctx sdk.Context, am sdk.AccountMapper, fromAddr sdk.Address, toAddr sdk.Address, amt sdk.Coins) (sdk.Tags, sdk.Error) { + _, subTags, err := subtractCoins(ctx, am, fromAddr, amt) if err != nil { - return err + return nil, err } - _, err = addCoins(ctx, am, toAddr, amt) + _, addTags, err := addCoins(ctx, am, toAddr, amt) if err != nil { - return err + return nil, err } - return nil + return sdk.AppendTags(subTags, addTags), nil } // InputOutputCoins handles a list of inputs and outputs // NOTE: Make sure to revert state changes from tx on error -func inputOutputCoins(ctx sdk.Context, am sdk.AccountMapper, inputs []Input, outputs []Output) sdk.Error { +func inputOutputCoins(ctx sdk.Context, am sdk.AccountMapper, inputs []Input, outputs []Output) (sdk.Tags, sdk.Error) { + allTags := sdk.EmptyTags() + for _, in := range inputs { - _, err := subtractCoins(ctx, am, in.Address, in.Coins) + _, tags, err := subtractCoins(ctx, am, in.Address, in.Coins) if err != nil { - return err + return nil, err } + allTags = sdk.AppendTags(allTags, tags) } for _, out := range outputs { - _, err := addCoins(ctx, am, out.Address, out.Coins) + _, tags, err := addCoins(ctx, am, out.Address, out.Coins) if err != nil { - return err + return nil, err } + allTags = sdk.AppendTags(allTags, tags) } - return nil + return allTags, nil } diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 4394392dde..3db16c5f92 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -65,7 +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}}) + _, _, 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}})) @@ -78,7 +78,7 @@ func TestKeeper(t *testing.T) { 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}}) + _, 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}})) @@ -147,7 +147,7 @@ func TestSendKeeper(t *testing.T) { 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}}) + _, 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}})) diff --git a/x/ibc/handler.go b/x/ibc/handler.go index 46cbf1e309..1f334166bf 100644 --- a/x/ibc/handler.go +++ b/x/ibc/handler.go @@ -25,7 +25,7 @@ func NewHandler(ibcm Mapper, ck bank.Keeper) sdk.Handler { func handleIBCTransferMsg(ctx sdk.Context, ibcm Mapper, ck bank.Keeper, msg IBCTransferMsg) sdk.Result { packet := msg.IBCPacket - _, err := ck.SubtractCoins(ctx, packet.SrcAddr, packet.Coins) + _, _, err := ck.SubtractCoins(ctx, packet.SrcAddr, packet.Coins) if err != nil { return err.Result() } @@ -47,7 +47,7 @@ func handleIBCReceiveMsg(ctx sdk.Context, ibcm Mapper, ck bank.Keeper, msg IBCRe return ErrInvalidSequence(ibcm.codespace).Result() } - _, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins) + _, _, err := ck.AddCoins(ctx, packet.DestAddr, packet.Coins) if err != nil { return err.Result() } diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index d0019002fd..60cc59bad9 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -34,7 +34,8 @@ func newAddress() crypto.Address { func getCoins(ck bank.Keeper, ctx sdk.Context, addr crypto.Address) (sdk.Coins, sdk.Error) { zero := sdk.Coins(nil) - return ck.AddCoins(ctx, addr, zero) + coins, _, err := ck.AddCoins(ctx, addr, zero) + return coins, err } func makeCodec() *wire.Codec { @@ -70,7 +71,7 @@ func TestIBC(t *testing.T) { zero := sdk.Coins(nil) mycoins := sdk.Coins{sdk.Coin{"mycoin", 10}} - coins, err := ck.AddCoins(ctx, src, mycoins) + coins, _, err := ck.AddCoins(ctx, src, mycoins) assert.Nil(t, err) assert.Equal(t, mycoins, coins) diff --git a/x/stake/handler.go b/x/stake/handler.go index 1408c5bb13..5eff822e6e 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -171,7 +171,7 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address, // Account new shares, save pool := k.GetPool(ctx) - _, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{bondAmt}) + _, _, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{bondAmt}) if err != nil { return err }