Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

proposal_test.gno

19.74 Kb · 616 lines
  1package validators
  2
  3import (
  4	"strconv"
  5	"testing"
  6
  7	"gno.land/p/nt/testutils/v0"
  8	"gno.land/p/nt/uassert/v0"
  9	"gno.land/p/nt/urequire/v0"
 10	sysparams "gno.land/r/sys/params"
 11)
 12
 13// seedCache populates valoperCache with the given (op, pubkey, kr)
 14// tuples — used in tests to satisfy NewValidatorProposalRequest's
 15// creation-time membership check without going through valopers.
 16// asGovDAOProxy is the realm these tests stand in as before invoking an
 17// executor directly. SimpleExecutor.Execute is invocable only from the
 18// r/gov/dao namespace; on the live route dao.ExecuteProposal and
 19// impl.ExecuteProposal thread the proxy's cur to it non-crossing, so
 20// Previous() inside Execute is this path. Calling Execute from the test's
 21// own realm is a route no transaction takes.
 22const asGovDAOProxy = "gno.land/r/gov/dao"
 23
 24func seedCache(t *testing.T, entries []struct {
 25	op          address
 26	pubKey      string
 27	keepRunning bool
 28}) {
 29	t.Helper()
 30	for _, e := range entries {
 31		signingAddr := mustAddr(t, e.pubKey)
 32		valoperCache.Set(e.op.String(), cacheEntry{
 33			SigningPubKey:  e.pubKey,
 34			SigningAddress: signingAddr,
 35			KeepRunning:    e.keepRunning,
 36		})
 37	}
 38}
 39
 40func TestNewValidatorProposalRequest_RejectsUnknownOperator(cur realm, t *testing.T) {
 41	resetCache()
 42
 43	op := testutils.TestAddress("ghost-op")
 44
 45	uassert.PanicsContains(t, cur, "unknown operator", func() {
 46		_ = NewValidatorProposalRequest(cur,
 47			[]ValoperChange{{OperatorAddress: op, Power: 1}},
 48			"add ghost",
 49			"",
 50		)
 51	})
 52}
 53
 54func TestNewValidatorProposalRequest_RejectsEmptyChanges(cur realm, t *testing.T) {
 55	resetCache()
 56
 57	uassert.PanicsContains(t, cur, errNoValoperChanges, func() {
 58		_ = NewValidatorProposalRequest(cur, nil, "title", "")
 59	})
 60}
 61
 62func TestNewValidatorProposalRequest_RejectsEmptyTitle(cur realm, t *testing.T) {
 63	resetCache()
 64	op := testutils.TestAddress("op-A")
 65	seedCache(t, []struct {
 66		op          address
 67		pubKey      string
 68		keepRunning bool
 69	}{{op: op, pubKey: pubKeyA, keepRunning: true}})
 70
 71	uassert.PanicsContains(t, cur, "proposal title is empty", func() {
 72		_ = NewValidatorProposalRequest(cur,
 73			[]ValoperChange{{OperatorAddress: op, Power: 1}},
 74			"   ",
 75			"",
 76		)
 77	})
 78}
 79
 80func TestNewValidatorProposalRequest_RejectsTooManyChanges(cur realm, t *testing.T) {
 81	resetCache()
 82
 83	// Seed 41 cache entries so the membership check passes; the
 84	// length cap should fire before the per-entry validation.
 85	changes := make([]ValoperChange, 41)
 86	pubkeys := []string{pubKeyA, pubKeyB, pubKeyC}
 87	for i := 0; i < 41; i++ {
 88		op := testutils.TestAddress("op-" + strconv.Itoa(i))
 89		pk := pubkeys[i%3]
 90		valoperCache.Set(op.String(), cacheEntry{
 91			SigningPubKey:  pk,
 92			SigningAddress: mustAddr(t, pk),
 93			KeepRunning:    true,
 94		})
 95		changes[i] = ValoperChange{OperatorAddress: op, Power: 1}
 96	}
 97
 98	uassert.PanicsContains(t, cur, "max number of allowed validators per proposal is 40", func() {
 99		_ = NewValidatorProposalRequest(cur, changes, "too many", "")
100	})
101}
102
103func TestNewValidatorProposalRequest_DescriptionRendering(cur realm, t *testing.T) {
104	resetCache()
105	opA := testutils.TestAddress("op-A")
106	opB := testutils.TestAddress("op-B")
107	seedCache(t, []struct {
108		op          address
109		pubKey      string
110		keepRunning bool
111	}{
112		{op: opA, pubKey: pubKeyA, keepRunning: true},
113		{op: opB, pubKey: pubKeyB, keepRunning: true},
114	})
115
116	pr := NewValidatorProposalRequest(cur,
117		[]ValoperChange{
118			{OperatorAddress: opA, Power: 5},
119			{OperatorAddress: opB, Power: 0},
120		},
121		"mixed changes",
122		"context line",
123	)
124
125	desc := pr.Description()
126	urequire.True(t, len(desc) > 0)
127	uassert.True(t, contains(desc, "context line"))
128	uassert.True(t, contains(desc, "## Validator Updates"))
129	uassert.True(t, contains(desc, opA.String()+": add (power 5)"))
130	uassert.True(t, contains(desc, opB.String()+": remove"))
131}
132
133func TestNewValidatorProposalRequest_ExecutorReResolvesPubkey(cur realm, t *testing.T) {
134	// Creation-time captured changes: ValoperChange refers to opA.
135	// Cache for opA points to pubKeyA at creation. Before execution,
136	// opA's cache entry is updated to pubKeyB. Executor must publish
137	// the NEW pubkey, not the creation-time one.
138	resetValset(t)
139	resetCache()
140
141	opA := testutils.TestAddress("op-A")
142	seedCache(t, []struct {
143		op          address
144		pubKey      string
145		keepRunning bool
146	}{{op: opA, pubKey: pubKeyA, keepRunning: true}})
147
148	changes := []ValoperChange{{OperatorAddress: opA, Power: 7}}
149
150	// Build the executor; it captures `changes` by reference (slice
151	// of structs) but resolves SigningPubKey at run-time via cache.
152	exec := newValoperChangeExecutor(cur, changes)
153
154	// Simulate operator rotation: opA's cache entry now points to
155	// pubKeyB. Captured changes slice is unchanged.
156	valoperCache.Set(opA.String(), cacheEntry{
157		SigningPubKey:  pubKeyB,
158		SigningAddress: mustAddr(t, pubKeyB),
159		KeepRunning:    true,
160	})
161
162	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
163
164	urequire.NoError(t, exec.Execute(cross(cur)))
165
166	// Effective valset should contain pubKeyB (post-rotation), not
167	// pubKeyA (creation-time).
168	effective := sysparams.GetValsetEffective()
169	urequire.Equal(t, 1, len(effective))
170	uassert.Equal(t, pubKeyB, effective[0].PubKey)
171	uassert.Equal(t, uint64(7), effective[0].VotingPower)
172}
173
174func TestNewValidatorProposalRequest_RemoveOperator(cur realm, t *testing.T) {
175	resetValset(t)
176	resetCache()
177
178	// Seed valset with opA already signing under pubKeyA.
179	testing.SetSysParamStrings(module, submodule, currKey, []string{pubKeyA + ":10"})
180
181	opA := testutils.TestAddress("op-A")
182	seedCache(t, []struct {
183		op          address
184		pubKey      string
185		keepRunning bool
186	}{{op: opA, pubKey: pubKeyA, keepRunning: false}})
187
188	// Liveness floor: removing the only validator empties the set.
189	// Executor runs inside a crossing dao.Executor.Execute call, so
190	// the panic surfaces as an abort, not a regular panic.
191	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
192
193	uassert.AbortsContains(t, cur, "would empty the validator set", func() {
194		_ = newValoperChangeExecutor(cur, []ValoperChange{{OperatorAddress: opA, Power: 0}}).Execute(cross(cur))
195	})
196}
197
198func TestNewValidatorProposalRequest_RemoveLeavesOthers(cur realm, t *testing.T) {
199	resetValset(t)
200	resetCache()
201
202	// Seed valset with two validators.
203	testing.SetSysParamStrings(module, submodule, currKey, []string{
204		pubKeyA + ":10",
205		pubKeyB + ":5",
206	})
207
208	opA := testutils.TestAddress("op-A")
209	opB := testutils.TestAddress("op-B")
210	seedCache(t, []struct {
211		op          address
212		pubKey      string
213		keepRunning bool
214	}{
215		{op: opA, pubKey: pubKeyA, keepRunning: true},
216		{op: opB, pubKey: pubKeyB, keepRunning: true},
217	})
218
219	changes := []ValoperChange{{OperatorAddress: opA, Power: 0}}
220	// Build the executor directly (private function, same package).
221	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
222
223	urequire.NoError(t, newValoperChangeExecutor(cur, changes).Execute(cross(cur)))
224
225	// Effective set: only opB / pubKeyB remains.
226	effective := sysparams.GetValsetEffective()
227	urequire.Equal(t, 1, len(effective))
228	uassert.Equal(t, pubKeyB, effective[0].PubKey)
229}
230
231func TestNewValidatorProposalRequest_AllowsFullValsetReplacement(cur realm, t *testing.T) {
232	resetValset(t)
233	resetCache()
234
235	testing.SetSysParamStrings(module, submodule, currKey, []string{pubKeyA + ":10"})
236
237	opA := testutils.TestAddress("op-A")
238	opB := testutils.TestAddress("op-B")
239	seedCache(t, []struct {
240		op          address
241		pubKey      string
242		keepRunning bool
243	}{
244		{op: opA, pubKey: pubKeyA, keepRunning: false},
245		{op: opB, pubKey: pubKeyB, keepRunning: true},
246	})
247
248	changes := []ValoperChange{
249		{OperatorAddress: opA, Power: 0},
250		{OperatorAddress: opB, Power: 10},
251	}
252	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
253
254	urequire.NoError(t, newValoperChangeExecutor(cur, changes).Execute(cross(cur)))
255
256	effective := sysparams.GetValsetEffective()
257	urequire.Equal(t, 1, len(effective))
258	uassert.Equal(t, pubKeyB, effective[0].PubKey)
259	uassert.Equal(t, uint64(10), effective[0].VotingPower)
260}
261
262func TestNewValidatorProposalRequest_RejectsKeepRunningFalseAtCreation(cur realm, t *testing.T) {
263	resetCache()
264	op := testutils.TestAddress("op-A")
265	seedCache(t, []struct {
266		op          address
267		pubKey      string
268		keepRunning bool
269	}{{op: op, pubKey: pubKeyA, keepRunning: false}})
270
271	uassert.PanicsContains(t, cur, "KeepRunning=false", func() {
272		_ = NewValidatorProposalRequest(cur,
273			[]ValoperChange{{OperatorAddress: op, Power: 1}},
274			"add opted-out", "",
275		)
276	})
277}
278
279func TestNewValidatorProposalRequest_AllowsRemoveOfKeepRunningFalse(cur realm, t *testing.T) {
280	// KeepRunning=false is the operator's opt-out signal; removing
281	// such an operator must still be allowed (it's the standard
282	// exit path). Only adds are gated.
283	resetValset(t)
284	resetCache()
285
286	// Seed the valset with two operators so removing one doesn't
287	// trip the empty-valset liveness floor.
288	testing.SetSysParamStrings(module, submodule, currKey, []string{
289		pubKeyA + ":10",
290		pubKeyB + ":5",
291	})
292
293	opA := testutils.TestAddress("op-A")
294	opB := testutils.TestAddress("op-B")
295	seedCache(t, []struct {
296		op          address
297		pubKey      string
298		keepRunning bool
299	}{
300		{op: opA, pubKey: pubKeyA, keepRunning: false}, // opted out
301		{op: opB, pubKey: pubKeyB, keepRunning: true},
302	})
303
304	// Build proposal succeeds (remove path: Power=0 ignores KeepRunning).
305	pr := NewValidatorProposalRequest(cur,
306		[]ValoperChange{{OperatorAddress: opA, Power: 0}},
307		"remove opted-out opA", "",
308	)
309	_ = pr
310
311	// Executor also succeeds.
312	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
313
314	urequire.NoError(t, newValoperChangeExecutor(cur, []ValoperChange{{OperatorAddress: opA, Power: 0}}).Execute(cross(cur)))
315}
316
317func TestNewValidatorProposalRequest_RejectsDuplicateOp(cur realm, t *testing.T) {
318	// Each operator may appear at most once per proposal; any shape
319	// that mentions the same op twice must panic at create-time.
320	resetCache()
321	op := testutils.TestAddress("op-A")
322	seedCache(t, []struct {
323		op          address
324		pubKey      string
325		keepRunning bool
326	}{{op: op, pubKey: pubKeyA, keepRunning: true}})
327
328	cases := [][]ValoperChange{
329		{{OperatorAddress: op, Power: 0}, {OperatorAddress: op, Power: 7}}, // remove + re-add
330		{{OperatorAddress: op, Power: 7}, {OperatorAddress: op, Power: 8}}, // double add
331		{{OperatorAddress: op, Power: 0}, {OperatorAddress: op, Power: 0}}, // double remove
332	}
333	for _, changes := range cases {
334		uassert.PanicsContains(t, cur, "duplicate operator in proposal", func() {
335			_ = NewValidatorProposalRequest(cur, changes, "dup", "")
336		})
337	}
338}
339
340func TestNewValidatorProposalRequest_RejectsPowerUpdatePairForOptedOutOp(cur realm, t *testing.T) {
341	// KeepRunning=false is binding: no proposal shape can keep an
342	// opted-out operator in the active set. The dedupe rejection
343	// fires before the KR check is even reached.
344	resetCache()
345	op := testutils.TestAddress("op-A")
346	seedCache(t, []struct {
347		op          address
348		pubKey      string
349		keepRunning bool
350	}{{op: op, pubKey: pubKeyA, keepRunning: false}})
351
352	uassert.PanicsContains(t, cur, "duplicate operator in proposal", func() {
353		_ = NewValidatorProposalRequest(cur,
354			[]ValoperChange{
355				{OperatorAddress: op, Power: 0},
356				{OperatorAddress: op, Power: 7},
357			},
358			"bypass attempt", "",
359		)
360	})
361}
362
363func TestNewValidatorProposalRequest_UpsertExistingValidator(cur realm, t *testing.T) {
364	// Single-entry {op, newPower} against an op already in the
365	// effective valset must upsert: the existing entry's power is
366	// overwritten, no remove/re-add ceremony required.
367	resetValset(t)
368	resetCache()
369
370	// Seed valset with two validators; we upsert opA's power.
371	testing.SetSysParamStrings(module, submodule, currKey, []string{
372		pubKeyA + ":1",
373		pubKeyB + ":1",
374	})
375
376	opA := testutils.TestAddress("op-A")
377	opB := testutils.TestAddress("op-B")
378	seedCache(t, []struct {
379		op          address
380		pubKey      string
381		keepRunning bool
382	}{
383		{op: opA, pubKey: pubKeyA, keepRunning: true},
384		{op: opB, pubKey: pubKeyB, keepRunning: true},
385	})
386
387	changes := []ValoperChange{{OperatorAddress: opA, Power: 9}}
388	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
389
390	urequire.NoError(t, newValoperChangeExecutor(cur, changes).Execute(cross(cur)))
391
392	effective := sysparams.GetValsetEffective()
393	powerOf := map[string]uint64{}
394	for _, v := range effective {
395		powerOf[v.PubKey] = v.VotingPower
396	}
397	uassert.Equal(t, uint64(9), powerOf[pubKeyA], "opA power upserted from 1 to 9")
398	uassert.Equal(t, uint64(1), powerOf[pubKeyB], "opB unchanged")
399}
400
401func TestNewValidatorProposalRequest_ExecutorRejectsRaceFlippedKeepRunning(cur realm, t *testing.T) {
402	// KeepRunning=true at proposal-create time; operator flips to
403	// false BEFORE the executor runs. Race-safety check rejects.
404	resetValset(t)
405	resetCache()
406
407	op := testutils.TestAddress("op-A")
408	seedCache(t, []struct {
409		op          address
410		pubKey      string
411		keepRunning bool
412	}{{op: op, pubKey: pubKeyA, keepRunning: true}})
413
414	changes := []ValoperChange{{OperatorAddress: op, Power: 5}}
415	exec := newValoperChangeExecutor(cur, changes)
416
417	// Operator flips KeepRunning=false BEFORE the executor runs.
418	valoperCache.Set(op.String(), cacheEntry{
419		SigningPubKey:  pubKeyA,
420		SigningAddress: mustAddr(t, pubKeyA),
421		KeepRunning:    false,
422	})
423
424	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
425
426	uassert.AbortsContains(t, cur, "KeepRunning=false at execution", func() {
427		_ = exec.Execute(cross(cur))
428	})
429}
430
431func TestNewValidatorProposalRequest_NaturalRotationFlow_NoGhost(cur realm, t *testing.T) {
432	// RotateValoperSigningKey publishes valset:proposed before any
433	// subsequent executor reads, so baseline always reflects the
434	// post-rotation state. A later power-update upserts at NEW only;
435	// no OLD ghost.
436	resetValset(t)
437	resetCache()
438
439	testing.SetSysParamStrings(module, submodule, currKey, []string{
440		pubKeyA + ":1",
441		pubKeyB + ":5",
442	})
443	opA := testutils.TestAddress("op-A")
444	opB := testutils.TestAddress("op-B")
445	seedCache(t, []struct {
446		op          address
447		pubKey      string
448		keepRunning bool
449	}{
450		{op: opA, pubKey: pubKeyA, keepRunning: true},
451		{op: opB, pubKey: pubKeyB, keepRunning: true},
452	})
453
454	testing.SetRealm(testing.NewCodeRealm(valopersRealmPath))
455	RotateValoperSigningKey(cross(cur), opA, pubKeyA, pubKeyC)
456	NotifyValoperChanged(cross(cur), opA, pubKeyC, mustAddr(t, pubKeyC), true)
457
458	testing.SetRealm(testing.NewCodeRealm("gno.land/r/gov/dao/impl/v0"))
459	urequire.NoError(t, newValoperChangeExecutor(cur,
460		[]ValoperChange{{OperatorAddress: opA, Power: 2}},
461	).Execute(cross(cur)))
462
463	effective := sysparams.GetValsetEffective()
464	powerOf := map[string]uint64{}
465	for _, v := range effective {
466		powerOf[v.PubKey] = v.VotingPower
467	}
468	uassert.Equal(t, uint64(2), powerOf[pubKeyC], "opA published at NEW power=2")
469	uassert.Equal(t, uint64(5), powerOf[pubKeyB], "opB unchanged")
470	_, ghost := powerOf[pubKeyA]
471	uassert.False(t, ghost, "OLD signing key (pubKeyA) must not linger in valset")
472	urequire.Equal(t, 2, len(effective), "exactly two entries — opA(NEW), opB")
473}
474
475func TestNewValidatorProposalRequest_PhantomBaselineDocumentsUnreachableState(cur realm, t *testing.T) {
476	// Pin executor behavior on a phantom state (cache=NEW,
477	// valset:current=OLD, dirty=false) — unreachable via natural
478	// flow because RotateValoperSigningKey publishes proposed
479	// before NotifyValoperChanged updates the cache. If a future
480	// code path ever updates the cache without going through
481	// Rotate, the asymmetry would be a real bug; this test pins
482	// the current behavior so the divergence surfaces.
483	resetValset(t)
484	resetCache()
485
486	testing.SetSysParamStrings(module, submodule, currKey, []string{
487		pubKeyA + ":1",
488		pubKeyB + ":5",
489	})
490
491	opA := testutils.TestAddress("op-A")
492	opB := testutils.TestAddress("op-B")
493	seedCache(t, []struct {
494		op          address
495		pubKey      string
496		keepRunning bool
497	}{
498		{op: opA, pubKey: pubKeyC, keepRunning: true},
499		{op: opB, pubKey: pubKeyB, keepRunning: true},
500	})
501
502	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
503
504	urequire.NoError(t, newValoperChangeExecutor(cur,
505		[]ValoperChange{{OperatorAddress: opA, Power: 2}},
506	).Execute(cross(cur)))
507
508	effective := sysparams.GetValsetEffective()
509	powerOf := map[string]uint64{}
510	for _, v := range effective {
511		powerOf[v.PubKey] = v.VotingPower
512	}
513	uassert.Equal(t, uint64(1), powerOf[pubKeyA], "phantom OLD lingers from baseline")
514	uassert.Equal(t, uint64(2), powerOf[pubKeyC], "executor upserts at NEW")
515	uassert.Equal(t, uint64(5), powerOf[pubKeyB], "opB unchanged")
516	urequire.Equal(t, 3, len(effective),
517		"three entries — phantom-state ghost; this state is unreachable via natural flow")
518}
519
520func TestNewValidatorProposalRequest_SameBlockExecuteThenRotate(cur realm, t *testing.T) {
521	// Same-block ordering: proposal-execute writes proposed
522	// (dirty=true); a subsequent rotation reads proposed-when-dirty
523	// and accumulates the prior power change rather than clobbering
524	// back to current.
525	resetValset(t)
526	resetCache()
527
528	testing.SetSysParamStrings(module, submodule, currKey, []string{
529		pubKeyA + ":1",
530		pubKeyB + ":5",
531	})
532
533	opA := testutils.TestAddress("op-A")
534	opB := testutils.TestAddress("op-B")
535	seedCache(t, []struct {
536		op          address
537		pubKey      string
538		keepRunning bool
539	}{
540		{op: opA, pubKey: pubKeyA, keepRunning: true},
541		{op: opB, pubKey: pubKeyB, keepRunning: true},
542	})
543
544	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
545
546	urequire.NoError(t, newValoperChangeExecutor(cur,
547		[]ValoperChange{{OperatorAddress: opA, Power: 3}},
548	).Execute(cross(cur)))
549
550	testing.SetRealm(testing.NewCodeRealm(valopersRealmPath))
551	RotateValoperSigningKey(cross(cur), opA, pubKeyA, pubKeyC)
552	NotifyValoperChanged(cross(cur), opA, pubKeyC, mustAddr(t, pubKeyC), true)
553
554	effective := sysparams.GetValsetEffective()
555	powerOf := map[string]uint64{}
556	for _, v := range effective {
557		powerOf[v.PubKey] = v.VotingPower
558	}
559	uassert.Equal(t, uint64(3), powerOf[pubKeyC], "rotation accumulates with prior upsert; final power=3")
560	uassert.Equal(t, uint64(5), powerOf[pubKeyB], "opB unchanged")
561	_, gotOld := powerOf[pubKeyA]
562	uassert.False(t, gotOld, "OLD signing key (pubKeyA) must not appear after rotation")
563	urequire.Equal(t, 2, len(effective), "exactly two entries — opA(NEW) and opB")
564}
565
566// TestNewValidatorProposalRequest_ExecutorVanishedCacheEntryPanics pins
567// the "operator vanished from valoperCache between propose and execute"
568// branch. No production path deletes from valoperCache today (only Set
569// is called by NotifyValoperChanged), but bptree.BPTree exposes Remove
570// so the underlying data structure does support deletion. This test
571// simulates that hypothetical state directly via the package-private
572// cache var to confirm the executor panics with the documented message
573// rather than silently mis-publishing an empty/wrong valset.
574//
575// If a future commit ever introduces a public cache-delete path, this
576// test still passes — it's a contract-pinning test for the panic itself.
577// If the team decides the branch is unreachable enough to drop, this
578// test is the first thing to break.
579func TestNewValidatorProposalRequest_ExecutorVanishedCacheEntryPanics(cur realm, t *testing.T) {
580	resetValset(t)
581	resetCache()
582
583	op := testutils.TestAddress("op-A")
584	seedCache(t, []struct {
585		op          address
586		pubKey      string
587		keepRunning bool
588	}{{op: op, pubKey: pubKeyA, keepRunning: true}})
589
590	exec := newValoperChangeExecutor(cur,
591		[]ValoperChange{{OperatorAddress: op, Power: 5}},
592	)
593
594	// Simulate the unreachable-today state: cache entry deleted between
595	// proposal-create and proposal-execute. Direct package-private mutation
596	// (NOT a public API) — production code has no path here today.
597	_, removed := valoperCache.Remove(op.String())
598	urequire.True(t, removed, "fixture: cache entry must have been present before Remove")
599
600	testing.SetRealm(testing.NewCodeRealm(asGovDAOProxy))
601
602	uassert.AbortsContains(t, cur, "operator vanished from valoperCache between propose and execute", func() {
603		_ = exec.Execute(cross(cur))
604	})
605}
606
607// contains is a tiny strings.Contains shim so tests don't import a
608// new package.
609func contains(s, substr string) bool {
610	for i := 0; i+len(substr) <= len(s); i++ {
611		if s[i:i+len(substr)] == substr {
612			return true
613		}
614	}
615	return false
616}