-
Notifications
You must be signed in to change notification settings - Fork 610
/
misbehaviour.go
125 lines (108 loc) · 4.63 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
package tendermint
import (
"time"
errorsmod "cosmossdk.io/errors"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
cmttypes "github.com/cometbft/cometbft/types"
clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
host "github.com/cosmos/ibc-go/v9/modules/core/24-host"
"github.com/cosmos/ibc-go/v9/modules/core/exported"
)
var _ exported.ClientMessage = (*Misbehaviour)(nil)
// 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) 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 errorsmod.Wrap(ErrInvalidHeader, "misbehaviour Header1 cannot be nil")
}
if misbehaviour.Header2 == nil {
return errorsmod.Wrap(ErrInvalidHeader, "misbehaviour Header2 cannot be nil")
}
if misbehaviour.Header1.TrustedHeight.RevisionHeight == 0 {
return errorsmod.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header1 cannot have zero revision height")
}
if misbehaviour.Header2.TrustedHeight.RevisionHeight == 0 {
return errorsmod.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header2 cannot have zero revision height")
}
if misbehaviour.Header1.TrustedValidators == nil {
return errorsmod.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header1 cannot be empty")
}
if misbehaviour.Header2.TrustedValidators == nil {
return errorsmod.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header2 cannot be empty")
}
if misbehaviour.Header1.Header.ChainID != misbehaviour.Header2.Header.ChainID {
return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers must have identical chainIDs")
}
if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil {
return errorsmod.Wrap(err, "misbehaviour client ID is invalid")
}
// ValidateBasic on both validators
if err := misbehaviour.Header1.ValidateBasic(); err != nil {
return errorsmod.Wrap(
clienttypes.ErrInvalidMisbehaviour,
errorsmod.Wrap(err, "header 1 failed validation").Error(),
)
}
if err := misbehaviour.Header2.ValidateBasic(); err != nil {
return errorsmod.Wrap(
clienttypes.ErrInvalidMisbehaviour,
errorsmod.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 errorsmod.Wrapf(clienttypes.ErrInvalidMisbehaviour, "Header1 height is less than Header2 height (%s < %s)", misbehaviour.Header1.GetHeight(), misbehaviour.Header2.GetHeight())
}
blockID1, err := cmttypes.BlockIDFromProto(&misbehaviour.Header1.SignedHeader.Commit.BlockID)
if err != nil {
return errorsmod.Wrap(err, "invalid block ID from header 1 in misbehaviour")
}
blockID2, err := cmttypes.BlockIDFromProto(&misbehaviour.Header2.SignedHeader.Commit.BlockID)
if err != nil {
return errorsmod.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
}
return validCommit(misbehaviour.Header2.Header.ChainID, *blockID2,
misbehaviour.Header2.Commit, misbehaviour.Header2.ValidatorSet)
}
// validCommit checks if the given commit is a valid commit from the passed-in validatorset
func validCommit(chainID string, blockID cmttypes.BlockID, commit *cmtproto.Commit, valSet *cmtproto.ValidatorSet) (err error) {
tmCommit, err := cmttypes.CommitFromProto(commit)
if err != nil {
return errorsmod.Wrap(err, "commit is not tendermint commit type")
}
tmValset, err := cmttypes.ValidatorSetFromProto(valSet)
if err != nil {
return errorsmod.Wrap(err, "validator set is not tendermint validator set type")
}
if err := tmValset.VerifyCommitLight(chainID, blockID, tmCommit.Height, tmCommit); err != nil {
return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "validator set did not commit to header")
}
return nil
}