state_test.gno
10.77 Kb · 299 lines
1package tendermint_test
2
3import (
4 "bytes"
5 "strings"
6 "testing"
7 "time"
8
9 "gno.land/p/aib/ibc/lightclient"
10 "gno.land/p/aib/ibc/lightclient/tendermint"
11 "gno.land/p/aib/ibc/types"
12 "gno.land/p/aib/ics23"
13 "gno.land/p/nt/uassert/v0"
14)
15
16const (
17 chainID = "gno.land"
18 // Do not change the length of these variables
19 fiftyCharChainID = "12345678901234567890123456789012345678901234567890"
20 fiftyOneCharChainID = "123456789012345678901234567890123456789012345678901"
21
22 trustingPeriod time.Duration = time.Hour * 24 * 7 * 2
23 ubdPeriod time.Duration = time.Hour * 24 * 7 * 3
24 maxClockDrift time.Duration = time.Second * 10
25)
26
27var (
28 invalidHash = []byte("hash_too_small")
29 height = types.NewHeight(0, 4)
30 upgradePath = []string{"upgrade", "upgradedIBCState"}
31 invalidUpgradePath = []string{"upgrade", ""}
32)
33
34func TestClientStateValidateBasic(t *testing.T) {
35 testCases := []struct {
36 name string
37 clientState *tendermint.ClientState
38 expErr string
39 }{
40 {
41 name: "valid client",
42 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
43 },
44 {
45 name: "valid client with nil upgrade path",
46 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), nil),
47 },
48 {
49 name: "invalid chainID",
50 clientState: tendermint.NewClientState(" ", tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
51 expErr: "chainID cannot be empty string",
52 },
53 {
54 name: "valid chainID - chainID validation did not fail for chainID of length 50!",
55 clientState: tendermint.NewClientState(fiftyCharChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
56 },
57 {
58 // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions!
59 // Do not only fix the test, fix the code!
60 // https://github.com/cosmos/ibc-go/issues/177
61 name: "invalid chainID - chainID validation failed for chainID of length 51!",
62 clientState: tendermint.NewClientState(fiftyOneCharChainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
63 expErr: "chainID is too long",
64 },
65 {
66 name: "invalid trust level",
67 clientState: tendermint.NewClientState(chainID, tendermint.NewFraction(0, 1), trustingPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
68 expErr: "trustLevel must be within [1/3, 1]",
69 },
70 {
71 name: "invalid zero trusting period",
72 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
73 expErr: "trusting period must be greater than zero",
74 },
75 {
76 name: "invalid negative trusting period",
77 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, -1, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
78 expErr: "trusting period must be greater than zero",
79 },
80 {
81 name: "invalid zero unbonding period",
82 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
83 expErr: "unbonding period must be greater than zero",
84 },
85 {
86 name: "invalid negative unbonding period",
87 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, -1, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
88 expErr: "unbonding period must be greater than zero",
89 },
90 {
91 name: "invalid zero max clock drift",
92 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, ics23.GetSDKProofSpecs(), upgradePath),
93 expErr: "max clock drift must be greater than zero",
94 },
95 {
96 name: "invalid negative max clock drift",
97 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, -1, height, ics23.GetSDKProofSpecs(), upgradePath),
98 expErr: "max clock drift must be greater than zero",
99 },
100 {
101 name: "invalid revision number",
102 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(1, 1), ics23.GetSDKProofSpecs(), upgradePath),
103 expErr: "latest height revision number must match chain id revision number (1 != 0)",
104 },
105 {
106 name: "invalid revision height",
107 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), ics23.GetSDKProofSpecs(), upgradePath),
108 expErr: "tendermint client's latest height revision height cannot be zero",
109 },
110 {
111 name: "trusting period not less than unbonding period",
112 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), upgradePath),
113 expErr: "trusting period (504h0m0s) should be < unbonding period",
114 },
115 {
116 name: "proof specs is nil",
117 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath),
118 expErr: "proof specs cannot be nil for tm client",
119 },
120 {
121 name: "proof specs contains nil",
122 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec(), nil}, upgradePath),
123 expErr: "proof spec cannot be nil at index: 1",
124 },
125 {
126 name: "invalid upgrade path",
127 clientState: tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, ics23.GetSDKProofSpecs(), invalidUpgradePath),
128 expErr: "key in upgrade path at index 1 cannot be empty",
129 },
130 }
131
132 for _, tc := range testCases {
133 t.Run(tc.name, func(t *testing.T) {
134 if lightclient.Tendermint != tc.clientState.ClientType() {
135 t.Fatal("client type is not tendermint")
136 }
137
138 err := tc.clientState.ValidateBasic()
139
140 if tc.expErr == "" && err != nil {
141 t.Errorf("expected no error, got %s", err)
142 return
143 }
144 if tc.expErr != "" {
145 if err == nil || !strings.Contains(err.Error(), tc.expErr) {
146 t.Errorf("expected error %s, got %s", tc.expErr, err)
147 }
148 }
149 })
150 }
151}
152
153func TestConsensusStateValidateBasic(t *testing.T) {
154 var (
155 now = time.Now()
156 valshash = bytes.Repeat([]byte{1}, 32)
157 )
158 testCases := []struct {
159 msg string
160 consensusState *tendermint.ConsensusState
161 expectPass bool
162 }{
163 {
164 "success",
165 &tendermint.ConsensusState{
166 Timestamp: now,
167 Root: tendermint.NewMerkleRoot([]byte("app_hash")),
168 NextValidatorsHash: valshash[:],
169 },
170 true,
171 },
172 {
173 "success with sentinel",
174 &tendermint.ConsensusState{
175 Timestamp: now,
176 Root: tendermint.NewMerkleRoot([]byte(tendermint.SentinelRoot)),
177 NextValidatorsHash: valshash[:],
178 },
179 true,
180 },
181 {
182 "root is nil",
183 &tendermint.ConsensusState{
184 Timestamp: now,
185 Root: tendermint.MerkleRoot{},
186 NextValidatorsHash: valshash[:],
187 },
188 false,
189 },
190 {
191 "root is empty",
192 &tendermint.ConsensusState{
193 Timestamp: now,
194 Root: tendermint.MerkleRoot{},
195 NextValidatorsHash: valshash[:],
196 },
197 false,
198 },
199 {
200 "nextvalshash is invalid",
201 &tendermint.ConsensusState{
202 Timestamp: now,
203 Root: tendermint.NewMerkleRoot([]byte("app_hash")),
204 NextValidatorsHash: []byte("hi"),
205 },
206 false,
207 },
208
209 {
210 "timestamp is zero",
211 &tendermint.ConsensusState{
212 Timestamp: time.Time{},
213 Root: tendermint.NewMerkleRoot([]byte("app_hash")),
214 NextValidatorsHash: valshash[:],
215 },
216 false,
217 },
218 }
219
220 for _, tc := range testCases {
221 t.Run(tc.msg, func(t *testing.T) {
222 if lightclient.Tendermint != tc.consensusState.ClientType() {
223 t.Fatal("client type is not tendermint")
224 }
225
226 err := tc.consensusState.ValidateBasic()
227
228 if tc.expectPass && err != nil {
229 t.Errorf("expected pass but got error %s", err)
230 return
231 }
232 if !tc.expectPass && err == nil {
233 t.Errorf("expected fail but got not error")
234 }
235 })
236 }
237}
238
239func TestClientStateZeroCustomFields(t *testing.T) {
240 cs := tendermint.ClientState{
241 ChainID: "chain-a",
242 TrustLevel: tendermint.NewFraction(2, 3),
243 TrustingPeriod: trustingPeriod,
244 UnbondingPeriod: ubdPeriod,
245 MaxClockDrift: maxClockDrift,
246 FrozenHeight: types.NewHeight(0, 5),
247 LatestHeight: types.NewHeight(1, 100),
248 ProofSpecs: ics23.GetSDKProofSpecs(),
249 UpgradePath: []string{"upgrade", "upgradedIBCState"},
250 }
251
252 zeroed := cs.ZeroCustomFields()
253
254 // Preserved chain-specified fields.
255 uassert.Equal(t, "chain-a", zeroed.ChainID)
256 uassert.Equal(t, int64(ubdPeriod), int64(zeroed.UnbondingPeriod))
257 uassert.True(t, zeroed.LatestHeight.EQ(types.NewHeight(1, 100)))
258 uassert.Equal(t, len(cs.ProofSpecs), len(zeroed.ProofSpecs))
259 uassert.Equal(t, len(cs.UpgradePath), len(zeroed.UpgradePath))
260
261 // Zeroed customizable fields.
262 uassert.Equal(t, uint64(0), zeroed.TrustLevel.Numerator)
263 uassert.Equal(t, uint64(0), zeroed.TrustLevel.Denominator)
264 uassert.Equal(t, int64(0), int64(zeroed.TrustingPeriod))
265 uassert.Equal(t, int64(0), int64(zeroed.MaxClockDrift))
266 uassert.True(t, zeroed.FrozenHeight.IsZero())
267
268 // Caller's value untouched.
269 uassert.Equal(t, int64(trustingPeriod), int64(cs.TrustingPeriod))
270}
271
272func TestClientStateProtoMarshalSerializesProofSpecs(t *testing.T) {
273 // Build a ZeroCustomFields client state and confirm its marshal
274 // includes a ProofSpecs field (8) for each spec the chain pinned.
275 // We aren't comparing against an external golden; we just need to
276 // verify the field 8 tag (0x42) appears once per spec.
277 cs := tendermint.ClientState{
278 ChainID: "chain-a",
279 UnbondingPeriod: ubdPeriod,
280 LatestHeight: types.NewHeight(1, 100),
281 ProofSpecs: ics23.GetSDKProofSpecs(),
282 UpgradePath: []string{"upgrade", "upgradedIBCState"},
283 }
284
285 bz := cs.ProtoMarshal()
286
287 // 0x42 = (8<<3)|2 — the LEN-wire tag for field 8.
288 got := 0
289 for _, b := range bz {
290 if b == 0x42 {
291 got++
292 }
293 }
294 // SDK has 2 proof specs (Iavl, Tendermint), so the tag appears at
295 // least twice. (Could appear inside nested bytes too — that's why
296 // we use >=. The point is: it's not zero, which means ProofSpecs is
297 // no longer skipped.)
298 uassert.True(t, got >= 2, "expected ProofSpecs field 8 tag to appear at least twice")
299}