Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:state:Ignore market balance after nv23 #11976

Merged
merged 8 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,12 @@ workflows:
- build
suite: itest-splitstore
target: "./itests/splitstore_test.go"
- test:
name: test-itest-supply
requires:
- build
suite: itest-supply
target: "./itests/supply_test.go"
- test:
name: test-itest-verifreg
requires:
Expand Down
37 changes: 24 additions & 13 deletions chain/stmgr/supply.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"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/network"
msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"

"github.com/filecoin-project/lotus/api"
Expand Down Expand Up @@ -303,7 +304,10 @@ func getFilPowerLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmoun
return pst.TotalLocked()
}

func GetFilLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) {
func GetFilLocked(ctx context.Context, st *state.StateTree, nv network.Version) (abi.TokenAmount, error) {
if nv >= network.Version23 {
return getFilPowerLocked(ctx, st)
}

filMarketLocked, err := getFilMarketLocked(ctx, st)
if err != nil {
Expand Down Expand Up @@ -337,6 +341,7 @@ func (sm *StateManager) GetVMCirculatingSupply(ctx context.Context, height abi.C
}

func (sm *StateManager) GetVMCirculatingSupplyDetailed(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) {
nv := sm.GetNetworkVersion(ctx, height)
filVested, err := sm.GetFilVested(ctx, height)
if err != nil {
return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filVested: %w", err)
Expand All @@ -360,7 +365,7 @@ func (sm *StateManager) GetVMCirculatingSupplyDetailed(ctx context.Context, heig
return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filBurnt: %w", err)
}

filLocked, err := GetFilLocked(ctx, st)
filLocked, err := GetFilLocked(ctx, st, nv)
if err != nil {
return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filLocked: %w", err)
}
Expand All @@ -387,6 +392,8 @@ func (sm *StateManager) GetVMCirculatingSupplyDetailed(ctx context.Context, heig
func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) {
circ := big.Zero()
unCirc := big.Zero()
nv := sm.GetNetworkVersion(ctx, height)

err := st.ForEach(func(a address.Address, actor *types.Actor) error {
// this can be a lengthy operation, we need to cancel early when
// the context is cancelled to avoid resource exhaustion
Expand Down Expand Up @@ -415,19 +422,23 @@ func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.Cha
unCirc = big.Add(unCirc, actor.Balance)

case a == market.Address:
mst, err := market.Load(sm.cs.ActorStore(ctx), actor)
if err != nil {
return err
}

lb, err := mst.TotalLocked()
if err != nil {
return err
if nv >= network.Version23 {
circ = big.Add(circ, actor.Balance)
} else {
mst, err := market.Load(sm.cs.ActorStore(ctx), actor)
if err != nil {
return err
}

lb, err := mst.TotalLocked()
if err != nil {
return err
}

circ = big.Add(circ, big.Sub(actor.Balance, lb))
unCirc = big.Add(unCirc, lb)
}

circ = big.Add(circ, big.Sub(actor.Balance, lb))
unCirc = big.Add(unCirc, lb)

case builtin.IsAccountActor(actor.Code) ||
builtin.IsPaymentChannelActor(actor.Code) ||
builtin.IsEthAccountActor(actor.Code) ||
Expand Down
205 changes: 205 additions & 0 deletions itests/supply_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
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
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
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,
}
}