diff --git a/plugins/ibc/ibc.go b/plugins/ibc/ibc.go index 1fa3727f4b..087f336d91 100644 --- a/plugins/ibc/ibc.go +++ b/plugins/ibc/ibc.go @@ -21,6 +21,7 @@ const ( _STATE = "state" _HEADER = "header" _EGRESS = "egress" + _INGRESS = "ingress" _CONNECTION = "connection" ) @@ -29,6 +30,7 @@ type IBCPluginState struct { // @[:ibc, :blockchain, :state, ChainID] <~ BlockchainState // @[:ibc, :blockchain, :header, ChainID, Height] <~ tm.Header // @[:ibc, :egress, Src, Dst, Sequence] <~ Packet + // @[:ibc, :ingress, Dst, Src, Sequence] <~ Packet // @[:ibc, :connection, Src, Dst] <~ Connection # TODO - keep connection state } @@ -60,8 +62,11 @@ const ( IBCTxTypePacketCreate = byte(0x03) IBCTxTypePacketPost = byte(0x04) - IBCCodeEncodingError = abci.CodeType(1001) - IBCCodeChainAlreadyExists = abci.CodeType(1002) + IBCCodeEncodingError = abci.CodeType(1001) + IBCCodeChainAlreadyExists = abci.CodeType(1002) + IBCCodePacketAlreadyExists = abci.CodeType(1003) + IBCCodeUnknownHeight = abci.CodeType(1004) + IBCCodeInvalidProof = abci.CodeType(1005) ) var _ = wire.RegisterInterface( @@ -277,15 +282,85 @@ func (sm *IBCStateMachine) runUpdateChainTx(tx IBCUpdateChainTx) { } func (sm *IBCStateMachine) runPacketCreateTx(tx IBCPacketCreateTx) { - // TODO Store packet in egress + packet := tx.Packet + packetKey := toKey(_IBC, _EGRESS, + packet.SrcChainID, + packet.DstChainID, + cmn.Fmt("%v", packet.Sequence), + ) + // Make sure packet doesn't already exist + if exists(sm.store, packetKey) { + sm.res.Code = IBCCodePacketAlreadyExists + sm.res.AppendLog("Already exists") + return + } + // Save new Packet + save(sm.store, packetKey, wire.BinaryBytes(packet)) } func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) { - // TODO Make sure packat doesn't already exist - // TODO Load associated blockHash and make sure it exists - // TODO compute packet key - // TODO Make sure packet's proof matches given (packet, key, blockhash) - // TODO Store packet + packet := tx.Packet + packetKeyEgress := toKey(_IBC, _EGRESS, + packet.SrcChainID, + packet.DstChainID, + cmn.Fmt("%v", packet.Sequence), + ) + packetKeyIngress := toKey(_IBC, _INGRESS, + packet.DstChainID, + packet.SrcChainID, + cmn.Fmt("%v", packet.Sequence), + ) + headerKey := toKey(_IBC, _BLOCKCHAIN, _HEADER, + tx.FromChainID, + cmn.Fmt("%v", tx.FromChainHeight), + ) + + // Make sure packet doesn't already exist + if exists(sm.store, packetKeyIngress) { + sm.res.Code = IBCCodePacketAlreadyExists + sm.res.AppendLog("Already exists") + return + } + + // Save new Packet + save(sm.store, packetKeyIngress, wire.BinaryBytes(packet)) + + // Load Header and make sure it exists + var header tm.Header + exists, err := load(sm.store, headerKey, &header) + if err != nil { + sm.res = abci.ErrInternalError.AppendLog(cmn.Fmt("Loading Header: %v", err.Error())) + return + } + if !exists { + sm.res.Code = IBCCodeUnknownHeight + sm.res.AppendLog(cmn.Fmt("Loading Header: %v", err.Error())) + return + } + + /* + // Read Proof + var proof *merkle.IAVLProof + err = wire.ReadBinaryBytes(tx.Proof, &proof) + if err != nil { + sm.res.Code = IBCEncodingError + sm.res.AppendLog(cmn.Fmt("Reading Proof: %v", err.Error())) + return + } + */ + proof := tx.Proof + packetBytes := wire.BinaryBytes(packet) + + // Make sure packet's proof matches given (packet, key, blockhash) + ok := proof.Verify(packetKeyEgress, packetBytes, header.AppHash) + if !ok { + sm.res.Code = IBCCodeInvalidProof + sm.res.AppendLog("Proof is invalid") + return + } + + return + } func (ibc *IBCPlugin) InitChain(store types.KVStore, vals []*abci.Validator) { diff --git a/plugins/ibc/ibc_test.go b/plugins/ibc/ibc_test.go index 1de57e541d..393f5003f3 100644 --- a/plugins/ibc/ibc_test.go +++ b/plugins/ibc/ibc_test.go @@ -5,10 +5,12 @@ import ( "testing" "github.com/stretchr/testify/assert" + abci "github.com/tendermint/abci/types" "github.com/tendermint/basecoin/testutils" "github.com/tendermint/basecoin/types" cmn "github.com/tendermint/go-common" "github.com/tendermint/go-wire" + eyes "github.com/tendermint/merkleeyes/client" tm "github.com/tendermint/tendermint/types" ) @@ -36,7 +38,8 @@ func genGenesisDoc(chainID string, numVals int) (*tm.GenesisDoc, []*tm.Validator func TestIBCPlugin(t *testing.T) { - store := types.NewKVCache(nil) + tree := eyes.NewLocalClient("", 0) + store := types.NewKVCache(tree) store.SetLogging() // Log all activity ibcPlugin := New() @@ -68,7 +71,7 @@ func TestIBCPlugin(t *testing.T) { Genesis: string(genDocJSON_1), }, }})) - assert.True(t, res.IsOK(), res) + assert.True(t, res.IsOK(), res.Log) t.Log(">>", strings.Join(store.GetLogLines(), "\n")) store.ClearLogLines() @@ -79,9 +82,42 @@ func TestIBCPlugin(t *testing.T) { Genesis: string(genDocJSON_1), }, }})) - assert.Equal(t, res.Code, IBCCodeChainAlreadyExists, res) + assert.Equal(t, res.Code, IBCCodeChainAlreadyExists, res.Log) t.Log(">>", strings.Join(store.GetLogLines(), "\n")) store.ClearLogLines() - t.Log(">>", vals_1) + // Create a new packet (for testing) + res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{ + Packet{ + SrcChainID: "test_chain", + DstChainID: "dst_chain", + Sequence: 0, + Type: "data", + Payload: []byte("hello world"), + }, + }})) + assert.Equal(t, res.Code, abci.CodeType(0), res.Log) + t.Log(">>", strings.Join(store.GetLogLines(), "\n")) + store.ClearLogLines() + + // Post a duplicate packet + res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{ + Packet{ + SrcChainID: "test_chain", + DstChainID: "dst_chain", + Sequence: 0, + Type: "data", + Payload: []byte("hello world"), + }, + }})) + assert.Equal(t, res.Code, IBCCodePacketAlreadyExists, res.Log) + t.Log(">>", strings.Join(store.GetLogLines(), "\n")) + store.ClearLogLines() + + // Update a chain + //header, commit := + + store.Sync() + resCommit := tree.CommitSync() + t.Log(">>", vals_1, tree, resCommit.Data) }