Skip to content

Commit

Permalink
refactor: creating validateClientMessage fn for Header and Misbehaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
seantking committed Mar 13, 2022
1 parent c345354 commit f45fd50
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 100 deletions.
33 changes: 2 additions & 31 deletions modules/light-clients/07-tendermint/types/misbehaviour_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,37 +58,8 @@ func (cs ClientState) CheckMisbehaviourAndUpdateState(
}
}

// Regardless of the type of misbehaviour, ensure that both headers are valid and would have been accepted by light-client

// Retrieve trusted consensus states for each Header in misbehaviour
// and unmarshal from clientStore

// Get consensus bytes from clientStore
tmConsensusState1, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header1.TrustedHeight)
if err != nil {
return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", tmMisbehaviour.Header1)
}

// Get consensus bytes from clientStore
tmConsensusState2, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header2.TrustedHeight)
if err != nil {
return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", tmMisbehaviour.Header2)
}

// Check the validity of the two conflicting headers against their respective
// trusted consensus states
// NOTE: header height and commitment root assertions are checked in
// misbehaviour.ValidateBasic by the client keeper and msg.ValidateBasic
// by the base application.
if err := checkMisbehaviourHeader(
&cs, tmConsensusState1, tmMisbehaviour.Header1, ctx.BlockTime(),
); err != nil {
return nil, sdkerrors.Wrap(err, "verifying Header1 in Misbehaviour failed")
}
if err := checkMisbehaviourHeader(
&cs, tmConsensusState2, tmMisbehaviour.Header2, ctx.BlockTime(),
); err != nil {
return nil, sdkerrors.Wrap(err, "verifying Header2 in Misbehaviour failed")
if err := validateClientMessage(&cs, ctx, clientStore, cdc, nil, misbehaviour, ctx.BlockTime()); err != nil {
return nil, err
}

cs.FrozenHeight = FrozenHeight
Expand Down
177 changes: 108 additions & 69 deletions modules/light-clients/07-tendermint/types/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (cs ClientState) CheckHeaderAndUpdateState(
)
}

if err := checkValidity(&cs, trustedConsState, tmHeader, ctx.BlockTime()); err != nil {
if err := validateClientMessage(&cs, ctx, clientStore, cdc, trustedConsState, tmHeader, ctx.BlockTime()); err != nil {
return nil, nil, err
}

Expand Down Expand Up @@ -162,85 +162,124 @@ func checkTrustedHeader(header *Header, consState *ConsensusState) error {
return nil
}

// checkValidity checks if the Tendermint header is valid.
// CONTRACT: consState.Height == header.TrustedHeight
func checkValidity(
clientState *ClientState, consState *ConsensusState,
header *Header, currentTimestamp time.Time,
// validateClientMessage checks if the clientMessage is of type Header or Misbehaviour and validates the message
func validateClientMessage(
clientState *ClientState, ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, consState *ConsensusState,
header exported.ClientMessage, currentTimestamp time.Time,
) error {
if err := checkTrustedHeader(header, consState); err != nil {
return err
}
switch header.(type) {
case *Header:
header := header.(*Header)

// UpdateClient only accepts updates with a header at the same revision
// as the trusted consensus state
if header.GetHeight().GetRevisionNumber() != header.TrustedHeight.RevisionNumber {
return sdkerrors.Wrapf(
ErrInvalidHeaderHeight,
"header height revision %d does not match trusted header revision %d",
header.GetHeight().GetRevisionNumber(), header.TrustedHeight.RevisionNumber,
)
}
if err := checkTrustedHeader(header, consState); err != nil {
return err
}

tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators)
if err != nil {
return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type")
}
// UpdateClient only accepts updates with a header at the same revision
// as the trusted consensus state
if header.GetHeight().GetRevisionNumber() != header.TrustedHeight.RevisionNumber {
return sdkerrors.Wrapf(
ErrInvalidHeaderHeight,
"header height revision %d does not match trusted header revision %d",
header.GetHeight().GetRevisionNumber(), header.TrustedHeight.RevisionNumber,
)
}

tmSignedHeader, err := tmtypes.SignedHeaderFromProto(header.SignedHeader)
if err != nil {
return sdkerrors.Wrap(err, "signed header in not tendermint signed header type")
}
tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators)
if err != nil {
return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type")
}

tmValidatorSet, err := tmtypes.ValidatorSetFromProto(header.ValidatorSet)
if err != nil {
return sdkerrors.Wrap(err, "validator set in not tendermint validator set type")
}
tmSignedHeader, err := tmtypes.SignedHeaderFromProto(header.SignedHeader)
if err != nil {
return sdkerrors.Wrap(err, "signed header in not tendermint signed header type")
}

// assert header height is newer than consensus state
if header.GetHeight().LTE(header.TrustedHeight) {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header height ≤ consensus state height (%s ≤ %s)", header.GetHeight(), header.TrustedHeight,
tmValidatorSet, err := tmtypes.ValidatorSetFromProto(header.ValidatorSet)
if err != nil {
return sdkerrors.Wrap(err, "validator set in not tendermint validator set type")
}

// assert header height is newer than consensus state
if header.GetHeight().LTE(header.TrustedHeight) {
return sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader,
"header height ≤ consensus state height (%s ≤ %s)", header.GetHeight(), header.TrustedHeight,
)
}

chainID := clientState.GetChainID()
// If chainID is in revision format, then set revision number of chainID with the revision number
// of the header we are verifying
// This is useful if the update is at a previous revision rather than an update to the latest revision
// of the client.
// The chainID must be set correctly for the previous revision before attempting verification.
// Updates for previous revisions are not supported if the chainID is not in revision format.
if clienttypes.IsRevisionFormat(chainID) {
chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber())
}

// Construct a trusted header using the fields in consensus state
// Only Height, Time, and NextValidatorsHash are necessary for verification
trustedHeader := tmtypes.Header{
ChainID: chainID,
Height: int64(header.TrustedHeight.RevisionHeight),
Time: consState.Timestamp,
NextValidatorsHash: consState.NextValidatorsHash,
}
signedHeader := tmtypes.SignedHeader{
Header: &trustedHeader,
}

// Verify next header with the passed-in trustedVals
// - asserts trusting period not passed
// - assert header timestamp is not past the trusting period
// - assert header timestamp is past latest stored consensus state timestamp
// - assert that a TrustLevel proportion of TrustedValidators signed new Commit
err = light.Verify(
&signedHeader,
tmTrustedValidators, tmSignedHeader, tmValidatorSet,
clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(),
)
}
if err != nil {
return sdkerrors.Wrap(err, "failed to verify header")
}
case *Misbehaviour:
misbehaviour := header.(*Misbehaviour)
// Regardless of the type of misbehaviour, ensure that both headers are valid and would have been accepted by light-client

chainID := clientState.GetChainID()
// If chainID is in revision format, then set revision number of chainID with the revision number
// of the header we are verifying
// This is useful if the update is at a previous revision rather than an update to the latest revision
// of the client.
// The chainID must be set correctly for the previous revision before attempting verification.
// Updates for previous revisions are not supported if the chainID is not in revision format.
if clienttypes.IsRevisionFormat(chainID) {
chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber())
}
// Retrieve trusted consensus states for each Header in misbehaviour
// and unmarshal from clientStore

// Construct a trusted header using the fields in consensus state
// Only Height, Time, and NextValidatorsHash are necessary for verification
trustedHeader := tmtypes.Header{
ChainID: chainID,
Height: int64(header.TrustedHeight.RevisionHeight),
Time: consState.Timestamp,
NextValidatorsHash: consState.NextValidatorsHash,
}
signedHeader := tmtypes.SignedHeader{
Header: &trustedHeader,
}
// Get consensus bytes from clientStore
tmConsensusState1, err := GetConsensusState(clientStore, cdc, misbehaviour.Header1.TrustedHeight)
if err != nil {
return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", misbehaviour.Header1)
}

// Verify next header with the passed-in trustedVals
// - asserts trusting period not passed
// - assert header timestamp is not past the trusting period
// - assert header timestamp is past latest stored consensus state timestamp
// - assert that a TrustLevel proportion of TrustedValidators signed new Commit
err = light.Verify(
&signedHeader,
tmTrustedValidators, tmSignedHeader, tmValidatorSet,
clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(),
)
if err != nil {
return sdkerrors.Wrap(err, "failed to verify header")
// Get consensus bytes from clientStore
tmConsensusState2, err := GetConsensusState(clientStore, cdc, misbehaviour.Header2.TrustedHeight)
if err != nil {
return sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", misbehaviour.Header2)
}

// Check the validity of the two conflicting headers against their respective
// trusted consensus states
// NOTE: header height and commitment root assertions are checked in
// misbehaviour.ValidateBasic by the client keeper and msg.ValidateBasic
// by the base application.
if err := checkMisbehaviourHeader(
clientState, tmConsensusState1, misbehaviour.Header1, ctx.BlockTime(),
); err != nil {
return sdkerrors.Wrap(err, "verifying Header1 in Misbehaviour failed")
}
if err := checkMisbehaviourHeader(
clientState, tmConsensusState2, misbehaviour.Header2, ctx.BlockTime(),
); err != nil {
return sdkerrors.Wrap(err, "verifying Header2 in Misbehaviour failed")
}
}

return nil
}

Expand Down

0 comments on commit f45fd50

Please sign in to comment.