refactor: use spec-actors paych with AMT for lane states

This commit is contained in:
Dirk McCormick 2020-08-11 17:01:16 -04:00 committed by Steven Allen
parent 4308d37c84
commit 335d165db6
7 changed files with 90 additions and 27 deletions

6
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/drand/drand v1.0.3-0.20200714175734-29705eaf09d4 github.com/drand/drand v1.0.3-0.20200714175734-29705eaf09d4
github.com/drand/kyber v1.1.1 github.com/drand/kyber v1.1.1
github.com/fatih/color v1.8.0 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/filecoin-ffi v0.30.4-0.20200716204036-cddc56607e1d
github.com/filecoin-project/go-address v0.0.3 github.com/filecoin-project/go-address v0.0.3
github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20200731171407-e559a0579161 // indirect 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-statestore v0.1.0
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b 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/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/specs-storage v0.1.1-0.20200730063404-f7db367e9401
github.com/filecoin-project/storage-fsm v0.0.0-20200805013058-9d9ea4e6331f github.com/filecoin-project/storage-fsm v0.0.0-20200805013058-9d9ea4e6331f
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
@ -115,7 +115,7 @@ require (
github.com/syndtr/goleveldb v1.0.0 github.com/syndtr/goleveldb v1.0.0
github.com/urfave/cli/v2 v2.2.0 github.com/urfave/cli/v2 v2.2.0
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba 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/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d
github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542

8
go.sum
View File

@ -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 h1:5bzFgL+oy7JITMTxUPJ00n7VxmYd/PdMp5mHFX40/RY=
github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8= 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/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.20200813000554-40c22fe26eef h1:MtQRSnJLsQOOlmsd/Ua5KWXimpxcaa715h6FUh/eJPY=
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/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.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.2-0.20200218010043-eb9bb40ed5be/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0=
github.com/filecoin-project/go-address v0.0.3 h1:eVfbdjEbpbzIrbiSa+PiGUY+oDK9HnUn+M1R/ggoHf8= 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.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 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.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 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.20200622113353-88a9704877ea/go.mod h1:Pr5ntAaxsh+sLG/LYiL4tKzvA83Vk5vLODYhfNwOg7k=
github.com/filecoin-project/specs-storage v0.1.1-0.20200730063404-f7db367e9401 h1:jLzN1hwO5WpKPu8ASbW8fs1FUCsOWNvoBXzQhv+8/E8= 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-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 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-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 h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= 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= github.com/whyrusleeping/go-ctrlnet v0.0.0-20180313164037-f564fbbdaa95/go.mod h1:SJqKCCPXRfBFCwXjfNT/skfsceF7+MBFLI2OrvuRA7g=

View File

@ -13,6 +13,7 @@ import (
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/specs-actors/actors/builtin/paych" "github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
@ -39,6 +40,7 @@ type PaychAPI struct {
type stateManagerAPI interface { type stateManagerAPI interface {
LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error) 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) 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 // 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 // managerAPIImpl is used to create a composite that implements managerAPI
type managerAPIImpl struct { type managerAPIImpl struct {
stateManagerAPI *stmgr.StateManager
paychAPI paychAPI
} }
func (m *managerAPIImpl) AdtStore(ctx context.Context) adt.Store {
return m.ChainStore().Store(ctx)
}
type Manager struct { type Manager struct {
// The Manager context is used to terminate wait operations on shutdown // The Manager context is used to terminate wait operations on shutdown
ctx context.Context ctx context.Context
@ -76,7 +82,7 @@ func NewManager(mctx helpers.MetricsCtx, lc fx.Lifecycle, sm *stmgr.StateManager
ctx := helpers.LifecycleCtx(mctx, lc) ctx := helpers.LifecycleCtx(mctx, lc)
ctx, shutdown := context.WithCancel(ctx) ctx, shutdown := context.WithCancel(ctx)
impl := &managerAPIImpl{stateManagerAPI: sm, paychAPI: &api} impl := &managerAPIImpl{StateManager: sm, paychAPI: &api}
return &Manager{ return &Manager{
ctx: ctx, ctx: ctx,
shutdown: shutdown, shutdown: shutdown,

View File

@ -5,6 +5,10 @@ import (
"fmt" "fmt"
"sync" "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/go-address"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
@ -34,6 +38,7 @@ type mockStateManager struct {
lk sync.Mutex lk sync.Mutex
accountState map[address.Address]account.State accountState map[address.Address]account.State
paychState map[address.Address]mockPchState paychState map[address.Address]mockPchState
store adt.Store
response *api.InvocResult response *api.InvocResult
} }
@ -41,9 +46,14 @@ func newMockStateManager() *mockStateManager {
return &mockStateManager{ return &mockStateManager{
accountState: make(map[address.Address]account.State), accountState: make(map[address.Address]account.State),
paychState: make(map[address.Address]mockPchState), 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) { func (sm *mockStateManager) setAccountState(a address.Address, state account.State) {
sm.lk.Lock() sm.lk.Lock()
defer sm.lk.Unlock() defer sm.lk.Unlock()
@ -56,6 +66,16 @@ func (sm *mockStateManager) setPaychState(a address.Address, actor *types.Actor,
sm.paychState[a] = mockPchState{actor, state} 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) { func (sm *mockStateManager) LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error) {
sm.lk.Lock() sm.lk.Lock()
defer sm.lk.Unlock() defer sm.lk.Unlock()

View File

@ -5,6 +5,8 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/filecoin-project/go-address" "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 // 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 { if err != nil {
return nil, err 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 // laneState gets the LaneStates from chain, then applies all vouchers in
// the data store over the chain state // 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 // TODO: we probably want to call UpdateChannelState with all vouchers to be fully correct
// (but technically dont't need to) // (but technically dont't need to)
laneStates := make(map[uint64]*paych.LaneState, len(state.LaneStates))
// Get the lane state from the chain // Get the lane state from the chain
for _, laneState := range state.LaneStates { store := ca.api.AdtStore(ctx)
laneStates[laneState.ID] = laneState 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] = &current
return nil
})
// Apply locally stored vouchers // Apply locally stored vouchers
vouchers, err := ca.store.VouchersForPaych(ch) vouchers, err := ca.store.VouchersForPaych(ch)
if err != nil && err != ErrChannelNotTracked { if err != nil && err != ErrChannelNotTracked {

View File

@ -47,7 +47,6 @@ func TestPaychOutbound(t *testing.T) {
ToSend: big.NewInt(0), ToSend: big.NewInt(0),
SettlingAt: abi.ChainEpoch(0), SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0),
LaneStates: []*paych.LaneState{},
}) })
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
@ -85,7 +84,6 @@ func TestPaychInbound(t *testing.T) {
ToSend: big.NewInt(0), ToSend: big.NewInt(0),
SettlingAt: abi.ChainEpoch(0), SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0),
LaneStates: []*paych.LaneState{},
}) })
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
@ -262,13 +260,17 @@ func TestCheckVoucherValid(t *testing.T) {
Nonce: 0, Nonce: 0,
Balance: tcase.actorBalance, Balance: tcase.actorBalance,
} }
laneStates, err := mock.storeLaneStates(tcase.laneStates)
require.NoError(t, err)
mock.setPaychState(ch, act, paych.State{ mock.setPaychState(ch, act, paych.State{
From: fromAcct, From: fromAcct,
To: toAcct, To: toAcct,
ToSend: tcase.toSend, ToSend: tcase.toSend,
SettlingAt: abi.ChainEpoch(0), SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0),
LaneStates: tcase.laneStates, LaneStates: laneStates,
}) })
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
@ -325,13 +327,16 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
Nonce: 0, Nonce: 0,
Balance: actorBalance, Balance: actorBalance,
} }
lsCid, err := mock.storeLaneStates(laneStates)
require.NoError(t, err)
mock.setPaychState(ch, act, paych.State{ mock.setPaychState(ch, act, paych.State{
From: fromAcct, From: fromAcct,
To: toAcct, To: toAcct,
ToSend: toSend, ToSend: toSend,
SettlingAt: abi.ChainEpoch(0), SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0),
LaneStates: laneStates, LaneStates: lsCid,
}) })
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
@ -640,7 +645,6 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, addre
ToSend: big.NewInt(0), ToSend: big.NewInt(0),
SettlingAt: abi.ChainEpoch(0), SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0),
LaneStates: []*paych.LaneState{},
}) })
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore())) store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))

View File

@ -3,6 +3,8 @@ package paychmgr
import ( import (
"context" "context"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/specs-actors/actors/builtin/account" "github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
@ -43,10 +45,15 @@ func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Ad
} }
to := account.Address to := account.Address
nextLane, err := ca.nextLaneFromState(ctx, st)
if err != nil {
return nil, err
}
ci := &ChannelInfo{ ci := &ChannelInfo{
Channel: &ch, Channel: &ch,
Direction: dir, Direction: dir,
NextLane: nextLaneFromState(st), NextLane: nextLane,
} }
if dir == DirOutbound { if dir == DirOutbound {
@ -60,16 +67,24 @@ func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Ad
return ci, nil return ci, nil
} }
func nextLaneFromState(st *paych.State) uint64 { func (ca *stateAccessor) nextLaneFromState(ctx context.Context, st *paych.State) (uint64, error) {
if len(st.LaneStates) == 0 { store := ca.sm.AdtStore(ctx)
return 0 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 maxLane := uint64(0)
for _, state := range st.LaneStates { var ls paych.LaneState
if state.ID > maxLane { laneStates.ForEach(&ls, func(i int64) error {
maxLane = state.ID if ls.ID > maxLane {
maxLane = ls.ID
} }
} return nil
return maxLane + 1 })
return maxLane + 1, nil
} }