Test sendtx with foreign addr creates proper ibc packet

This commit is contained in:
Ethan Frey 2017-07-21 19:53:52 +02:00
parent aad5a0f3a0
commit b7f31ad70a
15 changed files with 137 additions and 60 deletions

View File

@ -35,7 +35,7 @@ test_cli: tests/cli/shunit2
./tests/cli/roles.sh
./tests/cli/counter.sh
./tests/cli/restart.sh
# @./tests/cli/ibc.sh
./tests/cli/ibc.sh
test_tutorial: docs/guide/shunit2
@shelldown ${TUTORIALS}

View File

@ -25,7 +25,6 @@ import (
// DefaultHandler for the tests (coin, roles, ibc)
func DefaultHandler(feeDenom string) basecoin.Handler {
// use the default stack
c := coin.NewHandler()
r := roles.NewHandler()
i := ibc.NewHandler()
@ -44,7 +43,7 @@ func DefaultHandler(feeDenom string) basecoin.Handler {
stack.Checkpoint{OnDeliver: true},
).
Dispatch(
stack.WrapHandler(c),
coin.NewHandler(),
stack.WrapHandler(r),
stack.WrapHandler(i),
)

View File

@ -19,11 +19,6 @@ import (
// BuildApp constructs the stack we want to use for this app
func BuildApp(feeDenom string) basecoin.Handler {
// use the default stack
c := coin.NewHandler()
r := roles.NewHandler()
i := ibc.NewHandler()
return stack.New(
base.Logger{},
stack.Recovery{},
@ -39,9 +34,9 @@ func BuildApp(feeDenom string) basecoin.Handler {
stack.Checkpoint{OnDeliver: true},
).
Dispatch(
stack.WrapHandler(c),
stack.WrapHandler(r),
stack.WrapHandler(i),
coin.NewHandler(),
stack.WrapHandler(roles.NewHandler()),
stack.WrapHandler(ibc.NewHandler()),
)
}

View File

@ -12,7 +12,9 @@ import (
"github.com/tendermint/basecoin/modules/base"
"github.com/tendermint/basecoin/modules/coin"
"github.com/tendermint/basecoin/modules/fee"
"github.com/tendermint/basecoin/modules/ibc"
"github.com/tendermint/basecoin/modules/nonce"
"github.com/tendermint/basecoin/modules/roles"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
)
@ -90,13 +92,6 @@ func ErrDecoding() error {
// NewHandler returns a new counter transaction processing handler
func NewHandler(feeDenom string) basecoin.Handler {
// use the default stack
ch := coin.NewHandler()
counter := Handler{}
dispatcher := stack.NewDispatcher(
stack.WrapHandler(ch),
counter,
)
return stack.New(
base.Logger{},
stack.Recovery{},
@ -104,9 +99,17 @@ func NewHandler(feeDenom string) basecoin.Handler {
base.Chain{},
stack.Checkpoint{OnCheck: true},
nonce.ReplayCheck{},
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
stack.Checkpoint{OnDeliver: true},
).Use(dispatcher)
).
IBC(ibc.NewMiddleware()).
Apps(
roles.NewMiddleware(),
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
stack.Checkpoint{OnDeliver: true},
).
Dispatch(
coin.NewHandler(),
Handler{},
)
}
// Handler the counter transaction processing handler

View File

@ -11,7 +11,7 @@ import (
"github.com/tendermint/basecoin/state"
)
func makeHandler() basecoin.Handler {
func makeHandler() stack.Dispatchable {
return NewHandler()
}
@ -28,7 +28,7 @@ func BenchmarkSimpleTransfer(b *testing.B) {
// set the initial account
acct := NewAccountWithKey(Coins{{"mycoin", 1234567890}})
h.SetOption(logger, store, NameCoin, "account", acct.MakeOption())
h.SetOption(logger, store, NameCoin, "account", acct.MakeOption(), nil)
sender := acct.Actor()
receiver := basecoin.Actor{App: "foo", Address: cmn.RandBytes(20)}
@ -36,7 +36,7 @@ func BenchmarkSimpleTransfer(b *testing.B) {
for i := 1; i <= b.N; i++ {
ctx := stack.MockContext("foo", 100).WithPermissions(sender)
tx := makeSimpleTx(sender, receiver, Coins{{"mycoin", 2}})
_, err := h.DeliverTx(ctx, store, tx)
_, err := h.DeliverTx(ctx, store, tx, nil)
// never should error
if err != nil {
panic(err)

View File

@ -1,12 +1,16 @@
package coin
import (
"fmt"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/basecoin"
"github.com/tendermint/basecoin/errors"
"github.com/tendermint/basecoin/modules/auth"
"github.com/tendermint/basecoin/modules/ibc"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
)
@ -16,7 +20,7 @@ const NameCoin = "coin"
// Handler includes an accountant
type Handler struct{}
var _ basecoin.Handler = Handler{}
var _ stack.Dispatchable = Handler{}
// NewHandler - new accountant handler for the coin module
func NewHandler() Handler {
@ -28,8 +32,13 @@ func (Handler) Name() string {
return NameCoin
}
// AssertDispatcher - to fulfill Dispatchable interface
func (Handler) AssertDispatcher() {}
// CheckTx checks if there is enough money in the account
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB,
tx basecoin.Tx, _ basecoin.Checker) (res basecoin.Result, err error) {
send, err := checkTx(ctx, tx)
if err != nil {
return res, err
@ -48,34 +57,63 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin
}
// DeliverTx moves the money
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
tx basecoin.Tx, cb basecoin.Deliver) (res basecoin.Result, err error) {
send, err := checkTx(ctx, tx)
if err != nil {
return res, err
}
// deduct from all input accounts
senders := basecoin.Actors{}
for _, in := range send.Inputs {
_, err = ChangeCoins(store, in.Address, in.Coins.Negative())
if err != nil {
return res, err
}
senders = append(senders, in.Address)
}
// add to all output accounts
for _, out := range send.Outputs {
// TODO: cleaner way, this makes sure we don't consider
// incoming ibc packets with our chain to be remote packets
if out.Address.ChainID == ctx.ChainID() {
out.Address.ChainID = ""
}
fmt.Printf("Giving %#v to %#v\n\n", out.Coins, out.Address)
_, err = ChangeCoins(store, out.Address, out.Coins)
if err != nil {
return res, err
}
// now send ibc packet if needed...
if out.Address.ChainID != "" {
// FIXME: if there are many outputs, we need to adjust inputs
// so the amounts in and out match. how?
outTx := NewSendTx(send.Inputs, []TxOutput{out})
packet := ibc.CreatePacketTx{
DestChain: out.Address.ChainID,
Permissions: senders,
Tx: outTx,
}
ibcCtx := ctx.WithPermissions(ibc.AllowIBC(NameCoin))
_, err := cb.DeliverTx(ibcCtx, store, packet.Wrap())
if err != nil {
return res, err
}
}
}
// a-ok!
return basecoin.Result{}, nil
return res, nil
}
// SetOption - sets the genesis account balance
func (h Handler) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (log string, err error) {
func (h Handler) SetOption(l log.Logger, store state.SimpleDB,
module, key, value string, _ basecoin.SetOptioner) (log string, err error) {
if module != NameCoin {
return "", errors.ErrUnknownModule(module)
}

View File

@ -149,7 +149,7 @@ func TestDeliverTx(t *testing.T) {
}
ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...)
_, err := h.DeliverTx(ctx, store, tc.tx)
_, err := h.DeliverTx(ctx, store, tc.tx, nil)
if len(tc.final) > 0 { // valid
assert.Nil(err, "%d: %+v", i, err)
// make sure the final balances are correct
@ -204,7 +204,7 @@ func TestSetOption(t *testing.T) {
for j, gen := range tc.init {
value, err := json.Marshal(gen)
require.Nil(err, "%d,%d: %+v", i, j, err)
_, err = h.SetOption(l, store, NameCoin, key, string(value))
_, err = h.SetOption(l, store, NameCoin, key, string(value), nil)
require.Nil(err)
}
@ -215,5 +215,4 @@ func TestSetOption(t *testing.T) {
assert.Equal(f.coins, acct.Coins)
}
}
}

View File

@ -10,6 +10,8 @@ import (
"github.com/tendermint/basecoin/modules/auth"
"github.com/tendermint/basecoin/modules/ibc"
"github.com/tendermint/basecoin/stack"
"github.com/tendermint/basecoin/state"
wire "github.com/tendermint/go-wire"
)
// TODO: other test making sure tx is output on send, balance is updated
@ -28,7 +30,7 @@ func TestIBCPostPacket(t *testing.T) {
app := stack.New().
IBC(ibc.NewMiddleware()).
Dispatch(
stack.WrapHandler(NewHandler()),
NewHandler(),
stack.WrapHandler(ibc.NewHandler()),
)
ourChain := ibc.NewAppChain(app, ourID)
@ -57,14 +59,15 @@ func TestIBCPostPacket(t *testing.T) {
require.Nil(err, "%+v", err)
require.Equal(wealth, acct.Coins)
// make sure there is a proper packet for this....
istore := ourChain.GetStore(ibc.NameIBC)
assertPacket(t, istore, otherID, wealth)
// these are the people for testing incoming ibc from the other chain
recipient := basecoin.Actor{ChainID: ourID, App: auth.NameSigs, Address: []byte("bar")}
sender := basecoin.Actor{ChainID: otherID, App: auth.NameSigs, Address: []byte("foo")}
coinTx := NewSendOneTx(
sender,
recipient,
Coins{{"eth", 100}, {"ltc", 300}},
)
payment := Coins{{"eth", 100}, {"ltc", 300}}
coinTx := NewSendOneTx(sender, recipient, payment)
wrongCoin := NewSendOneTx(sender, recipient, Coins{{"missing", 20}})
p0 := ibc.NewPacket(coinTx, ourID, 0, sender)
@ -102,4 +105,36 @@ func TestIBCPostPacket(t *testing.T) {
_, err := ourChain.DeliverTx(tc.packet.Wrap(), tc.permissions...)
assert.True(tc.checker(err), "%d: %+v", i, err)
}
// now, make sure the recipient got credited for the 2 successful sendtx
cstore = ourChain.GetStore(NameCoin)
// FIXME: we need to strip off this when it is local chain-id...
// think this throw and handle this better
local := recipient.WithChain("")
acct, err = GetAccount(cstore, local)
require.Nil(err, "%+v", err)
assert.Equal(payment.Plus(payment), acct.Coins)
}
func assertPacket(t *testing.T, istore state.KVStore, destID string, amount Coins) {
assert := assert.New(t)
require := require.New(t)
iq := ibc.InputQueue(istore, destID)
require.Equal(0, iq.Size())
q := ibc.OutputQueue(istore, destID)
require.Equal(1, q.Size())
d := q.Item(0)
var res ibc.Packet
err := wire.ReadBinaryBytes(d, &res)
require.Nil(err, "%+v", err)
assert.Equal(destID, res.DestChain)
assert.EqualValues(0, res.Sequence)
stx, ok := res.Tx.Unwrap().(SendTx)
if assert.True(ok) {
assert.Equal(1, len(stx.Outputs))
assert.Equal(amount, stx.Outputs[0].Coins)
}
}

View File

@ -40,7 +40,7 @@ func TestFeeChecks(t *testing.T) {
// OKHandler will just return success to a RawTx
stack.WrapHandler(stack.OKHandler{}),
// coin is needed to handle the IPC call from Fee middleware
stack.WrapHandler(coin.NewHandler()),
coin.NewHandler(),
)
// app1 requires no fees
app1 := stack.New(fee.NewSimpleFeeMiddleware(atom(0), collector)).Use(disp)

View File

@ -50,7 +50,7 @@ func (Handler) Name() string {
}
// SetOption sets the registrar for IBC
func (h Handler) SetOption(l log.Logger, store state.KVStore, module, key, value string) (log string, err error) {
func (h Handler) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (log string, err error) {
if module != NameIBC {
return "", errors.ErrUnknownModule(module)
}
@ -70,7 +70,7 @@ func (h Handler) SetOption(l log.Logger, store state.KVStore, module, key, value
// CheckTx verifies the packet is formated correctly, and has the proper sequence
// for a registered chain
func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
err = tx.ValidateBasic()
if err != nil {
return res, err
@ -95,7 +95,7 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.
// DeliverTx verifies all signatures on the tx and updates the chain state
// apropriately
func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
err = tx.ValidateBasic()
if err != nil {
return res, err
@ -122,7 +122,7 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoi
// accepts it as the root of trust.
//
// only the registrar, if set, is allowed to do this
func (h Handler) initSeed(ctx basecoin.Context, store state.KVStore,
func (h Handler) initSeed(ctx basecoin.Context, store state.SimpleDB,
t RegisterChainTx) (res basecoin.Result, err error) {
// verify that the header looks reasonable
@ -141,7 +141,7 @@ func (h Handler) initSeed(ctx basecoin.Context, store state.KVStore,
// updateSeed checks the seed against the existing chain data and rejects it if it
// doesn't fit (or no chain data)
func (h Handler) updateSeed(ctx basecoin.Context, store state.KVStore,
func (h Handler) updateSeed(ctx basecoin.Context, store state.SimpleDB,
t UpdateChainTx) (res basecoin.Result, err error) {
chainID := t.ChainID()
@ -171,7 +171,7 @@ func (h Handler) updateSeed(ctx basecoin.Context, store state.KVStore,
// createPacket makes sure all permissions are good and the destination
// chain is registed. If so, it appends it to the outgoing queue
func (h Handler) createPacket(ctx basecoin.Context, store state.KVStore,
func (h Handler) createPacket(ctx basecoin.Context, store state.SimpleDB,
t CreatePacketTx) (res basecoin.Result, err error) {
// make sure the chain is registed

View File

@ -26,7 +26,7 @@ func (Middleware) Name() string {
// CheckTx verifies the named chain and height is present, and verifies
// the merkle proof in the packet
func (m Middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
func (m Middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
// if it is not a PostPacket, just let it go through
post, ok := tx.Unwrap().(PostPacketTx)
if !ok {
@ -43,7 +43,7 @@ func (m Middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx baseco
// DeliverTx verifies the named chain and height is present, and verifies
// the merkle proof in the packet
func (m Middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
func (m Middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
// if it is not a PostPacket, just let it go through
post, ok := tx.Unwrap().(PostPacketTx)
if !ok {
@ -60,7 +60,7 @@ func (m Middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx base
// verifyPost accepts a message bound for this chain...
// TODO: think about relay
func (m Middleware) verifyPost(ctx basecoin.Context, store state.KVStore,
func (m Middleware) verifyPost(ctx basecoin.Context, store state.SimpleDB,
tx PostPacketTx) (ictx basecoin.Context, itx basecoin.Tx, err error) {
// make sure the chain is registered

View File

@ -17,7 +17,7 @@ const (
// newCertifier loads up the current state of this chain to make a proper certifier
// it will load the most recent height before block h if h is positive
// if h < 0, it will load the latest height
func newCertifier(store state.KVStore, chainID string, h int) (*certifiers.InquiringCertifier, error) {
func newCertifier(store state.SimpleDB, chainID string, h int) (*certifiers.InquiringCertifier, error) {
// each chain has their own prefixed subspace
p := newDBProvider(store)
@ -42,11 +42,11 @@ func newCertifier(store state.KVStore, chainID string, h int) (*certifiers.Inqui
// dbProvider wraps our kv store so it integrates with light-client verification
type dbProvider struct {
byHash state.KVStore
byHash state.SimpleDB
byHeight *state.Span
}
func newDBProvider(store state.KVStore) *dbProvider {
func newDBProvider(store state.SimpleDB) *dbProvider {
return &dbProvider{
byHash: stack.PrefixedStore(prefixHash, store),
byHeight: state.NewSpan(stack.PrefixedStore(prefixHeight, store)),

View File

@ -13,13 +13,13 @@ type HandlerInfo struct {
}
// Save the HandlerInfo to the store
func (h HandlerInfo) Save(store state.KVStore) {
func (h HandlerInfo) Save(store state.SimpleDB) {
b := wire.BinaryBytes(h)
store.Set(HandlerKey(), b)
}
// LoadInfo loads the HandlerInfo from the data store
func LoadInfo(store state.KVStore) (h HandlerInfo) {
func LoadInfo(store state.SimpleDB) (h HandlerInfo) {
b := store.Get(HandlerKey())
if len(b) > 0 {
wire.ReadBinaryBytes(b, &h)
@ -40,7 +40,7 @@ type ChainSet struct {
}
// NewChainSet loads or initialized the ChainSet
func NewChainSet(store state.KVStore) ChainSet {
func NewChainSet(store state.SimpleDB) ChainSet {
space := stack.PrefixedStore(prefixChains, store)
return ChainSet{
Set: state.NewSet(space),
@ -108,14 +108,14 @@ func (p Packet) Bytes() []byte {
}
// InputQueue returns the queue of input packets from this chain
func InputQueue(store state.KVStore, chainID string) *state.Queue {
func InputQueue(store state.SimpleDB, chainID string) *state.Queue {
ch := stack.PrefixedStore(chainID, store)
space := stack.PrefixedStore(prefixInput, ch)
return state.NewQueue(space)
}
// OutputQueue returns the queue of output packets destined for this chain
func OutputQueue(store state.KVStore, chainID string) *state.Queue {
func OutputQueue(store state.SimpleDB, chainID string) *state.Queue {
ch := stack.PrefixedStore(chainID, store)
space := stack.PrefixedStore(prefixOutput, ch)
return state.NewQueue(space)

View File

@ -77,7 +77,7 @@ func makePostPacket(tree *iavl.IAVLTree, packet Packet, fromID string, fromHeigh
type AppChain struct {
chainID string
app basecoin.Handler
store state.KVStore
store state.SimpleDB
height int
}
@ -102,11 +102,11 @@ func (a *AppChain) IncrementHeight(delta int) int {
// by one.
func (a *AppChain) DeliverTx(tx basecoin.Tx, perms ...basecoin.Actor) (basecoin.Result, error) {
ctx := stack.MockContext(a.chainID, uint64(a.height)).WithPermissions(perms...)
store := state.NewKVCache(a.store)
store := a.store.Checkpoint()
res, err := a.app.DeliverTx(ctx, store, tx)
if err == nil {
// commit data on success
store.Sync()
a.store.Commit(store)
}
return res, err
}
@ -124,6 +124,6 @@ func (a *AppChain) SetOption(mod, key, value string) (string, error) {
}
// GetStore is used to get the app-specific sub-store
func (a *AppChain) GetStore(app string) state.KVStore {
func (a *AppChain) GetStore(app string) state.SimpleDB {
return stack.PrefixedStore(app, a.store)
}

View File

@ -71,6 +71,14 @@ func (q *Queue) Pop() []byte {
return value
}
// Item looks at any element in the queue, without modifying anything
func (q *Queue) Item(seq uint64) []byte {
if seq >= q.tail || seq < q.head {
return nil
}
return q.store.Get(makeKey(seq))
}
func (q *Queue) setCount(key []byte, val uint64) {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, val)