feat(state): add predicate for deal id watching
This commit is contained in:
parent
cd8537e76f
commit
f4720ddb2c
@ -10,6 +10,7 @@ import (
|
||||
typegen "github.com/whyrusleeping/cbor-gen"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
@ -86,6 +87,50 @@ func (sp *StatePredicates) OnStorageMarketActorChanged(diffStorageMarketState Di
|
||||
})
|
||||
}
|
||||
|
||||
type BalanceTables struct {
|
||||
EscrowTable *adt.BalanceTable
|
||||
LockedTable *adt.BalanceTable
|
||||
}
|
||||
|
||||
// DiffBalanceTablesFunc compares two balance tables
|
||||
type DiffBalanceTablesFunc func(ctx context.Context, oldBalanceTable, newBalanceTable BalanceTables) (changed bool, user UserData, err error)
|
||||
|
||||
// OnBalanceChanged runs when the escrow table for available balances changes
|
||||
func (sp *StatePredicates) OnBalanceChanged(diffBalances DiffBalanceTablesFunc) DiffStorageMarketStateFunc {
|
||||
return func(ctx context.Context, oldState *market.State, newState *market.State) (changed bool, user UserData, err error) {
|
||||
if oldState.EscrowTable.Equals(newState.EscrowTable) && oldState.LockedTable.Equals(newState.LockedTable) {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
ctxStore := &contextStore{
|
||||
ctx: ctx,
|
||||
cst: sp.cst,
|
||||
}
|
||||
|
||||
oldEscrowRoot, err := adt.AsBalanceTable(ctxStore, oldState.EscrowTable)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
oldLockedRoot, err := adt.AsBalanceTable(ctxStore, oldState.LockedTable)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
newEscrowRoot, err := adt.AsBalanceTable(ctxStore, newState.EscrowTable)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
newLockedRoot, err := adt.AsBalanceTable(ctxStore, newState.LockedTable)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
return diffBalances(ctx, BalanceTables{oldEscrowRoot, oldLockedRoot}, BalanceTables{newEscrowRoot, newLockedRoot})
|
||||
}
|
||||
}
|
||||
|
||||
type DiffAdtArraysFunc func(ctx context.Context, oldDealStateRoot, newDealStateRoot *adt.Array) (changed bool, user UserData, err error)
|
||||
|
||||
// OnDealStateChanged calls diffDealStates when the market deal state changes
|
||||
@ -309,6 +354,57 @@ func (sp *StatePredicates) DealStateChangedForIDs(dealIds []abi.DealID) DiffAdtA
|
||||
}
|
||||
}
|
||||
|
||||
// ChangedBalances is a set of changes to deal state
|
||||
type ChangedBalances map[address.Address]BalanceChange
|
||||
|
||||
// BalanceChange is a change in balance from -> to
|
||||
type BalanceChange struct {
|
||||
From abi.TokenAmount
|
||||
To abi.TokenAmount
|
||||
}
|
||||
|
||||
// AvailableBalanceChangedForAddresses detects changes in the escrow table for the given addresses
|
||||
func (sp *StatePredicates) AvailableBalanceChangedForAddresses(getAddrs func() []address.Address) DiffBalanceTablesFunc {
|
||||
return func(ctx context.Context, oldBalances, newBalances BalanceTables) (changed bool, user UserData, err error) {
|
||||
changedBalances := make(ChangedBalances)
|
||||
addrs := getAddrs()
|
||||
for _, addr := range addrs {
|
||||
// If the deal has been removed, we just set it to nil
|
||||
oldEscrowBalance, err := oldBalances.EscrowTable.Get(addr)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
oldLockedBalance, err := oldBalances.LockedTable.Get(addr)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
oldBalance := big.Sub(oldEscrowBalance, oldLockedBalance)
|
||||
|
||||
newEscrowBalance, err := newBalances.EscrowTable.Get(addr)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
newLockedBalance, err := newBalances.LockedTable.Get(addr)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
newBalance := big.Sub(newEscrowBalance, newLockedBalance)
|
||||
|
||||
if !oldBalance.Equals(newBalance) {
|
||||
changedBalances[addr] = BalanceChange{oldBalance, newBalance}
|
||||
}
|
||||
}
|
||||
if len(changedBalances) > 0 {
|
||||
return true, changedBalances, nil
|
||||
}
|
||||
return false, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
type DiffMinerActorStateFunc func(ctx context.Context, oldState *miner.State, newState *miner.State) (changed bool, user UserData, err error)
|
||||
|
||||
func (sp *StatePredicates) OnMinerActorChange(minerAddr address.Address, diffMinerActorState DiffMinerActorStateFunc) DiffTipSetKeyFunc {
|
||||
|
@ -2,9 +2,10 @@ package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
@ -112,7 +113,14 @@ func TestMarketPredicates(t *testing.T) {
|
||||
abi.DealID(2): oldProp2,
|
||||
}
|
||||
|
||||
oldStateC := createMarketState(ctx, t, store, oldDeals, oldProps)
|
||||
oldBalances := map[address.Address]balance{
|
||||
tutils.NewIDAddr(t, 1): balance{abi.NewTokenAmount(1000), abi.NewTokenAmount(1000)},
|
||||
tutils.NewIDAddr(t, 2): balance{abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
|
||||
tutils.NewIDAddr(t, 3): balance{abi.NewTokenAmount(3000), abi.NewTokenAmount(2000)},
|
||||
tutils.NewIDAddr(t, 5): balance{abi.NewTokenAmount(3000), abi.NewTokenAmount(1000)},
|
||||
}
|
||||
|
||||
oldStateC := createMarketState(ctx, t, store, oldDeals, oldProps, oldBalances)
|
||||
|
||||
newDeal1 := &market.DealState{
|
||||
SectorStartEpoch: 1,
|
||||
@ -153,7 +161,14 @@ func TestMarketPredicates(t *testing.T) {
|
||||
abi.DealID(3): newProp3, // new
|
||||
// NB: DealProposals cannot be modified, so don't test that case.
|
||||
}
|
||||
newStateC := createMarketState(ctx, t, store, newDeals, newProps)
|
||||
newBalances := map[address.Address]balance{
|
||||
tutils.NewIDAddr(t, 1): balance{abi.NewTokenAmount(3000), abi.NewTokenAmount(0)},
|
||||
tutils.NewIDAddr(t, 2): balance{abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
|
||||
tutils.NewIDAddr(t, 4): balance{abi.NewTokenAmount(5000), abi.NewTokenAmount(0)},
|
||||
tutils.NewIDAddr(t, 5): balance{abi.NewTokenAmount(1000), abi.NewTokenAmount(3000)},
|
||||
}
|
||||
|
||||
newStateC := createMarketState(ctx, t, store, newDeals, newProps, newBalances)
|
||||
|
||||
minerAddr, err := address.NewFromString("t00")
|
||||
require.NoError(t, err)
|
||||
@ -276,6 +291,63 @@ func TestMarketPredicates(t *testing.T) {
|
||||
require.Equal(t, abi.DealID(2), changedProps.Removed[0].ID)
|
||||
require.Equal(t, *oldProp2, changedProps.Removed[0].Proposal)
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
marketState := createEmptyMarketState(t, store)
|
||||
changed, _, err = diffDealBalancesFn(ctx, marketState, marketState)
|
||||
require.NoError(t, err)
|
||||
require.False(t, changed)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestMinerSectorChange(t *testing.T) {
|
||||
@ -373,13 +445,20 @@ func mockTipset(minerAddr address.Address, timestamp uint64) (*types.TipSet, err
|
||||
}})
|
||||
}
|
||||
|
||||
func createMarketState(ctx context.Context, t *testing.T, store adt.Store, deals map[abi.DealID]*market.DealState, props map[abi.DealID]*market.DealProposal) cid.Cid {
|
||||
type balance struct {
|
||||
available abi.TokenAmount
|
||||
locked abi.TokenAmount
|
||||
}
|
||||
|
||||
func createMarketState(ctx context.Context, t *testing.T, store adt.Store, deals map[abi.DealID]*market.DealState, props map[abi.DealID]*market.DealProposal, balances map[address.Address]balance) cid.Cid {
|
||||
dealRootCid := createDealAMT(ctx, t, store, deals)
|
||||
propRootCid := createProposalAMT(ctx, t, store, props)
|
||||
|
||||
balancesCids := createBalanceTable(ctx, t, store, balances)
|
||||
state := createEmptyMarketState(t, store)
|
||||
state.States = dealRootCid
|
||||
state.Proposals = propRootCid
|
||||
state.EscrowTable = balancesCids[0]
|
||||
state.LockedTable = balancesCids[1]
|
||||
|
||||
stateC, err := store.Put(ctx, state)
|
||||
require.NoError(t, err)
|
||||
@ -416,6 +495,31 @@ func createProposalAMT(ctx context.Context, t *testing.T, store adt.Store, props
|
||||
return rootCid
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
func createMinerState(ctx context.Context, t *testing.T, store adt.Store, owner, worker address.Address, sectors []miner.SectorOnChainInfo) cid.Cid {
|
||||
rootCid := createSectorsAMT(ctx, t, store, sectors)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user