lotus/itests/supply_test.go

206 lines
6.3 KiB
Go

package itests
import (
"bytes"
"context"
"testing"
"time"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/go-state-types/builtin/v9/market"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-state-types/network"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/itests/kit"
)
func TestCirciulationSupplyUpgrade(t *testing.T) {
kit.QuietMiningLogs()
ctx := context.Background()
// Choosing something divisible by epochs per day to remove error with simple deal duration
lockedClientBalance := big.Mul(abi.NewTokenAmount(11_520_000), abi.NewTokenAmount(1e18))
lockedProviderBalance := big.Mul(abi.NewTokenAmount(1_000_000), abi.NewTokenAmount(1e18))
var height0 abi.ChainEpoch
var height1 abi.ChainEpoch
// Lock collateral in market on nv22 network
fullNode0, blockMiner0, ens0 := kit.EnsembleMinimal(t,
kit.GenesisNetworkVersion(network.Version22),
kit.MockProofs(),
)
{
worker0 := blockMiner0.OwnerKey.Address
ens0.InterconnectAll().BeginMining(50 * time.Millisecond)
// Lock collateral in market actor
wallet0, err := fullNode0.WalletDefaultAddress(ctx)
require.NoError(t, err)
// Add 1 FIL to cover provider collateral
c00, err := fullNode0.MarketAddBalance(ctx, wallet0, wallet0, lockedClientBalance)
require.NoError(t, err)
fullNode0.WaitMsg(ctx, c00)
c01, err := blockMiner0.FullNode.MarketAddBalance(ctx, worker0, blockMiner0.ActorAddr, lockedProviderBalance)
require.NoError(t, err)
fullNode0.WaitMsg(ctx, c01)
psd0, err := fullNode0.MpoolPushMessage(ctx,
makePSDMessage(
ctx,
t,
blockMiner0.ActorAddr,
worker0,
wallet0,
lockedProviderBalance,
lockedClientBalance,
fullNode0.WalletSign,
),
nil,
)
require.NoError(t, err)
fullNode0.WaitMsg(ctx, psd0.Cid())
head, err := fullNode0.ChainHead(ctx)
require.NoError(t, err)
height0 = head.Height()
}
// Lock collateral in market on nv23 network
fullNode1, blockMiner1, ens1 := kit.EnsembleMinimal(t,
kit.GenesisNetworkVersion(network.Version23),
kit.MockProofs(),
)
{
worker1 := blockMiner1.OwnerKey.Address
ens1.InterconnectAll().BeginMining(50 * time.Millisecond)
// Lock collateral in market actor
wallet1, err := fullNode1.WalletDefaultAddress(ctx)
require.NoError(t, err)
c10, err := fullNode1.MarketAddBalance(ctx, wallet1, wallet1, lockedClientBalance)
require.NoError(t, err)
fullNode1.WaitMsg(ctx, c10)
c11, err := blockMiner1.FullNode.MarketAddBalance(ctx, worker1, blockMiner1.ActorAddr, lockedProviderBalance)
require.NoError(t, err)
fullNode1.WaitMsg(ctx, c11)
psd1, err := fullNode1.MpoolPushMessage(ctx,
makePSDMessage(
ctx,
t,
blockMiner1.ActorAddr,
worker1,
wallet1,
lockedProviderBalance,
lockedClientBalance,
fullNode1.WalletSign,
),
nil,
)
require.NoError(t, err)
fullNode1.WaitMsg(ctx, psd1.Cid())
head, err := fullNode1.ChainHead(ctx)
require.NoError(t, err)
height1 = head.Height()
}
// Measure each circulating supply at the latest height where market balance was locked
// This allows us to normalize against fluctuations in circulating supply based on the underlying
// dynamics irrelevant to this change
max := height0
if height0 < height1 {
max = height1
}
max = max + 1 // Measure supply at height after the deal locking funds was published
// Let both chains catch up
fullNode0.WaitTillChain(ctx, func(ts *types.TipSet) bool {
return ts.Height() >= max
})
fullNode1.WaitTillChain(ctx, func(ts *types.TipSet) bool {
return ts.Height() >= max
})
ts0, err := fullNode0.ChainGetTipSetByHeight(ctx, max, types.EmptyTSK)
require.NoError(t, err)
ts1, err := fullNode1.ChainGetTipSetByHeight(ctx, max, types.EmptyTSK)
require.NoError(t, err)
nv22Supply, err := fullNode0.StateVMCirculatingSupplyInternal(ctx, ts0.Key())
require.NoError(t, err, "Failed to fetch nv22 circulating supply")
nv23Supply, err := fullNode1.StateVMCirculatingSupplyInternal(ctx, ts1.Key())
require.NoError(t, err, "Failed to fetch nv23 circulating supply")
// Unfortunately there's still some non-determinism in supply dynamics so check for equality within a tolerance
tolerance := big.Mul(abi.NewTokenAmount(1000), abi.NewTokenAmount(1e18))
totalLocked := big.Sum(lockedClientBalance, lockedProviderBalance)
diff := big.Sub(
big.Sum(totalLocked, nv23Supply.FilLocked),
nv22Supply.FilLocked,
)
assert.Equal(t, -1, big.Cmp(
diff.Abs(),
tolerance), "Difference from expected locked supply between versions exceeds tolerance")
}
// Message will be valid and lock funds but the data is fake so the deal will never be activated
func makePSDMessage(
ctx context.Context,
t *testing.T,
provider,
worker,
client address.Address,
providerLocked,
clientLocked abi.TokenAmount,
signFunc func(context.Context, address.Address, []byte) (*crypto.Signature, error)) *types.Message {
dummyCid, err := cid.Parse("baga6ea4seaqflae5c3k2odz4sqfufmrmoegplhk5jbq3co4fgmmy56yc2qfh4aq")
require.NoError(t, err)
duration := 2880 * 200 // 200 days
ppe := big.Div(clientLocked, big.NewInt(2880*200))
proposal := market.DealProposal{
PieceCID: dummyCid,
PieceSize: abi.PaddedPieceSize(128),
VerifiedDeal: false,
Client: client,
Provider: provider,
ClientCollateral: big.Zero(),
ProviderCollateral: providerLocked,
StartEpoch: 10000,
EndEpoch: 10000 + abi.ChainEpoch(duration),
StoragePricePerEpoch: ppe,
}
buf := bytes.NewBuffer(nil)
require.NoError(t, proposal.MarshalCBOR(buf))
sig, err := signFunc(ctx, client, buf.Bytes())
require.NoError(t, err)
// Publish storage deal
params, err := actors.SerializeParams(&market.PublishStorageDealsParams{
Deals: []market.ClientDealProposal{
{
Proposal: proposal,
ClientSignature: *sig,
},
},
})
require.NoError(t, err)
return &types.Message{
To: builtin.StorageMarketActorAddr,
From: worker,
Value: types.NewInt(0),
Method: builtin.MethodsMarket.PublishStorageDeals,
Params: params,
}
}