From a925c8545c913c4adfdf16b5e6808ed6b93edd39 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 21 Jul 2017 13:44:33 +0200 Subject: [PATCH] Refactored ibc test packet gen --- modules/ibc/ibc_test.go | 126 +++++++---------------------------- modules/ibc/store.go | 10 +++ modules/ibc/test_helpers.go | 129 ++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 101 deletions(-) create mode 100644 modules/ibc/test_helpers.go diff --git a/modules/ibc/ibc_test.go b/modules/ibc/ibc_test.go index 61e58e92b3..3ac88ff224 100644 --- a/modules/ibc/ibc_test.go +++ b/modules/ibc/ibc_test.go @@ -2,7 +2,6 @@ package ibc import ( "encoding/json" - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -10,7 +9,6 @@ import ( wire "github.com/tendermint/go-wire" "github.com/tendermint/light-client/certifiers" - "github.com/tendermint/merkleeyes/iavl" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" @@ -27,14 +25,6 @@ func noErr(err error) bool { return err == nil } -func genEmptySeed(keys certifiers.ValKeys, chain string, h int, - appHash []byte, count int) certifiers.Seed { - - vals := keys.ToValidators(10, 0) - cp := keys.GenCheckpoint(chain, h, nil, vals, appHash, 0, count) - return certifiers.Seed{cp, vals} -} - // this tests registration without registrar permissions func TestIBCRegister(t *testing.T) { assert := assert.New(t) @@ -339,73 +329,43 @@ func TestIBCCreatePacket(t *testing.T) { } } -func makePostPacket(tree *iavl.IAVLTree, packet Packet, fromID string, fromHeight int) PostPacketTx { - key := []byte(fmt.Sprintf("some-long-prefix-%06d", packet.Sequence)) - tree.Set(key, packet.Bytes()) - _, proof := tree.ConstructProof(key) - if proof == nil { - panic("wtf?") - } - - return PostPacketTx{ - FromChainID: fromID, - FromChainHeight: uint64(fromHeight), - Proof: proof, - Key: key, - Packet: packet, - } -} - -func updateChain(app basecoin.Handler, store state.KVStore, keys certifiers.ValKeys, - chain string, h int, appHash []byte) error { - seed := genEmptySeed(keys, chain, h, appHash, len(keys)) - tx := UpdateChainTx{seed}.Wrap() - ctx := stack.MockContext("foo", 123) - _, err := app.DeliverTx(ctx, store, tx) - return err -} - func TestIBCPostPacket(t *testing.T) { assert := assert.New(t) require := require.New(t) otherID := "chain-1" ourID := "hub" + start := 200 - // this is the root seed, that others are evaluated against - keys := certifiers.GenValKeys(7) - appHash := []byte("this is just random garbage") - start := 100 // initial height - root := genEmptySeed(keys, otherID, start, appHash, len(keys)) - - // create the app and register the root of trust (for chain-1) - ctx := stack.MockContext(ourID, 50) - store := state.NewMemKVStore() + // create the app and our chain app := stack.New(). IBC(NewMiddleware()). Dispatch( stack.WrapHandler(NewHandler()), stack.WrapHandler(coin.NewHandler()), ) - tx := RegisterChainTx{root}.Wrap() - _, err := app.DeliverTx(ctx, store, tx) + ourChain := NewAppChain(app, ourID) + + // set up the other chain and register it with us + otherChain := NewMockChain(otherID, 7) + registerTx := otherChain.GetRegistrationTx(start).Wrap() + _, err := ourChain.DeliverTx(registerTx) require.Nil(err, "%+v", err) // set up a rich guy on this chain wealth := coin.Coins{{"btc", 300}, {"eth", 2000}, {"ltc", 5000}} rich := coin.NewAccountWithKey(wealth) - _, err = app.SetOption(log.NewNopLogger(), store, - "coin", "account", rich.MakeOption()) + _, err = ourChain.SetOption("coin", "account", rich.MakeOption()) require.Nil(err, "%+v", err) // sends money to another guy on a different chain, now other chain has credit buddy := basecoin.Actor{ChainID: otherID, App: auth.NameSigs, Address: []byte("dude")} outTx := coin.NewSendOneTx(rich.Actor(), buddy, wealth) - _, err = app.DeliverTx(ctx.WithPermissions(rich.Actor()), store, outTx) + _, err = ourChain.DeliverTx(outTx, rich.Actor()) require.Nil(err, "%+v", err) // make sure the money moved to the other chain... - cstore := stack.PrefixedStore(coin.NameCoin, store) + cstore := ourChain.GetStore(coin.NameCoin) acct, err := coin.GetAccount(cstore, coin.ChainAddr(buddy)) require.Nil(err, "%+v", err) require.Equal(wealth, acct.Coins) @@ -418,53 +378,29 @@ func TestIBCPostPacket(t *testing.T) { recipient, coin.Coins{{"eth", 100}, {"ltc", 300}}, ) + wrongCoin := coin.NewSendOneTx(sender, recipient, coin.Coins{{"missing", 20}}) - // make proofs for some packets.... - tree := iavl.NewIAVLTree(0, nil) - pbad := Packet{ - DestChain: "something-else", - Sequence: 0, - Tx: coinTx, - } - packetBad := makePostPacket(tree, pbad, "something-else", 123) + randomChain := NewMockChain("something-else", 4) + pbad := NewPacket(coinTx, "something-else", 0) + packetBad, _ := randomChain.MakePostPacket(pbad, 123) - p0 := Packet{ - DestChain: ourID, - Sequence: 0, - Permissions: basecoin.Actors{sender}, - Tx: coinTx, - } - p1 := Packet{ - DestChain: ourID, - Sequence: 1, - Permissions: basecoin.Actors{sender}, - Tx: coinTx, - } - // this sends money we don't have registered - p2 := Packet{ - DestChain: ourID, - Sequence: 2, - Permissions: basecoin.Actors{sender}, - Tx: coin.NewSendOneTx(sender, recipient, coin.Coins{{"missing", 20}}), - } - - packet0 := makePostPacket(tree, p0, otherID, start+5) - err = updateChain(app, store, keys, otherID, start+5, tree.Hash()) - require.Nil(err, "%+v", err) + p0 := NewPacket(coinTx, ourID, 0, sender) + packet0, update0 := otherChain.MakePostPacket(p0, start+5) + require.Nil(ourChain.Update(update0)) packet0badHeight := packet0 packet0badHeight.FromChainHeight -= 2 - packet1 := makePostPacket(tree, p1, otherID, start+25) - err = updateChain(app, store, keys, otherID, start+25, tree.Hash()) - require.Nil(err, "%+v", err) + p1 := NewPacket(coinTx, ourID, 1, sender) + packet1, update1 := otherChain.MakePostPacket(p1, start+25) + require.Nil(ourChain.Update(update1)) packet1badProof := packet1 packet1badProof.Key = []byte("random-data") - packet2 := makePostPacket(tree, p2, otherID, start+50) - err = updateChain(app, store, keys, otherID, start+50, tree.Hash()) - require.Nil(err, "%+v", err) + p2 := NewPacket(wrongCoin, ourID, 2, sender) + packet2, update2 := otherChain.MakePostPacket(p2, start+50) + require.Nil(ourChain.Update(update2)) ibcPerm := basecoin.Actors{AllowIBC(coin.NameCoin)} cases := []struct { @@ -501,19 +437,7 @@ func TestIBCPostPacket(t *testing.T) { } for i, tc := range cases { - // cache wrap it like an app, so no state change on error... - myStore := state.NewKVCache(store) - - myCtx := ctx - if len(tc.permissions) > 0 { - myCtx = myCtx.WithPermissions(tc.permissions...) - } - _, err := app.DeliverTx(myCtx, myStore, tc.packet.Wrap()) + _, err := ourChain.DeliverTx(tc.packet.Wrap(), tc.permissions...) assert.True(tc.checker(err), "%d: %+v", i, err) - - // only commit changes on success - if err == nil { - myStore.Sync() - } } } diff --git a/modules/ibc/store.go b/modules/ibc/store.go index fc37b04146..fc85416aad 100644 --- a/modules/ibc/store.go +++ b/modules/ibc/store.go @@ -86,6 +86,16 @@ type Packet struct { Tx basecoin.Tx `json:"tx"` } +// NewPacket creates a new outgoing packet +func NewPacket(tx basecoin.Tx, dest string, seq uint64, perm ...basecoin.Actor) Packet { + return Packet{ + DestChain: dest, + Sequence: seq, + Permissions: perm, + Tx: tx, + } +} + // Bytes returns a serialization of the Packet func (p Packet) Bytes() []byte { return wire.BinaryBytes(p) diff --git a/modules/ibc/test_helpers.go b/modules/ibc/test_helpers.go new file mode 100644 index 0000000000..844a7c9ac5 --- /dev/null +++ b/modules/ibc/test_helpers.go @@ -0,0 +1,129 @@ +package ibc + +import ( + "fmt" + + "github.com/tendermint/light-client/certifiers" + "github.com/tendermint/merkleeyes/iavl" + "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/stack" + "github.com/tendermint/basecoin/state" +) + +// MockChain is used to simulate a chain for ibc tests. +// It is able to produce ibc packets and all verification for +// them, but cannot respond to any responses. +type MockChain struct { + keys certifiers.ValKeys + chainID string + tree *iavl.IAVLTree +} + +// NewMockChain initializes a teststore and test validators +func NewMockChain(chainID string, numKeys int) MockChain { + return MockChain{ + keys: certifiers.GenValKeys(numKeys), + chainID: chainID, + tree: iavl.NewIAVLTree(0, nil), + } +} + +// GetRegistrationTx returns a valid tx to register this chain +func (m MockChain) GetRegistrationTx(h int) RegisterChainTx { + seed := genEmptySeed(m.keys, m.chainID, h, m.tree.Hash(), len(m.keys)) + return RegisterChainTx{seed} +} + +// MakePostPacket commits the packet locally and returns the proof, +// in the form of two packets to update the header and prove this packet. +func (m MockChain) MakePostPacket(packet Packet, h int) ( + PostPacketTx, UpdateChainTx) { + + post := makePostPacket(m.tree, packet, m.chainID, h) + seed := genEmptySeed(m.keys, m.chainID, h, m.tree.Hash(), len(m.keys)) + update := UpdateChainTx{seed} + + return post, update +} + +func genEmptySeed(keys certifiers.ValKeys, chain string, h int, + appHash []byte, count int) certifiers.Seed { + + vals := keys.ToValidators(10, 0) + cp := keys.GenCheckpoint(chain, h, nil, vals, appHash, 0, count) + return certifiers.Seed{cp, vals} +} + +func makePostPacket(tree *iavl.IAVLTree, packet Packet, fromID string, fromHeight int) PostPacketTx { + key := []byte(fmt.Sprintf("some-long-prefix-%06d", packet.Sequence)) + tree.Set(key, packet.Bytes()) + _, proof := tree.ConstructProof(key) + if proof == nil { + panic("wtf?") + } + + return PostPacketTx{ + FromChainID: fromID, + FromChainHeight: uint64(fromHeight), + Proof: proof, + Key: key, + Packet: packet, + } +} + +// AppChain is ready to handle tx +type AppChain struct { + chainID string + app basecoin.Handler + store state.KVStore + height int +} + +// NewAppChain returns a chain that is ready to respond to tx +func NewAppChain(app basecoin.Handler, chainID string) *AppChain { + return &AppChain{ + chainID: chainID, + app: app, + store: state.NewMemKVStore(), + height: 123, + } +} + +// IncrementHeight allows us to jump heights, more than the auto-step +// of 1. It returns the new height we are at. +func (a *AppChain) IncrementHeight(delta int) int { + a.height += delta + return a.height +} + +// DeliverTx runs the tx and commits the new tree, incrementing height +// 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) + res, err := a.app.DeliverTx(ctx, store, tx) + if err == nil { + // commit data on success + store.Sync() + } + return res, err +} + +// Update is a shortcut to DeliverTx with this. Also one return value +// to test inline +func (a *AppChain) Update(tx UpdateChainTx) error { + _, err := a.DeliverTx(tx.Wrap()) + return err +} + +// SetOption sets the option on our app +func (a *AppChain) SetOption(mod, key, value string) (string, error) { + return a.app.SetOption(log.NewNopLogger(), a.store, mod, key, value) +} + +// GetStore is used to get the app-specific sub-store +func (a *AppChain) GetStore(app string) state.KVStore { + return stack.PrefixedStore(app, a.store) +}