Refactored ibc test packet gen

This commit is contained in:
Ethan Frey 2017-07-21 13:44:33 +02:00
parent 0c5f0bdf77
commit a925c8545c
3 changed files with 164 additions and 101 deletions

View File

@ -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()
}
}
}

View File

@ -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)

129
modules/ibc/test_helpers.go Normal file
View File

@ -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)
}