Merge pull request #918 from cosmos/cwgoes/abci-response-tags

ABCI response tagging
This commit is contained in:
Christopher Goes
2018-05-10 22:11:48 +02:00
committed by GitHub
13 changed files with 144 additions and 50 deletions
+2
View File
@@ -30,6 +30,8 @@ FEATURES:
* Context now has access to the application-configured logger
* Add (non-proof) subspace query helper functions
* Add more staking query functions: candidates, delegator-bonds
* Bank module now tags transactions with sender/recipient for indexing & later retrieval
* Stake module now tags transactions with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy
BUG FIXES
* Gaia now uses stake, ported from github.com/cosmos/gaia
+1 -1
View File
@@ -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()
}
+1 -1
View File
@@ -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
}
+2 -2
View File
@@ -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
}
+1 -2
View File
@@ -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.
+48
View File
@@ -0,0 +1,48 @@
package types
import (
cmn "github.com/tendermint/tmlibs/common"
)
// Type synonym for convenience
type Tag = cmn.KVPair
// Type synonym for convenience
type Tags cmn.KVPairs
// New empty tags
func EmptyTags() Tags {
return make(Tags, 0)
}
// Append a single tag
func (t Tags) AppendTag(k string, v []byte) Tags {
return append(t, MakeTag(k, v))
}
// Append two lists of tags
func (t Tags) AppendTags(a Tags) Tags {
return append(t, a...)
}
// New variadic tags, must be k string, v []byte repeating
func NewTags(tags ...interface{}) Tags {
var ret Tags
if len(tags)%2 != 0 {
panic("must specify key-value pairs as varargs")
}
i := 0
for {
if i == len(tags) {
break
}
ret = append(ret, Tag{Key: []byte(tags[i].(string)), Value: tags[i+1].([]byte)})
i += 2
}
return ret
}
// Make a tag from a key and a value
func MakeTag(k string, v []byte) Tag {
return Tag{Key: []byte(k), Value: v}
}
+24
View File
@@ -0,0 +1,24 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestAppendTags(t *testing.T) {
a := NewTags("a", []byte("1"))
b := NewTags("b", []byte("2"))
c := a.AppendTags(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 TestNewTags(t *testing.T) {
b := NewTags("a", []byte("1"))
require.Equal(t, b, Tags{MakeTag("a", []byte("1"))})
}
+4 -3
View File
@@ -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.
+30 -24
View File
@@ -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.NewTags("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.NewTags("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 subTags.AppendTags(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 = allTags.AppendTags(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 = allTags.AppendTags(tags)
}
return nil
return allTags, nil
}
+3 -3
View File
@@ -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}}))
+2 -2
View File
@@ -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()
}
+3 -2
View File
@@ -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)
+23 -10
View File
@@ -97,14 +97,18 @@ func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keepe
candidate := NewCandidate(msg.CandidateAddr, msg.PubKey, msg.Description)
k.setCandidate(ctx, candidate)
tags := sdk.NewTags("action", []byte("declareCandidacy"), "candidate", msg.CandidateAddr.Bytes(), "moniker", []byte(msg.Description.Moniker), "identity", []byte(msg.Description.Identity))
// move coins from the msg.Address account to a (self-bond) delegator account
// the candidate account and global shares are updated within here
err := delegate(ctx, k, msg.CandidateAddr, msg.Bond, candidate)
delegateTags, err := delegate(ctx, k, msg.CandidateAddr, msg.Bond, candidate)
if err != nil {
return err.Result()
}
return sdk.Result{}
tags = tags.AppendTags(delegateTags)
return sdk.Result{
Tags: tags,
}
}
func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk.Result {
@@ -128,7 +132,10 @@ func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk
candidate.Description.Details = msg.Description.Details
k.setCandidate(ctx, candidate)
return sdk.Result{}
tags := sdk.NewTags("action", []byte("editCandidacy"), "candidate", msg.CandidateAddr.Bytes(), "moniker", []byte(msg.Description.Moniker), "identity", []byte(msg.Description.Identity))
return sdk.Result{
Tags: tags,
}
}
func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
@@ -148,16 +155,18 @@ func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
GasUsed: GasDelegate,
}
}
err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, candidate)
tags, err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, candidate)
if err != nil {
return err.Result()
}
return sdk.Result{}
return sdk.Result{
Tags: tags,
}
}
// common functionality between handlers
func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
bondAmt sdk.Coin, candidate Candidate) sdk.Error {
bondAmt sdk.Coin, candidate Candidate) (sdk.Tags, sdk.Error) {
// Get or create the delegator bond
bond, found := k.GetDelegatorBond(ctx, delegatorAddr, candidate.Address)
@@ -171,9 +180,9 @@ 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
return nil, err
}
pool, candidate, newShares := pool.candidateAddTokens(candidate, bondAmt.Amount)
bond.Shares = bond.Shares.Add(newShares)
@@ -184,7 +193,8 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
k.setDelegatorBond(ctx, bond)
k.setCandidate(ctx, candidate)
k.setPool(ctx, pool)
return nil
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "candidate", candidate.Address.Bytes())
return tags, nil
}
func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
@@ -281,5 +291,8 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
k.setCandidate(ctx, candidate)
}
k.setPool(ctx, p)
return sdk.Result{}
tags := sdk.NewTags("action", []byte("unbond"), "delegator", msg.DelegatorAddr.Bytes(), "candidate", msg.CandidateAddr.Bytes())
return sdk.Result{
Tags: tags,
}
}