2020-08-26 05:11:31 -07:00
|
|
|
package ibctesting
|
2020-08-24 03:06:48 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/tendermint/tendermint/crypto"
|
|
|
|
|
2020-09-07 05:46:48 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
2020-09-28 04:00:33 -07:00
|
|
|
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
|
2020-09-21 09:48:28 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
2020-09-28 04:00:33 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
|
|
|
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
2020-09-21 09:48:28 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
2020-09-03 13:23:20 -07:00
|
|
|
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
|
2020-08-24 03:06:48 -07:00
|
|
|
solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Solomachine is a testing helper used to simulate a counterparty
|
|
|
|
// solo machine client.
|
|
|
|
type Solomachine struct {
|
|
|
|
t *testing.T
|
|
|
|
|
2020-09-07 05:46:48 -07:00
|
|
|
cdc codec.BinaryMarshaler
|
|
|
|
ClientID string
|
2020-09-28 04:00:33 -07:00
|
|
|
PrivateKeys []crypto.PrivKey // keys used for signing
|
|
|
|
PublicKeys []crypto.PubKey // keys used for generating solo machine pub key
|
|
|
|
PublicKey crypto.PubKey // key used for verification
|
2020-09-07 05:46:48 -07:00
|
|
|
Sequence uint64
|
|
|
|
Time uint64
|
|
|
|
Diversifier string
|
2020-08-24 03:06:48 -07:00
|
|
|
}
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
// NewSolomachine returns a new solomachine instance with an `nKeys` amount of
|
|
|
|
// generated private/public key pairs and a sequence starting at 1. If nKeys
|
|
|
|
// is greater than 1 then a multisig public key is used.
|
|
|
|
func NewSolomachine(t *testing.T, cdc codec.BinaryMarshaler, clientID, diversifier string, nKeys uint64) *Solomachine {
|
|
|
|
privKeys, pubKeys, pk := GenerateKeys(t, nKeys)
|
2020-08-24 03:06:48 -07:00
|
|
|
|
|
|
|
return &Solomachine{
|
2020-09-07 05:46:48 -07:00
|
|
|
t: t,
|
|
|
|
cdc: cdc,
|
|
|
|
ClientID: clientID,
|
2020-09-28 04:00:33 -07:00
|
|
|
PrivateKeys: privKeys,
|
|
|
|
PublicKeys: pubKeys,
|
|
|
|
PublicKey: pk,
|
2020-09-07 05:46:48 -07:00
|
|
|
Sequence: 1,
|
|
|
|
Time: 10,
|
|
|
|
Diversifier: diversifier,
|
2020-08-24 03:06:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
// GenerateKeys generates a new set of secp256k1 private keys and public keys.
|
|
|
|
// If the number of keys is greater than one then the public key returned represents
|
|
|
|
// a multisig public key. The private keys are used for signing, the public
|
|
|
|
// keys are used for generating the public key and the public key is used for
|
|
|
|
// solo machine verification. The usage of secp256k1 is entirely arbitrary.
|
|
|
|
// The key type can be swapped for any key type supported by the PublicKey
|
|
|
|
// interface, if needed. The same is true for the amino based Multisignature
|
|
|
|
// public key.
|
|
|
|
func GenerateKeys(t *testing.T, n uint64) ([]crypto.PrivKey, []crypto.PubKey, crypto.PubKey) {
|
|
|
|
require.NotEqual(t, uint64(0), n, "generation of zero keys is not allowed")
|
|
|
|
|
|
|
|
privKeys := make([]crypto.PrivKey, n)
|
|
|
|
pubKeys := make([]crypto.PubKey, n)
|
|
|
|
for i := uint64(0); i < n; i++ {
|
|
|
|
privKeys[i] = secp256k1.GenPrivKey()
|
|
|
|
pubKeys[i] = privKeys[i].PubKey()
|
|
|
|
}
|
|
|
|
|
|
|
|
var pk crypto.PubKey
|
|
|
|
if len(privKeys) > 1 {
|
|
|
|
// generate multi sig pk
|
|
|
|
pk = kmultisig.NewLegacyAminoPubKey(int(n), pubKeys)
|
|
|
|
} else {
|
|
|
|
pk = privKeys[0].PubKey()
|
|
|
|
}
|
|
|
|
|
|
|
|
return privKeys, pubKeys, pk
|
|
|
|
}
|
|
|
|
|
2020-09-07 05:46:48 -07:00
|
|
|
// ClientState returns a new solo machine ClientState instance. Default usage does not allow update
|
|
|
|
// after governance proposal
|
2020-08-24 03:06:48 -07:00
|
|
|
func (solo *Solomachine) ClientState() *solomachinetypes.ClientState {
|
2020-09-09 08:14:11 -07:00
|
|
|
return solomachinetypes.NewClientState(solo.Sequence, solo.ConsensusState(), false)
|
2020-08-24 03:06:48 -07:00
|
|
|
}
|
|
|
|
|
2020-09-07 05:46:48 -07:00
|
|
|
// ConsensusState returns a new solo machine ConsensusState instance
|
2020-08-24 03:06:48 -07:00
|
|
|
func (solo *Solomachine) ConsensusState() *solomachinetypes.ConsensusState {
|
2020-09-21 09:48:28 -07:00
|
|
|
publicKey, err := tx.PubKeyToAny(solo.PublicKey)
|
2020-08-24 03:06:48 -07:00
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
|
|
|
return &solomachinetypes.ConsensusState{
|
2020-09-07 05:46:48 -07:00
|
|
|
PublicKey: publicKey,
|
|
|
|
Diversifier: solo.Diversifier,
|
|
|
|
Timestamp: solo.Time,
|
2020-08-24 03:06:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-03 13:23:20 -07:00
|
|
|
// GetHeight returns an exported.Height with Sequence as EpochHeight
|
|
|
|
func (solo *Solomachine) GetHeight() exported.Height {
|
|
|
|
return clienttypes.NewHeight(0, solo.Sequence)
|
|
|
|
}
|
|
|
|
|
2020-08-24 03:06:48 -07:00
|
|
|
// CreateHeader generates a new private/public key pair and creates the
|
|
|
|
// necessary signature to construct a valid solo machine header.
|
2020-08-25 07:17:38 -07:00
|
|
|
func (solo *Solomachine) CreateHeader() *solomachinetypes.Header {
|
2020-09-28 04:00:33 -07:00
|
|
|
// generate new private keys and signature for header
|
|
|
|
newPrivKeys, newPubKeys, newPubKey := GenerateKeys(solo.t, uint64(len(solo.PrivateKeys)))
|
2020-08-24 03:06:48 -07:00
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
publicKey, err := tx.PubKeyToAny(newPubKey)
|
2020-08-24 03:06:48 -07:00
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
2020-09-07 05:46:48 -07:00
|
|
|
data := &solomachinetypes.HeaderData{
|
|
|
|
NewPubKey: publicKey,
|
|
|
|
NewDiversifier: solo.Diversifier,
|
|
|
|
}
|
|
|
|
|
|
|
|
dataBz, err := solo.cdc.MarshalBinaryBare(data)
|
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
|
|
|
signBytes := &solomachinetypes.SignBytes{
|
|
|
|
Sequence: solo.Sequence,
|
|
|
|
Timestamp: solo.Time,
|
|
|
|
Diversifier: solo.Diversifier,
|
|
|
|
Data: dataBz,
|
|
|
|
}
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
bz, err := solo.cdc.MarshalBinaryBare(signBytes)
|
2020-09-07 05:46:48 -07:00
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
sig := solo.GenerateSignature(bz)
|
2020-09-07 05:46:48 -07:00
|
|
|
|
2020-08-25 07:17:38 -07:00
|
|
|
header := &solomachinetypes.Header{
|
2020-09-07 05:46:48 -07:00
|
|
|
Sequence: solo.Sequence,
|
|
|
|
Timestamp: solo.Time,
|
2020-09-28 04:00:33 -07:00
|
|
|
Signature: sig,
|
2020-09-07 05:46:48 -07:00
|
|
|
NewPublicKey: publicKey,
|
|
|
|
NewDiversifier: solo.Diversifier,
|
2020-08-24 03:06:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// assumes successful header update
|
|
|
|
solo.Sequence++
|
2020-09-28 04:00:33 -07:00
|
|
|
solo.PrivateKeys = newPrivKeys
|
|
|
|
solo.PublicKeys = newPubKeys
|
|
|
|
solo.PublicKey = newPubKey
|
2020-08-24 03:06:48 -07:00
|
|
|
|
|
|
|
return header
|
|
|
|
}
|
|
|
|
|
2020-08-26 10:51:13 -07:00
|
|
|
// CreateMisbehaviour constructs testing misbehaviour for the solo machine client
|
2020-08-24 03:06:48 -07:00
|
|
|
// by signing over two different data bytes at the same sequence.
|
2020-08-26 10:51:13 -07:00
|
|
|
func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour {
|
2020-08-24 03:06:48 -07:00
|
|
|
dataOne := []byte("DATA ONE")
|
|
|
|
dataTwo := []byte("DATA TWO")
|
|
|
|
|
2020-09-07 05:46:48 -07:00
|
|
|
signBytes := &solomachinetypes.SignBytes{
|
|
|
|
Sequence: solo.Sequence,
|
|
|
|
Timestamp: solo.Time,
|
|
|
|
Diversifier: solo.Diversifier,
|
|
|
|
Data: dataOne,
|
|
|
|
}
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
bz, err := solo.cdc.MarshalBinaryBare(signBytes)
|
2020-08-24 03:06:48 -07:00
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
sig := solo.GenerateSignature(bz)
|
2020-08-24 03:06:48 -07:00
|
|
|
signatureOne := solomachinetypes.SignatureAndData{
|
|
|
|
Signature: sig,
|
|
|
|
Data: dataOne,
|
|
|
|
}
|
|
|
|
|
2020-09-07 05:46:48 -07:00
|
|
|
signBytes = &solomachinetypes.SignBytes{
|
|
|
|
Sequence: solo.Sequence,
|
|
|
|
Timestamp: solo.Time,
|
|
|
|
Diversifier: solo.Diversifier,
|
|
|
|
Data: dataTwo,
|
|
|
|
}
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
bz, err = solo.cdc.MarshalBinaryBare(signBytes)
|
2020-08-24 03:06:48 -07:00
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
2020-09-28 04:00:33 -07:00
|
|
|
sig = solo.GenerateSignature(bz)
|
2020-08-24 03:06:48 -07:00
|
|
|
signatureTwo := solomachinetypes.SignatureAndData{
|
|
|
|
Signature: sig,
|
|
|
|
Data: dataTwo,
|
|
|
|
}
|
|
|
|
|
2020-08-26 10:51:13 -07:00
|
|
|
return &solomachinetypes.Misbehaviour{
|
2020-08-24 03:06:48 -07:00
|
|
|
ClientId: solo.ClientID,
|
|
|
|
Sequence: solo.Sequence,
|
|
|
|
SignatureOne: &signatureOne,
|
|
|
|
SignatureTwo: &signatureTwo,
|
|
|
|
}
|
|
|
|
}
|
2020-09-28 04:00:33 -07:00
|
|
|
|
|
|
|
// GenerateSignature uses the stored private keys to generate a signature
|
|
|
|
// over the sign bytes with each key. If the amount of keys is greater than
|
|
|
|
// 1 then a multisig data type is returned.
|
|
|
|
func (solo *Solomachine) GenerateSignature(signBytes []byte) []byte {
|
|
|
|
sigs := make([]signing.SignatureData, len(solo.PrivateKeys))
|
|
|
|
for i, key := range solo.PrivateKeys {
|
|
|
|
sig, err := key.Sign(signBytes)
|
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
|
|
|
sigs[i] = &signing.SingleSignatureData{
|
|
|
|
Signature: sig,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var sigData signing.SignatureData
|
|
|
|
if len(sigs) == 1 {
|
|
|
|
// single public key
|
|
|
|
sigData = sigs[0]
|
|
|
|
} else {
|
|
|
|
// generate multi signature data
|
|
|
|
multiSigData := multisig.NewMultisig(len(sigs))
|
|
|
|
for i, sig := range sigs {
|
|
|
|
multisig.AddSignature(multiSigData, sig, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
sigData = multiSigData
|
|
|
|
}
|
|
|
|
|
|
|
|
protoSigData := signing.SignatureDataToProto(sigData)
|
|
|
|
bz, err := solo.cdc.MarshalBinaryBare(protoSigData)
|
|
|
|
require.NoError(solo.t, err)
|
|
|
|
|
|
|
|
return bz
|
|
|
|
}
|