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