Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}