2020-06-26 15:51:45 +00:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-06-26 19:36:48 +00:00
|
|
|
"testing"
|
|
|
|
|
2020-09-17 02:34:13 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
|
|
|
|
2020-08-05 00:28:05 +00:00
|
|
|
"github.com/filecoin-project/go-bitfield"
|
|
|
|
|
2020-07-02 01:20:54 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"golang.org/x/xerrors"
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-07-02 01:20:54 +00:00
|
|
|
"github.com/ipfs/go-cid"
|
2020-06-26 15:51:45 +00:00
|
|
|
cbornode "github.com/ipfs/go-ipld-cbor"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-address"
|
2020-09-07 03:49:10 +00:00
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
|
|
"github.com/filecoin-project/go-state-types/big"
|
|
|
|
"github.com/filecoin-project/go-state-types/crypto"
|
2020-09-17 02:13:12 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
2020-09-18 21:59:27 +00:00
|
|
|
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
|
|
|
|
market0 "github.com/filecoin-project/specs-actors/actors/builtin/market"
|
2020-09-17 02:13:12 +00:00
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
2020-07-07 23:39:32 +00:00
|
|
|
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
2020-07-02 01:20:54 +00:00
|
|
|
tutils "github.com/filecoin-project/specs-actors/support/testing"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
2020-07-23 02:05:11 +00:00
|
|
|
bstore "github.com/filecoin-project/lotus/lib/blockstore"
|
2020-06-26 15:51:45 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var dummyCid cid.Cid
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
dummyCid, _ = cid.Parse("bafkqaaa")
|
|
|
|
}
|
|
|
|
|
2020-06-26 19:36:48 +00:00
|
|
|
type mockAPI struct {
|
2020-06-26 15:51:45 +00:00
|
|
|
ts map[types.TipSetKey]*types.Actor
|
|
|
|
bs bstore.Blockstore
|
|
|
|
}
|
|
|
|
|
2020-06-26 19:36:48 +00:00
|
|
|
func newMockAPI(bs bstore.Blockstore) *mockAPI {
|
|
|
|
return &mockAPI{
|
2020-06-26 15:51:45 +00:00
|
|
|
bs: bs,
|
|
|
|
ts: make(map[types.TipSetKey]*types.Actor),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-26 19:36:48 +00:00
|
|
|
func (m mockAPI) ChainHasObj(ctx context.Context, c cid.Cid) (bool, error) {
|
2020-06-26 15:51:45 +00:00
|
|
|
return m.bs.Has(c)
|
|
|
|
}
|
|
|
|
|
2020-06-26 19:36:48 +00:00
|
|
|
func (m mockAPI) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
|
2020-06-26 15:51:45 +00:00
|
|
|
blk, err := m.bs.Get(c)
|
|
|
|
if err != nil {
|
|
|
|
return nil, xerrors.Errorf("blockstore get: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return blk.RawData(), nil
|
|
|
|
}
|
|
|
|
|
2020-06-26 19:36:48 +00:00
|
|
|
func (m mockAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) {
|
2020-06-26 15:51:45 +00:00
|
|
|
return m.ts[tsk], nil
|
|
|
|
}
|
|
|
|
|
2020-06-26 19:36:48 +00:00
|
|
|
func (m mockAPI) setActor(tsk types.TipSetKey, act *types.Actor) {
|
2020-06-26 15:51:45 +00:00
|
|
|
m.ts[tsk] = act
|
|
|
|
}
|
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
func TestMarketPredicates(t *testing.T) {
|
2020-06-26 15:51:45 +00:00
|
|
|
ctx := context.Background()
|
2020-07-23 02:05:11 +00:00
|
|
|
bs := bstore.NewTemporarySync()
|
2020-07-23 00:14:54 +00:00
|
|
|
store := adt.WrapStore(ctx, cbornode.NewCborStore(bs))
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
oldDeal1 := &market0.DealState{
|
2020-07-07 23:39:32 +00:00
|
|
|
SectorStartEpoch: 1,
|
|
|
|
LastUpdatedEpoch: 2,
|
|
|
|
SlashEpoch: 0,
|
|
|
|
}
|
2020-09-18 21:59:27 +00:00
|
|
|
oldDeal2 := &market0.DealState{
|
2020-07-07 23:39:32 +00:00
|
|
|
SectorStartEpoch: 4,
|
|
|
|
LastUpdatedEpoch: 5,
|
|
|
|
SlashEpoch: 0,
|
|
|
|
}
|
2020-09-18 21:59:27 +00:00
|
|
|
oldDeals := map[abi.DealID]*market0.DealState{
|
2020-07-07 23:39:32 +00:00
|
|
|
abi.DealID(1): oldDeal1,
|
|
|
|
abi.DealID(2): oldDeal2,
|
|
|
|
}
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
oldProp1 := &market0.DealProposal{
|
2020-07-07 23:39:32 +00:00
|
|
|
PieceCID: dummyCid,
|
|
|
|
PieceSize: 0,
|
|
|
|
VerifiedDeal: false,
|
|
|
|
Client: tutils.NewIDAddr(t, 1),
|
|
|
|
Provider: tutils.NewIDAddr(t, 1),
|
|
|
|
StartEpoch: 1,
|
|
|
|
EndEpoch: 2,
|
|
|
|
StoragePricePerEpoch: big.Zero(),
|
|
|
|
ProviderCollateral: big.Zero(),
|
|
|
|
ClientCollateral: big.Zero(),
|
2020-06-26 15:51:45 +00:00
|
|
|
}
|
2020-09-18 21:59:27 +00:00
|
|
|
oldProp2 := &market0.DealProposal{
|
2020-07-07 23:39:32 +00:00
|
|
|
PieceCID: dummyCid,
|
|
|
|
PieceSize: 0,
|
|
|
|
VerifiedDeal: false,
|
|
|
|
Client: tutils.NewIDAddr(t, 1),
|
|
|
|
Provider: tutils.NewIDAddr(t, 1),
|
|
|
|
StartEpoch: 2,
|
|
|
|
EndEpoch: 3,
|
|
|
|
StoragePricePerEpoch: big.Zero(),
|
|
|
|
ProviderCollateral: big.Zero(),
|
|
|
|
ClientCollateral: big.Zero(),
|
|
|
|
}
|
2020-09-18 21:59:27 +00:00
|
|
|
oldProps := map[abi.DealID]*market0.DealProposal{
|
2020-07-07 23:39:32 +00:00
|
|
|
abi.DealID(1): oldProp1,
|
|
|
|
abi.DealID(2): oldProp2,
|
|
|
|
}
|
|
|
|
|
2020-08-05 00:28:05 +00:00
|
|
|
oldBalances := map[address.Address]balance{
|
2020-08-20 04:49:10 +00:00
|
|
|
tutils.NewIDAddr(t, 1): {abi.NewTokenAmount(1000), abi.NewTokenAmount(1000)},
|
|
|
|
tutils.NewIDAddr(t, 2): {abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
|
|
|
|
tutils.NewIDAddr(t, 3): {abi.NewTokenAmount(3000), abi.NewTokenAmount(2000)},
|
|
|
|
tutils.NewIDAddr(t, 5): {abi.NewTokenAmount(3000), abi.NewTokenAmount(1000)},
|
2020-08-05 00:28:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
oldStateC := createMarketState(ctx, t, store, oldDeals, oldProps, oldBalances)
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
newDeal1 := &market0.DealState{
|
2020-07-07 23:39:32 +00:00
|
|
|
SectorStartEpoch: 1,
|
|
|
|
LastUpdatedEpoch: 3,
|
|
|
|
SlashEpoch: 0,
|
2020-06-26 15:51:45 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 20:24:29 +00:00
|
|
|
// deal 2 removed
|
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
// added
|
2020-09-18 21:59:27 +00:00
|
|
|
newDeal3 := &market0.DealState{
|
2020-07-07 23:39:32 +00:00
|
|
|
SectorStartEpoch: 1,
|
|
|
|
LastUpdatedEpoch: 2,
|
|
|
|
SlashEpoch: 3,
|
|
|
|
}
|
2020-09-18 21:59:27 +00:00
|
|
|
newDeals := map[abi.DealID]*market0.DealState{
|
2020-07-07 23:39:32 +00:00
|
|
|
abi.DealID(1): newDeal1,
|
2020-07-10 20:24:29 +00:00
|
|
|
// deal 2 was removed
|
2020-07-07 23:39:32 +00:00
|
|
|
abi.DealID(3): newDeal3,
|
|
|
|
}
|
|
|
|
|
|
|
|
// added
|
2020-09-18 21:59:27 +00:00
|
|
|
newProp3 := &market0.DealProposal{
|
2020-07-07 23:39:32 +00:00
|
|
|
PieceCID: dummyCid,
|
|
|
|
PieceSize: 0,
|
|
|
|
VerifiedDeal: false,
|
|
|
|
Client: tutils.NewIDAddr(t, 1),
|
|
|
|
Provider: tutils.NewIDAddr(t, 1),
|
|
|
|
StartEpoch: 4,
|
|
|
|
EndEpoch: 4,
|
|
|
|
StoragePricePerEpoch: big.Zero(),
|
|
|
|
ProviderCollateral: big.Zero(),
|
|
|
|
ClientCollateral: big.Zero(),
|
|
|
|
}
|
2020-09-18 21:59:27 +00:00
|
|
|
newProps := map[abi.DealID]*market0.DealProposal{
|
2020-07-07 23:39:32 +00:00
|
|
|
abi.DealID(1): oldProp1, // 1 was persisted
|
|
|
|
// prop 2 was removed
|
|
|
|
abi.DealID(3): newProp3, // new
|
|
|
|
// NB: DealProposals cannot be modified, so don't test that case.
|
2020-06-26 15:51:45 +00:00
|
|
|
}
|
2020-08-05 00:28:05 +00:00
|
|
|
newBalances := map[address.Address]balance{
|
2020-08-20 04:49:10 +00:00
|
|
|
tutils.NewIDAddr(t, 1): {abi.NewTokenAmount(3000), abi.NewTokenAmount(0)},
|
|
|
|
tutils.NewIDAddr(t, 2): {abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
|
|
|
|
tutils.NewIDAddr(t, 4): {abi.NewTokenAmount(5000), abi.NewTokenAmount(0)},
|
|
|
|
tutils.NewIDAddr(t, 5): {abi.NewTokenAmount(1000), abi.NewTokenAmount(3000)},
|
2020-08-05 00:28:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
newStateC := createMarketState(ctx, t, store, newDeals, newProps, newBalances)
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
minerAddr, err := address.NewFromString("t00")
|
2020-06-26 15:51:45 +00:00
|
|
|
require.NoError(t, err)
|
2020-07-07 23:39:32 +00:00
|
|
|
oldState, err := mockTipset(minerAddr, 1)
|
2020-06-26 15:51:45 +00:00
|
|
|
require.NoError(t, err)
|
2020-07-07 23:39:32 +00:00
|
|
|
newState, err := mockTipset(minerAddr, 2)
|
2020-06-26 15:51:45 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-06-26 19:36:48 +00:00
|
|
|
api := newMockAPI(bs)
|
2020-09-18 21:59:27 +00:00
|
|
|
api.setActor(oldState.Key(), &types.Actor{Code: builtin0.StorageMarketActorCodeID, Head: oldStateC})
|
|
|
|
api.setActor(newState.Key(), &types.Actor{Code: builtin0.StorageMarketActorCodeID, Head: newStateC})
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
t.Run("deal ID predicate", func(t *testing.T) {
|
|
|
|
preds := NewStatePredicates(api)
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
dealIds := []abi.DealID{abi.DealID(1), abi.DealID(2)}
|
|
|
|
diffIDFn := preds.OnStorageMarketActorChanged(preds.OnDealStateChanged(preds.DealStateChangedForIDs(dealIds)))
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
// Diff a state against itself: expect no change
|
|
|
|
changed, _, err := diffIDFn(ctx, oldState.Key(), oldState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
// Diff old state against new state
|
|
|
|
changed, valIDs, err := diffIDFn(ctx, oldState.Key(), newState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, changed)
|
|
|
|
|
|
|
|
changedDealIDs, ok := valIDs.(ChangedDeals)
|
|
|
|
require.True(t, ok)
|
|
|
|
require.Len(t, changedDealIDs, 2)
|
|
|
|
require.Contains(t, changedDealIDs, abi.DealID(1))
|
|
|
|
require.Contains(t, changedDealIDs, abi.DealID(2))
|
|
|
|
deal1 := changedDealIDs[abi.DealID(1)]
|
2020-09-17 07:32:10 +00:00
|
|
|
if deal1.From.LastUpdatedEpoch != 2 || deal1.To.LastUpdatedEpoch != 3 {
|
2020-07-07 23:39:32 +00:00
|
|
|
t.Fatal("Unexpected change to LastUpdatedEpoch")
|
|
|
|
}
|
|
|
|
deal2 := changedDealIDs[abi.DealID(2)]
|
2020-09-17 07:32:10 +00:00
|
|
|
if deal2.From.LastUpdatedEpoch != 5 || deal2.To != nil {
|
2020-07-10 20:24:29 +00:00
|
|
|
t.Fatal("Expected To to be nil")
|
2020-07-07 23:39:32 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 20:24:29 +00:00
|
|
|
// Diff with non-existent deal.
|
|
|
|
noDeal := []abi.DealID{4}
|
|
|
|
diffNoDealFn := preds.OnStorageMarketActorChanged(preds.OnDealStateChanged(preds.DealStateChangedForIDs(noDeal)))
|
|
|
|
changed, _, err = diffNoDealFn(ctx, oldState.Key(), newState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
2020-06-26 15:51:45 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
// Test that OnActorStateChanged does not call the callback if the state has not changed
|
|
|
|
mockAddr, err := address.NewFromString("t01")
|
|
|
|
require.NoError(t, err)
|
2020-09-16 04:06:04 +00:00
|
|
|
actorDiffFn := preds.OnActorStateChanged(mockAddr, func(context.Context, *types.Actor, *types.Actor) (bool, UserData, error) {
|
2020-07-07 23:39:32 +00:00
|
|
|
t.Fatal("No state change so this should not be called")
|
|
|
|
return false, nil, nil
|
|
|
|
})
|
|
|
|
changed, _, err = actorDiffFn(ctx, oldState.Key(), oldState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
|
|
|
|
|
|
|
// Test that OnDealStateChanged does not call the callback if the state has not changed
|
2020-09-17 02:13:12 +00:00
|
|
|
diffDealStateFn := preds.OnDealStateChanged(func(context.Context, market.DealStates, market.DealStates) (bool, UserData, error) {
|
2020-07-07 23:39:32 +00:00
|
|
|
t.Fatal("No state change so this should not be called")
|
|
|
|
return false, nil, nil
|
|
|
|
})
|
2020-09-18 21:59:27 +00:00
|
|
|
marketState0 := createEmptyMarketState(t, store)
|
|
|
|
marketCid, err := store.Put(ctx, marketState0)
|
2020-09-17 02:13:12 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
marketState, err := market.Load(store, &types.Actor{
|
2020-09-18 21:59:27 +00:00
|
|
|
Code: builtin0.StorageMarketActorCodeID,
|
2020-09-17 02:13:12 +00:00
|
|
|
Head: marketCid,
|
|
|
|
})
|
2020-09-18 07:06:28 +00:00
|
|
|
require.NoError(t, err)
|
2020-07-07 23:39:32 +00:00
|
|
|
changed, _, err = diffDealStateFn(ctx, marketState, marketState)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
|
|
|
})
|
2020-06-29 22:34:24 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
t.Run("deal state array predicate", func(t *testing.T) {
|
|
|
|
preds := NewStatePredicates(api)
|
|
|
|
diffArrFn := preds.OnStorageMarketActorChanged(preds.OnDealStateChanged(preds.OnDealStateAmtChanged()))
|
2020-06-29 15:21:29 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
changed, _, err := diffArrFn(ctx, oldState.Key(), oldState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
|
|
|
|
|
|
|
changed, valArr, err := diffArrFn(ctx, oldState.Key(), newState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, changed)
|
|
|
|
|
2020-09-17 02:13:12 +00:00
|
|
|
changedDeals, ok := valArr.(*market.DealStateChanges)
|
2020-07-07 23:39:32 +00:00
|
|
|
require.True(t, ok)
|
|
|
|
require.Len(t, changedDeals.Added, 1)
|
|
|
|
require.Equal(t, abi.DealID(3), changedDeals.Added[0].ID)
|
2020-09-17 02:13:12 +00:00
|
|
|
require.True(t, dealEquality(*newDeal3, changedDeals.Added[0].Deal))
|
2020-07-07 23:39:32 +00:00
|
|
|
|
2020-07-10 20:24:29 +00:00
|
|
|
require.Len(t, changedDeals.Removed, 1)
|
2020-07-07 23:39:32 +00:00
|
|
|
|
2020-07-10 20:24:29 +00:00
|
|
|
require.Len(t, changedDeals.Modified, 1)
|
2020-07-07 23:39:32 +00:00
|
|
|
require.Equal(t, abi.DealID(1), changedDeals.Modified[0].ID)
|
2020-09-17 07:32:10 +00:00
|
|
|
require.True(t, dealEquality(*newDeal1, *changedDeals.Modified[0].To))
|
|
|
|
require.True(t, dealEquality(*oldDeal1, *changedDeals.Modified[0].From))
|
2020-07-07 23:39:32 +00:00
|
|
|
|
2020-07-10 20:24:29 +00:00
|
|
|
require.Equal(t, abi.DealID(2), changedDeals.Removed[0].ID)
|
2020-06-29 15:21:29 +00:00
|
|
|
})
|
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
t.Run("deal proposal array predicate", func(t *testing.T) {
|
|
|
|
preds := NewStatePredicates(api)
|
|
|
|
diffArrFn := preds.OnStorageMarketActorChanged(preds.OnDealProposalChanged(preds.OnDealProposalAmtChanged()))
|
|
|
|
changed, _, err := diffArrFn(ctx, oldState.Key(), oldState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
|
|
|
|
|
|
|
changed, valArr, err := diffArrFn(ctx, oldState.Key(), newState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, changed)
|
|
|
|
|
2020-09-17 02:13:12 +00:00
|
|
|
changedProps, ok := valArr.(*market.DealProposalChanges)
|
2020-07-07 23:39:32 +00:00
|
|
|
require.True(t, ok)
|
|
|
|
require.Len(t, changedProps.Added, 1)
|
|
|
|
require.Equal(t, abi.DealID(3), changedProps.Added[0].ID)
|
|
|
|
|
|
|
|
// proposals cannot be modified -- no modified testing
|
|
|
|
|
|
|
|
require.Len(t, changedProps.Removed, 1)
|
|
|
|
require.Equal(t, abi.DealID(2), changedProps.Removed[0].ID)
|
2020-06-29 15:21:29 +00:00
|
|
|
})
|
2020-08-05 00:28:05 +00:00
|
|
|
|
|
|
|
t.Run("balances predicate", func(t *testing.T) {
|
|
|
|
preds := NewStatePredicates(api)
|
|
|
|
|
|
|
|
getAddresses := func() []address.Address {
|
|
|
|
return []address.Address{tutils.NewIDAddr(t, 1), tutils.NewIDAddr(t, 2), tutils.NewIDAddr(t, 3), tutils.NewIDAddr(t, 4)}
|
|
|
|
}
|
|
|
|
diffBalancesFn := preds.OnStorageMarketActorChanged(preds.OnBalanceChanged(preds.AvailableBalanceChangedForAddresses(getAddresses)))
|
|
|
|
|
|
|
|
// Diff a state against itself: expect no change
|
|
|
|
changed, _, err := diffBalancesFn(ctx, oldState.Key(), oldState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
|
|
|
|
|
|
|
// Diff old state against new state
|
|
|
|
changed, valIDs, err := diffBalancesFn(ctx, oldState.Key(), newState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, changed)
|
|
|
|
|
|
|
|
changedBalances, ok := valIDs.(ChangedBalances)
|
|
|
|
require.True(t, ok)
|
|
|
|
require.Len(t, changedBalances, 3)
|
|
|
|
require.Contains(t, changedBalances, tutils.NewIDAddr(t, 1))
|
|
|
|
require.Contains(t, changedBalances, tutils.NewIDAddr(t, 3))
|
|
|
|
require.Contains(t, changedBalances, tutils.NewIDAddr(t, 4))
|
|
|
|
|
|
|
|
balance1 := changedBalances[tutils.NewIDAddr(t, 1)]
|
|
|
|
if !balance1.From.Equals(abi.NewTokenAmount(1000)) || !balance1.To.Equals(abi.NewTokenAmount(3000)) {
|
|
|
|
t.Fatal("Unexpected change to balance")
|
|
|
|
}
|
|
|
|
balance3 := changedBalances[tutils.NewIDAddr(t, 3)]
|
|
|
|
if !balance3.From.Equals(abi.NewTokenAmount(3000)) || !balance3.To.Equals(abi.NewTokenAmount(0)) {
|
|
|
|
t.Fatal("Unexpected change to balance")
|
|
|
|
}
|
|
|
|
balance4 := changedBalances[tutils.NewIDAddr(t, 4)]
|
|
|
|
if !balance4.From.Equals(abi.NewTokenAmount(0)) || !balance4.To.Equals(abi.NewTokenAmount(5000)) {
|
|
|
|
t.Fatal("Unexpected change to balance")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Diff with non-existent address.
|
|
|
|
getNoAddress := func() []address.Address { return []address.Address{tutils.NewIDAddr(t, 6)} }
|
|
|
|
diffNoAddressFn := preds.OnStorageMarketActorChanged(preds.OnBalanceChanged(preds.AvailableBalanceChangedForAddresses(getNoAddress)))
|
|
|
|
changed, _, err = diffNoAddressFn(ctx, oldState.Key(), newState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
|
|
|
|
|
|
|
// Test that OnBalanceChanged does not call the callback if the state has not changed
|
|
|
|
diffDealBalancesFn := preds.OnBalanceChanged(func(context.Context, BalanceTables, BalanceTables) (bool, UserData, error) {
|
|
|
|
t.Fatal("No state change so this should not be called")
|
|
|
|
return false, nil, nil
|
|
|
|
})
|
2020-09-18 21:59:27 +00:00
|
|
|
marketState0 := createEmptyMarketState(t, store)
|
|
|
|
marketCid, err := store.Put(ctx, marketState0)
|
2020-09-17 02:13:12 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
marketState, err := market.Load(store, &types.Actor{
|
2020-09-18 21:59:27 +00:00
|
|
|
Code: builtin0.StorageMarketActorCodeID,
|
2020-09-17 02:13:12 +00:00
|
|
|
Head: marketCid,
|
|
|
|
})
|
2020-09-18 07:06:28 +00:00
|
|
|
require.NoError(t, err)
|
2020-08-05 00:28:05 +00:00
|
|
|
changed, _, err = diffDealBalancesFn(ctx, marketState, marketState)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, changed)
|
|
|
|
})
|
|
|
|
|
2020-06-26 15:51:45 +00:00
|
|
|
}
|
|
|
|
|
2020-07-02 01:20:54 +00:00
|
|
|
func TestMinerSectorChange(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
2020-07-23 02:05:11 +00:00
|
|
|
bs := bstore.NewTemporarySync()
|
2020-07-23 00:14:54 +00:00
|
|
|
store := adt.WrapStore(ctx, cbornode.NewCborStore(bs))
|
2020-07-02 01:20:54 +00:00
|
|
|
|
|
|
|
nextID := uint64(0)
|
|
|
|
nextIDAddrF := func() address.Address {
|
|
|
|
defer func() { nextID++ }()
|
|
|
|
return tutils.NewIDAddr(t, nextID)
|
|
|
|
}
|
|
|
|
|
|
|
|
owner, worker := nextIDAddrF(), nextIDAddrF()
|
2020-09-18 21:59:27 +00:00
|
|
|
si0 := newSectorOnChainInfo(0, tutils.MakeCID("0", &miner0.SealedCIDPrefix), big.NewInt(0), abi.ChainEpoch(0), abi.ChainEpoch(10))
|
|
|
|
si1 := newSectorOnChainInfo(1, tutils.MakeCID("1", &miner0.SealedCIDPrefix), big.NewInt(1), abi.ChainEpoch(1), abi.ChainEpoch(11))
|
|
|
|
si2 := newSectorOnChainInfo(2, tutils.MakeCID("2", &miner0.SealedCIDPrefix), big.NewInt(2), abi.ChainEpoch(2), abi.ChainEpoch(11))
|
2020-07-02 01:20:54 +00:00
|
|
|
oldMinerC := createMinerState(ctx, t, store, owner, worker, []miner.SectorOnChainInfo{si0, si1, si2})
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
si3 := newSectorOnChainInfo(3, tutils.MakeCID("3", &miner0.SealedCIDPrefix), big.NewInt(3), abi.ChainEpoch(3), abi.ChainEpoch(12))
|
2020-07-02 01:20:54 +00:00
|
|
|
// 0 delete
|
|
|
|
// 1 extend
|
|
|
|
// 2 same
|
|
|
|
// 3 added
|
|
|
|
si1Ext := si1
|
2020-07-08 12:35:40 +00:00
|
|
|
si1Ext.Expiration++
|
2020-07-02 01:20:54 +00:00
|
|
|
newMinerC := createMinerState(ctx, t, store, owner, worker, []miner.SectorOnChainInfo{si1Ext, si2, si3})
|
|
|
|
|
|
|
|
minerAddr := nextIDAddrF()
|
|
|
|
oldState, err := mockTipset(minerAddr, 1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
newState, err := mockTipset(minerAddr, 2)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
api := newMockAPI(bs)
|
2020-09-18 21:59:27 +00:00
|
|
|
api.setActor(oldState.Key(), &types.Actor{Head: oldMinerC, Code: builtin0.StorageMinerActorCodeID})
|
|
|
|
api.setActor(newState.Key(), &types.Actor{Head: newMinerC, Code: builtin0.StorageMinerActorCodeID})
|
2020-07-02 01:20:54 +00:00
|
|
|
|
|
|
|
preds := NewStatePredicates(api)
|
|
|
|
|
|
|
|
minerDiffFn := preds.OnMinerActorChange(minerAddr, preds.OnMinerSectorChange())
|
|
|
|
change, val, err := minerDiffFn(ctx, oldState.Key(), newState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, change)
|
|
|
|
require.NotNil(t, val)
|
|
|
|
|
2020-09-18 04:39:34 +00:00
|
|
|
sectorChanges, ok := val.(*miner.SectorChanges)
|
2020-07-02 01:20:54 +00:00
|
|
|
require.True(t, ok)
|
|
|
|
|
|
|
|
require.Equal(t, len(sectorChanges.Added), 1)
|
2020-07-07 23:39:32 +00:00
|
|
|
require.Equal(t, 1, len(sectorChanges.Added))
|
|
|
|
require.Equal(t, si3, sectorChanges.Added[0])
|
2020-07-02 01:20:54 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
require.Equal(t, 1, len(sectorChanges.Removed))
|
|
|
|
require.Equal(t, si0, sectorChanges.Removed[0])
|
2020-07-02 01:20:54 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
require.Equal(t, 1, len(sectorChanges.Extended))
|
|
|
|
require.Equal(t, si1, sectorChanges.Extended[0].From)
|
|
|
|
require.Equal(t, si1Ext, sectorChanges.Extended[0].To)
|
2020-07-02 01:20:54 +00:00
|
|
|
|
|
|
|
change, val, err = minerDiffFn(ctx, oldState.Key(), oldState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, change)
|
|
|
|
require.Nil(t, val)
|
|
|
|
|
|
|
|
change, val, err = minerDiffFn(ctx, newState.Key(), oldState.Key())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, change)
|
|
|
|
require.NotNil(t, val)
|
|
|
|
|
2020-09-18 04:39:34 +00:00
|
|
|
sectorChanges, ok = val.(*miner.SectorChanges)
|
2020-07-02 01:20:54 +00:00
|
|
|
require.True(t, ok)
|
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
require.Equal(t, 1, len(sectorChanges.Added))
|
|
|
|
require.Equal(t, si0, sectorChanges.Added[0])
|
2020-07-02 01:20:54 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
require.Equal(t, 1, len(sectorChanges.Removed))
|
|
|
|
require.Equal(t, si3, sectorChanges.Removed[0])
|
2020-07-02 01:20:54 +00:00
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
require.Equal(t, 1, len(sectorChanges.Extended))
|
|
|
|
require.Equal(t, si1, sectorChanges.Extended[0].To)
|
|
|
|
require.Equal(t, si1Ext, sectorChanges.Extended[0].From)
|
2020-07-02 01:20:54 +00:00
|
|
|
}
|
|
|
|
|
2020-07-07 23:39:32 +00:00
|
|
|
func mockTipset(minerAddr address.Address, timestamp uint64) (*types.TipSet, error) {
|
2020-06-26 15:51:45 +00:00
|
|
|
return types.NewTipSet([]*types.BlockHeader{{
|
2020-07-07 23:39:32 +00:00
|
|
|
Miner: minerAddr,
|
2020-06-26 15:51:45 +00:00
|
|
|
Height: 5,
|
|
|
|
ParentStateRoot: dummyCid,
|
|
|
|
Messages: dummyCid,
|
|
|
|
ParentMessageReceipts: dummyCid,
|
|
|
|
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
|
|
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
|
|
|
Timestamp: timestamp,
|
|
|
|
}})
|
|
|
|
}
|
|
|
|
|
2020-08-05 00:28:05 +00:00
|
|
|
type balance struct {
|
|
|
|
available abi.TokenAmount
|
|
|
|
locked abi.TokenAmount
|
|
|
|
}
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
func createMarketState(ctx context.Context, t *testing.T, store adt.Store, deals map[abi.DealID]*market0.DealState, props map[abi.DealID]*market0.DealProposal, balances map[address.Address]balance) cid.Cid {
|
2020-07-07 23:39:32 +00:00
|
|
|
dealRootCid := createDealAMT(ctx, t, store, deals)
|
|
|
|
propRootCid := createProposalAMT(ctx, t, store, props)
|
2020-08-05 00:28:05 +00:00
|
|
|
balancesCids := createBalanceTable(ctx, t, store, balances)
|
2020-06-29 15:21:29 +00:00
|
|
|
state := createEmptyMarketState(t, store)
|
2020-07-07 23:39:32 +00:00
|
|
|
state.States = dealRootCid
|
|
|
|
state.Proposals = propRootCid
|
2020-08-05 00:28:05 +00:00
|
|
|
state.EscrowTable = balancesCids[0]
|
|
|
|
state.LockedTable = balancesCids[1]
|
2020-06-26 15:51:45 +00:00
|
|
|
|
|
|
|
stateC, err := store.Put(ctx, state)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return stateC
|
|
|
|
}
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
func createEmptyMarketState(t *testing.T, store adt.Store) *market0.State {
|
2020-07-23 00:14:54 +00:00
|
|
|
emptyArrayCid, err := adt.MakeEmptyArray(store).Root()
|
2020-06-29 15:21:29 +00:00
|
|
|
require.NoError(t, err)
|
2020-07-23 00:14:54 +00:00
|
|
|
emptyMap, err := adt.MakeEmptyMap(store).Root()
|
2020-06-29 15:21:29 +00:00
|
|
|
require.NoError(t, err)
|
2020-09-18 21:59:27 +00:00
|
|
|
return market0.ConstructState(emptyArrayCid, emptyMap, emptyMap)
|
2020-06-29 15:21:29 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
func createDealAMT(ctx context.Context, t *testing.T, store adt.Store, deals map[abi.DealID]*market0.DealState) cid.Cid {
|
2020-07-23 00:14:54 +00:00
|
|
|
root := adt.MakeEmptyArray(store)
|
2020-06-26 19:36:48 +00:00
|
|
|
for dealID, dealState := range deals {
|
2020-07-23 00:14:54 +00:00
|
|
|
err := root.Set(uint64(dealID), dealState)
|
2020-06-26 15:51:45 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
2020-07-23 00:14:54 +00:00
|
|
|
rootCid, err := root.Root()
|
2020-06-26 15:51:45 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
return rootCid
|
|
|
|
}
|
2020-07-02 01:20:54 +00:00
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
func createProposalAMT(ctx context.Context, t *testing.T, store adt.Store, props map[abi.DealID]*market0.DealProposal) cid.Cid {
|
2020-07-23 00:14:54 +00:00
|
|
|
root := adt.MakeEmptyArray(store)
|
2020-07-07 23:39:32 +00:00
|
|
|
for dealID, prop := range props {
|
2020-07-23 00:14:54 +00:00
|
|
|
err := root.Set(uint64(dealID), prop)
|
2020-07-07 23:39:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
2020-07-23 00:14:54 +00:00
|
|
|
rootCid, err := root.Root()
|
2020-07-07 23:39:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
return rootCid
|
|
|
|
}
|
|
|
|
|
2020-08-05 00:28:05 +00:00
|
|
|
func createBalanceTable(ctx context.Context, t *testing.T, store adt.Store, balances map[address.Address]balance) [2]cid.Cid {
|
|
|
|
escrowMapRoot := adt.MakeEmptyMap(store)
|
|
|
|
escrowMapRootCid, err := escrowMapRoot.Root()
|
|
|
|
require.NoError(t, err)
|
|
|
|
escrowRoot, err := adt.AsBalanceTable(store, escrowMapRootCid)
|
|
|
|
require.NoError(t, err)
|
|
|
|
lockedMapRoot := adt.MakeEmptyMap(store)
|
|
|
|
lockedMapRootCid, err := lockedMapRoot.Root()
|
|
|
|
require.NoError(t, err)
|
|
|
|
lockedRoot, err := adt.AsBalanceTable(store, lockedMapRootCid)
|
2020-08-20 04:49:10 +00:00
|
|
|
require.NoError(t, err)
|
2020-08-05 00:28:05 +00:00
|
|
|
|
|
|
|
for addr, balance := range balances {
|
|
|
|
err := escrowRoot.Add(addr, big.Add(balance.available, balance.locked))
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = lockedRoot.Add(addr, balance.locked)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
}
|
|
|
|
escrowRootCid, err := escrowRoot.Root()
|
|
|
|
require.NoError(t, err)
|
|
|
|
lockedRootCid, err := lockedRoot.Root()
|
|
|
|
require.NoError(t, err)
|
|
|
|
return [2]cid.Cid{escrowRootCid, lockedRootCid}
|
|
|
|
}
|
|
|
|
|
2020-07-23 00:14:54 +00:00
|
|
|
func createMinerState(ctx context.Context, t *testing.T, store adt.Store, owner, worker address.Address, sectors []miner.SectorOnChainInfo) cid.Cid {
|
2020-07-02 01:20:54 +00:00
|
|
|
rootCid := createSectorsAMT(ctx, t, store, sectors)
|
|
|
|
|
|
|
|
state := createEmptyMinerState(ctx, t, store, owner, worker)
|
|
|
|
state.Sectors = rootCid
|
|
|
|
|
|
|
|
stateC, err := store.Put(ctx, state)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return stateC
|
|
|
|
}
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
func createEmptyMinerState(ctx context.Context, t *testing.T, store adt.Store, owner, worker address.Address) *miner0.State {
|
2020-07-23 00:14:54 +00:00
|
|
|
emptyArrayCid, err := adt.MakeEmptyArray(store).Root()
|
2020-07-02 01:20:54 +00:00
|
|
|
require.NoError(t, err)
|
2020-07-23 00:14:54 +00:00
|
|
|
emptyMap, err := adt.MakeEmptyMap(store).Root()
|
2020-07-02 01:20:54 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
emptyDeadline, err := store.Put(store.Context(), miner0.ConstructDeadline(emptyArrayCid))
|
2020-07-14 20:30:21 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
emptyVestingFunds := miner0.ConstructVestingFunds()
|
2020-08-12 02:47:24 +00:00
|
|
|
emptyVestingFundsCid, err := store.Put(store.Context(), emptyVestingFunds)
|
2020-08-20 04:49:10 +00:00
|
|
|
require.NoError(t, err)
|
2020-08-12 02:47:24 +00:00
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
emptyDeadlines := miner0.ConstructDeadlines(emptyDeadline)
|
2020-08-12 02:47:24 +00:00
|
|
|
emptyDeadlinesCid, err := store.Put(store.Context(), emptyDeadlines)
|
2020-07-02 01:20:54 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-07-08 12:35:40 +00:00
|
|
|
minerInfo := emptyMap
|
|
|
|
|
2020-07-30 12:31:31 +00:00
|
|
|
emptyBitfield := bitfield.NewFromSet(nil)
|
2020-08-12 02:47:24 +00:00
|
|
|
emptyBitfieldCid, err := store.Put(store.Context(), emptyBitfield)
|
2020-07-30 12:31:31 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
state, err := miner0.ConstructState(minerInfo, 123, emptyBitfieldCid, emptyArrayCid, emptyMap, emptyDeadlinesCid, emptyVestingFundsCid)
|
2020-07-02 01:20:54 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
return state
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-07-23 00:14:54 +00:00
|
|
|
func createSectorsAMT(ctx context.Context, t *testing.T, store adt.Store, sectors []miner.SectorOnChainInfo) cid.Cid {
|
|
|
|
root := adt.MakeEmptyArray(store)
|
2020-07-02 01:20:54 +00:00
|
|
|
for _, sector := range sectors {
|
2020-09-23 19:47:41 +00:00
|
|
|
sector := (miner0.SectorOnChainInfo)(sector)
|
2020-07-23 00:14:54 +00:00
|
|
|
err := root.Set(uint64(sector.SectorNumber), §or)
|
2020-07-02 01:20:54 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
2020-07-23 00:14:54 +00:00
|
|
|
rootCid, err := root.Root()
|
2020-07-02 01:20:54 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
return rootCid
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns a unique SectorOnChainInfo with each invocation with SectorNumber set to `sectorNo`.
|
|
|
|
func newSectorOnChainInfo(sectorNo abi.SectorNumber, sealed cid.Cid, weight big.Int, activation, expiration abi.ChainEpoch) miner.SectorOnChainInfo {
|
|
|
|
info := newSectorPreCommitInfo(sectorNo, sealed, expiration)
|
|
|
|
return miner.SectorOnChainInfo{
|
2020-07-08 12:35:53 +00:00
|
|
|
SectorNumber: info.SectorNumber,
|
|
|
|
SealProof: info.SealProof,
|
|
|
|
SealedCID: info.SealedCID,
|
|
|
|
DealIDs: info.DealIDs,
|
|
|
|
Expiration: info.Expiration,
|
2020-07-08 12:35:40 +00:00
|
|
|
|
2020-07-31 04:22:00 +00:00
|
|
|
Activation: activation,
|
|
|
|
DealWeight: weight,
|
|
|
|
VerifiedDealWeight: weight,
|
|
|
|
InitialPledge: big.Zero(),
|
|
|
|
ExpectedDayReward: big.Zero(),
|
|
|
|
ExpectedStoragePledge: big.Zero(),
|
2020-07-02 01:20:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
sectorSealRandEpochValue = abi.ChainEpoch(1)
|
|
|
|
)
|
|
|
|
|
|
|
|
// returns a unique SectorPreCommitInfo with each invocation with SectorNumber set to `sectorNo`.
|
2020-09-23 19:47:41 +00:00
|
|
|
func newSectorPreCommitInfo(sectorNo abi.SectorNumber, sealed cid.Cid, expiration abi.ChainEpoch) *miner0.SectorPreCommitInfo {
|
|
|
|
return &miner0.SectorPreCommitInfo{
|
2020-07-02 01:20:54 +00:00
|
|
|
SealProof: abi.RegisteredSealProof_StackedDrg32GiBV1,
|
|
|
|
SectorNumber: sectorNo,
|
|
|
|
SealedCID: sealed,
|
|
|
|
SealRandEpoch: sectorSealRandEpochValue,
|
|
|
|
DealIDs: nil,
|
|
|
|
Expiration: expiration,
|
|
|
|
}
|
|
|
|
}
|
2020-09-17 02:13:12 +00:00
|
|
|
|
2020-09-18 21:59:27 +00:00
|
|
|
func dealEquality(expected market0.DealState, actual market.DealState) bool {
|
2020-09-17 07:32:10 +00:00
|
|
|
return expected.LastUpdatedEpoch == actual.LastUpdatedEpoch &&
|
|
|
|
expected.SectorStartEpoch == actual.SectorStartEpoch &&
|
|
|
|
expected.SlashEpoch == actual.SlashEpoch
|
2020-09-17 02:13:12 +00:00
|
|
|
}
|