-
Notifications
You must be signed in to change notification settings - Fork 0
/
update.go
162 lines (141 loc) · 6.2 KB
/
update.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package types
import (
"bytes"
"github.com/ci123chain/ci123chain/pkg/abci/codec"
sdk "github.com/ci123chain/ci123chain/pkg/abci/types"
sdkerrors "github.com/ci123chain/ci123chain/pkg/abci/types/errors"
clienttypes "github.com/ci123chain/ci123chain/pkg/ibc/core/clients/types"
commitmenttypes "github.com/ci123chain/ci123chain/pkg/ibc/core/commitment/types"
"github.com/ci123chain/ci123chain/pkg/ibc/core/exported"
"github.com/tendermint/tendermint/light"
tmtypes "github.com/tendermint/tendermint/types"
"time"
)
func (cs ClientState) CheckHeaderAndUpdateState(ctx sdk.Context, cdc codec.BinaryMarshaler, clientStore sdk.KVStore,
header exported.Header) (exported.ClientState, exported.ConsensusState, error) {
tmHeader, ok := header.(*Header)
if !ok {
return nil, nil, sdkerrors.Wrapf(
clienttypes.ErrInvalidHeader, "expected type %T, got %T", &Header{}, header,
)
}
// get consensus state from clientStore
tmConsState, err := GetConsensusState(clientStore, cdc, tmHeader.TrustedHeight)
if err != nil {
return nil, nil, sdkerrors.Wrapf(
err, "could not get consensus state from clientstore at TrustedHeight: %s", tmHeader.TrustedHeight,
)
}
if err := checkValidity(&cs, tmConsState, tmHeader, ctx.BlockHeader().Time); err != nil {
return nil, nil, err
}
newClientState, consensusState := update(ctx, clientStore, &cs, tmHeader)
return newClientState, consensusState, 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,
) error {
if err := checkTrustedHeader(header, consState); err != nil {
return err
}
// 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,
)
}
tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators)
if err != nil {
return sdkerrors.Wrap(err, "trusted 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")
}
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")
}
return nil
}
// update the consensus state from a new header and set processed time metadata
func update(ctx sdk.Context, clientStore sdk.KVStore, clientState *ClientState, header *Header) (*ClientState, *ConsensusState) {
height := header.GetHeight().(clienttypes.Height)
if height.GT(clientState.LatestHeight) {
clientState.LatestHeight = height
}
consensusState := &ConsensusState{
Timestamp: header.GetTime(),
Root: commitmenttypes.NewMerkleRoot(header.Header.AppHash),
NextValidatorsHash: header.Header.NextValidatorsHash,
}
// set context time as processed time as this is state internal to tendermint client logic.
// client state and consensus state will be set by client keeper
SetProcessedTime(clientStore, header.GetHeight(), uint64(ctx.BlockHeader().Time.UnixNano()))
return clientState, consensusState
}
// checkTrustedHeader checks that consensus state matches trusted fields of Header
func checkTrustedHeader(header *Header, consState *ConsensusState) error {
tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators)
if err != nil {
return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type")
}
// assert that trustedVals is NextValidators of last trusted header
// to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash
tvalHash := tmTrustedValidators.Hash()
if !bytes.Equal(consState.NextValidatorsHash, tvalHash) {
return sdkerrors.Wrapf(
ErrInvalidValidatorSet,
"trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X",
header.TrustedValidators, consState.NextValidatorsHash, tvalHash,
)
}
return nil
}