@@ -10,13 +10,15 @@ import (
1010 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
1111 "github.com/dydxprotocol/v4-chain/protocol/lib"
1212
13+ storetypes "cosmossdk.io/store/types"
1314 sdk "github.com/cosmos/cosmos-sdk/types"
1415 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
1516 "github.com/dydxprotocol/v4-chain/protocol/dtypes"
1617 indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events"
1718 bank_testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/bank"
1819 big_testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/big"
1920 "github.com/dydxprotocol/v4-chain/protocol/testutil/constants"
21+ keepertest "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper"
2022 testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper"
2123 "github.com/dydxprotocol/v4-chain/protocol/testutil/nullify"
2224 perptest "github.com/dydxprotocol/v4-chain/protocol/testutil/perpetuals"
@@ -5937,3 +5939,115 @@ func TestIsValidStateTransitionForUndercollateralizedSubaccount_ZeroMarginRequir
59375939 })
59385940 }
59395941}
5942+
5943+ func TestGetAllRelevantPerpetuals_Deterministic (t * testing.T ) {
5944+ tests := map [string ]struct {
5945+ // state
5946+ perpetuals []perptypes.Perpetual
5947+
5948+ // subaccount state
5949+ assetPositions []* types.AssetPosition
5950+ perpetualPositions []* types.PerpetualPosition
5951+
5952+ // updates
5953+ assetUpdates []types.AssetUpdate
5954+ perpetualUpdates []types.PerpetualUpdate
5955+ }{
5956+ "Gas used is deterministic when erroring on gas usage" : {
5957+ assetPositions : testutil .CreateUsdcAssetPosition (big .NewInt (10_000_000_001 )), // $10,000.000001
5958+ perpetuals : []perptypes.Perpetual {
5959+ constants .BtcUsd_NoMarginRequirement ,
5960+ constants .EthUsd_NoMarginRequirement ,
5961+ constants .SolUsd_20PercentInitial_10PercentMaintenance ,
5962+ },
5963+ perpetualPositions : []* types.PerpetualPosition {
5964+ & constants .PerpetualPosition_OneBTCLong ,
5965+ & constants .PerpetualPosition_OneTenthEthLong ,
5966+ & constants .PerpetualPosition_OneSolLong ,
5967+ },
5968+ assetUpdates : []types.AssetUpdate {
5969+ {
5970+ AssetId : constants .Usdc .Id ,
5971+ BigQuantumsDelta : big .NewInt (1_000_000 ), // +1 USDC
5972+ },
5973+ },
5974+ perpetualUpdates : []types.PerpetualUpdate {
5975+ {
5976+ PerpetualId : uint32 (0 ),
5977+ BigQuantumsDelta : big .NewInt (- 200_000_000 ), // -2 BTC
5978+ },
5979+ {
5980+ PerpetualId : uint32 (1 ),
5981+ BigQuantumsDelta : big .NewInt (250_000_000 ), // .25 ETH
5982+ },
5983+ {
5984+ PerpetualId : uint32 (2 ),
5985+ BigQuantumsDelta : big .NewInt (500_000_000 ), // .005 SOL
5986+ },
5987+ },
5988+ },
5989+ }
5990+
5991+ for name , tc := range tests {
5992+ t .Run (name , func (t * testing.T ) {
5993+ // Setup.
5994+ ctx , keeper , pricesKeeper , perpetualsKeeper , _ , _ , assetsKeeper , _ , _ := keepertest .SubaccountsKeepers (
5995+ t ,
5996+ true ,
5997+ )
5998+ keepertest .CreateTestMarkets (t , ctx , pricesKeeper )
5999+ keepertest .CreateTestLiquidityTiers (t , ctx , perpetualsKeeper )
6000+ keepertest .CreateTestPerpetuals (t , ctx , perpetualsKeeper )
6001+ for _ , p := range tc .perpetuals {
6002+ perpetualsKeeper .SetPerpetualForTest (ctx , p )
6003+ }
6004+ require .NoError (t , keepertest .CreateUsdcAsset (ctx , assetsKeeper ))
6005+
6006+ subaccount := createNSubaccount (keeper , ctx , 1 , big .NewInt (1_000 ))[0 ]
6007+ subaccount .PerpetualPositions = tc .perpetualPositions
6008+ subaccount .AssetPositions = tc .assetPositions
6009+ keeper .SetSubaccount (ctx , subaccount )
6010+ subaccountId := * subaccount .Id
6011+
6012+ update := types.Update {
6013+ SubaccountId : subaccountId ,
6014+ AssetUpdates : tc .assetUpdates ,
6015+ PerpetualUpdates : tc .perpetualUpdates ,
6016+ }
6017+
6018+ // Execute.
6019+ gasUsedBefore := ctx .GasMeter ().GasConsumed ()
6020+ _ , err := keeper .GetAllRelevantPerpetuals (ctx , []types.Update {update })
6021+ require .NoError (t , err )
6022+ gasUsedAfter := ctx .GasMeter ().GasConsumed ()
6023+
6024+ gasUsed := uint64 (0 )
6025+ // Run 100 times since it's highly unlikely gas usage is deterministic over 100 times if
6026+ // there's non-determinism.
6027+ for range 100 {
6028+ // divide by 2 so that the state read fails at least second to last time.
6029+ ctxWithLimitedGas := ctx .WithGasMeter (storetypes .NewGasMeter ((gasUsedAfter - gasUsedBefore ) / 2 ))
6030+
6031+ require .PanicsWithValue (
6032+ t ,
6033+ storetypes.ErrorOutOfGas {Descriptor : "ReadPerByte" },
6034+ func () {
6035+ _ , _ = keeper .GetAllRelevantPerpetuals (ctxWithLimitedGas , []types.Update {update })
6036+ },
6037+ )
6038+
6039+ if gasUsed == 0 {
6040+ gasUsed = ctxWithLimitedGas .GasMeter ().GasConsumed ()
6041+ require .Greater (t , gasUsed , uint64 (0 ))
6042+ } else {
6043+ require .Equal (
6044+ t ,
6045+ gasUsed ,
6046+ ctxWithLimitedGas .GasMeter ().GasConsumed (),
6047+ "Gas usage when out of gas is not deterministic" ,
6048+ )
6049+ }
6050+ }
6051+ })
6052+ }
6053+ }
0 commit comments