From 335d165db6c982b327b0bf6d17bb18dc75bd2a28 Mon Sep 17 00:00:00 2001 From: Dirk McCormick Date: Tue, 11 Aug 2020 17:01:16 -0400 Subject: [PATCH] refactor: use spec-actors paych with AMT for lane states --- go.mod | 6 +++--- go.sum | 8 ++++++-- paychmgr/manager.go | 10 ++++++++-- paychmgr/mock_test.go | 20 ++++++++++++++++++++ paychmgr/paych.go | 24 +++++++++++++++++++----- paychmgr/paych_test.go | 14 +++++++++----- paychmgr/state.go | 35 +++++++++++++++++++++++++---------- 7 files changed, 90 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 20f09a37a..e217dfaa5 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/drand/drand v1.0.3-0.20200714175734-29705eaf09d4 github.com/drand/kyber v1.1.1 github.com/fatih/color v1.8.0 - github.com/filecoin-project/chain-validation v0.0.6-0.20200812173150-c013d785a8d5 + github.com/filecoin-project/chain-validation v0.0.6-0.20200813000554-40c22fe26eef github.com/filecoin-project/filecoin-ffi v0.30.4-0.20200716204036-cddc56607e1d github.com/filecoin-project/go-address v0.0.3 github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20200731171407-e559a0579161 // indirect @@ -35,7 +35,7 @@ require ( github.com/filecoin-project/go-statestore v0.1.0 github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b github.com/filecoin-project/sector-storage v0.0.0-20200810171746-eac70842d8e0 - github.com/filecoin-project/specs-actors v0.8.7-0.20200811223639-8db91253c07a + github.com/filecoin-project/specs-actors v0.9.2 github.com/filecoin-project/specs-storage v0.1.1-0.20200730063404-f7db367e9401 github.com/filecoin-project/storage-fsm v0.0.0-20200805013058-9d9ea4e6331f github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 @@ -115,7 +115,7 @@ require ( github.com/syndtr/goleveldb v1.0.0 github.com/urfave/cli/v2 v2.2.0 github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba - github.com/whyrusleeping/cbor-gen v0.0.0-20200811225321-4fed70922d45 + github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 diff --git a/go.sum b/go.sum index fc2398e4d..121a3b955 100644 --- a/go.sum +++ b/go.sum @@ -215,8 +215,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.8.0 h1:5bzFgL+oy7JITMTxUPJ00n7VxmYd/PdMp5mHFX40/RY= github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8= github.com/fd/go-nat v1.0.0/go.mod h1:BTBu/CKvMmOMUPkKVef1pngt2WFH/lg7E6yQnulfp6E= -github.com/filecoin-project/chain-validation v0.0.6-0.20200812173150-c013d785a8d5 h1:1oXwXT5RTSKrcOuxes8a4JzZF+PJ3yYjPWxpgIh/igg= -github.com/filecoin-project/chain-validation v0.0.6-0.20200812173150-c013d785a8d5/go.mod h1:/R8d9VnHt2kkbiC+dbDV8cbZJ5EgRjKBgNQgtl5dubQ= +github.com/filecoin-project/chain-validation v0.0.6-0.20200813000554-40c22fe26eef h1:MtQRSnJLsQOOlmsd/Ua5KWXimpxcaa715h6FUh/eJPY= +github.com/filecoin-project/chain-validation v0.0.6-0.20200813000554-40c22fe26eef/go.mod h1:SMj5VK1pYgqC8FXVEtOBRTc+9AIrYu+C+K3tAXi2Rk8= github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0= github.com/filecoin-project/go-address v0.0.2-0.20200218010043-eb9bb40ed5be/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0= github.com/filecoin-project/go-address v0.0.3 h1:eVfbdjEbpbzIrbiSa+PiGUY+oDK9HnUn+M1R/ggoHf8= @@ -266,6 +266,8 @@ github.com/filecoin-project/specs-actors v0.8.7-0.20200811203034-272d022c1923 h1 github.com/filecoin-project/specs-actors v0.8.7-0.20200811203034-272d022c1923/go.mod h1:hukRu6vKQrrS7Nt+fC/ql4PqWLSfmAWNshD/VDtARZU= github.com/filecoin-project/specs-actors v0.8.7-0.20200811223639-8db91253c07a h1:DIOf9d5S4aBs6jwqGPzNSGhuMjn5w3R4kbHU3NpNBtw= github.com/filecoin-project/specs-actors v0.8.7-0.20200811223639-8db91253c07a/go.mod h1:hukRu6vKQrrS7Nt+fC/ql4PqWLSfmAWNshD/VDtARZU= +github.com/filecoin-project/specs-actors v0.9.2 h1:0JG0QLHw8pO6BPqPRe9eQxQW60biHAQsx1rlQ9QbzZ0= +github.com/filecoin-project/specs-actors v0.9.2/go.mod h1:YasnVUOUha0DN5wB+twl+V8LlDKVNknRG00kTJpsfFA= github.com/filecoin-project/specs-storage v0.1.1-0.20200622113353-88a9704877ea h1:iixjULRQFPn7Q9KlIqfwLJnlAXO10bbkI+xy5GKGdLY= github.com/filecoin-project/specs-storage v0.1.1-0.20200622113353-88a9704877ea/go.mod h1:Pr5ntAaxsh+sLG/LYiL4tKzvA83Vk5vLODYhfNwOg7k= github.com/filecoin-project/specs-storage v0.1.1-0.20200730063404-f7db367e9401 h1:jLzN1hwO5WpKPu8ASbW8fs1FUCsOWNvoBXzQhv+8/E8= @@ -1383,6 +1385,8 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20200810223238-211df3b9e24c h1:BMg3YUwL github.com/whyrusleeping/cbor-gen v0.0.0-20200810223238-211df3b9e24c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20200811225321-4fed70922d45 h1:2QQ0rYt7Y8NhRPtuAlZMBNdqoVCh2dR6BQAtGJLlZgw= github.com/whyrusleeping/cbor-gen v0.0.0-20200811225321-4fed70922d45/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c h1:otRnI08JoahNBxUFqX3372Ab9GnTj8L5J9iP5ImyxGU= +github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-ctrlnet v0.0.0-20180313164037-f564fbbdaa95/go.mod h1:SJqKCCPXRfBFCwXjfNT/skfsceF7+MBFLI2OrvuRA7g= diff --git a/paychmgr/manager.go b/paychmgr/manager.go index 6d8b8d912..6244d9104 100644 --- a/paychmgr/manager.go +++ b/paychmgr/manager.go @@ -13,6 +13,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/specs-actors/actors/builtin/paych" + "github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" @@ -39,6 +40,7 @@ type PaychAPI struct { type stateManagerAPI interface { LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) + AdtStore(ctx context.Context) adt.Store } // paychAPI defines the API methods needed by the payment channel manager @@ -55,10 +57,14 @@ type managerAPI interface { // managerAPIImpl is used to create a composite that implements managerAPI type managerAPIImpl struct { - stateManagerAPI + *stmgr.StateManager paychAPI } +func (m *managerAPIImpl) AdtStore(ctx context.Context) adt.Store { + return m.ChainStore().Store(ctx) +} + type Manager struct { // The Manager context is used to terminate wait operations on shutdown ctx context.Context @@ -76,7 +82,7 @@ func NewManager(mctx helpers.MetricsCtx, lc fx.Lifecycle, sm *stmgr.StateManager ctx := helpers.LifecycleCtx(mctx, lc) ctx, shutdown := context.WithCancel(ctx) - impl := &managerAPIImpl{stateManagerAPI: sm, paychAPI: &api} + impl := &managerAPIImpl{StateManager: sm, paychAPI: &api} return &Manager{ ctx: ctx, shutdown: shutdown, diff --git a/paychmgr/mock_test.go b/paychmgr/mock_test.go index 1cb66e1af..6b46b710a 100644 --- a/paychmgr/mock_test.go +++ b/paychmgr/mock_test.go @@ -5,6 +5,10 @@ import ( "fmt" "sync" + cbornode "github.com/ipfs/go-ipld-cbor" + + "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" @@ -34,6 +38,7 @@ type mockStateManager struct { lk sync.Mutex accountState map[address.Address]account.State paychState map[address.Address]mockPchState + store adt.Store response *api.InvocResult } @@ -41,9 +46,14 @@ func newMockStateManager() *mockStateManager { return &mockStateManager{ accountState: make(map[address.Address]account.State), paychState: make(map[address.Address]mockPchState), + store: adt.WrapStore(context.Background(), cbornode.NewMemCborStore()), } } +func (sm *mockStateManager) AdtStore(ctx context.Context) adt.Store { + return sm.store +} + func (sm *mockStateManager) setAccountState(a address.Address, state account.State) { sm.lk.Lock() defer sm.lk.Unlock() @@ -56,6 +66,16 @@ func (sm *mockStateManager) setPaychState(a address.Address, actor *types.Actor, sm.paychState[a] = mockPchState{actor, state} } +func (sm *mockStateManager) storeLaneStates(laneStates []*paych.LaneState) (cid.Cid, error) { + arr := adt.MakeEmptyArray(sm.store) + for _, ls := range laneStates { + if err := arr.Set(ls.ID, ls); err != nil { + return cid.Undef, err + } + } + return arr.Root() +} + func (sm *mockStateManager) LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error) { sm.lk.Lock() defer sm.lk.Unlock() diff --git a/paychmgr/paych.go b/paychmgr/paych.go index ba110b186..87576dec2 100644 --- a/paychmgr/paych.go +++ b/paychmgr/paych.go @@ -5,6 +5,8 @@ import ( "context" "fmt" + "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/ipfs/go-cid" "github.com/filecoin-project/go-address" @@ -88,7 +90,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add } // Check the voucher against the highest known voucher nonce / value - laneStates, err := ca.laneState(pchState, ch) + laneStates, err := ca.laneState(ctx, pchState, ch) if err != nil { return nil, err } @@ -313,16 +315,28 @@ func (ca *channelAccessor) nextNonceForLane(ctx context.Context, ch address.Addr // laneState gets the LaneStates from chain, then applies all vouchers in // the data store over the chain state -func (ca *channelAccessor) laneState(state *paych.State, ch address.Address) (map[uint64]*paych.LaneState, error) { +func (ca *channelAccessor) laneState(ctx context.Context, state *paych.State, ch address.Address) (map[uint64]*paych.LaneState, error) { // TODO: we probably want to call UpdateChannelState with all vouchers to be fully correct // (but technically dont't need to) - laneStates := make(map[uint64]*paych.LaneState, len(state.LaneStates)) // Get the lane state from the chain - for _, laneState := range state.LaneStates { - laneStates[laneState.ID] = laneState + store := ca.api.AdtStore(ctx) + lsamt, err := adt.AsArray(store, state.LaneStates) + if err != nil { + return nil, err } + // Note: we use a map instead of an array to store laneStates because the + // client sets the lane ID (the index) and potentially they could use a + // very large index. + var ls paych.LaneState + laneStates := make(map[uint64]*paych.LaneState, lsamt.Length()) + lsamt.ForEach(&ls, func(i int64) error { + current := ls + laneStates[ls.ID] = ¤t + return nil + }) + // Apply locally stored vouchers vouchers, err := ca.store.VouchersForPaych(ch) if err != nil && err != ErrChannelNotTracked { diff --git a/paychmgr/paych_test.go b/paychmgr/paych_test.go index 69aa39119..14746d7f7 100644 --- a/paychmgr/paych_test.go +++ b/paychmgr/paych_test.go @@ -47,7 +47,6 @@ func TestPaychOutbound(t *testing.T) { ToSend: big.NewInt(0), SettlingAt: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0), - LaneStates: []*paych.LaneState{}, }) mgr, err := newManager(store, mock) @@ -85,7 +84,6 @@ func TestPaychInbound(t *testing.T) { ToSend: big.NewInt(0), SettlingAt: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0), - LaneStates: []*paych.LaneState{}, }) mgr, err := newManager(store, mock) @@ -262,13 +260,17 @@ func TestCheckVoucherValid(t *testing.T) { Nonce: 0, Balance: tcase.actorBalance, } + + laneStates, err := mock.storeLaneStates(tcase.laneStates) + require.NoError(t, err) + mock.setPaychState(ch, act, paych.State{ From: fromAcct, To: toAcct, ToSend: tcase.toSend, SettlingAt: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0), - LaneStates: tcase.laneStates, + LaneStates: laneStates, }) mgr, err := newManager(store, mock) @@ -325,13 +327,16 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) { Nonce: 0, Balance: actorBalance, } + + lsCid, err := mock.storeLaneStates(laneStates) + require.NoError(t, err) mock.setPaychState(ch, act, paych.State{ From: fromAcct, To: toAcct, ToSend: toSend, SettlingAt: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0), - LaneStates: laneStates, + LaneStates: lsCid, }) mgr, err := newManager(store, mock) @@ -640,7 +645,6 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, addre ToSend: big.NewInt(0), SettlingAt: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0), - LaneStates: []*paych.LaneState{}, }) store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore())) diff --git a/paychmgr/state.go b/paychmgr/state.go index 1502cc8cb..03c44a8a7 100644 --- a/paychmgr/state.go +++ b/paychmgr/state.go @@ -3,6 +3,8 @@ package paychmgr import ( "context" + "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/filecoin-project/specs-actors/actors/builtin/account" "github.com/filecoin-project/go-address" @@ -43,10 +45,15 @@ func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Ad } to := account.Address + nextLane, err := ca.nextLaneFromState(ctx, st) + if err != nil { + return nil, err + } + ci := &ChannelInfo{ Channel: &ch, Direction: dir, - NextLane: nextLaneFromState(st), + NextLane: nextLane, } if dir == DirOutbound { @@ -60,16 +67,24 @@ func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Ad return ci, nil } -func nextLaneFromState(st *paych.State) uint64 { - if len(st.LaneStates) == 0 { - return 0 +func (ca *stateAccessor) nextLaneFromState(ctx context.Context, st *paych.State) (uint64, error) { + store := ca.sm.AdtStore(ctx) + laneStates, err := adt.AsArray(store, st.LaneStates) + if err != nil { + return 0, err + } + if laneStates.Length() == 0 { + return 0, nil } - maxLane := st.LaneStates[0].ID - for _, state := range st.LaneStates { - if state.ID > maxLane { - maxLane = state.ID + maxLane := uint64(0) + var ls paych.LaneState + laneStates.ForEach(&ls, func(i int64) error { + if ls.ID > maxLane { + maxLane = ls.ID } - } - return maxLane + 1 + return nil + }) + + return maxLane + 1, nil }