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(market): update state diffing for market actor #3889

Merged
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package state
package adt

import (
"bytes"

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/specs-actors/actors/util/adt"
typegen "github.com/whyrusleeping/cbor-gen"
)

Expand All @@ -27,7 +26,7 @@ type AdtArrayDiff interface {
// - All values that exist in curArr nnd not in prevArr are passed to adtArrayDiff.Add()
// - All values that exist in preArr and in curArr are passed to AdtArrayDiff.Modify()
// - It is the responsibility of AdtArrayDiff.Modify() to determine if the values it was passed have been modified.
func DiffAdtArray(preArr, curArr *adt.Array, out AdtArrayDiff) error {
func DiffAdtArray(preArr, curArr Array, out AdtArrayDiff) error {
prevVal := new(typegen.Deferred)
if err := preArr.ForEach(prevVal, func(i int64) error {
curVal := new(typegen.Deferred)
Expand Down Expand Up @@ -76,7 +75,7 @@ type AdtMapDiff interface {
Remove(key string, val *typegen.Deferred) error
}

func DiffAdtMap(preMap, curMap *adt.Map, out AdtMapDiff) error {
func DiffAdtMap(preMap, curMap Map, out AdtMapDiff) error {
prevVal := new(typegen.Deferred)
if err := preMap.ForEach(prevVal, func(key string) error {
curVal := new(typegen.Deferred)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package state
package adt

import (
"bytes"
Expand All @@ -13,7 +13,7 @@ import (

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/filecoin-project/specs-actors/actors/util/adt"
v0adt "github.com/filecoin-project/specs-actors/actors/util/adt"

bstore "github.com/filecoin-project/lotus/lib/blockstore"
)
Expand All @@ -22,8 +22,8 @@ func TestDiffAdtArray(t *testing.T) {
ctxstoreA := newContextStore()
ctxstoreB := newContextStore()

arrA := adt.MakeEmptyArray(ctxstoreA)
arrB := adt.MakeEmptyArray(ctxstoreB)
arrA := v0adt.MakeEmptyArray(ctxstoreA)
arrB := v0adt.MakeEmptyArray(ctxstoreB)

require.NoError(t, arrA.Set(0, runtime.CBORBytes([]byte{0}))) // delete

Expand Down Expand Up @@ -76,8 +76,8 @@ func TestDiffAdtMap(t *testing.T) {
ctxstoreA := newContextStore()
ctxstoreB := newContextStore()

mapA := adt.MakeEmptyMap(ctxstoreA)
mapB := adt.MakeEmptyMap(ctxstoreB)
mapA := v0adt.MakeEmptyMap(ctxstoreA)
mapB := v0adt.MakeEmptyMap(ctxstoreB)

require.NoError(t, mapA.Put(abi.UIntKey(0), runtime.CBORBytes([]byte{0}))) // delete

Expand Down Expand Up @@ -292,12 +292,9 @@ func (t *TestDiffArray) Remove(key uint64, val *typegen.Deferred) error {
return nil
}

func newContextStore() *contextStore {
func newContextStore() Store {
ctx := context.Background()
bs := bstore.NewTemporarySync()
store := cbornode.NewCborStore(bs)
return &contextStore{
ctx: ctx,
cst: store,
}
return WrapStore(ctx, store)
}
52 changes: 52 additions & 0 deletions chain/actors/builtin/market/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,14 @@ func Load(store adt.Store, act *types.Actor) (st State, err error) {

type State interface {
cbor.Marshaler
BalancesChanged(State) bool
EscrowTable() (BalanceTable, error)
LockedTable() (BalanceTable, error)
TotalLocked() (abi.TokenAmount, error)
StatesChanged(State) bool
States() (DealStates, error)
ProposalsChanged(State) bool
Proposals() (DealProposals, error)
VerifyDealsForActivation(
minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch,
) (weight, verifiedWeight abi.DealWeight, err error)
Expand All @@ -40,3 +45,50 @@ type State interface {
type BalanceTable interface {
Get(key address.Address) (abi.TokenAmount, error)
}

type DealStates interface {
GetDeal(key abi.DealID) (DealState, error)
Diff(DealStates) (*DealStateChanges, error)
}

type DealProposals interface {
Diff(DealProposals) (*DealProposalChanges, error)
}

type DealState interface {
SectorStartEpoch() abi.ChainEpoch
SlashEpoch() abi.ChainEpoch
LastUpdatedEpoch() abi.ChainEpoch
Equals(DealState) bool
}

type DealProposal interface {
}

type DealStateChanges struct {
Added []DealIDState
Modified []DealStateChange
Removed []DealIDState
}

type DealIDState struct {
ID abi.DealID
Deal DealState
}

// DealStateChange is a change in deal state from -> to
type DealStateChange struct {
ID abi.DealID
From DealState
To DealState
}

type DealProposalChanges struct {
Added []ProposalIDState
Removed []ProposalIDState
}

type ProposalIDState struct {
ID abi.DealID
Proposal DealProposal
}
201 changes: 198 additions & 3 deletions chain/actors/builtin/market/v0.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package market

import (
"bytes"
"errors"
"fmt"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/specs-actors/actors/util/adt"
v0adt "github.com/filecoin-project/specs-actors/actors/util/adt"
typegen "github.com/whyrusleeping/cbor-gen"
)

type v0State struct {
Expand All @@ -19,16 +25,205 @@ func (s *v0State) TotalLocked() (abi.TokenAmount, error) {
return fml, nil
}

func (s *v0State) BalancesChanged(otherState State) bool {
v0otherState, ok := otherState.(*v0State)
if !ok {
// there's no way to compare differnt versions of the state, so let's
// just say that means the state of balances has changed
return true
}
return !s.State.EscrowTable.Equals(v0otherState.State.EscrowTable) || !s.State.LockedTable.Equals(v0otherState.State.LockedTable)
}

func (s *v0State) StatesChanged(otherState State) bool {
v0otherState, ok := otherState.(*v0State)
if !ok {
// there's no way to compare differnt versions of the state, so let's
// just say that means the state of balances has changed
return true
}
return !s.State.States.Equals(v0otherState.State.States)
}

func (s *v0State) States() (DealStates, error) {
stateArray, err := v0adt.AsArray(s.store, s.State.States)
if err != nil {
return nil, err
}
return &v0DealStates{stateArray}, nil
}

func (s *v0State) ProposalsChanged(otherState State) bool {
v0otherState, ok := otherState.(*v0State)
if !ok {
// there's no way to compare differnt versions of the state, so let's
// just say that means the state of balances has changed
return true
}
return !s.State.Proposals.Equals(v0otherState.State.Proposals)
}

func (s *v0State) Proposals() (DealProposals, error) {
proposalArray, err := v0adt.AsArray(s.store, s.State.Proposals)
if err != nil {
return nil, err
}
return &v0DealProposals{proposalArray}, nil
}

func (s *v0State) EscrowTable() (BalanceTable, error) {
return adt.AsBalanceTable(s.store, s.State.EscrowTable)
return v0adt.AsBalanceTable(s.store, s.State.EscrowTable)
}

func (s *v0State) LockedTable() (BalanceTable, error) {
return adt.AsBalanceTable(s.store, s.State.LockedTable)
return v0adt.AsBalanceTable(s.store, s.State.LockedTable)
}

func (s *v0State) VerifyDealsForActivation(
minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch,
) (weight, verifiedWeight abi.DealWeight, err error) {
return market.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch)
}

type v0DealStates struct {
adt.Array
}

func (s *v0DealStates) GetDeal(dealID abi.DealID) (DealState, error) {
var deal market.DealState
found, err := s.Array.Get(uint64(dealID), &deal)
if err != nil {
return nil, err
}
if !found {
return nil, nil
}
return &v0DealState{deal}, nil
}

func (s *v0DealStates) Diff(other DealStates) (*DealStateChanges, error) {
v0other, ok := other.(*v0DealStates)
if !ok {
// TODO handle this if possible on a case by case basis but for now, just fail
return nil, errors.New("cannot compare deal states across versions")
}
results := new(DealStateChanges)
if err := adt.DiffAdtArray(s, v0other, &v0MarketStatesDiffer{results}); err != nil {
return nil, fmt.Errorf("diffing deal states: %w", err)
}

return results, nil
}

type v0MarketStatesDiffer struct {
Results *DealStateChanges
}

func (d *v0MarketStatesDiffer) Add(key uint64, val *typegen.Deferred) error {
ds := new(v0DealState)
err := ds.UnmarshalCBOR(bytes.NewReader(val.Raw))
if err != nil {
return err
}
d.Results.Added = append(d.Results.Added, DealIDState{abi.DealID(key), ds})
return nil
}

func (d *v0MarketStatesDiffer) Modify(key uint64, from, to *typegen.Deferred) error {
dsFrom := new(v0DealState)
if err := dsFrom.UnmarshalCBOR(bytes.NewReader(from.Raw)); err != nil {
return err
}

dsTo := new(v0DealState)
if err := dsTo.UnmarshalCBOR(bytes.NewReader(to.Raw)); err != nil {
return err
}

if *dsFrom != *dsTo {
d.Results.Modified = append(d.Results.Modified, DealStateChange{abi.DealID(key), dsFrom, dsTo})
}
return nil
}

func (d *v0MarketStatesDiffer) Remove(key uint64, val *typegen.Deferred) error {
ds := new(v0DealState)
err := ds.UnmarshalCBOR(bytes.NewReader(val.Raw))
if err != nil {
return err
}
d.Results.Removed = append(d.Results.Removed, DealIDState{abi.DealID(key), ds})
return nil
}

type v0DealState struct {
market.DealState
}

func (ds *v0DealState) SectorStartEpoch() abi.ChainEpoch {
return ds.DealState.SectorStartEpoch
}

func (ds *v0DealState) SlashEpoch() abi.ChainEpoch {
return ds.DealState.SlashEpoch
}

func (ds *v0DealState) LastUpdatedEpoch() abi.ChainEpoch {
return ds.DealState.LastUpdatedEpoch
}

func (ds *v0DealState) Equals(other DealState) bool {
v0other, ok := other.(*v0DealState)
return ok && *ds == *v0other
}

type v0DealProposals struct {
adt.Array
}

func (s *v0DealProposals) Diff(other DealProposals) (*DealProposalChanges, error) {
v0other, ok := other.(*v0DealProposals)
if !ok {
// TODO handle this if possible on a case by case basis but for now, just fail
return nil, errors.New("cannot compare deal proposals across versions")
}
results := new(DealProposalChanges)
if err := adt.DiffAdtArray(s, v0other, &v0MarketProposalsDiffer{results}); err != nil {
return nil, fmt.Errorf("diffing deal proposals: %w", err)
}

return results, nil
}

type v0MarketProposalsDiffer struct {
Results *DealProposalChanges
}

type v0DealProposal struct {
market.DealProposal
}

func (d *v0MarketProposalsDiffer) Add(key uint64, val *typegen.Deferred) error {
dp := new(v0DealProposal)
err := dp.UnmarshalCBOR(bytes.NewReader(val.Raw))
if err != nil {
return err
}
d.Results.Added = append(d.Results.Added, ProposalIDState{abi.DealID(key), dp})
return nil
}

func (d *v0MarketProposalsDiffer) Modify(key uint64, from, to *typegen.Deferred) error {
// short circuit, DealProposals are static
return nil
}

func (d *v0MarketProposalsDiffer) Remove(key uint64, val *typegen.Deferred) error {
dp := new(v0DealProposal)
err := dp.UnmarshalCBOR(bytes.NewReader(val.Raw))
if err != nil {
return err
}
d.Results.Removed = append(d.Results.Removed, ProposalIDState{abi.DealID(key), dp})
return nil
}
Loading