-
Notifications
You must be signed in to change notification settings - Fork 579
/
misbehaviour.go
127 lines (111 loc) · 4.69 KB
/
misbehaviour.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package tendermint
import (
"time"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
tmtypes "github.com/cometbft/cometbft/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
host "github.com/cosmos/ibc-go/v7/modules/core/24-host"
"github.com/cosmos/ibc-go/v7/modules/core/exported"
)
var _ exported.ClientMessage = &Misbehaviour{}
// FrozenHeight is same for all misbehaviour
var FrozenHeight = clienttypes.NewHeight(0, 1)
// NewMisbehaviour creates a new Misbehaviour instance.
func NewMisbehaviour(clientID string, header1, header2 *Header) *Misbehaviour {
return &Misbehaviour{
ClientId: clientID,
Header1: header1,
Header2: header2,
}
}
// ClientType is Tendermint light client
func (misbehaviour Misbehaviour) ClientType() string {
return exported.Tendermint
}
// GetTime returns the timestamp at which misbehaviour occurred. It uses the
// maximum value from both headers to prevent producing an invalid header outside
// of the misbehaviour age range.
func (misbehaviour Misbehaviour) GetTime() time.Time {
t1, t2 := misbehaviour.Header1.GetTime(), misbehaviour.Header2.GetTime()
if t1.After(t2) {
return t1
}
return t2
}
// ValidateBasic implements Misbehaviour interface
func (misbehaviour Misbehaviour) ValidateBasic() error {
if misbehaviour.Header1 == nil {
return sdkerrors.Wrap(ErrInvalidHeader, "misbehaviour Header1 cannot be nil")
}
if misbehaviour.Header2 == nil {
return sdkerrors.Wrap(ErrInvalidHeader, "misbehaviour Header2 cannot be nil")
}
if misbehaviour.Header1.TrustedHeight.RevisionHeight == 0 {
return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header1 cannot have zero revision height")
}
if misbehaviour.Header2.TrustedHeight.RevisionHeight == 0 {
return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header2 cannot have zero revision height")
}
if misbehaviour.Header1.TrustedValidators == nil {
return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header1 cannot be empty")
}
if misbehaviour.Header2.TrustedValidators == nil {
return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header2 cannot be empty")
}
if misbehaviour.Header1.Header.ChainID != misbehaviour.Header2.Header.ChainID {
return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers must have identical chainIDs")
}
if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil {
return sdkerrors.Wrap(err, "misbehaviour client ID is invalid")
}
// ValidateBasic on both validators
if err := misbehaviour.Header1.ValidateBasic(); err != nil {
return sdkerrors.Wrap(
clienttypes.ErrInvalidMisbehaviour,
sdkerrors.Wrap(err, "header 1 failed validation").Error(),
)
}
if err := misbehaviour.Header2.ValidateBasic(); err != nil {
return sdkerrors.Wrap(
clienttypes.ErrInvalidMisbehaviour,
sdkerrors.Wrap(err, "header 2 failed validation").Error(),
)
}
// Ensure that Height1 is greater than or equal to Height2
if misbehaviour.Header1.GetHeight().LT(misbehaviour.Header2.GetHeight()) {
return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "Header1 height is less than Header2 height (%s < %s)", misbehaviour.Header1.GetHeight(), misbehaviour.Header2.GetHeight())
}
blockID1, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.SignedHeader.Commit.BlockID)
if err != nil {
return sdkerrors.Wrap(err, "invalid block ID from header 1 in misbehaviour")
}
blockID2, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.SignedHeader.Commit.BlockID)
if err != nil {
return sdkerrors.Wrap(err, "invalid block ID from header 2 in misbehaviour")
}
if err := validCommit(misbehaviour.Header1.Header.ChainID, *blockID1,
misbehaviour.Header1.Commit, misbehaviour.Header1.ValidatorSet); err != nil {
return err
}
if err := validCommit(misbehaviour.Header2.Header.ChainID, *blockID2,
misbehaviour.Header2.Commit, misbehaviour.Header2.ValidatorSet); err != nil {
return err
}
return nil
}
// validCommit checks if the given commit is a valid commit from the passed-in validatorset
func validCommit(chainID string, blockID tmtypes.BlockID, commit *tmproto.Commit, valSet *tmproto.ValidatorSet) (err error) {
tmCommit, err := tmtypes.CommitFromProto(commit)
if err != nil {
return sdkerrors.Wrap(err, "commit is not tendermint commit type")
}
tmValset, err := tmtypes.ValidatorSetFromProto(valSet)
if err != nil {
return sdkerrors.Wrap(err, "validator set is not tendermint validator set type")
}
if err := tmValset.VerifyCommitLight(chainID, blockID, tmCommit.Height, tmCommit); err != nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "validator set did not commit to header")
}
return nil
}