Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: 06-solomachine adding CheckForMisbehaviour and UpdateStateOnMisbehaviour #1111

Merged
merged 7 commits into from
Mar 16, 2022
6 changes: 6 additions & 0 deletions modules/light-clients/06-solomachine/types/misbehaviour.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ func (misbehaviour Misbehaviour) Type() string {
return exported.TypeClientMisbehaviour
}

// TODO: Remove GetHeight() when new interface type ClientMessage is introduced
// GetHeight implements the exported.Header interface
func (misbehaviour Misbehaviour) GetHeight() exported.Height {
return nil
}

// ValidateBasic implements Misbehaviour interface.
func (misbehaviour Misbehaviour) ValidateBasic() error {
if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil {
Expand Down
138 changes: 88 additions & 50 deletions modules/light-clients/06-solomachine/types/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,112 @@ import (
// - the currently registered public key did not provide the update signature
func (cs ClientState) CheckHeaderAndUpdateState(
ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore,
header exported.Header,
msg exported.Header, // TODO: Update to exported.ClientMessage
) (exported.ClientState, exported.ConsensusState, error) {
smHeader, ok := header.(*Header)
if !ok {
return nil, nil, sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader, "header type %T, expected %T", header, &Header{},
)
if err := cs.VerifyClientMessage(cdc, msg); err != nil {
return nil, nil, err
}

if err := checkHeader(cdc, &cs, smHeader); err != nil {
return nil, nil, err
foundMisbehaviour := cs.CheckForMisbehaviour(ctx, cdc, clientStore, msg)
if foundMisbehaviour {
return cs.UpdateStateOnMisbehaviour(ctx, cdc, clientStore)
}

clientState, consensusState := update(&cs, smHeader)
return clientState, consensusState, nil
return cs.UpdateState(ctx, cdc, clientStore, msg)
}

// checkHeader checks if the Solo Machine update signature is valid.
func checkHeader(cdc codec.BinaryCodec, clientState *ClientState, header *Header) error {
// assert update sequence is current sequence
if header.Sequence != clientState.Sequence {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header sequence does not match the client state sequence (%d != %d)", header.Sequence, clientState.Sequence,
)
}
// VerifyClientMessage checks if the Solo Machine update signature(s) is valid.
func (cs ClientState) VerifyClientMessage(cdc codec.BinaryCodec, clientMsg exported.Header) error {
switch msg := clientMsg.(type) {
case *Header:
// assert update sequence is current sequence
if msg.Sequence != cs.Sequence {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header sequence does not match the client state sequence (%d != %d)", msg.Sequence, cs.Sequence,
)
}

// assert update timestamp is not less than current consensus state timestamp
if header.Timestamp < clientState.ConsensusState.Timestamp {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, clientState.ConsensusState.Timestamp,
)
}
// assert update timestamp is not less than current consensus state timestamp
if msg.Timestamp < cs.ConsensusState.Timestamp {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header timestamp is less than to the consensus state timestamp (%d < %d)", msg.Timestamp, cs.ConsensusState.Timestamp,
)
}

// assert currently registered public key signed over the new public key with correct sequence
data, err := HeaderSignBytes(cdc, header)
if err != nil {
return err
}
// assert currently registered public key signed over the new public key with correct sequence
data, err := HeaderSignBytes(cdc, msg)
if err != nil {
return err
}

sigData, err := UnmarshalSignatureData(cdc, header.Signature)
if err != nil {
return err
}
sigData, err := UnmarshalSignatureData(cdc, msg.Signature)
if err != nil {
return err
}

publicKey, err := cs.ConsensusState.GetPubKey()
if err != nil {
return err
}

if err := VerifySignature(publicKey, data, sigData); err != nil {
return sdkerrors.Wrap(ErrInvalidHeader, err.Error())
}
case *Misbehaviour:
// NOTE: a check that the misbehaviour message data are not equal is done by
// misbehaviour.ValidateBasic which is called by the 02-client keeper.
// verify first signature
if err := verifySignatureAndData(cdc, cs, msg, msg.SignatureOne); err != nil {
return sdkerrors.Wrap(err, "failed to verify signature one")
}

publicKey, err := clientState.ConsensusState.GetPubKey()
if err != nil {
return err
// verify second signature
if err := verifySignatureAndData(cdc, cs, msg, msg.SignatureTwo); err != nil {
return sdkerrors.Wrap(err, "failed to verify signature two")
}

default:
return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected type %T, got type %T", Header{}, msg)
}
return nil
}

if err := VerifySignature(publicKey, data, sigData); err != nil {
return sdkerrors.Wrap(ErrInvalidHeader, err.Error())
// CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false
func (cs ClientState) CheckForMisbehaviour(
damiannolan marked this conversation as resolved.
Show resolved Hide resolved
_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore,
msg exported.Header, // TODO: Update to exported.ClientMessage
) bool {
if _, ok := msg.(*Misbehaviour); ok {
return true
}

return nil
return false
}

// update the consensus state to the new public key and an incremented sequence
func update(clientState *ClientState, header *Header) (*ClientState, *ConsensusState) {
// UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour
// as it does not perform any misbehaviour checks.
func (cs ClientState) UpdateStateOnMisbehaviour(
_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, // prematurely include args for self storage of consensus state
) (*ClientState, exported.ConsensusState, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting to wonder if we should not return an error? I feel like the code should panic as any errors detected should be returned earlier

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I was expecting VerifyClientMessage to return an error, CheckForMisbehaviour to return a bool and both UpdateState and UpdateStateOnMisbehaviour to be void.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we just return a bool from CheckForMisbehavior we miss out on the ability to return an error message specific to the misbehavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we miss out on the ability to return an error message specific to the misbehavior

Can you give an instance of this?

cs.IsFrozen = true
return &cs, cs.ConsensusState, nil
}

// UpdateState updates the consensus state to the new public key and an incremented sequence.
func (cs ClientState) UpdateState(
ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore,
header exported.Header,
) (exported.ClientState, exported.ConsensusState, error) {
smHeader := header.(*Header)
consensusState := &ConsensusState{
PublicKey: header.NewPublicKey,
Diversifier: header.NewDiversifier,
Timestamp: header.Timestamp,
PublicKey: smHeader.NewPublicKey,
Diversifier: smHeader.NewDiversifier,
Timestamp: smHeader.Timestamp,
}

// increment sequence number
clientState.Sequence++
clientState.ConsensusState = consensusState
return clientState, consensusState
cs.Sequence++
cs.ConsensusState = consensusState
return &cs, consensusState, nil
}
Loading