@@ -415,3 +415,79 @@ func TestVoteChainMetaContractState(t *testing.T) {
415415 require .Equal (t , new (big.Int ).SetUint64 (observedAt ), got )
416416 })
417417}
418+
419+ // TestVoteChainMetaAbsoluteStaleness verifies that when all validators' observedAt timestamps
420+ // are older than ObservedAtStalenessThresholdSeconds relative to the current block time,
421+ // the EVM contract is NOT updated (it retains its previous value).
422+ //
423+ // These tests call VoteChainMeta directly on the keeper (bypassing authz) so that
424+ // block time can be freely manipulated without hitting authz grant expiry.
425+ func TestVoteChainMetaAbsoluteStaleness (t * testing.T ) {
426+ chainId := "eip155:11155111"
427+ threshold := uexecutortypes .ObservedAtStalenessThresholdSeconds // 300
428+
429+ universalCoreAddr := utils .GetDefaultAddresses ().HandlerAddr
430+
431+ readGasPrice := func (t * testing.T , testApp * app.ChainApp , ctx sdk.Context ) * big.Int {
432+ t .Helper ()
433+ ucABI , err := uexecutortypes .ParseUniversalCoreABI ()
434+ require .NoError (t , err )
435+ caller , _ := testApp .UexecutorKeeper .GetUeModuleAddress (ctx )
436+ res , err := testApp .EVMKeeper .CallEVM (ctx , ucABI , caller , universalCoreAddr , false , "gasPriceByChainNamespace" , chainId )
437+ require .NoError (t , err )
438+ return new (big.Int ).SetBytes (res .Ret )
439+ }
440+
441+ t .Run ("stale single vote does not update contract" , func (t * testing.T ) {
442+ testApp , ctx , _ , vals := setupVoteChainMetaTest (t , 1 )
443+
444+ staleObservedAt := uint64 (1_700_000_000 )
445+ // Block time is far past the staleness window
446+ staleCtx := ctx .WithBlockTime (time .Unix (int64 (staleObservedAt + threshold + 60 ), 0 ))
447+
448+ valAddr , err := sdk .ValAddressFromBech32 (vals [0 ].OperatorAddress )
449+ require .NoError (t , err )
450+
451+ require .NoError (t , testApp .UexecutorKeeper .VoteChainMeta (staleCtx , valAddr , chainId ,
452+ 100_000_000_000 , 12345 , staleObservedAt ))
453+
454+ // Vote was stored in state
455+ stored , found , err := testApp .UexecutorKeeper .GetChainMeta (staleCtx , chainId )
456+ require .NoError (t , err )
457+ require .True (t , found )
458+ require .Equal (t , uint64 (100_000_000_000 ), stored .Prices [0 ])
459+
460+ // Contract must NOT have been updated — should still be 0
461+ require .Zero (t , readGasPrice (t , testApp , staleCtx ).Sign (),
462+ "contract must not be updated when all validators are stale" )
463+ })
464+
465+ t .Run ("all validators stale does not update contract" , func (t * testing.T ) {
466+ testApp , ctx , _ , vals := setupVoteChainMetaTest (t , 3 )
467+
468+ freshObservedAt := uint64 (1_700_000_000 )
469+
470+ // First vote with fresh block time → contract gets updated
471+ freshCtx := ctx .WithBlockTime (time .Unix (int64 (freshObservedAt ), 0 ))
472+ for i := 0 ; i < 3 ; i ++ {
473+ valAddr , err := sdk .ValAddressFromBech32 (vals [i ].OperatorAddress )
474+ require .NoError (t , err )
475+ require .NoError (t , testApp .UexecutorKeeper .VoteChainMeta (freshCtx , valAddr , chainId ,
476+ 200_000_000_000 , uint64 (12345 + i ), freshObservedAt + uint64 (i )))
477+ }
478+ require .Equal (t , new (big.Int ).SetUint64 (200_000_000_000 ), readGasPrice (t , testApp , freshCtx ))
479+
480+ // Re-vote with same old timestamps but block time past staleness window
481+ futureCtx := ctx .WithBlockTime (time .Unix (int64 (freshObservedAt + threshold + 60 ), 0 ))
482+ for i := 0 ; i < 3 ; i ++ {
483+ valAddr , err := sdk .ValAddressFromBech32 (vals [i ].OperatorAddress )
484+ require .NoError (t , err )
485+ require .NoError (t , testApp .UexecutorKeeper .VoteChainMeta (futureCtx , valAddr , chainId ,
486+ 999_000_000_000 , uint64 (99999 + i ), freshObservedAt + uint64 (i )))
487+ }
488+
489+ // Contract must retain the old fresh value — stale votes must not overwrite it
490+ require .Equal (t , new (big.Int ).SetUint64 (200_000_000_000 ), readGasPrice (t , testApp , futureCtx ),
491+ "contract must retain last good value when all validators report stale data" )
492+ })
493+ }
0 commit comments