From 744c1ce250ba8fcbd1ec1f9f4bf0360cf4306c6c Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Wed, 10 Jun 2020 10:09:51 +0200 Subject: [PATCH] x/ibc: simulation store decoders (#6247) * x/ibc: simulation store decoders * x/ibc: missing decoders * fixes * 02-client: decoder test * 03-connection: decoder test * x/ibc: decoder test * address comments from review Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- x/ibc/02-client/simulation/decoder.go | 36 ++++++++ x/ibc/02-client/simulation/decoder_test.go | 74 ++++++++++++++++ x/ibc/03-connection/simulation/decoder.go | 33 +++++++ .../03-connection/simulation/decoder_test.go | 66 ++++++++++++++ x/ibc/04-channel/simulation/decoder.go | 49 +++++++++++ x/ibc/04-channel/simulation/decoder_test.go | 87 +++++++++++++++++++ x/ibc/24-host/keys.go | 13 +-- x/ibc/keeper/keeper.go | 18 +++- x/ibc/module.go | 5 +- x/ibc/simulation/decoder.go | 33 +++++++ x/ibc/simulation/decoder_test.go | 81 +++++++++++++++++ 11 files changed, 482 insertions(+), 13 deletions(-) create mode 100644 x/ibc/02-client/simulation/decoder.go create mode 100644 x/ibc/02-client/simulation/decoder_test.go create mode 100644 x/ibc/03-connection/simulation/decoder.go create mode 100644 x/ibc/03-connection/simulation/decoder_test.go create mode 100644 x/ibc/04-channel/simulation/decoder.go create mode 100644 x/ibc/04-channel/simulation/decoder_test.go create mode 100644 x/ibc/simulation/decoder.go create mode 100644 x/ibc/simulation/decoder_test.go diff --git a/x/ibc/02-client/simulation/decoder.go b/x/ibc/02-client/simulation/decoder.go new file mode 100644 index 0000000000..c7f566609d --- /dev/null +++ b/x/ibc/02-client/simulation/decoder.go @@ -0,0 +1,36 @@ +package simulation + +import ( + "bytes" + "fmt" + + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding client type. +func NewDecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) (string, bool) { + switch { + case bytes.HasPrefix(kvA.Key, host.KeyClientStorePrefix) && bytes.HasSuffix(kvA.Key, host.KeyClientState()): + var clientStateA, clientStateB exported.ClientState + cdc.MustUnmarshalBinaryBare(kvA.Value, &clientStateA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &clientStateB) + return fmt.Sprintf("ClientState A: %v\nClientState B: %v", clientStateA, clientStateB), true + + case bytes.HasPrefix(kvA.Key, host.KeyClientStorePrefix) && bytes.HasSuffix(kvA.Key, host.KeyClientType()): + return fmt.Sprintf("Client type A: %s\nClient type B: %s", string(kvA.Value), string(kvB.Value)), true + + case bytes.HasPrefix(kvA.Key, host.KeyClientStorePrefix) && bytes.Contains(kvA.Key, []byte("consensusState")): + var consensusStateA, consensusStateB exported.ConsensusState + cdc.MustUnmarshalBinaryBare(kvA.Value, &consensusStateA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &consensusStateB) + return fmt.Sprintf("ConsensusState A: %v\nConsensusState B: %v", consensusStateA, consensusStateB), true + + default: + return "", false + } +} diff --git a/x/ibc/02-client/simulation/decoder_test.go b/x/ibc/02-client/simulation/decoder_test.go new file mode 100644 index 0000000000..fed0cc74f8 --- /dev/null +++ b/x/ibc/02-client/simulation/decoder_test.go @@ -0,0 +1,74 @@ +package simulation_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/simulation" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + cdc := app.Codec() + clientID := "clientidone" + + clientState := ibctmtypes.ClientState{ + ID: clientID, + FrozenHeight: 10, + } + + consState := ibctmtypes.ConsensusState{ + Height: 10, + Timestamp: time.Now().UTC(), + } + + kvPairs := tmkv.Pairs{ + tmkv.Pair{ + Key: host.FullKeyClientPath(clientID, host.KeyClientState()), + Value: cdc.MustMarshalBinaryBare(clientState), + }, + tmkv.Pair{ + Key: host.FullKeyClientPath(clientID, host.KeyClientType()), + Value: []byte(exported.Tendermint.String()), + }, + tmkv.Pair{ + Key: host.FullKeyClientPath(clientID, host.KeyConsensusState(10)), + Value: cdc.MustMarshalBinaryBare(consState), + }, + tmkv.Pair{ + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"ClientState", fmt.Sprintf("ClientState A: %v\nClientState B: %v", clientState, clientState)}, + {"client type", fmt.Sprintf("Client type A: %s\nClient type B: %s", exported.Tendermint, exported.Tendermint)}, + {"ConsensusState", fmt.Sprintf("ConsensusState A: %v\nConsensusState B: %v", consState, consState)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + res, found := simulation.NewDecodeStore(cdc, kvPairs[i], kvPairs[i]) + if i == len(tests)-1 { + require.False(t, found, string(kvPairs[i].Key)) + require.Empty(t, res, string(kvPairs[i].Key)) + } else { + require.True(t, found, string(kvPairs[i].Key)) + require.Equal(t, tt.expectedLog, res, string(kvPairs[i].Key)) + } + }) + } +} diff --git a/x/ibc/03-connection/simulation/decoder.go b/x/ibc/03-connection/simulation/decoder.go new file mode 100644 index 0000000000..94e7edff5e --- /dev/null +++ b/x/ibc/03-connection/simulation/decoder.go @@ -0,0 +1,33 @@ +package simulation + +import ( + "bytes" + "fmt" + + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding connection type. +func NewDecodeStore(cdc codec.Marshaler, kvA, kvB tmkv.Pair) (string, bool) { + switch { + case bytes.HasPrefix(kvA.Key, host.KeyClientStorePrefix) && bytes.HasSuffix(kvA.Key, host.KeyConnectionPrefix): + var clientConnectionsA, clientConnectionsB types.ClientPaths + cdc.MustUnmarshalBinaryBare(kvA.Value, &clientConnectionsA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &clientConnectionsB) + return fmt.Sprintf("ClientPaths A: %v\nClientPaths B: %v", clientConnectionsA, clientConnectionsB), true + + case bytes.HasPrefix(kvA.Key, host.KeyConnectionPrefix): + var connectionA, connectionB types.ConnectionEnd + cdc.MustUnmarshalBinaryBare(kvA.Value, &connectionA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &connectionB) + return fmt.Sprintf("ConnectionEnd A: %v\nConnectionEnd B: %v", connectionA, connectionB), true + + default: + return "", false + } +} diff --git a/x/ibc/03-connection/simulation/decoder_test.go b/x/ibc/03-connection/simulation/decoder_test.go new file mode 100644 index 0000000000..2abe8d6286 --- /dev/null +++ b/x/ibc/03-connection/simulation/decoder_test.go @@ -0,0 +1,66 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + cdc := app.AppCodec() + + connection := types.ConnectionEnd{ + ID: "connectionidone", + ClientID: "clientidone", + Versions: []string{"1.0"}, + } + + paths := types.ClientPaths{ + Paths: []string{connection.ID}, + } + + kvPairs := tmkv.Pairs{ + tmkv.Pair{ + Key: host.KeyClientConnections(connection.ClientID), + Value: cdc.MustMarshalBinaryBare(&paths), + }, + tmkv.Pair{ + Key: host.KeyConnection(connection.ID), + Value: cdc.MustMarshalBinaryBare(&connection), + }, + tmkv.Pair{ + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"ClientPaths", fmt.Sprintf("ClientPaths A: %v\nClientPaths B: %v", paths, paths)}, + {"ConnectionEnd", fmt.Sprintf("ConnectionEnd A: %v\nConnectionEnd B: %v", connection, connection)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + res, found := simulation.NewDecodeStore(cdc, kvPairs[i], kvPairs[i]) + if i == len(tests)-1 { + require.False(t, found, string(kvPairs[i].Key)) + require.Empty(t, res, string(kvPairs[i].Key)) + } else { + require.True(t, found, string(kvPairs[i].Key)) + require.Equal(t, tt.expectedLog, res, string(kvPairs[i].Key)) + } + }) + } +} diff --git a/x/ibc/04-channel/simulation/decoder.go b/x/ibc/04-channel/simulation/decoder.go new file mode 100644 index 0000000000..4f08728f24 --- /dev/null +++ b/x/ibc/04-channel/simulation/decoder.go @@ -0,0 +1,49 @@ +package simulation + +import ( + "bytes" + "fmt" + + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding channel type. +func NewDecodeStore(cdc codec.Marshaler, kvA, kvB tmkv.Pair) (string, bool) { + switch { + case bytes.HasPrefix(kvA.Key, []byte(host.KeyChannelPrefix)): + var channelA, channelB types.Channel + cdc.MustUnmarshalBinaryBare(kvA.Value, &channelA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &channelB) + return fmt.Sprintf("Channel A: %v\nChannel B: %v", channelA, channelB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyNextSeqSendPrefix)): + seqA := sdk.BigEndianToUint64(kvA.Value) + seqB := sdk.BigEndianToUint64(kvB.Value) + return fmt.Sprintf("NextSeqSend A: %d\nNextSeqSend B: %d", seqA, seqB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyNextSeqRecvPrefix)): + seqA := sdk.BigEndianToUint64(kvA.Value) + seqB := sdk.BigEndianToUint64(kvB.Value) + return fmt.Sprintf("NextSeqRecv A: %d\nNextSeqRecv B: %d", seqA, seqB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyNextSeqAckPrefix)): + seqA := sdk.BigEndianToUint64(kvA.Value) + seqB := sdk.BigEndianToUint64(kvB.Value) + return fmt.Sprintf("NextSeqAck A: %d\nNextSeqAck B: %d", seqA, seqB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyPacketCommitmentPrefix)): + return fmt.Sprintf("CommitmentHash A: %X\nCommitmentHash B: %X", kvA.Value, kvB.Value), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyPacketAckPrefix)): + return fmt.Sprintf("AckHash A: %X\nAckHash B: %X", kvA.Value, kvB.Value), true + + default: + return "", false + } +} diff --git a/x/ibc/04-channel/simulation/decoder_test.go b/x/ibc/04-channel/simulation/decoder_test.go new file mode 100644 index 0000000000..af4ba2fc73 --- /dev/null +++ b/x/ibc/04-channel/simulation/decoder_test.go @@ -0,0 +1,87 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + cdc := app.AppCodec() + + channelID := "channelidone" + portID := "portidone" + + channel := types.Channel{ + State: types.OPEN, + Version: "1.0", + } + + bz := []byte{0x1, 0x2, 0x3} + + kvPairs := tmkv.Pairs{ + tmkv.Pair{ + Key: host.KeyChannel(portID, channelID), + Value: cdc.MustMarshalBinaryBare(&channel), + }, + tmkv.Pair{ + Key: host.KeyNextSequenceSend(portID, channelID), + Value: sdk.Uint64ToBigEndian(1), + }, + tmkv.Pair{ + Key: host.KeyNextSequenceRecv(portID, channelID), + Value: sdk.Uint64ToBigEndian(1), + }, + tmkv.Pair{ + Key: host.KeyNextSequenceAck(portID, channelID), + Value: sdk.Uint64ToBigEndian(1), + }, + tmkv.Pair{ + Key: host.KeyPacketCommitment(portID, channelID, 1), + Value: bz, + }, + tmkv.Pair{ + Key: host.KeyPacketAcknowledgement(portID, channelID, 1), + Value: bz, + }, + tmkv.Pair{ + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"Channel", fmt.Sprintf("Channel A: %v\nChannel B: %v", channel, channel)}, + {"NextSeqSend", "NextSeqSend A: 1\nNextSeqSend B: 1"}, + {"NextSeqRecv", "NextSeqRecv A: 1\nNextSeqRecv B: 1"}, + {"NextSeqAck", "NextSeqAck A: 1\nNextSeqAck B: 1"}, + {"CommitmentHash", fmt.Sprintf("CommitmentHash A: %X\nCommitmentHash B: %X", bz, bz)}, + {"AckHash", fmt.Sprintf("AckHash A: %X\nAckHash B: %X", bz, bz)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + res, found := simulation.NewDecodeStore(cdc, kvPairs[i], kvPairs[i]) + if i == len(tests)-1 { + require.False(t, found, string(kvPairs[i].Key)) + require.Empty(t, res, string(kvPairs[i].Key)) + } else { + require.True(t, found, string(kvPairs[i].Key)) + require.Equal(t, tt.expectedLog, res, string(kvPairs[i].Key)) + } + }) + } +} diff --git a/x/ibc/24-host/keys.go b/x/ibc/24-host/keys.go index 94de1182a0..99299053b3 100644 --- a/x/ibc/24-host/keys.go +++ b/x/ibc/24-host/keys.go @@ -40,6 +40,12 @@ func KeyPrefixBytes(prefix int) []byte { return []byte(fmt.Sprintf("%d/", prefix)) } +// FullKeyClientPath returns the full path of specific client path in the format: +// "clients/{clientID}/{path}". +func FullKeyClientPath(clientID string, path []byte) []byte { + return append(KeyClientStorePrefix, append([]byte("/"+clientID+"/"), path...)...) +} + // ICS02 // The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#path-space @@ -191,12 +197,7 @@ func channelPath(portID, channelID string) string { // ICS05 // The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-005-port-allocation#store-paths -// PortPath defines the path under which ports paths are stored +// PortPath defines the path under which ports paths are stored on the capability module func PortPath(portID string) string { return fmt.Sprintf("ports/%s", portID) } - -// KeyPort returns the store key for a particular port -func KeyPort(portID string) []byte { - return []byte(PortPath(portID)) -} diff --git a/x/ibc/keeper/keeper.go b/x/ibc/keeper/keeper.go index 06b2a5733a..31995b9685 100644 --- a/x/ibc/keeper/keeper.go +++ b/x/ibc/keeper/keeper.go @@ -12,6 +12,9 @@ import ( // Keeper defines each ICS keeper for IBC type Keeper struct { + aminoCdc *codec.Codec + cdc codec.Marshaler + ClientKeeper client.Keeper ConnectionKeeper connection.Keeper ChannelKeeper channel.Keeper @@ -21,14 +24,16 @@ type Keeper struct { // NewKeeper creates a new ibc Keeper func NewKeeper( - cdc *codec.Codec, appCodec codec.Marshaler, key sdk.StoreKey, stakingKeeper client.StakingKeeper, scopedKeeper capability.ScopedKeeper, + aminoCdc *codec.Codec, cdc codec.Marshaler, key sdk.StoreKey, stakingKeeper client.StakingKeeper, scopedKeeper capability.ScopedKeeper, ) *Keeper { - clientKeeper := client.NewKeeper(cdc, key, stakingKeeper) - connectionKeeper := connection.NewKeeper(cdc, appCodec, key, clientKeeper) + clientKeeper := client.NewKeeper(aminoCdc, key, stakingKeeper) + connectionKeeper := connection.NewKeeper(aminoCdc, cdc, key, clientKeeper) portKeeper := port.NewKeeper(scopedKeeper) - channelKeeper := channel.NewKeeper(appCodec, key, clientKeeper, connectionKeeper, portKeeper, scopedKeeper) + channelKeeper := channel.NewKeeper(cdc, key, clientKeeper, connectionKeeper, portKeeper, scopedKeeper) return &Keeper{ + aminoCdc: aminoCdc, + cdc: cdc, ClientKeeper: clientKeeper, ConnectionKeeper: connectionKeeper, ChannelKeeper: channelKeeper, @@ -36,6 +41,11 @@ func NewKeeper( } } +// Codecs returns the IBC module codec. +func (k Keeper) Codecs() (codec.Marshaler, *codec.Codec) { + return k.cdc, k.aminoCdc +} + // SetRouter sets the Router in IBC Keeper and seals it. The method panics if // there is an existing router that's already sealed. func (k *Keeper) SetRouter(rtr *port.Router) { diff --git a/x/ibc/module.go b/x/ibc/module.go index 8c1e51bab8..f68c4e5a8d 100644 --- a/x/ibc/module.go +++ b/x/ibc/module.go @@ -178,9 +178,8 @@ func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange { } // RegisterStoreDecoder registers a decoder for ibc module's types -func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) { - // TODO: in a following PR - // sdr[StoreKey] = simulation.NewDecodeStore(am.cdc) +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.NewDecodeStore(am.keeper.Codecs()) } // WeightedOperations returns the all the ibc module operations with their respective weights. diff --git a/x/ibc/simulation/decoder.go b/x/ibc/simulation/decoder.go new file mode 100644 index 0000000000..ddff3da4f2 --- /dev/null +++ b/x/ibc/simulation/decoder.go @@ -0,0 +1,33 @@ +package simulation + +import ( + "fmt" + + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/codec" + clientsim "github.com/cosmos/cosmos-sdk/x/ibc/02-client/simulation" + connectionsim "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/simulation" + channelsim "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/simulation" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding ibc type. +func NewDecodeStore(cdc codec.Marshaler, aminoCdc *codec.Codec) func(kvA, kvB tmkv.Pair) string { + return func(kvA, kvB tmkv.Pair) string { + if res, found := clientsim.NewDecodeStore(aminoCdc, kvA, kvB); found { + return res + } + + if res, found := connectionsim.NewDecodeStore(cdc, kvA, kvB); found { + return res + } + + if res, found := channelsim.NewDecodeStore(cdc, kvA, kvB); found { + return res + } + + panic(fmt.Sprintf("invalid %s key prefix: %s", host.ModuleName, string(kvA.Key))) + } +} diff --git a/x/ibc/simulation/decoder_test.go b/x/ibc/simulation/decoder_test.go new file mode 100644 index 0000000000..27b801fa3c --- /dev/null +++ b/x/ibc/simulation/decoder_test.go @@ -0,0 +1,81 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/simapp" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/simulation" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + cdc := app.AppCodec() + aminoCdc := app.Codec() + + dec := simulation.NewDecodeStore(app.IBCKeeper.Codecs()) + + clientID := "clientidone" + channelID := "channelidone" + portID := "portidone" + + clientState := ibctmtypes.ClientState{ + ID: clientID, + FrozenHeight: 10, + } + connection := connectiontypes.ConnectionEnd{ + ID: clientID, + ClientID: "clientidone", + Versions: []string{"1.0"}, + } + channel := channeltypes.Channel{ + State: channeltypes.OPEN, + Version: "1.0", + } + + kvPairs := tmkv.Pairs{ + tmkv.Pair{ + Key: host.FullKeyClientPath(clientID, host.KeyClientState()), + Value: aminoCdc.MustMarshalBinaryBare(clientState), + }, + tmkv.Pair{ + Key: host.KeyConnection(connection.ID), + Value: cdc.MustMarshalBinaryBare(&connection), + }, + tmkv.Pair{ + Key: host.KeyChannel(portID, channelID), + Value: cdc.MustMarshalBinaryBare(&channel), + }, + tmkv.Pair{ + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"ClientState", fmt.Sprintf("ClientState A: %v\nClientState B: %v", clientState, clientState)}, + {"ConnectionEnd", fmt.Sprintf("ConnectionEnd A: %v\nConnectionEnd B: %v", connection, connection)}, + {"Channel", fmt.Sprintf("Channel A: %v\nChannel B: %v", channel, channel)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + if i == len(tests)-1 { + require.Panics(t, func() { dec(kvPairs[i], kvPairs[i]) }, tt.name) + } else { + require.Equal(t, tt.expectedLog, dec(kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +}