@@ -10,6 +10,7 @@ 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"
@@ -6019,3 +6020,114 @@ func TestGetNetCollateralAndMarginRequirements(t *testing.T) {
60196020 })
60206021 }
60216022}
6023+
6024+ func TestGetAllRelevantPerpetuals_Deterministic (t * testing.T ) {
6025+ tests := map [string ]struct {
6026+ // state
6027+ perpetuals []perptypes.Perpetual
6028+
6029+ // subaccount state
6030+ assetPositions []* types.AssetPosition
6031+ perpetualPositions []* types.PerpetualPosition
6032+
6033+ // updates
6034+ assetUpdates []types.AssetUpdate
6035+ perpetualUpdates []types.PerpetualUpdate
6036+ }{
6037+ "Gas used is deterministic when erroring on gas usage" : {
6038+ assetPositions : testutil .CreateUsdcAssetPositions (big .NewInt (10_000_000_001 )), // $10,000.000001
6039+ perpetuals : []perptypes.Perpetual {
6040+ constants .BtcUsd_NoMarginRequirement ,
6041+ constants .EthUsd_NoMarginRequirement ,
6042+ constants .SolUsd_20PercentInitial_10PercentMaintenance ,
6043+ },
6044+ perpetualPositions : []* types.PerpetualPosition {
6045+ & constants .PerpetualPosition_OneBTCLong ,
6046+ & constants .PerpetualPosition_OneTenthEthLong ,
6047+ & constants .PerpetualPosition_OneSolLong ,
6048+ },
6049+ assetUpdates : []types.AssetUpdate {
6050+ {
6051+ AssetId : constants .Usdc .Id ,
6052+ BigQuantumsDelta : big .NewInt (1_000_000 ), // +1 USDC
6053+ },
6054+ },
6055+ perpetualUpdates : []types.PerpetualUpdate {
6056+ {
6057+ PerpetualId : uint32 (0 ),
6058+ BigQuantumsDelta : big .NewInt (- 200_000_000 ), // -2 BTC
6059+ },
6060+ {
6061+ PerpetualId : uint32 (1 ),
6062+ BigQuantumsDelta : big .NewInt (250_000_000 ), // .25 ETH
6063+ },
6064+ {
6065+ PerpetualId : uint32 (2 ),
6066+ BigQuantumsDelta : big .NewInt (500_000_000 ), // .005 SOL
6067+ },
6068+ },
6069+ },
6070+ }
6071+ for name , tc := range tests {
6072+ t .Run (name , func (t * testing.T ) {
6073+ // Setup.
6074+ ctx , keeper , pricesKeeper , perpetualsKeeper , _ , _ , assetsKeeper , _ , _ , _ , _ := keepertest .SubaccountsKeepers (
6075+ t ,
6076+ true ,
6077+ )
6078+ keepertest .CreateTestMarkets (t , ctx , pricesKeeper )
6079+ keepertest .CreateTestLiquidityTiers (t , ctx , perpetualsKeeper )
6080+ keepertest .CreateTestPerpetuals (t , ctx , perpetualsKeeper )
6081+ for _ , p := range tc .perpetuals {
6082+ perpetualsKeeper .SetPerpetualForTest (ctx , p )
6083+ }
6084+ require .NoError (t , keepertest .CreateUsdcAsset (ctx , assetsKeeper ))
6085+
6086+ subaccount := createNSubaccount (keeper , ctx , 1 , big .NewInt (1_000 ))[0 ]
6087+ subaccount .PerpetualPositions = tc .perpetualPositions
6088+ subaccount .AssetPositions = tc .assetPositions
6089+ keeper .SetSubaccount (ctx , subaccount )
6090+ subaccountId := * subaccount .Id
6091+
6092+ update := types.Update {
6093+ SubaccountId : subaccountId ,
6094+ AssetUpdates : tc .assetUpdates ,
6095+ PerpetualUpdates : tc .perpetualUpdates ,
6096+ }
6097+
6098+ // Execute.
6099+ gasUsedBefore := ctx .GasMeter ().GasConsumed ()
6100+ _ , err := keeper .GetAllRelevantPerpetuals (ctx , []types.Update {update })
6101+ require .NoError (t , err )
6102+ gasUsedAfter := ctx .GasMeter ().GasConsumed ()
6103+
6104+ gasUsed := uint64 (0 )
6105+ // Run 100 times since it's highly unlikely gas usage is deterministic over 100 times if
6106+ // there's non-determinism.
6107+ for range 100 {
6108+ // divide by 2 so that the state read fails at least second to last time.
6109+ ctxWithLimitedGas := ctx .WithGasMeter (storetypes .NewGasMeter ((gasUsedAfter - gasUsedBefore ) / 2 ))
6110+
6111+ require .PanicsWithValue (
6112+ t ,
6113+ storetypes.ErrorOutOfGas {Descriptor : "ReadFlat" },
6114+ func () {
6115+ _ , _ = keeper .GetAllRelevantPerpetuals (ctxWithLimitedGas , []types.Update {update })
6116+ },
6117+ )
6118+
6119+ if gasUsed == 0 {
6120+ gasUsed = ctxWithLimitedGas .GasMeter ().GasConsumed ()
6121+ require .Greater (t , gasUsed , uint64 (0 ))
6122+ } else {
6123+ require .Equal (
6124+ t ,
6125+ gasUsed ,
6126+ ctxWithLimitedGas .GasMeter ().GasConsumed (),
6127+ "Gas usage when out of gas is not deterministic" ,
6128+ )
6129+ }
6130+ }
6131+ })
6132+ }
6133+ }
0 commit comments