Search Apps Documentation Source Content File Folder Download Copy Actions Download

tendermint_test.gno

12.32 Kb · 333 lines
  1package tendermint_test
  2
  3import (
  4	"testing"
  5	"time"
  6
  7	"gno.land/p/aib/ibc/lightclient"
  8	"gno.land/p/aib/ibc/lightclient/tendermint"
  9	"gno.land/p/aib/ibc/types"
 10	"gno.land/p/aib/ics23"
 11	"gno.land/p/nt/uassert/v0"
 12	"gno.land/p/nt/urequire/v0"
 13)
 14
 15func TestGetNeighboringConsensusStates(t *testing.T) {
 16	tm := tendermint.NewTMLightClient()
 17	nextValsHash := []byte("nextVals")
 18	height01 := types.NewHeight(0, 1)
 19	cs01 := &tendermint.ConsensusState{
 20		Timestamp:          time.Now().UTC(),
 21		Root:               tendermint.NewMerkleRoot([]byte("hash0-1")),
 22		NextValidatorsHash: nextValsHash,
 23	}
 24	clientState := tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height01, ics23.GetSDKProofSpecs(), upgradePath)
 25	err := tm.Initialize(*clientState, *cs01)
 26	urequire.NoError(t, err)
 27
 28	cs04 := &tendermint.ConsensusState{
 29		Timestamp:          time.Now().UTC(),
 30		Root:               tendermint.NewMerkleRoot([]byte("hash0-4")),
 31		NextValidatorsHash: nextValsHash,
 32	}
 33	cs49 := &tendermint.ConsensusState{
 34		Timestamp:          time.Now().UTC(),
 35		Root:               tendermint.NewMerkleRoot([]byte("hash4-9")),
 36		NextValidatorsHash: nextValsHash,
 37	}
 38	height04 := types.NewHeight(0, 4)
 39	height49 := types.NewHeight(4, 9)
 40	tm.SetConsensusState(height04, cs04)
 41	tm.SetConsensusState(height49, cs49)
 42
 43	// Previous height01 should return nil
 44	prevCs01, ok := tm.GetPreviousConsensusState(height01)
 45	urequire.True(t, prevCs01 == nil, "consensus state exists before lowest consensus state")
 46	urequire.False(t, ok)
 47
 48	// Previous from non existing height02 should return cs01
 49	prevCs02, ok := tm.GetPreviousConsensusState(types.NewHeight(0, 2))
 50	urequire.Equal(t, string(cs01.Root.Hash), string(prevCs02.Root.Hash),
 51		"previous consensus state is not returned correctly")
 52	urequire.True(t, ok)
 53
 54	// Previous from height49 should return cs04
 55	prevCs49, ok := tm.GetPreviousConsensusState(height49)
 56	urequire.Equal(t, string(cs04.Root.Hash), string(prevCs49.Root.Hash),
 57		"previous consensus state is not returned correctly")
 58	urequire.True(t, ok)
 59
 60	// Next from height01 should return cs04
 61	nextCs01, ok := tm.GetNextConsensusState(height01)
 62	urequire.Equal(t, string(cs04.Root.Hash), string(nextCs01.Root.Hash),
 63		"next consensus state not returned correctly")
 64	urequire.True(t, ok)
 65
 66	// Next from non existing height02 should return cs04
 67	nextCs02, ok := tm.GetNextConsensusState(types.NewHeight(0, 2))
 68	urequire.Equal(t, string(cs04.Root.Hash), string(nextCs02.Root.Hash),
 69		"next consensus state not returned correctly")
 70	urequire.True(t, ok)
 71
 72	// Next from height49 should return nil
 73	nextCs49, ok := tm.GetNextConsensusState(height49)
 74	urequire.True(t, nextCs49 == nil, "next consensus state exists after highest consensus state")
 75	urequire.False(t, ok)
 76}
 77
 78func TestRecoverClient(t *testing.T) {
 79	newConsState := func(root string) *tendermint.ConsensusState {
 80		return &tendermint.ConsensusState{
 81			Timestamp:          time.Now().UTC(),
 82			Root:               tendermint.NewMerkleRoot([]byte(root)),
 83			NextValidatorsHash: []byte("nextVals"),
 84		}
 85	}
 86	// Build two clients with matching immutable parameters, different chainIDs
 87	// and different latest heights.
 88	newSubject := func() *tendermint.TMLightClient {
 89		tm := tendermint.NewTMLightClient()
 90		cs := tendermint.NewClientState("subject-chain", tendermint.DefaultTrustLevel,
 91			trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, 5),
 92			ics23.GetSDKProofSpecs(), upgradePath)
 93		urequire.NoError(t, tm.Initialize(*cs, *newConsState("subject-root")))
 94		// Freeze the subject so its status becomes Frozen (for realism; not
 95		// required by RecoverClient itself).
 96		tm.ClientState.FrozenHeight = tendermint.FrozenHeight
 97		return tm
 98	}
 99	newSubstitute := func() *tendermint.TMLightClient {
100		tm := tendermint.NewTMLightClient()
101		cs := tendermint.NewClientState("substitute-chain", tendermint.DefaultTrustLevel,
102			trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, 10),
103			ics23.GetSDKProofSpecs(), upgradePath)
104		urequire.NoError(t, tm.Initialize(*cs, *newConsState("substitute-root")))
105		return tm
106	}
107
108	t.Run("success", func(t *testing.T) {
109		subject := newSubject()
110		substitute := newSubstitute()
111
112		err := subject.RecoverClient(substitute)
113		urequire.NoError(t, err)
114
115		uassert.Equal(t, "substitute-chain", subject.ClientState.ChainID)
116		uassert.True(t, subject.ClientState.LatestHeight.EQ(types.NewHeight(0, 10)))
117		uassert.True(t, subject.ClientState.FrozenHeight.IsZero())
118		// Substitute consensus state at height (0,10) must have been copied.
119		cs, found := subject.GetConsensusState(types.NewHeight(0, 10))
120		urequire.True(t, found, "consensus state at substitute latest height not copied")
121		uassert.Equal(t, "substitute-root", string(cs.Root.Hash))
122	})
123
124	t.Run("failure: substitute is nil", func(t *testing.T) {
125		subject := newSubject()
126
127		err := subject.RecoverClient(nil)
128		urequire.Error(t, err)
129		urequire.ErrorContains(t, err, "substitute must not be nil")
130	})
131
132	t.Run("success: substitute TrustingPeriod is adopted", func(t *testing.T) {
133		subject := newSubject()
134		substitute := newSubstitute()
135		substitute.ClientState.TrustingPeriod = trustingPeriod + time.Hour
136
137		err := subject.RecoverClient(substitute)
138		urequire.NoError(t, err)
139		uassert.Equal(t, int64(trustingPeriod+time.Hour), int64(subject.ClientState.TrustingPeriod))
140	})
141
142	t.Run("failure: mismatching max clock drift", func(t *testing.T) {
143		subject := newSubject()
144		substitute := newSubstitute()
145		substitute.ClientState.MaxClockDrift = maxClockDrift + time.Second
146
147		err := subject.RecoverClient(substitute)
148		urequire.Error(t, err)
149		urequire.ErrorContains(t, err, "differ on MaxClockDrift")
150	})
151
152	t.Run("failure: mismatching trust level", func(t *testing.T) {
153		subject := newSubject()
154		substitute := newSubstitute()
155		substitute.ClientState.TrustLevel = tendermint.NewFraction(2, 3)
156
157		err := subject.RecoverClient(substitute)
158		urequire.Error(t, err)
159		urequire.ErrorContains(t, err, "differ on TrustLevel")
160	})
161
162	t.Run("failure: mismatching unbonding period", func(t *testing.T) {
163		subject := newSubject()
164		substitute := newSubstitute()
165		substitute.ClientState.UnbondingPeriod = ubdPeriod + time.Hour
166
167		err := subject.RecoverClient(substitute)
168		urequire.Error(t, err)
169		urequire.ErrorContains(t, err, "differ on UnbondingPeriod")
170	})
171
172	t.Run("failure: mismatching proof specs", func(t *testing.T) {
173		subject := newSubject()
174		substitute := newSubstitute()
175		substitute.ClientState.ProofSpecs = substitute.ClientState.ProofSpecs[:1]
176
177		err := subject.RecoverClient(substitute)
178		urequire.Error(t, err)
179		urequire.ErrorContains(t, err, "differ on ProofSpecs")
180	})
181
182	t.Run("failure: mismatching upgrade path", func(t *testing.T) {
183		subject := newSubject()
184		substitute := newSubstitute()
185		substitute.ClientState.UpgradePath = []string{"otherUpgrade"}
186
187		err := subject.RecoverClient(substitute)
188		urequire.Error(t, err)
189		urequire.ErrorContains(t, err, "differ on UpgradePath")
190	})
191
192	t.Run("failure: missing consensus state at substitute latest height", func(t *testing.T) {
193		subject := newSubject()
194		substitute := newSubstitute()
195		// Drop the consensus state that Initialize stored.
196		substitute.ConsensusStateByHeight.Remove(substitute.ClientState.LatestHeight.StringNatSort())
197
198		err := subject.RecoverClient(substitute)
199		urequire.Error(t, err)
200		urequire.ErrorContains(t, err, "substitute consensus state not found")
201	})
202
203	// Assertion that interface conformance holds when calling through the
204	// lightclient.Interface type (catches future signature drift).
205	t.Run("success via Interface", func(t *testing.T) {
206		subject := newSubject()
207		var substitute lightclient.Interface = newSubstitute()
208
209		err := subject.RecoverClient(substitute)
210		urequire.NoError(t, err)
211	})
212}
213
214func TestVerifyUpgradeAndUpdateStateFailures(t *testing.T) {
215	currentHeight := types.NewHeight(0, 5)
216	upgradedHeight := types.NewHeight(0, 10)
217
218	newClient := func() *tendermint.TMLightClient {
219		tm := tendermint.NewTMLightClient()
220		cs := tendermint.NewClientState(chainID, tendermint.DefaultTrustLevel,
221			trustingPeriod, ubdPeriod, maxClockDrift, currentHeight,
222			ics23.GetSDKProofSpecs(), upgradePath)
223		validHash := make([]byte, 32)
224		for i := range validHash {
225			validHash[i] = byte(i + 1)
226		}
227		consState := &tendermint.ConsensusState{
228			Timestamp:          time.Now().UTC(),
229			Root:               tendermint.NewMerkleRoot([]byte("current-root")),
230			NextValidatorsHash: validHash,
231		}
232		urequire.NoError(t, tm.Initialize(*cs, *consState))
233		return tm
234	}
235
236	validUpgradedClient := tendermint.ClientState{
237		ChainID:         "chain-after-upgrade",
238		TrustLevel:      tendermint.DefaultTrustLevel,
239		TrustingPeriod:  trustingPeriod,
240		UnbondingPeriod: ubdPeriod,
241		MaxClockDrift:   maxClockDrift,
242		LatestHeight:    upgradedHeight,
243		ProofSpecs:      ics23.GetSDKProofSpecs(),
244		UpgradePath:     upgradePath,
245	}
246	thirtyTwoBytes := make([]byte, 32)
247	for i := range thirtyTwoBytes {
248		thirtyTwoBytes[i] = byte(i)
249	}
250	validUpgradedConsState := tendermint.ConsensusState{
251		Timestamp:          time.Now().UTC(),
252		Root:               tendermint.NewMerkleRoot([]byte("upgraded-root")),
253		NextValidatorsHash: thirtyTwoBytes,
254	}
255	dummyProof := []ics23.CommitmentProof{ics23.CommitmentProof_Exist{Exist: &ics23.ExistenceProof{}}}
256
257	t.Run("failure: newClient wrong type", func(t *testing.T) {
258		tm := newClient()
259		err := tm.VerifyUpgradeAndUpdateState("not-a-client-state", validUpgradedConsState, dummyProof, dummyProof)
260		urequire.Error(t, err)
261		urequire.ErrorContains(t, err, "must be tendermint.ClientState")
262	})
263
264	t.Run("failure: invalid upgraded client state", func(t *testing.T) {
265		tm := newClient()
266		bad := validUpgradedClient
267		bad.ChainID = "" // fails ValidateBasic
268		err := tm.VerifyUpgradeAndUpdateState(bad, validUpgradedConsState, dummyProof, dummyProof)
269		urequire.Error(t, err)
270		urequire.ErrorContains(t, err, "invalid upgraded client state")
271	})
272
273	t.Run("failure: newConsState wrong type", func(t *testing.T) {
274		tm := newClient()
275		err := tm.VerifyUpgradeAndUpdateState(validUpgradedClient, "not-a-consensus-state", dummyProof, dummyProof)
276		urequire.Error(t, err)
277		urequire.ErrorContains(t, err, "must be tendermint.ConsensusState")
278	})
279
280	t.Run("failure: invalid upgraded consensus state", func(t *testing.T) {
281		tm := newClient()
282		bad := validUpgradedConsState
283		bad.Root = tendermint.MerkleRoot{} // empty root → ValidateBasic fails
284		err := tm.VerifyUpgradeAndUpdateState(validUpgradedClient, bad, dummyProof, dummyProof)
285		urequire.Error(t, err)
286		urequire.ErrorContains(t, err, "invalid upgraded consensus state")
287	})
288
289	t.Run("failure: empty UpgradePath on current client", func(t *testing.T) {
290		tm := newClient()
291		tm.ClientState.UpgradePath = nil
292		err := tm.VerifyUpgradeAndUpdateState(validUpgradedClient, validUpgradedConsState, dummyProof, dummyProof)
293		urequire.Error(t, err)
294		urequire.ErrorContains(t, err, "no upgrade path set")
295	})
296
297	t.Run("failure: upgraded latest height not greater than current", func(t *testing.T) {
298		tm := newClient()
299		notGreater := validUpgradedClient
300		notGreater.LatestHeight = currentHeight
301		err := tm.VerifyUpgradeAndUpdateState(notGreater, validUpgradedConsState, dummyProof, dummyProof)
302		urequire.Error(t, err)
303		urequire.ErrorContains(t, err, "must be greater than current latest height")
304	})
305
306	t.Run("failure: clientProof wrong type", func(t *testing.T) {
307		tm := newClient()
308		err := tm.VerifyUpgradeAndUpdateState(validUpgradedClient, validUpgradedConsState, []byte("raw"), dummyProof)
309		urequire.Error(t, err)
310		urequire.ErrorContains(t, err, "upgradeClientProof must be []ics23.CommitmentProof")
311	})
312
313	t.Run("failure: clientProof empty", func(t *testing.T) {
314		tm := newClient()
315		err := tm.VerifyUpgradeAndUpdateState(validUpgradedClient, validUpgradedConsState, []ics23.CommitmentProof{}, dummyProof)
316		urequire.Error(t, err)
317		urequire.ErrorContains(t, err, "upgradeClientProof cannot be empty")
318	})
319
320	t.Run("failure: consensusProof wrong type", func(t *testing.T) {
321		tm := newClient()
322		err := tm.VerifyUpgradeAndUpdateState(validUpgradedClient, validUpgradedConsState, dummyProof, "not-a-proof")
323		urequire.Error(t, err)
324		urequire.ErrorContains(t, err, "upgradeConsensusStateProof must be []ics23.CommitmentProof")
325	})
326
327	t.Run("failure: consensusProof empty", func(t *testing.T) {
328		tm := newClient()
329		err := tm.VerifyUpgradeAndUpdateState(validUpgradedClient, validUpgradedConsState, dummyProof, []ics23.CommitmentProof{})
330		urequire.Error(t, err)
331		urequire.ErrorContains(t, err, "upgradeConsensusStateProof cannot be empty")
332	})
333}