Add multisig support + tests to solo machine (#7383)
* add multisig support to testing pkg Adds single and multisig public key support to the testing package. Fix build associated with changes. * update clientstate tests to use singlesig and multsig * add singlesig and multisig tests for consensus state, header, and misbehaviour * add singlesig and multisig for solomachine handlers * add spec * fix lgtm * fixes from self review * fix lint? * fix build * increase code cov * Update x/ibc/light-clients/solomachine/types/client_state.go Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * apply @fedekunze review suggestions Add comment to VerifyMultisignature explaining why it uses the nested function. Switch panic to require. Add comment for secp256k1 key usage. Ref: https://github.com/cosmos/cosmos-sdk/pull/7383#pullrequestreview-496591518 Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Christopher Goes <cwgoes@pluranimity.org> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
8601dcdbb7
commit
be59020f29
|
@ -16,7 +16,7 @@ func (suite *TypesTestSuite) TestMarshalConsensusStateWithHeight() {
|
|||
}{
|
||||
{
|
||||
"solo machine client", func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 1)
|
||||
cswh = types.NewConsensusStateWithHeight(types.NewHeight(0, soloMachine.Sequence), soloMachine.ConsensusState())
|
||||
},
|
||||
},
|
||||
|
|
|
@ -25,7 +25,7 @@ func (suite *TypesTestSuite) TestPackClientState() {
|
|||
}{
|
||||
{
|
||||
"solo machine client",
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "").ClientState(),
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ClientState(),
|
||||
true,
|
||||
},
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ func (suite *TypesTestSuite) TestPackConsensusState() {
|
|||
}{
|
||||
{
|
||||
"solo machine consensus",
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "").ConsensusState(),
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(),
|
||||
true,
|
||||
},
|
||||
{
|
||||
|
@ -123,7 +123,7 @@ func (suite *TypesTestSuite) TestPackHeader() {
|
|||
}{
|
||||
{
|
||||
"solo machine header",
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "").CreateHeader(),
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader(),
|
||||
true,
|
||||
},
|
||||
{
|
||||
|
@ -170,7 +170,7 @@ func (suite *TypesTestSuite) TestPackMisbehaviour() {
|
|||
}{
|
||||
{
|
||||
"solo machine misbehaviour",
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "").CreateMisbehaviour(),
|
||||
ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateMisbehaviour(),
|
||||
true,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -49,7 +49,7 @@ func (suite *TypesTestSuite) TestMarshalMsgCreateClient() {
|
|||
}{
|
||||
{
|
||||
"solo machine client", func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgCreateClient(soloMachine.ClientID, soloMachine.ClientState(), soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -149,7 +149,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() {
|
|||
{
|
||||
"valid - solomachine client",
|
||||
func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgCreateClient(soloMachine.ClientID, soloMachine.ClientState(), soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -158,7 +158,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() {
|
|||
{
|
||||
"invalid solomachine client",
|
||||
func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgCreateClient(soloMachine.ClientID, &solomachinetypes.ClientState{}, soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -167,7 +167,7 @@ func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() {
|
|||
{
|
||||
"invalid solomachine consensus state",
|
||||
func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgCreateClient(soloMachine.ClientID, soloMachine.ClientState(), &solomachinetypes.ConsensusState{}, suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -209,7 +209,7 @@ func (suite *TypesTestSuite) TestMarshalMsgUpdateClient() {
|
|||
}{
|
||||
{
|
||||
"solo machine client", func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -298,7 +298,7 @@ func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() {
|
|||
{
|
||||
"valid - solomachine header",
|
||||
func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -347,7 +347,7 @@ func (suite *TypesTestSuite) TestMarshalMsgSubmitMisbehaviour() {
|
|||
}{
|
||||
{
|
||||
"solo machine client", func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgSubmitMisbehaviour(soloMachine.ClientID, soloMachine.CreateMisbehaviour(), suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -448,7 +448,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() {
|
|||
{
|
||||
"valid - solomachine misbehaviour",
|
||||
func() {
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "")
|
||||
soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2)
|
||||
msg, err = types.NewMsgSubmitMisbehaviour(soloMachine.ClientID, soloMachine.CreateMisbehaviour(), suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
@ -465,7 +465,7 @@ func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() {
|
|||
{
|
||||
"client-id mismatch",
|
||||
func() {
|
||||
soloMachineMisbehaviour := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "").CreateMisbehaviour()
|
||||
soloMachineMisbehaviour := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateMisbehaviour()
|
||||
msg, err = types.NewMsgSubmitMisbehaviour("external", soloMachineMisbehaviour, suite.chainA.SenderAccount.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
|
|
|
@ -19,7 +19,7 @@ func (suite *TypesTestSuite) TestNewUpdateClientProposal() {
|
|||
|
||||
func (suite *TypesTestSuite) TestValidateBasic() {
|
||||
// use solo machine header for testing
|
||||
solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, clientID, "")
|
||||
solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, clientID, "", 2)
|
||||
smHeader := solomachine.CreateHeader()
|
||||
header, err := types.PackHeader(smHeader)
|
||||
suite.Require().NoError(err)
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<!--
|
||||
order: 1
|
||||
-->
|
||||
|
||||
# Concepts
|
||||
|
||||
## Proofs
|
||||
|
||||
A solo machine proof should verify that the solomachine public key signed
|
||||
over some specified data. The format for generating marshaled proofs for
|
||||
the SDK's implementation of solo machine is as follows:
|
||||
|
||||
Construct the data using the associated protobuf definition and marshal it.
|
||||
|
||||
For example:
|
||||
```go
|
||||
data := &ClientStateData{
|
||||
Path: []byte(path.String()),
|
||||
ClientState: any,
|
||||
}
|
||||
|
||||
dataBz, err := cdc.MarshalBinaryBare(data)
|
||||
```
|
||||
|
||||
Construct the `SignBytes` and marshal it.
|
||||
|
||||
For example:
|
||||
```go
|
||||
signBytes := &SignBytes{
|
||||
Sequence: sequence,
|
||||
Timestamp: timestamp,
|
||||
Diversifier: diversifier,
|
||||
Data: dataBz,
|
||||
}
|
||||
|
||||
signBz, err := cdc.MarshalBinaryBare(signBytes)
|
||||
```
|
||||
|
||||
The helper functions in [proofs.go](../types/proofs.go) handle the above actions.
|
||||
|
||||
Sign the sign bytes. Embed the signatures into either `SingleSignatureData` or
|
||||
`MultiSignatureData`. Convert the `SignatureData` to proto and marshal it.
|
||||
|
||||
For example:
|
||||
```go
|
||||
sig, err := key.Sign(signBz)
|
||||
sigData := &signing.SingleSignatureData{
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
protoSigData := signing.SignatureDataToProto(sigData)
|
||||
bz, err := cdc.MarshalBinaryBare(protoSigData)
|
||||
```
|
||||
|
||||
Construct a `TimestampedSignature` and marshal it. The marshaled result can be
|
||||
passed in as the proof parameter to the verification functions.
|
||||
|
||||
For example:
|
||||
```go
|
||||
timestampedSignature := &types.TimestampedSignature{
|
||||
Signature: sig,
|
||||
Timestamp: solomachine.Time,
|
||||
}
|
||||
|
||||
proof, err := cdc.MarshalBinaryBare(timestampedSignature)
|
||||
```
|
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
order: 0
|
||||
title: Solo Machine Client
|
||||
parent:
|
||||
title: "solomachine"
|
||||
-->
|
||||
|
||||
# `solomachine`
|
||||
|
||||
## Abstract
|
||||
|
||||
This paper defines the implementation of the ICS06 protocol on the Cosmos SDK.
|
||||
|
||||
For the general specification please refer to the [ICS06 Specification](https://github.com/cosmos/ics/tree/master/spec/ics-006-solo-machine-client).
|
||||
|
||||
## Contents
|
||||
|
||||
1. **[Concepts](01_concepts.md)**
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
|
||||
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
|
||||
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
|
||||
|
@ -79,7 +80,7 @@ func (cs ClientState) VerifyClientState(
|
|||
proof []byte,
|
||||
clientState exported.ClientState,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -90,17 +91,17 @@ func (cs ClientState) VerifyClientState(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := ClientStateSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path, clientState)
|
||||
signBz, err := ClientStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, clientState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -118,7 +119,7 @@ func (cs ClientState) VerifyClientConsensusState(
|
|||
proof []byte,
|
||||
consensusState exported.ConsensusState,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -129,17 +130,17 @@ func (cs ClientState) VerifyClientConsensusState(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := ConsensusStateSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path, consensusState)
|
||||
signBz, err := ConsensusStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, consensusState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -155,7 +156,7 @@ func (cs ClientState) VerifyConnectionState(
|
|||
connectionID string,
|
||||
connectionEnd exported.ConnectionI,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -165,17 +166,17 @@ func (cs ClientState) VerifyConnectionState(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := ConnectionStateSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path, connectionEnd)
|
||||
signBz, err := ConnectionStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, connectionEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -192,7 +193,7 @@ func (cs ClientState) VerifyChannelState(
|
|||
channelID string,
|
||||
channel exported.ChannelI,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -202,17 +203,17 @@ func (cs ClientState) VerifyChannelState(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := ChannelStateSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path, channel)
|
||||
signBz, err := ChannelStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, channel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -230,7 +231,7 @@ func (cs ClientState) VerifyPacketCommitment(
|
|||
packetSequence uint64,
|
||||
commitmentBytes []byte,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -240,17 +241,17 @@ func (cs ClientState) VerifyPacketCommitment(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := PacketCommitmentSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path, commitmentBytes)
|
||||
signBz, err := PacketCommitmentSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, commitmentBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -268,7 +269,7 @@ func (cs ClientState) VerifyPacketAcknowledgement(
|
|||
packetSequence uint64,
|
||||
acknowledgement []byte,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -278,17 +279,17 @@ func (cs ClientState) VerifyPacketAcknowledgement(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := PacketAcknowledgementSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path, acknowledgement)
|
||||
signBz, err := PacketAcknowledgementSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, acknowledgement)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -306,7 +307,7 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence(
|
|||
channelID string,
|
||||
packetSequence uint64,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -316,17 +317,17 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := PacketAcknowledgementAbsenceSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path)
|
||||
signBz, err := PacketAcknowledgementAbsenceSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -343,7 +344,7 @@ func (cs ClientState) VerifyNextSequenceRecv(
|
|||
channelID string,
|
||||
nextSequenceRecv uint64,
|
||||
) error {
|
||||
signature, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -353,17 +354,17 @@ func (cs ClientState) VerifyNextSequenceRecv(
|
|||
return err
|
||||
}
|
||||
|
||||
signBz, err := NextSequenceRecvSignBytes(cdc, sequence, signature.Timestamp, cs.ConsensusState.Diversifier, path, nextSequenceRecv)
|
||||
signBz, err := NextSequenceRecvSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, nextSequenceRecv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, signature.Signature); err != nil {
|
||||
if err := VerifySignature(cs.ConsensusState.GetPubKey(), signBz, sigData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Sequence++
|
||||
cs.ConsensusState.Timestamp = signature.Timestamp
|
||||
cs.ConsensusState.Timestamp = timestamp
|
||||
setClientState(store, cdc, &cs)
|
||||
return nil
|
||||
}
|
||||
|
@ -378,50 +379,62 @@ func produceVerificationArgs(
|
|||
height exported.Height,
|
||||
prefix exported.Prefix,
|
||||
proof []byte,
|
||||
) (signature TimestampedSignature, sequence uint64, err error) {
|
||||
) (signing.SignatureData, uint64, uint64, error) {
|
||||
if epoch := height.GetEpochNumber(); epoch != 0 {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "epoch must be 0 for solomachine, got epoch-number: %d", epoch)
|
||||
return nil, 0, 0, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "epoch must be 0 for solomachine, got epoch-number: %d", epoch)
|
||||
}
|
||||
// sequence is encoded in the epoch height of height struct
|
||||
sequence = height.GetEpochHeight()
|
||||
sequence := height.GetEpochHeight()
|
||||
if cs.IsFrozen() {
|
||||
return TimestampedSignature{}, 0, clienttypes.ErrClientFrozen
|
||||
return nil, 0, 0, clienttypes.ErrClientFrozen
|
||||
}
|
||||
|
||||
if prefix == nil {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty")
|
||||
return nil, 0, 0, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty")
|
||||
}
|
||||
|
||||
_, ok := prefix.(commitmenttypes.MerklePrefix)
|
||||
if !ok {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected MerklePrefix", prefix)
|
||||
return nil, 0, 0, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected MerklePrefix", prefix)
|
||||
}
|
||||
|
||||
if proof == nil {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "proof cannot be empty")
|
||||
return nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "proof cannot be empty")
|
||||
}
|
||||
|
||||
if err = cdc.UnmarshalBinaryBare(proof, &signature); err != nil {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrapf(ErrInvalidProof, "failed to unmarshal proof into type %T", TimestampedSignature{})
|
||||
timestampedSignature := &TimestampedSignature{}
|
||||
if err := cdc.UnmarshalBinaryBare(proof, timestampedSignature); err != nil {
|
||||
return nil, 0, 0, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", timestampedSignature)
|
||||
}
|
||||
|
||||
timestamp := timestampedSignature.Timestamp
|
||||
|
||||
if len(timestampedSignature.Signature) == 0 {
|
||||
return nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "signature data cannot be empty")
|
||||
}
|
||||
|
||||
sigData, err := UnmarshalSignatureData(cdc, timestampedSignature.Signature)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
if cs.ConsensusState == nil {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty")
|
||||
return nil, 0, 0, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty")
|
||||
}
|
||||
|
||||
latestSequence := cs.GetLatestHeight().GetEpochHeight()
|
||||
if latestSequence < sequence {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrapf(
|
||||
return nil, 0, 0, sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInvalidHeight,
|
||||
"client state sequence < proof sequence (%d < %d)", latestSequence, sequence,
|
||||
)
|
||||
}
|
||||
|
||||
if cs.ConsensusState.GetTimestamp() > signature.Timestamp {
|
||||
return TimestampedSignature{}, 0, sdkerrors.Wrapf(ErrInvalidProof, "the consensus state timestamp is greater than the signature timestamp (%d >= %d)", cs.ConsensusState.GetTimestamp(), signature.Timestamp)
|
||||
if cs.ConsensusState.GetTimestamp() > timestamp {
|
||||
return nil, 0, 0, sdkerrors.Wrapf(ErrInvalidProof, "the consensus state timestamp is greater than the signature timestamp (%d >= %d)", cs.ConsensusState.GetTimestamp(), timestamp)
|
||||
}
|
||||
|
||||
return signature, sequence, nil
|
||||
return sigData, timestamp, sequence, nil
|
||||
}
|
||||
|
||||
// sets the client state to the store
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,8 @@ package types
|
|||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
|
||||
)
|
||||
|
||||
|
@ -29,9 +31,20 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
|
|||
|
||||
var (
|
||||
// SubModuleCdc references the global x/ibc/light-clients/solomachine module codec. Note, the codec
|
||||
// should ONLY be used in certain instances of tests and for JSON encoding..
|
||||
// should ONLY be used in certain instances of tests and for JSON encoding.
|
||||
//
|
||||
// The actual codec used for serialization should be provided to x/ibc/light-clients/solomachine and
|
||||
// defined at the application level.
|
||||
SubModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
|
||||
)
|
||||
|
||||
func UnmarshalSignatureData(cdc codec.BinaryMarshaler, data []byte) (signing.SignatureData, error) {
|
||||
protoSigData := &signing.SignatureDescriptor_Data{}
|
||||
if err := cdc.UnmarshalBinaryBare(data, protoSigData); err != nil {
|
||||
return nil, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", protoSigData)
|
||||
}
|
||||
|
||||
sigData := signing.SignatureDataFromProto(protoSigData)
|
||||
|
||||
return sigData, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package types_test
|
|||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
|
||||
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
|
||||
)
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestConsensusState() {
|
||||
|
@ -13,57 +14,61 @@ func (suite *SoloMachineTestSuite) TestConsensusState() {
|
|||
}
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestConsensusStateValidateBasic() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
consensusState *types.ConsensusState
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid consensus state",
|
||||
suite.solomachine.ConsensusState(),
|
||||
true,
|
||||
},
|
||||
{
|
||||
"timestamp is zero",
|
||||
&types.ConsensusState{
|
||||
PublicKey: suite.solomachine.ConsensusState().PublicKey,
|
||||
Timestamp: 0,
|
||||
Diversifier: suite.solomachine.Diversifier,
|
||||
// test singlesig and multisig public keys
|
||||
for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} {
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
consensusState *types.ConsensusState
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid consensus state",
|
||||
solomachine.ConsensusState(),
|
||||
true,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"diversifier is blank",
|
||||
&types.ConsensusState{
|
||||
PublicKey: suite.solomachine.ConsensusState().PublicKey,
|
||||
Timestamp: suite.solomachine.Time,
|
||||
Diversifier: " ",
|
||||
{
|
||||
"timestamp is zero",
|
||||
&types.ConsensusState{
|
||||
PublicKey: solomachine.ConsensusState().PublicKey,
|
||||
Timestamp: 0,
|
||||
Diversifier: solomachine.Diversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"pubkey is nil",
|
||||
&types.ConsensusState{
|
||||
Timestamp: suite.solomachine.Time,
|
||||
Diversifier: suite.solomachine.Diversifier,
|
||||
PublicKey: nil,
|
||||
{
|
||||
"diversifier is blank",
|
||||
&types.ConsensusState{
|
||||
PublicKey: solomachine.ConsensusState().PublicKey,
|
||||
Timestamp: solomachine.Time,
|
||||
Diversifier: " ",
|
||||
},
|
||||
false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
{
|
||||
"pubkey is nil",
|
||||
&types.ConsensusState{
|
||||
Timestamp: solomachine.Time,
|
||||
Diversifier: solomachine.Diversifier,
|
||||
PublicKey: nil,
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
suite.Run(tc.name, func() {
|
||||
|
||||
err := tc.consensusState.ValidateBasic()
|
||||
err := tc.consensusState.ValidateBasic()
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,91 +2,96 @@ package types_test
|
|||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
|
||||
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
|
||||
)
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() {
|
||||
header := suite.solomachine.CreateHeader()
|
||||
// test singlesig and multisig public keys
|
||||
for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} {
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
header *types.Header
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid header",
|
||||
header,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"sequence is zero",
|
||||
&types.Header{
|
||||
Sequence: 0,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"timestamp is zero",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: 0,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature is empty",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: []byte{},
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"diversifier contains only spaces",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: " ",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"public key is nil",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: nil,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
header := solomachine.CreateHeader()
|
||||
|
||||
suite.Require().Equal(types.SoloMachine, header.ClientType())
|
||||
cases := []struct {
|
||||
name string
|
||||
header *types.Header
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid header",
|
||||
header,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"sequence is zero",
|
||||
&types.Header{
|
||||
Sequence: 0,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"timestamp is zero",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: 0,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature is empty",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: []byte{},
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"diversifier contains only spaces",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: header.NewPublicKey,
|
||||
NewDiversifier: " ",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"public key is nil",
|
||||
&types.Header{
|
||||
Sequence: header.Sequence,
|
||||
Timestamp: header.Timestamp,
|
||||
Signature: header.Signature,
|
||||
NewPublicKey: nil,
|
||||
NewDiversifier: header.NewDiversifier,
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
suite.Require().Equal(types.SoloMachine, header.ClientType())
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
err := tc.header.ValidateBasic()
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
suite.Run(tc.name, func() {
|
||||
err := tc.header.ValidateBasic()
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,8 +56,13 @@ func checkMisbehaviour(cdc codec.BinaryMarshaler, clientState ClientState, soloM
|
|||
return err
|
||||
}
|
||||
|
||||
sigData, err := UnmarshalSignatureData(cdc, soloMisbehaviour.SignatureOne.Signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check first signature
|
||||
if err := VerifySignature(pubKey, data, soloMisbehaviour.SignatureOne.Signature); err != nil {
|
||||
if err := VerifySignature(pubKey, data, sigData); err != nil {
|
||||
return sdkerrors.Wrap(err, "misbehaviour signature one failed to be verified")
|
||||
}
|
||||
|
||||
|
@ -71,8 +76,13 @@ func checkMisbehaviour(cdc codec.BinaryMarshaler, clientState ClientState, soloM
|
|||
return err
|
||||
}
|
||||
|
||||
sigData, err = UnmarshalSignatureData(cdc, soloMisbehaviour.SignatureTwo.Signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check second signature
|
||||
if err := VerifySignature(pubKey, data, soloMisbehaviour.SignatureTwo.Signature); err != nil {
|
||||
if err := VerifySignature(pubKey, data, sigData); err != nil {
|
||||
return sdkerrors.Wrap(err, "misbehaviour signature two failed to be verified")
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
|
||||
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
|
||||
)
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() {
|
||||
|
@ -12,162 +13,186 @@ func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() {
|
|||
misbehaviour exported.Misbehaviour
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
setup func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid misbehaviour",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
misbehaviour = suite.solomachine.CreateMisbehaviour()
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"client is frozen",
|
||||
func() {
|
||||
cs := suite.solomachine.ClientState()
|
||||
cs.FrozenSequence = 1
|
||||
clientState = cs
|
||||
misbehaviour = suite.solomachine.CreateMisbehaviour()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong client state type",
|
||||
func() {
|
||||
clientState = &ibctmtypes.ClientState{}
|
||||
misbehaviour = suite.solomachine.CreateMisbehaviour()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid misbehaviour type",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
misbehaviour = ibctmtypes.Misbehaviour{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid first signature",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
// test singlesig and multisig public keys
|
||||
for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} {
|
||||
|
||||
// store in temp before assigning to interface type
|
||||
m := suite.solomachine.CreateMisbehaviour()
|
||||
testCases := []struct {
|
||||
name string
|
||||
setup func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid misbehaviour",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
misbehaviour = solomachine.CreateMisbehaviour()
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"client is frozen",
|
||||
func() {
|
||||
cs := solomachine.ClientState()
|
||||
cs.FrozenSequence = 1
|
||||
clientState = cs
|
||||
misbehaviour = solomachine.CreateMisbehaviour()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong client state type",
|
||||
func() {
|
||||
clientState = &ibctmtypes.ClientState{}
|
||||
misbehaviour = solomachine.CreateMisbehaviour()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid misbehaviour type",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
misbehaviour = ibctmtypes.Misbehaviour{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid SignatureOne signature",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
m := solomachine.CreateMisbehaviour()
|
||||
|
||||
msg := []byte("DATA ONE")
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: suite.solomachine.Sequence + 1,
|
||||
Data: msg,
|
||||
m.SignatureOne.Signature = suite.GetInvalidProof()
|
||||
misbehaviour = m
|
||||
}, false,
|
||||
},
|
||||
{
|
||||
"invalid SignatureTwo signature",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
m := solomachine.CreateMisbehaviour()
|
||||
|
||||
m.SignatureTwo.Signature = suite.GetInvalidProof()
|
||||
misbehaviour = m
|
||||
}, false,
|
||||
},
|
||||
{
|
||||
"invalid first signature",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
|
||||
// store in temp before assigning to interface type
|
||||
m := solomachine.CreateMisbehaviour()
|
||||
|
||||
msg := []byte("DATA ONE")
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: solomachine.Sequence + 1,
|
||||
Timestamp: solomachine.Time,
|
||||
Diversifier: solomachine.Diversifier,
|
||||
Data: msg,
|
||||
}
|
||||
|
||||
data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig := solomachine.GenerateSignature(data)
|
||||
|
||||
m.SignatureOne.Signature = sig
|
||||
m.SignatureOne.Data = msg
|
||||
misbehaviour = m
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid second signature",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
|
||||
// store in temp before assigning to interface type
|
||||
m := solomachine.CreateMisbehaviour()
|
||||
|
||||
msg := []byte("DATA TWO")
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: solomachine.Sequence + 1,
|
||||
Timestamp: solomachine.Time,
|
||||
Diversifier: solomachine.Diversifier,
|
||||
Data: msg,
|
||||
}
|
||||
|
||||
data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig := solomachine.GenerateSignature(data)
|
||||
|
||||
m.SignatureTwo.Signature = sig
|
||||
m.SignatureTwo.Data = msg
|
||||
misbehaviour = m
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signatures sign over different sequence",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
|
||||
// store in temp before assigning to interface type
|
||||
m := solomachine.CreateMisbehaviour()
|
||||
|
||||
// Signature One
|
||||
msg := []byte("DATA ONE")
|
||||
// sequence used is plus 1
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: solomachine.Sequence + 1,
|
||||
Data: msg,
|
||||
}
|
||||
|
||||
data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig := solomachine.GenerateSignature(data)
|
||||
|
||||
m.SignatureOne.Signature = sig
|
||||
m.SignatureOne.Data = msg
|
||||
|
||||
// Signature Two
|
||||
msg = []byte("DATA TWO")
|
||||
// sequence used is minus 1
|
||||
|
||||
signBytes = &types.SignBytes{
|
||||
Sequence: solomachine.Sequence - 1,
|
||||
Data: msg,
|
||||
}
|
||||
data, err = suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig = solomachine.GenerateSignature(data)
|
||||
|
||||
m.SignatureTwo.Signature = sig
|
||||
m.SignatureTwo.Data = msg
|
||||
|
||||
misbehaviour = m
|
||||
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
// setup test
|
||||
tc.setup()
|
||||
|
||||
clientState, err := clientState.CheckMisbehaviourAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.store, misbehaviour)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().True(clientState.IsFrozen(), "client not frozen")
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(clientState)
|
||||
}
|
||||
|
||||
data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig, err := suite.solomachine.PrivateKey.Sign(data)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
m.SignatureOne.Signature = sig
|
||||
m.SignatureOne.Data = msg
|
||||
misbehaviour = m
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid second signature",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
|
||||
// store in temp before assigning to interface type
|
||||
m := suite.solomachine.CreateMisbehaviour()
|
||||
|
||||
msg := []byte("DATA TWO")
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: suite.solomachine.Sequence + 1,
|
||||
Data: msg,
|
||||
}
|
||||
|
||||
data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig, err := suite.solomachine.PrivateKey.Sign(data)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
m.SignatureTwo.Signature = sig
|
||||
m.SignatureTwo.Data = msg
|
||||
misbehaviour = m
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signatures sign over different sequence",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
|
||||
// store in temp before assigning to interface type
|
||||
m := suite.solomachine.CreateMisbehaviour()
|
||||
|
||||
// Signature One
|
||||
msg := []byte("DATA ONE")
|
||||
// sequence used is plus 1
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: suite.solomachine.Sequence + 1,
|
||||
Data: msg,
|
||||
}
|
||||
|
||||
data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig, err := suite.solomachine.PrivateKey.Sign(data)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
m.SignatureOne.Signature = sig
|
||||
m.SignatureOne.Data = msg
|
||||
|
||||
// Signature Two
|
||||
msg = []byte("DATA TWO")
|
||||
// sequence used is minus 1
|
||||
|
||||
signBytes = &types.SignBytes{
|
||||
Sequence: suite.solomachine.Sequence - 1,
|
||||
Data: msg,
|
||||
}
|
||||
data, err = suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig, err = suite.solomachine.PrivateKey.Sign(data)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
m.SignatureTwo.Signature = sig
|
||||
m.SignatureTwo.Data = msg
|
||||
|
||||
misbehaviour = m
|
||||
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
// setup test
|
||||
tc.setup()
|
||||
|
||||
clientState, err := clientState.CheckMisbehaviourAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.store, misbehaviour)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().True(clientState.IsFrozen(), "client not frozen")
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(clientState)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package types_test
|
|||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
|
||||
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
|
||||
)
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestMisbehaviour() {
|
||||
|
@ -14,89 +15,93 @@ func (suite *SoloMachineTestSuite) TestMisbehaviour() {
|
|||
}
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleateMisbehaviour func(misbehaviour *types.Misbehaviour)
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid misbehaviour",
|
||||
func(*types.Misbehaviour) {},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid client ID",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.ClientId = "(badclientid)"
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sequence is zero",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.Sequence = 0
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature one sig is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureOne.Signature = []byte{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature two sig is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Signature = []byte{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature one data is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureOne.Data = nil
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature two data is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Data = []byte{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signatures are identical",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Signature = misbehaviour.SignatureOne.Signature
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"data signed is identical",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
// test singlesig and multisig public keys
|
||||
for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} {
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleateMisbehaviour func(misbehaviour *types.Misbehaviour)
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid misbehaviour",
|
||||
func(*types.Misbehaviour) {},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid client ID",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.ClientId = "(badclientid)"
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sequence is zero",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.Sequence = 0
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature one sig is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureOne.Signature = []byte{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature two sig is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Signature = []byte{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature one data is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureOne.Data = nil
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature two data is empty",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Data = []byte{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signatures are identical",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Signature = misbehaviour.SignatureOne.Signature
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"data signed is identical",
|
||||
func(misbehaviour *types.Misbehaviour) {
|
||||
misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
misbehaviour := suite.solomachine.CreateMisbehaviour()
|
||||
tc.malleateMisbehaviour(misbehaviour)
|
||||
suite.Run(tc.name, func() {
|
||||
|
||||
err := misbehaviour.ValidateBasic()
|
||||
misbehaviour := solomachine.CreateMisbehaviour()
|
||||
tc.malleateMisbehaviour(misbehaviour)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
err := misbehaviour.ValidateBasic()
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
|
||||
connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
|
||||
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
|
||||
|
@ -13,10 +15,33 @@ import (
|
|||
)
|
||||
|
||||
// VerifySignature verifies if the the provided public key generated the signature
|
||||
// over the given data.
|
||||
func VerifySignature(pubKey crypto.PubKey, data, signature []byte) error {
|
||||
if !pubKey.VerifySignature(data, signature) {
|
||||
return ErrSignatureVerificationFailed
|
||||
// over the given data. Single and Multi signature public keys are supported.
|
||||
// The type of the signature data determines how the public key is used to
|
||||
// verify the signature. An error is returned if signature verification fails
|
||||
// or an invalid SignatureData type is provided.
|
||||
func VerifySignature(pubKey crypto.PubKey, signBytes []byte, sigData signing.SignatureData) error {
|
||||
switch data := sigData.(type) {
|
||||
case *signing.SingleSignatureData:
|
||||
if !pubKey.VerifySignature(signBytes, data.Signature) {
|
||||
return ErrSignatureVerificationFailed
|
||||
}
|
||||
|
||||
case *signing.MultiSignatureData:
|
||||
multiPK, ok := pubKey.(multisig.PubKey)
|
||||
if !ok {
|
||||
return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid pubkey type: expected %T, got %T", (multisig.PubKey)(nil), pubKey)
|
||||
}
|
||||
|
||||
// The function supplied fulfills the VerifyMultisignature interface. No special
|
||||
// adjustments need to be made to the sign bytes based on the sign mode.
|
||||
if err := multiPK.VerifyMultisignature(func(signing.SignMode) ([]byte, error) {
|
||||
return signBytes, nil
|
||||
}, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "unsupported signature data type %T", data)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -4,77 +4,81 @@ import (
|
|||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
|
||||
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
|
||||
)
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestCheckProposedHeaderAndUpdateState() {
|
||||
var header exported.Header
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid header", func() {
|
||||
header = suite.solomachine.CreateHeader()
|
||||
}, true,
|
||||
},
|
||||
{
|
||||
"nil header", func() {
|
||||
header = &ibctmtypes.Header{}
|
||||
}, false,
|
||||
},
|
||||
{
|
||||
"header does not update public key", func() {
|
||||
header = &types.Header{
|
||||
Sequence: 1,
|
||||
NewPublicKey: suite.solomachine.ConsensusState().PublicKey,
|
||||
}
|
||||
}, false,
|
||||
},
|
||||
}
|
||||
// test singlesig and multisig public keys
|
||||
for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} {
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"valid header", func() {
|
||||
header = solomachine.CreateHeader()
|
||||
}, true,
|
||||
},
|
||||
{
|
||||
"nil header", func() {
|
||||
header = &ibctmtypes.Header{}
|
||||
}, false,
|
||||
},
|
||||
{
|
||||
"header does not update public key", func() {
|
||||
header = &types.Header{
|
||||
Sequence: 1,
|
||||
NewPublicKey: solomachine.ConsensusState().PublicKey,
|
||||
}
|
||||
}, false,
|
||||
},
|
||||
}
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest()
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
clientState := suite.solomachine.ClientState()
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest()
|
||||
|
||||
tc.malleate()
|
||||
clientState := solomachine.ClientState()
|
||||
|
||||
clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), suite.solomachine.ClientID)
|
||||
tc.malleate()
|
||||
|
||||
// all cases should always fail if the client has 'AllowUpdateAfterProposal' set to false
|
||||
clientState.AllowUpdateAfterProposal = false
|
||||
cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header)
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(cs)
|
||||
suite.Require().Nil(consState)
|
||||
clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), solomachine.ClientID)
|
||||
|
||||
clientState.AllowUpdateAfterProposal = true
|
||||
cs, consState, err = clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
|
||||
smConsState, ok := consState.(*types.ConsensusState)
|
||||
suite.Require().True(ok)
|
||||
smHeader, ok := header.(*types.Header)
|
||||
suite.Require().True(ok)
|
||||
|
||||
suite.Require().Equal(cs.(*types.ClientState).ConsensusState, consState)
|
||||
suite.Require().Equal(smHeader.GetPubKey(), smConsState.GetPubKey())
|
||||
suite.Require().Equal(smHeader.NewDiversifier, smConsState.Diversifier)
|
||||
suite.Require().Equal(smHeader.Timestamp, smConsState.Timestamp)
|
||||
suite.Require().Equal(smHeader.GetHeight().GetEpochHeight(), cs.(*types.ClientState).Sequence)
|
||||
} else {
|
||||
// all cases should always fail if the client has 'AllowUpdateAfterProposal' set to false
|
||||
clientState.AllowUpdateAfterProposal = false
|
||||
cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header)
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(cs)
|
||||
suite.Require().Nil(consState)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
clientState.AllowUpdateAfterProposal = true
|
||||
cs, consState, err = clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
|
||||
smConsState, ok := consState.(*types.ConsensusState)
|
||||
suite.Require().True(ok)
|
||||
smHeader, ok := header.(*types.Header)
|
||||
suite.Require().True(ok)
|
||||
|
||||
suite.Require().Equal(cs.(*types.ClientState).ConsensusState, consState)
|
||||
suite.Require().Equal(smHeader.GetPubKey(), smConsState.GetPubKey())
|
||||
suite.Require().Equal(smHeader.NewDiversifier, smConsState.Diversifier)
|
||||
suite.Require().Equal(smHeader.Timestamp, smConsState.Timestamp)
|
||||
suite.Require().Equal(smHeader.GetHeight().GetEpochHeight(), cs.(*types.ClientState).Sequence)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(cs)
|
||||
suite.Require().Nil(consState)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@ import (
|
|||
type SoloMachineTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
solomachine *ibctesting.Solomachine
|
||||
coordinator *ibctesting.Coordinator
|
||||
solomachine *ibctesting.Solomachine // singlesig public key
|
||||
solomachineMulti *ibctesting.Solomachine // multisig public key
|
||||
coordinator *ibctesting.Coordinator
|
||||
|
||||
// testing chain used for convenience and readability
|
||||
chainA *ibctesting.TestChain
|
||||
|
@ -37,12 +38,10 @@ func (suite *SoloMachineTestSuite) SetupTest() {
|
|||
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0))
|
||||
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1))
|
||||
|
||||
suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "testingsolomachine", "testing")
|
||||
suite.store = suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), types.SoloMachine)
|
||||
suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1)
|
||||
suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinemulti", "testing", 4)
|
||||
|
||||
bz, err := codec.MarshalAny(suite.chainA.Codec, suite.solomachine.ClientState())
|
||||
suite.Require().NoError(err)
|
||||
suite.store.Set(host.KeyClientState(), bz)
|
||||
suite.store = suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), types.SoloMachine)
|
||||
}
|
||||
|
||||
func TestSoloMachineTestSuite(t *testing.T) {
|
||||
|
|
|
@ -57,7 +57,12 @@ func checkHeader(cdc codec.BinaryMarshaler, clientState *ClientState, header *He
|
|||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(clientState.ConsensusState.GetPubKey(), data, header.Signature); err != nil {
|
||||
sigData, err := UnmarshalSignatureData(cdc, header.Signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := VerifySignature(clientState.ConsensusState.GetPubKey(), data, sigData); err != nil {
|
||||
return sdkerrors.Wrap(ErrInvalidHeader, err.Error())
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/light-clients/solomachine/types"
|
||||
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
|
||||
)
|
||||
|
||||
func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() {
|
||||
|
@ -14,144 +15,156 @@ func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() {
|
|||
header exported.Header
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
setup func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"successful update",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
header = suite.solomachine.CreateHeader()
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"wrong client state type",
|
||||
func() {
|
||||
clientState = &ibctmtypes.ClientState{}
|
||||
header = suite.solomachine.CreateHeader()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid header type",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
header = &ibctmtypes.Header{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong sequence in header",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
// store in temp before assigning to interface type
|
||||
h := suite.solomachine.CreateHeader()
|
||||
h.Sequence++
|
||||
header = h
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid timestamp in header",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
h := suite.solomachine.CreateHeader()
|
||||
h.Timestamp--
|
||||
header = h
|
||||
}, false,
|
||||
},
|
||||
{
|
||||
"signature uses wrong sequence",
|
||||
func() {
|
||||
clientState = suite.solomachine.ClientState()
|
||||
suite.solomachine.Sequence++
|
||||
header = suite.solomachine.CreateHeader()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature uses new pubkey to sign",
|
||||
func() {
|
||||
// store in temp before assinging to interface type
|
||||
cs := suite.solomachine.ClientState()
|
||||
h := suite.solomachine.CreateHeader()
|
||||
// test singlesig and multisig public keys
|
||||
for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} {
|
||||
|
||||
publicKey, err := tx.PubKeyToAny(suite.solomachine.PublicKey)
|
||||
suite.NoError(err)
|
||||
testCases := []struct {
|
||||
name string
|
||||
setup func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"successful update",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
header = solomachine.CreateHeader()
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"wrong client state type",
|
||||
func() {
|
||||
clientState = &ibctmtypes.ClientState{}
|
||||
header = solomachine.CreateHeader()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid header type",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
header = &ibctmtypes.Header{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong sequence in header",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
// store in temp before assigning to interface type
|
||||
h := solomachine.CreateHeader()
|
||||
h.Sequence++
|
||||
header = h
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid header Signature",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
h := solomachine.CreateHeader()
|
||||
h.Signature = suite.GetInvalidProof()
|
||||
header = h
|
||||
}, false,
|
||||
},
|
||||
{
|
||||
"invalid timestamp in header",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
h := solomachine.CreateHeader()
|
||||
h.Timestamp--
|
||||
header = h
|
||||
}, false,
|
||||
},
|
||||
{
|
||||
"signature uses wrong sequence",
|
||||
func() {
|
||||
clientState = solomachine.ClientState()
|
||||
solomachine.Sequence++
|
||||
header = solomachine.CreateHeader()
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature uses new pubkey to sign",
|
||||
func() {
|
||||
// store in temp before assinging to interface type
|
||||
cs := solomachine.ClientState()
|
||||
h := solomachine.CreateHeader()
|
||||
|
||||
data := &types.HeaderData{
|
||||
NewPubKey: publicKey,
|
||||
NewDiversifier: h.NewDiversifier,
|
||||
publicKey, err := tx.PubKeyToAny(solomachine.PublicKey)
|
||||
suite.NoError(err)
|
||||
|
||||
data := &types.HeaderData{
|
||||
NewPubKey: publicKey,
|
||||
NewDiversifier: h.NewDiversifier,
|
||||
}
|
||||
|
||||
dataBz, err := suite.chainA.Codec.MarshalBinaryBare(data)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// generate invalid signature
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: cs.Sequence,
|
||||
Timestamp: solomachine.Time,
|
||||
Diversifier: solomachine.Diversifier,
|
||||
Data: dataBz,
|
||||
}
|
||||
|
||||
signBz, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig := solomachine.GenerateSignature(signBz)
|
||||
suite.Require().NoError(err)
|
||||
h.Signature = sig
|
||||
|
||||
clientState = cs
|
||||
header = h
|
||||
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature signs over old pubkey",
|
||||
func() {
|
||||
// store in temp before assinging to interface type
|
||||
cs := solomachine.ClientState()
|
||||
oldPubKey := solomachine.PublicKey
|
||||
h := solomachine.CreateHeader()
|
||||
|
||||
// generate invalid signature
|
||||
data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...)
|
||||
sig := solomachine.GenerateSignature(data)
|
||||
h.Signature = sig
|
||||
|
||||
clientState = cs
|
||||
header = h
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
// setup test
|
||||
tc.setup()
|
||||
|
||||
clientState, consensusState, err := clientState.CheckHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, header)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(header.(*types.Header).NewPublicKey, clientState.(*types.ClientState).ConsensusState.PublicKey)
|
||||
suite.Require().Equal(uint64(0), clientState.(*types.ClientState).FrozenSequence)
|
||||
suite.Require().Equal(header.(*types.Header).Sequence+1, clientState.(*types.ClientState).Sequence)
|
||||
suite.Require().Equal(consensusState, clientState.(*types.ClientState).ConsensusState)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(clientState)
|
||||
suite.Require().Nil(consensusState)
|
||||
}
|
||||
|
||||
dataBz, err := suite.chainA.Codec.MarshalBinaryBare(data)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// generate invalid signature
|
||||
signBytes := &types.SignBytes{
|
||||
Sequence: cs.Sequence,
|
||||
Timestamp: suite.solomachine.Time,
|
||||
Diversifier: suite.solomachine.Diversifier,
|
||||
Data: dataBz,
|
||||
}
|
||||
|
||||
signBz, err := suite.chainA.Codec.MarshalBinaryBare(signBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
sig, err := suite.solomachine.PrivateKey.Sign(signBz)
|
||||
suite.Require().NoError(err)
|
||||
h.Signature = sig
|
||||
|
||||
clientState = cs
|
||||
header = h
|
||||
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"signature signs over old pubkey",
|
||||
func() {
|
||||
// store in temp before assinging to interface type
|
||||
cs := suite.solomachine.ClientState()
|
||||
oldPrivKey := suite.solomachine.PrivateKey
|
||||
h := suite.solomachine.CreateHeader()
|
||||
|
||||
// generate invalid signature
|
||||
data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPrivKey.PubKey().Bytes()...)
|
||||
sig, err := oldPrivKey.Sign(data)
|
||||
suite.Require().NoError(err)
|
||||
h.Signature = sig
|
||||
|
||||
clientState = cs
|
||||
header = h
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
suite.Run(tc.name, func() {
|
||||
// setup test
|
||||
tc.setup()
|
||||
|
||||
clientState, consensusState, err := clientState.CheckHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, header)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(header.(*types.Header).NewPublicKey, clientState.(*types.ClientState).ConsensusState.PublicKey)
|
||||
suite.Require().Equal(uint64(0), clientState.(*types.ClientState).FrozenSequence)
|
||||
suite.Require().Equal(header.(*types.Header).Sequence+1, clientState.(*types.ClientState).Sequence)
|
||||
suite.Require().Equal(consensusState, clientState.(*types.ClientState).ConsensusState)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(clientState)
|
||||
suite.Require().Nil(consensusState)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -396,7 +396,7 @@ func (chain *TestChain) ConstructMsgCreateClient(counterparty *TestChain, client
|
|||
)
|
||||
consensusState = counterparty.LastHeader.ConsensusState()
|
||||
case SoloMachine:
|
||||
solo := NewSolomachine(chain.t, chain.Codec, clientID, "")
|
||||
solo := NewSolomachine(chain.t, chain.Codec, clientID, "", 1)
|
||||
clientState = solo.ClientState()
|
||||
consensusState = solo.ConsensusState()
|
||||
default:
|
||||
|
|
|
@ -7,7 +7,10 @@ import (
|
|||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
|
||||
|
@ -21,30 +24,62 @@ type Solomachine struct {
|
|||
|
||||
cdc codec.BinaryMarshaler
|
||||
ClientID string
|
||||
PrivateKey crypto.PrivKey
|
||||
PublicKey crypto.PubKey
|
||||
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
|
||||
Sequence uint64
|
||||
Time uint64
|
||||
Diversifier string
|
||||
}
|
||||
|
||||
// NewSolomachine returns a new solomachine instance with a generated private/public
|
||||
// key pair and a sequence starting at 1.
|
||||
func NewSolomachine(t *testing.T, cdc codec.BinaryMarshaler, clientID, diversifier string) *Solomachine {
|
||||
privKey := secp256k1.GenPrivKey()
|
||||
// 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)
|
||||
|
||||
return &Solomachine{
|
||||
t: t,
|
||||
cdc: cdc,
|
||||
ClientID: clientID,
|
||||
PrivateKey: privKey,
|
||||
PublicKey: privKey.PubKey(),
|
||||
PrivateKeys: privKeys,
|
||||
PublicKeys: pubKeys,
|
||||
PublicKey: pk,
|
||||
Sequence: 1,
|
||||
Time: 10,
|
||||
Diversifier: diversifier,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ClientState returns a new solo machine ClientState instance. Default usage does not allow update
|
||||
// after governance proposal
|
||||
func (solo *Solomachine) ClientState() *solomachinetypes.ClientState {
|
||||
|
@ -71,10 +106,10 @@ func (solo *Solomachine) GetHeight() exported.Height {
|
|||
// CreateHeader generates a new private/public key pair and creates the
|
||||
// necessary signature to construct a valid solo machine header.
|
||||
func (solo *Solomachine) CreateHeader() *solomachinetypes.Header {
|
||||
// generate new private key and signature for header
|
||||
newPrivKey := secp256k1.GenPrivKey()
|
||||
// generate new private keys and signature for header
|
||||
newPrivKeys, newPubKeys, newPubKey := GenerateKeys(solo.t, uint64(len(solo.PrivateKeys)))
|
||||
|
||||
publicKey, err := tx.PubKeyToAny(newPrivKey.PubKey())
|
||||
publicKey, err := tx.PubKeyToAny(newPubKey)
|
||||
require.NoError(solo.t, err)
|
||||
|
||||
data := &solomachinetypes.HeaderData{
|
||||
|
@ -92,24 +127,24 @@ func (solo *Solomachine) CreateHeader() *solomachinetypes.Header {
|
|||
Data: dataBz,
|
||||
}
|
||||
|
||||
signBz, err := solo.cdc.MarshalBinaryBare(signBytes)
|
||||
bz, err := solo.cdc.MarshalBinaryBare(signBytes)
|
||||
require.NoError(solo.t, err)
|
||||
|
||||
signature, err := solo.PrivateKey.Sign(signBz)
|
||||
require.NoError(solo.t, err)
|
||||
sig := solo.GenerateSignature(bz)
|
||||
|
||||
header := &solomachinetypes.Header{
|
||||
Sequence: solo.Sequence,
|
||||
Timestamp: solo.Time,
|
||||
Signature: signature,
|
||||
Signature: sig,
|
||||
NewPublicKey: publicKey,
|
||||
NewDiversifier: solo.Diversifier,
|
||||
}
|
||||
|
||||
// assumes successful header update
|
||||
solo.Sequence++
|
||||
solo.PrivateKey = newPrivKey
|
||||
solo.PublicKey = newPrivKey.PubKey()
|
||||
solo.PrivateKeys = newPrivKeys
|
||||
solo.PublicKeys = newPubKeys
|
||||
solo.PublicKey = newPubKey
|
||||
|
||||
return header
|
||||
}
|
||||
|
@ -127,12 +162,10 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour {
|
|||
Data: dataOne,
|
||||
}
|
||||
|
||||
signBz, err := solo.cdc.MarshalBinaryBare(signBytes)
|
||||
require.NoError(solo.t, err)
|
||||
|
||||
sig, err := solo.PrivateKey.Sign(signBz)
|
||||
bz, err := solo.cdc.MarshalBinaryBare(signBytes)
|
||||
require.NoError(solo.t, err)
|
||||
|
||||
sig := solo.GenerateSignature(bz)
|
||||
signatureOne := solomachinetypes.SignatureAndData{
|
||||
Signature: sig,
|
||||
Data: dataOne,
|
||||
|
@ -145,12 +178,10 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour {
|
|||
Data: dataTwo,
|
||||
}
|
||||
|
||||
signBz, err = solo.cdc.MarshalBinaryBare(signBytes)
|
||||
require.NoError(solo.t, err)
|
||||
|
||||
sig, err = solo.PrivateKey.Sign(signBz)
|
||||
bz, err = solo.cdc.MarshalBinaryBare(signBytes)
|
||||
require.NoError(solo.t, err)
|
||||
|
||||
sig = solo.GenerateSignature(bz)
|
||||
signatureTwo := solomachinetypes.SignatureAndData{
|
||||
Signature: sig,
|
||||
Data: dataTwo,
|
||||
|
@ -163,3 +194,38 @@ func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour {
|
|||
SignatureTwo: &signatureTwo,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue