feat(paych): convert paych actor

build abstraction for paych actor and switch to using it in payment channel manager and state
predicates
This commit is contained in:
hannahhoward 2020-09-15 21:06:04 -07:00
parent 4e01fad0d4
commit 05c11531b1
15 changed files with 460 additions and 346 deletions

View File

@ -0,0 +1,89 @@
package mock
import (
"io"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
)
type mockState struct {
from address.Address
to address.Address
settlingAt abi.ChainEpoch
toSend abi.TokenAmount
lanes map[uint64]paych.LaneState
}
type mockLaneState struct {
redeemed big.Int
nonce uint64
}
// NewMockPayChState constructs a state for a payment channel with the set fixed values
// that satisfies the paych.State interface.
func NewMockPayChState(from address.Address,
to address.Address,
settlingAt abi.ChainEpoch,
toSend abi.TokenAmount,
lanes map[uint64]paych.LaneState,
) paych.State {
return &mockState{from, to, settlingAt, toSend, lanes}
}
// NewMockLaneState constructs a state for a payment channel lane with the set fixed values
// that satisfies the paych.LaneState interface. Useful for populating lanes when
// calling NewMockPayChState
func NewMockLaneState(redeemed big.Int, nonce uint64) paych.LaneState {
return &mockLaneState{redeemed, nonce}
}
func (ms *mockState) MarshalCBOR(io.Writer) error {
panic("not implemented")
}
// Channel owner, who has funded the actor
func (ms *mockState) From() address.Address {
return ms.from
}
// Recipient of payouts from channel
func (ms *mockState) To() address.Address {
return ms.to
}
// Height at which the channel can be `Collected`
func (ms *mockState) SettlingAt() abi.ChainEpoch {
return ms.settlingAt
}
// Amount successfully redeemed through the payment channel, paid out on `Collect()`
func (ms *mockState) ToSend() abi.TokenAmount {
return ms.toSend
}
// Get total number of lanes
func (ms *mockState) LaneCount() (uint64, error) {
return uint64(len(ms.lanes)), nil
}
// Iterate lane states
func (ms *mockState) ForEachLaneState(cb func(idx uint64, dl paych.LaneState) error) error {
var lastErr error
for lane, state := range ms.lanes {
if err := cb(lane, state); err != nil {
lastErr = err
}
}
return lastErr
}
func (mls *mockLaneState) Redeemed() big.Int {
return mls.redeemed
}
func (mls *mockLaneState) Nonce() uint64 {
return mls.nonce
}

View File

@ -0,0 +1,56 @@
package paych
import (
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
big "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/cbor"
v0builtin "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/types"
)
// Load returns an abstract copy of payment channel state, irregardless of actor version
func Load(store adt.Store, act *types.Actor) (State, error) {
switch act.Code {
case v0builtin.PaymentChannelActorCodeID:
out := v0State{store: store}
err := store.Get(store.Context(), act.Head, &out)
if err != nil {
return nil, err
}
return &out, nil
}
return nil, xerrors.Errorf("unknown actor code %s", act.Code)
}
// State is an abstract version of payment channel state that works across
// versions
type State interface {
cbor.Marshaler
// Channel owner, who has funded the actor
From() address.Address
// Recipient of payouts from channel
To() address.Address
// Height at which the channel can be `Collected`
SettlingAt() abi.ChainEpoch
// Amount successfully redeemed through the payment channel, paid out on `Collect()`
ToSend() abi.TokenAmount
// Get total number of lanes
LaneCount() (uint64, error)
// Iterate lane states
ForEachLaneState(cb func(idx uint64, dl LaneState) error) error
}
// LaneState is an abstract copy of the state of a single lane
type LaneState interface {
Redeemed() big.Int
Nonce() uint64
}

View File

@ -0,0 +1,89 @@
package paych
import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
big "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
v0adt "github.com/filecoin-project/specs-actors/actors/util/adt"
)
type v0State struct {
paych.State
store adt.Store
lsAmt *v0adt.Array
}
// Channel owner, who has funded the actor
func (s *v0State) From() address.Address {
return s.State.From
}
// Recipient of payouts from channel
func (s *v0State) To() address.Address {
return s.State.From
}
// Height at which the channel can be `Collected`
func (s *v0State) SettlingAt() abi.ChainEpoch {
return s.State.SettlingAt
}
// Amount successfully redeemed through the payment channel, paid out on `Collect()`
func (s *v0State) ToSend() abi.TokenAmount {
return s.State.ToSend
}
func (s *v0State) getOrLoadLsAmt() (*v0adt.Array, error) {
if s.lsAmt != nil {
return s.lsAmt, nil
}
// Get the lane state from the chain
lsamt, err := v0adt.AsArray(s.store, s.State.LaneStates)
if err != nil {
return nil, err
}
s.lsAmt = lsamt
return lsamt, nil
}
// Get total number of lanes
func (s *v0State) LaneCount() (uint64, error) {
lsamt, err := s.getOrLoadLsAmt()
if err != nil {
return 0, err
}
return lsamt.Length(), nil
}
// Iterate lane states
func (s *v0State) ForEachLaneState(cb func(idx uint64, dl LaneState) error) error {
// Get the lane state from the chain
lsamt, err := s.getOrLoadLsAmt()
if err != nil {
return 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
return lsamt.ForEach(&ls, func(i int64) error {
return cb(uint64(i), &v0LaneState{ls})
})
}
type v0LaneState struct {
paych.LaneState
}
func (ls *v0LaneState) Redeemed() big.Int {
return ls.LaneState.Redeemed
}
func (ls *v0LaneState) Nonce() uint64 {
return ls.LaneState.Nonce
}

View File

@ -7,13 +7,12 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/specs-actors/actors/builtin"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
typegen "github.com/whyrusleeping/cbor-gen"
@ -49,7 +48,7 @@ func NewStatePredicates(api ChainAPI) *StatePredicates {
// - err
type DiffTipSetKeyFunc func(ctx context.Context, oldState, newState types.TipSetKey) (changed bool, user UserData, err error)
type DiffActorStateFunc func(ctx context.Context, oldActorStateHead, newActorStateHead cid.Cid) (changed bool, user UserData, err error)
type DiffActorStateFunc func(ctx context.Context, oldActorState *types.Actor, newActorState *types.Actor) (changed bool, user UserData, err error)
// OnActorStateChanged calls diffStateFunc when the state changes for the given actor
func (sp *StatePredicates) OnActorStateChanged(addr address.Address, diffStateFunc DiffActorStateFunc) DiffTipSetKeyFunc {
@ -66,7 +65,7 @@ func (sp *StatePredicates) OnActorStateChanged(addr address.Address, diffStateFu
if oldActor.Head.Equals(newActor.Head) {
return false, nil, nil
}
return diffStateFunc(ctx, oldActor.Head, newActor.Head)
return diffStateFunc(ctx, oldActor, newActor)
}
}
@ -74,13 +73,13 @@ type DiffStorageMarketStateFunc func(ctx context.Context, oldState *market.State
// OnStorageMarketActorChanged calls diffStorageMarketState when the state changes for the market actor
func (sp *StatePredicates) OnStorageMarketActorChanged(diffStorageMarketState DiffStorageMarketStateFunc) DiffTipSetKeyFunc {
return sp.OnActorStateChanged(builtin.StorageMarketActorAddr, func(ctx context.Context, oldActorStateHead, newActorStateHead cid.Cid) (changed bool, user UserData, err error) {
return sp.OnActorStateChanged(builtin.StorageMarketActorAddr, func(ctx context.Context, oldActorState, newActorState *types.Actor) (changed bool, user UserData, err error) {
var oldState market.State
if err := sp.cst.Get(ctx, oldActorStateHead, &oldState); err != nil {
if err := sp.cst.Get(ctx, oldActorState.Head, &oldState); err != nil {
return false, nil, err
}
var newState market.State
if err := sp.cst.Get(ctx, newActorStateHead, &newState); err != nil {
if err := sp.cst.Get(ctx, newActorState.Head, &newState); err != nil {
return false, nil, err
}
return diffStorageMarketState(ctx, &oldState, &newState)
@ -408,13 +407,13 @@ func (sp *StatePredicates) AvailableBalanceChangedForAddresses(getAddrs func() [
type DiffMinerActorStateFunc func(ctx context.Context, oldState *miner.State, newState *miner.State) (changed bool, user UserData, err error)
func (sp *StatePredicates) OnInitActorChange(diffInitActorState DiffInitActorStateFunc) DiffTipSetKeyFunc {
return sp.OnActorStateChanged(builtin.InitActorAddr, func(ctx context.Context, oldActorStateHead, newActorStateHead cid.Cid) (changed bool, user UserData, err error) {
return sp.OnActorStateChanged(builtin.InitActorAddr, func(ctx context.Context, oldActorState, newActorState *types.Actor) (changed bool, user UserData, err error) {
var oldState init_.State
if err := sp.cst.Get(ctx, oldActorStateHead, &oldState); err != nil {
if err := sp.cst.Get(ctx, oldActorState.Head, &oldState); err != nil {
return false, nil, err
}
var newState init_.State
if err := sp.cst.Get(ctx, newActorStateHead, &newState); err != nil {
if err := sp.cst.Get(ctx, newActorState.Head, &newState); err != nil {
return false, nil, err
}
return diffInitActorState(ctx, &oldState, &newState)
@ -423,13 +422,13 @@ func (sp *StatePredicates) OnInitActorChange(diffInitActorState DiffInitActorSta
}
func (sp *StatePredicates) OnMinerActorChange(minerAddr address.Address, diffMinerActorState DiffMinerActorStateFunc) DiffTipSetKeyFunc {
return sp.OnActorStateChanged(minerAddr, func(ctx context.Context, oldActorStateHead, newActorStateHead cid.Cid) (changed bool, user UserData, err error) {
return sp.OnActorStateChanged(minerAddr, func(ctx context.Context, oldActorState, newActorState *types.Actor) (changed bool, user UserData, err error) {
var oldState miner.State
if err := sp.cst.Get(ctx, oldActorStateHead, &oldState); err != nil {
if err := sp.cst.Get(ctx, oldActorState.Head, &oldState); err != nil {
return false, nil, err
}
var newState miner.State
if err := sp.cst.Get(ctx, newActorStateHead, &newState); err != nil {
if err := sp.cst.Get(ctx, newActorState.Head, &newState); err != nil {
return false, nil, err
}
return diffMinerActorState(ctx, &oldState, &newState)
@ -608,20 +607,20 @@ func (sp *StatePredicates) OnMinerPreCommitChange() DiffMinerActorStateFunc {
}
// DiffPaymentChannelStateFunc is function that compares two states for the payment channel
type DiffPaymentChannelStateFunc func(ctx context.Context, oldState *paych.State, newState *paych.State) (changed bool, user UserData, err error)
type DiffPaymentChannelStateFunc func(ctx context.Context, oldState paych.State, newState paych.State) (changed bool, user UserData, err error)
// OnPaymentChannelActorChanged calls diffPaymentChannelState when the state changes for the the payment channel actor
func (sp *StatePredicates) OnPaymentChannelActorChanged(paychAddr address.Address, diffPaymentChannelState DiffPaymentChannelStateFunc) DiffTipSetKeyFunc {
return sp.OnActorStateChanged(paychAddr, func(ctx context.Context, oldActorStateHead, newActorStateHead cid.Cid) (changed bool, user UserData, err error) {
var oldState paych.State
if err := sp.cst.Get(ctx, oldActorStateHead, &oldState); err != nil {
return sp.OnActorStateChanged(paychAddr, func(ctx context.Context, oldActorState, newActorState *types.Actor) (changed bool, user UserData, err error) {
oldState, err := paych.Load(adt.WrapStore(ctx, sp.cst), oldActorState)
if err != nil {
return false, nil, err
}
var newState paych.State
if err := sp.cst.Get(ctx, newActorStateHead, &newState); err != nil {
newState, err := paych.Load(adt.WrapStore(ctx, sp.cst), newActorState)
if err != nil {
return false, nil, err
}
return diffPaymentChannelState(ctx, &oldState, &newState)
return diffPaymentChannelState(ctx, oldState, newState)
})
}
@ -633,13 +632,13 @@ type PayChToSendChange struct {
// OnToSendAmountChanges monitors changes on the total amount to send from one party to the other on a payment channel
func (sp *StatePredicates) OnToSendAmountChanges() DiffPaymentChannelStateFunc {
return func(ctx context.Context, oldState *paych.State, newState *paych.State) (changed bool, user UserData, err error) {
if oldState.ToSend.Equals(newState.ToSend) {
return func(ctx context.Context, oldState paych.State, newState paych.State) (changed bool, user UserData, err error) {
if oldState.ToSend().Equals(newState.ToSend()) {
return false, nil, nil
}
return true, &PayChToSendChange{
OldToSend: oldState.ToSend,
NewToSend: newState.ToSend,
OldToSend: oldState.ToSend(),
NewToSend: newState.ToSend(),
}, nil
}
}

View File

@ -221,7 +221,7 @@ func TestMarketPredicates(t *testing.T) {
// Test that OnActorStateChanged does not call the callback if the state has not changed
mockAddr, err := address.NewFromString("t01")
require.NoError(t, err)
actorDiffFn := preds.OnActorStateChanged(mockAddr, func(context.Context, cid.Cid, cid.Cid) (bool, UserData, error) {
actorDiffFn := preds.OnActorStateChanged(mockAddr, func(context.Context, *types.Actor, *types.Actor) (bool, UserData, error) {
t.Fatal("No state change so this should not be called")
return false, nil, nil
})

View File

@ -25,6 +25,7 @@ import (
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/store"
@ -1145,3 +1146,21 @@ func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoc
return build.NewestNetworkVersion
}
func (sm *StateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) {
st, err := sm.ParentState(ts)
if err != nil {
return nil, nil, err
}
act, err := st.GetActor(addr)
if err != nil {
return nil, nil, err
}
actState, err := paych.Load(sm.cs.Store(ctx), act)
if err != nil {
return nil, nil, err
}
return act, actState, nil
}

View File

@ -24,7 +24,8 @@ import (
"github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/api/apibstore"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/filecoin-project/go-address"
@ -88,7 +89,7 @@ func TestPaymentChannels(t *testing.T) {
// Wait for the chain to reach the settle height
chState := getPaychState(ctx, t, paymentReceiver, chAddr)
waitForHeight(ctx, t, paymentReceiver, chState.SettlingAt)
waitForHeight(ctx, t, paymentReceiver, chState.SettlingAt())
// receiver: paych collect <channel>
cmd = []string{chAddr.String()}
@ -540,8 +541,7 @@ func getPaychState(ctx context.Context, t *testing.T, node test.TestNode, chAddr
require.NoError(t, err)
store := cbor.NewCborStore(apibstore.NewAPIBlockstore(node))
var chState paych.State
err = store.Get(ctx, act.Head, &chState)
chState, err := paych.Load(adt.WrapStore(ctx, store), act)
require.NoError(t, err)
return chState

View File

@ -4,28 +4,23 @@ import (
"context"
"sync"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/node/modules/helpers"
"github.com/ipfs/go-datastore"
xerrors "golang.org/x/xerrors"
"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"
"github.com/ipfs/go-datastore"
logging "github.com/ipfs/go-log/v2"
"go.uber.org/fx"
xerrors "golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
v0paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/impl/full"
"github.com/filecoin-project/lotus/node/modules/helpers"
)
var log = logging.Logger("paych")
@ -40,9 +35,9 @@ type PaychAPI struct {
// stateManagerAPI defines the methods needed from StateManager
type stateManagerAPI interface {
LoadActorState(ctx context.Context, a address.Address, out interface{}, ts *types.TipSet) (*types.Actor, error)
ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error)
GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, 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
@ -226,7 +221,7 @@ func (pm *Manager) GetChannelInfo(addr address.Address) (*ChannelInfo, error) {
return ca.getChannelInfo(addr)
}
func (pm *Manager) CreateVoucher(ctx context.Context, ch address.Address, voucher paych.SignedVoucher) (*api.VoucherCreateResult, error) {
func (pm *Manager) CreateVoucher(ctx context.Context, ch address.Address, voucher v0paych.SignedVoucher) (*api.VoucherCreateResult, error) {
ca, err := pm.accessorByAddress(ch)
if err != nil {
return nil, err
@ -238,7 +233,7 @@ func (pm *Manager) CreateVoucher(ctx context.Context, ch address.Address, vouche
// CheckVoucherValid checks if the given voucher is valid (is or could become spendable at some point).
// If the channel is not in the store, fetches the channel from state (and checks that
// the channel To address is owned by the wallet).
func (pm *Manager) CheckVoucherValid(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) error {
func (pm *Manager) CheckVoucherValid(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher) error {
// Get an accessor for the channel, creating it from state if necessary
ca, err := pm.inboundChannelAccessor(ctx, ch)
if err != nil {
@ -250,7 +245,7 @@ func (pm *Manager) CheckVoucherValid(ctx context.Context, ch address.Address, sv
}
// CheckVoucherSpendable checks if the given voucher is currently spendable
func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
ca, err := pm.accessorByAddress(ch)
if err != nil {
return false, err
@ -261,7 +256,7 @@ func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address
// AddVoucherOutbound adds a voucher for an outbound channel.
// Returns an error if the channel is not already in the store.
func (pm *Manager) AddVoucherOutbound(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
func (pm *Manager) AddVoucherOutbound(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
ca, err := pm.accessorByAddress(ch)
if err != nil {
return types.NewInt(0), err
@ -272,7 +267,7 @@ func (pm *Manager) AddVoucherOutbound(ctx context.Context, ch address.Address, s
// AddVoucherInbound adds a voucher for an inbound channel.
// If the channel is not in the store, fetches the channel from state (and checks that
// the channel To address is owned by the wallet).
func (pm *Manager) AddVoucherInbound(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
func (pm *Manager) AddVoucherInbound(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
// Get an accessor for the channel, creating it from state if necessary
ca, err := pm.inboundChannelAccessor(ctx, ch)
if err != nil {
@ -341,7 +336,7 @@ func (pm *Manager) trackInboundChannel(ctx context.Context, ch address.Address)
return pm.store.TrackChannel(stateCi)
}
func (pm *Manager) SubmitVoucher(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (cid.Cid, error) {
func (pm *Manager) SubmitVoucher(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, secret []byte, proof []byte) (cid.Cid, error) {
ca, err := pm.accessorByAddress(ch)
if err != nil {
return cid.Undef, err

View File

@ -2,23 +2,18 @@ package paychmgr
import (
"context"
"fmt"
"errors"
"sync"
"github.com/filecoin-project/lotus/lib/sigs"
"github.com/filecoin-project/go-state-types/crypto"
cbornode "github.com/ipfs/go-ipld-cbor"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/lib/sigs"
)
type mockManagerAPI struct {
@ -40,29 +35,23 @@ type mockPchState struct {
type mockStateManager struct {
lk sync.Mutex
accountState map[address.Address]account.State
accountState map[address.Address]address.Address
paychState map[address.Address]mockPchState
store adt.Store
response *api.InvocResult
lastCall *types.Message
}
func newMockStateManager() *mockStateManager {
return &mockStateManager{
accountState: make(map[address.Address]account.State),
accountState: make(map[address.Address]address.Address),
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) setAccountAddress(a address.Address, lookup address.Address) {
sm.lk.Lock()
defer sm.lk.Unlock()
sm.accountState[a] = state
sm.accountState[a] = lookup
}
func (sm *mockStateManager) setPaychState(a address.Address, actor *types.Actor, state paych.State) {
@ -71,31 +60,24 @@ func (sm *mockStateManager) setPaychState(a address.Address, actor *types.Actor,
sm.paychState[a] = mockPchState{actor, state}
}
func (sm *mockStateManager) storeLaneStates(laneStates map[uint64]paych.LaneState) (cid.Cid, error) {
arr := adt.MakeEmptyArray(sm.store)
for i, ls := range laneStates {
ls := ls
if err := arr.Set(i, &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) ResolveToKeyAddress(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
sm.lk.Lock()
defer sm.lk.Unlock()
keyAddr, ok := sm.accountState[addr]
if !ok {
return address.Undef, errors.New("not found")
}
return keyAddr, nil
}
if outState, ok := out.(*account.State); ok {
*outState = sm.accountState[a]
return nil, nil
func (sm *mockStateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) {
sm.lk.Lock()
defer sm.lk.Unlock()
info, ok := sm.paychState[addr]
if !ok {
return nil, nil, errors.New("not found")
}
if outState, ok := out.(*paych.State); ok {
info := sm.paychState[a]
*outState = info.state
return info.actor, nil
}
panic(fmt.Sprintf("unexpected state type %v", out))
return info.actor, info.state, nil
}
func (sm *mockStateManager) setCallResponse(response *api.InvocResult) {

View File

@ -4,9 +4,7 @@ import (
"testing"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
)

View File

@ -5,22 +5,20 @@ import (
"context"
"fmt"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
v0paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"golang.org/x/xerrors"
)
// insufficientFundsErr indicates that there are not enough funds in the
@ -45,6 +43,19 @@ func (e *ErrInsufficientFunds) Shortfall() types.BigInt {
return e.shortfall
}
type laneState struct {
redeemed big.Int
nonce uint64
}
func (ls laneState) Redeemed() big.Int {
return ls.redeemed
}
func (ls laneState) Nonce() uint64 {
return ls.nonce
}
// channelAccessor is used to simplify locking when accessing a channel
type channelAccessor struct {
from address.Address
@ -92,7 +103,7 @@ func (ca *channelAccessor) outboundActiveByFromTo(from, to address.Address) (*Ch
// nonce, signing the voucher and storing it in the local datastore.
// If there are not enough funds in the channel to create the voucher, returns
// the shortfall in funds.
func (ca *channelAccessor) createVoucher(ctx context.Context, ch address.Address, voucher paych.SignedVoucher) (*api.VoucherCreateResult, error) {
func (ca *channelAccessor) createVoucher(ctx context.Context, ch address.Address, voucher v0paych.SignedVoucher) (*api.VoucherCreateResult, error) {
ca.lk.Lock()
defer ca.lk.Unlock()
@ -151,14 +162,14 @@ func (ca *channelAccessor) nextNonceForLane(ci *ChannelInfo, lane uint64) uint64
return maxnonce + 1
}
func (ca *channelAccessor) checkVoucherValid(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (map[uint64]*paych.LaneState, error) {
func (ca *channelAccessor) checkVoucherValid(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher) (map[uint64]paych.LaneState, error) {
ca.lk.Lock()
defer ca.lk.Unlock()
return ca.checkVoucherValidUnlocked(ctx, ch, sv)
}
func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (map[uint64]*paych.LaneState, error) {
func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher) (map[uint64]paych.LaneState, error) {
if sv.ChannelAddr != ch {
return nil, xerrors.Errorf("voucher ChannelAddr doesn't match channel address, got %s, expected %s", sv.ChannelAddr, ch)
}
@ -170,12 +181,10 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
}
// Load channel "From" account actor state
var actState account.State
_, err = ca.api.LoadActorState(ctx, pchState.From, &actState, nil)
from, err := ca.api.ResolveToKeyAddress(ctx, pchState.From(), nil)
if err != nil {
return nil, err
}
from := actState.Address
// verify voucher signature
vb, err := sv.SigningBytes()
@ -199,12 +208,12 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
// If the new voucher nonce value is less than the highest known
// nonce for the lane
ls, lsExists := laneStates[sv.Lane]
if lsExists && sv.Nonce <= ls.Nonce {
if lsExists && sv.Nonce <= ls.Nonce() {
return nil, fmt.Errorf("nonce too low")
}
// If the voucher amount is less than the highest known voucher amount
if lsExists && sv.Amount.LessThanEqual(ls.Redeemed) {
if lsExists && sv.Amount.LessThanEqual(ls.Redeemed()) {
return nil, fmt.Errorf("voucher amount is lower than amount for voucher with lower nonce")
}
@ -230,7 +239,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
// Total required balance = total redeemed + toSend
// Must not exceed actor balance
newTotal := types.BigAdd(totalRedeemed, pchState.ToSend)
newTotal := types.BigAdd(totalRedeemed, pchState.ToSend())
if act.Balance.LessThan(newTotal) {
return nil, newErrInsufficientFunds(types.BigSub(newTotal, act.Balance))
}
@ -242,7 +251,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
return laneStates, nil
}
func (ca *channelAccessor) checkVoucherSpendable(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
func (ca *channelAccessor) checkVoucherSpendable(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
ca.lk.Lock()
defer ca.lk.Unlock()
@ -281,7 +290,7 @@ func (ca *channelAccessor) checkVoucherSpendable(ctx context.Context, ch address
}
}
enc, err := actors.SerializeParams(&paych.UpdateChannelStateParams{
enc, err := actors.SerializeParams(&v0paych.UpdateChannelStateParams{
Sv: *sv,
Secret: secret,
Proof: proof,
@ -308,22 +317,22 @@ func (ca *channelAccessor) checkVoucherSpendable(ctx context.Context, ch address
}
func (ca *channelAccessor) getPaychRecipient(ctx context.Context, ch address.Address) (address.Address, error) {
var state paych.State
if _, err := ca.api.LoadActorState(ctx, ch, &state, nil); err != nil {
_, state, err := ca.api.GetPaychState(ctx, ch, nil)
if err != nil {
return address.Address{}, err
}
return state.To, nil
return state.To(), nil
}
func (ca *channelAccessor) addVoucher(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
func (ca *channelAccessor) addVoucher(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
ca.lk.Lock()
defer ca.lk.Unlock()
return ca.addVoucherUnlocked(ctx, ch, sv, proof, minDelta)
}
func (ca *channelAccessor) addVoucherUnlocked(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
func (ca *channelAccessor) addVoucherUnlocked(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
ci, err := ca.store.ByAddress(ch)
if err != nil {
return types.BigInt{}, err
@ -367,7 +376,7 @@ func (ca *channelAccessor) addVoucherUnlocked(ctx context.Context, ch address.Ad
laneState, exists := laneStates[sv.Lane]
redeemed := big.NewInt(0)
if exists {
redeemed = laneState.Redeemed
redeemed = laneState.Redeemed()
}
delta := types.BigSub(sv.Amount, redeemed)
@ -387,7 +396,7 @@ func (ca *channelAccessor) addVoucherUnlocked(ctx context.Context, ch address.Ad
return delta, ca.store.putChannelInfo(ci)
}
func (ca *channelAccessor) submitVoucher(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (cid.Cid, error) {
func (ca *channelAccessor) submitVoucher(ctx context.Context, ch address.Address, sv *v0paych.SignedVoucher, secret []byte, proof []byte) (cid.Cid, error) {
ca.lk.Lock()
defer ca.lk.Unlock()
@ -428,7 +437,7 @@ func (ca *channelAccessor) submitVoucher(ctx context.Context, ch address.Address
}
}
enc, err := actors.SerializeParams(&paych.UpdateChannelStateParams{
enc, err := actors.SerializeParams(&v0paych.UpdateChannelStateParams{
Sv: *sv,
Secret: secret,
Proof: proof,
@ -487,13 +496,11 @@ func (ca *channelAccessor) listVouchers(ctx context.Context, ch address.Address)
// laneState gets the LaneStates from chain, then applies all vouchers in
// the data store over the chain state
func (ca *channelAccessor) laneState(ctx context.Context, 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)
// Get the lane state from the chain
store := ca.api.AdtStore(ctx)
lsamt, err := adt.AsArray(store, state.LaneStates)
laneCount, err := state.LaneCount()
if err != nil {
return nil, err
}
@ -501,11 +508,9 @@ func (ca *channelAccessor) laneState(ctx context.Context, state *paych.State, ch
// 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())
err = lsamt.ForEach(&ls, func(i int64) error {
current := ls
laneStates[uint64(i)] = &current
laneStates := make(map[uint64]paych.LaneState, laneCount)
err = state.ForEachLaneState(func(idx uint64, ls paych.LaneState) error {
laneStates[idx] = ls
return nil
})
if err != nil {
@ -526,27 +531,19 @@ func (ca *channelAccessor) laneState(ctx context.Context, state *paych.State, ch
// If there's a voucher for a lane that isn't in chain state just
// create it
ls, ok := laneStates[v.Voucher.Lane]
if !ok {
ls = &paych.LaneState{
Redeemed: types.NewInt(0),
Nonce: 0,
}
laneStates[v.Voucher.Lane] = ls
}
if v.Voucher.Nonce < ls.Nonce {
if ok && v.Voucher.Nonce < ls.Nonce() {
continue
}
ls.Nonce = v.Voucher.Nonce
ls.Redeemed = v.Voucher.Amount
laneStates[v.Voucher.Lane] = laneState{v.Voucher.Amount, v.Voucher.Nonce}
}
return laneStates, nil
}
// Get the total redeemed amount across all lanes, after applying the voucher
func (ca *channelAccessor) totalRedeemedWithVoucher(laneStates map[uint64]*paych.LaneState, sv *paych.SignedVoucher) (big.Int, error) {
func (ca *channelAccessor) totalRedeemedWithVoucher(laneStates map[uint64]paych.LaneState, sv *v0paych.SignedVoucher) (big.Int, error) {
// TODO: merges
if len(sv.Merges) != 0 {
return big.Int{}, xerrors.Errorf("dont currently support paych lane merges")
@ -554,17 +551,17 @@ func (ca *channelAccessor) totalRedeemedWithVoucher(laneStates map[uint64]*paych
total := big.NewInt(0)
for _, ls := range laneStates {
total = big.Add(total, ls.Redeemed)
total = big.Add(total, ls.Redeemed())
}
lane, ok := laneStates[sv.Lane]
if ok {
// If the voucher is for an existing lane, and the voucher nonce
// is higher than the lane nonce
if sv.Nonce > lane.Nonce {
if sv.Nonce > lane.Nonce() {
// Add the delta between the redeemed amount and the voucher
// amount to the total
delta := big.Sub(sv.Amount, lane.Redeemed)
delta := big.Sub(sv.Amount, lane.Redeemed())
total = big.Add(total, delta)
}
} else {

View File

@ -5,31 +5,24 @@ import (
"context"
"testing"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/lib/sigs"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/abi"
tutils "github.com/filecoin-project/specs-actors/support/testing"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
ds "github.com/ipfs/go-datastore"
ds_sync "github.com/ipfs/go-datastore/sync"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/specs-actors/actors/builtin"
v0paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
tutils "github.com/filecoin-project/specs-actors/support/testing"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
paychmock "github.com/filecoin-project/lotus/chain/actors/builtin/paych/mock"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs"
)
func TestCheckVoucherValid(t *testing.T) {
@ -46,8 +39,8 @@ func TestCheckVoucherValid(t *testing.T) {
toAcct := tutils.NewActorAddr(t, "toAct")
mock := newMockManagerAPI()
mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to})
mock.setAccountAddress(fromAcct, from)
mock.setAccountAddress(toAcct, to)
tcases := []struct {
name string
@ -96,10 +89,7 @@ func TestCheckVoucherValid(t *testing.T) {
voucherLane: 1,
voucherNonce: 2,
laneStates: map[uint64]paych.LaneState{
1: {
Redeemed: big.NewInt(2),
Nonce: 3,
},
1: paychmock.NewMockLaneState(big.NewInt(2), 3),
},
}, {
name: "passes when nonce higher",
@ -110,10 +100,7 @@ func TestCheckVoucherValid(t *testing.T) {
voucherLane: 1,
voucherNonce: 3,
laneStates: map[uint64]paych.LaneState{
1: {
Redeemed: big.NewInt(2),
Nonce: 2,
},
1: paychmock.NewMockLaneState(big.NewInt(2), 2),
},
}, {
name: "passes when nonce for different lane",
@ -124,10 +111,7 @@ func TestCheckVoucherValid(t *testing.T) {
voucherLane: 2,
voucherNonce: 2,
laneStates: map[uint64]paych.LaneState{
1: {
Redeemed: big.NewInt(2),
Nonce: 3,
},
1: paychmock.NewMockLaneState(big.NewInt(2), 3),
},
}, {
name: "fails when voucher has higher nonce but lower value than lane state",
@ -139,10 +123,7 @@ func TestCheckVoucherValid(t *testing.T) {
voucherLane: 1,
voucherNonce: 3,
laneStates: map[uint64]paych.LaneState{
1: {
Redeemed: big.NewInt(6),
Nonce: 2,
},
1: paychmock.NewMockLaneState(big.NewInt(6), 2),
},
}, {
name: "fails when voucher + ToSend > balance",
@ -168,10 +149,7 @@ func TestCheckVoucherValid(t *testing.T) {
voucherNonce: 2,
laneStates: map[uint64]paych.LaneState{
// Lane 1 (same as voucher lane 1)
1: {
Redeemed: big.NewInt(4),
Nonce: 1,
},
1: paychmock.NewMockLaneState(big.NewInt(4), 1),
},
}, {
// required balance = toSend + total redeemed
@ -188,10 +166,7 @@ func TestCheckVoucherValid(t *testing.T) {
voucherNonce: 1,
laneStates: map[uint64]paych.LaneState{
// Lane 2 (different from voucher lane 1)
2: {
Redeemed: big.NewInt(4),
Nonce: 1,
},
2: paychmock.NewMockLaneState(big.NewInt(4), 1),
},
}}
@ -208,18 +183,8 @@ func TestCheckVoucherValid(t *testing.T) {
Balance: tcase.actorBalance,
}
// Set the state of the channel's lanes
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: laneStates,
})
mock.setPaychState(ch, act, paychmock.NewMockPayChState(
fromAcct, toAcct, abi.ChainEpoch(0), tcase.toSend, tcase.laneStates))
// Create a manager
mgr, err := newManager(store, mock)
@ -255,22 +220,16 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
minDelta := big.NewInt(0)
mock := newMockManagerAPI()
mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to})
mock.setAccountAddress(fromAcct, from)
mock.setAccountAddress(toAcct, to)
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
actorBalance := big.NewInt(10)
toSend := big.NewInt(1)
laneStates := map[uint64]paych.LaneState{
1: {
Nonce: 1,
Redeemed: big.NewInt(3),
},
2: {
Nonce: 1,
Redeemed: big.NewInt(4),
},
1: paychmock.NewMockLaneState(big.NewInt(3), 1),
2: paychmock.NewMockLaneState(big.NewInt(4), 1),
}
act := &types.Actor{
@ -280,16 +239,7 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
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: lsCid,
})
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), toSend, laneStates))
mgr, err := newManager(store, mock)
require.NoError(t, err)
@ -389,7 +339,7 @@ func TestCreateVoucher(t *testing.T) {
// Create a voucher in lane 1
voucherLane1Amt := big.NewInt(5)
voucher := paych.SignedVoucher{
voucher := v0paych.SignedVoucher{
Lane: 1,
Amount: voucherLane1Amt,
}
@ -404,7 +354,7 @@ func TestCreateVoucher(t *testing.T) {
// Create a voucher in lane 1 again, with a higher amount
voucherLane1Amt = big.NewInt(8)
voucher = paych.SignedVoucher{
voucher = v0paych.SignedVoucher{
Lane: 1,
Amount: voucherLane1Amt,
}
@ -419,7 +369,7 @@ func TestCreateVoucher(t *testing.T) {
// Create a voucher in lane 2 that covers all the remaining funds
// in the channel
voucherLane2Amt := big.Sub(s.amt, voucherLane1Amt)
voucher = paych.SignedVoucher{
voucher = v0paych.SignedVoucher{
Lane: 2,
Amount: voucherLane2Amt,
}
@ -433,7 +383,7 @@ func TestCreateVoucher(t *testing.T) {
// Create a voucher in lane 2 that exceeds the remaining funds in the
// channel
voucherLane2Amt = big.Add(voucherLane2Amt, big.NewInt(1))
voucher = paych.SignedVoucher{
voucher = v0paych.SignedVoucher{
Lane: 2,
Amount: voucherLane2Amt,
}
@ -567,8 +517,8 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
toAcct := tutils.NewActorAddr(t, "toAct")
mock := newMockManagerAPI()
mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to})
mock.setAccountAddress(fromAcct, from)
mock.setAccountAddress(toAcct, to)
mock.addWalletAddress(to)
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
@ -584,16 +534,7 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
Balance: actorBalance,
}
arr, err := adt.MakeEmptyArray(mock.store).Root()
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: arr,
})
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), toSend, make(map[uint64]paych.LaneState)))
mgr, err := newManager(store, mock)
require.NoError(t, err)
@ -681,19 +622,10 @@ func TestAddVoucherInboundWalletKey(t *testing.T) {
}
mock := newMockManagerAPI()
arr, err := adt.MakeEmptyArray(mock.store).Root()
require.NoError(t, err)
mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to})
mock.setAccountAddress(fromAcct, from)
mock.setAccountAddress(toAcct, to)
mock.setPaychState(ch, act, paych.State{
From: fromAcct,
To: toAcct,
ToSend: types.NewInt(0),
SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0),
LaneStates: arr,
})
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), types.NewInt(0), make(map[uint64]paych.LaneState)))
// Create a manager
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
@ -840,7 +772,7 @@ func TestCheckSpendable(t *testing.T) {
// Check that the secret and proof were passed through correctly
lastCall := s.mock.getLastCall()
var p paych.UpdateChannelStateParams
var p v0paych.UpdateChannelStateParams
err = p.UnmarshalCBOR(bytes.NewReader(lastCall.Params))
require.NoError(t, err)
require.Equal(t, otherProof, p.Proof)
@ -854,7 +786,7 @@ func TestCheckSpendable(t *testing.T) {
require.True(t, spendable)
lastCall = s.mock.getLastCall()
var p2 paych.UpdateChannelStateParams
var p2 v0paych.UpdateChannelStateParams
err = p2.UnmarshalCBOR(bytes.NewReader(lastCall.Params))
require.NoError(t, err)
require.Equal(t, proof, p2.Proof)
@ -911,7 +843,7 @@ func TestSubmitVoucher(t *testing.T) {
// Check that the secret and proof were passed through correctly
msg := s.mock.pushedMessages(submitCid)
var p paych.UpdateChannelStateParams
var p v0paych.UpdateChannelStateParams
err = p.UnmarshalCBOR(bytes.NewReader(msg.Message.Params))
require.NoError(t, err)
require.Equal(t, submitProof, p.Proof)
@ -931,7 +863,7 @@ func TestSubmitVoucher(t *testing.T) {
require.NoError(t, err)
msg = s.mock.pushedMessages(submitCid)
var p2 paych.UpdateChannelStateParams
var p2 v0paych.UpdateChannelStateParams
err = p2.UnmarshalCBOR(bytes.NewReader(msg.Message.Params))
require.NoError(t, err)
require.Equal(t, addVoucherProof2, p2.Proof)
@ -947,7 +879,7 @@ func TestSubmitVoucher(t *testing.T) {
require.NoError(t, err)
msg = s.mock.pushedMessages(submitCid)
var p3 paych.UpdateChannelStateParams
var p3 v0paych.UpdateChannelStateParams
err = p3.UnmarshalCBOR(bytes.NewReader(msg.Message.Params))
require.NoError(t, err)
require.Equal(t, proof3, p3.Proof)
@ -986,10 +918,8 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) *testScaffold {
toAcct := tutils.NewActorAddr(t, "toAct")
mock := newMockManagerAPI()
arr, err := adt.MakeEmptyArray(mock.store).Root()
require.NoError(t, err)
mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to})
mock.setAccountAddress(fromAcct, from)
mock.setAccountAddress(toAcct, to)
// Create channel in state
balance := big.NewInt(20)
@ -999,14 +929,7 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) *testScaffold {
Nonce: 0,
Balance: balance,
}
mock.setPaychState(ch, act, paych.State{
From: fromAcct,
To: toAcct,
ToSend: big.NewInt(0),
SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0),
LaneStates: arr,
})
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), big.NewInt(0), make(map[uint64]paych.LaneState)))
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
mgr, err := newManager(store, mock)
@ -1043,8 +966,8 @@ func testGenerateKeyPair(t *testing.T) ([]byte, []byte) {
return priv, pub
}
func createTestVoucher(t *testing.T, ch address.Address, voucherLane uint64, nonce uint64, voucherAmount big.Int, key []byte) *paych.SignedVoucher {
sv := &paych.SignedVoucher{
func createTestVoucher(t *testing.T, ch address.Address, voucherLane uint64, nonce uint64, voucherAmount big.Int, key []byte) *v0paych.SignedVoucher {
sv := &v0paych.SignedVoucher{
ChannelAddr: ch,
Lane: voucherLane,
Nonce: nonce,
@ -1059,13 +982,13 @@ func createTestVoucher(t *testing.T, ch address.Address, voucherLane uint64, non
return sv
}
func createTestVoucherWithExtra(t *testing.T, ch address.Address, voucherLane uint64, nonce uint64, voucherAmount big.Int, key []byte) *paych.SignedVoucher {
sv := &paych.SignedVoucher{
func createTestVoucherWithExtra(t *testing.T, ch address.Address, voucherLane uint64, nonce uint64, voucherAmount big.Int, key []byte) *v0paych.SignedVoucher {
sv := &v0paych.SignedVoucher{
ChannelAddr: ch,
Lane: voucherLane,
Nonce: nonce,
Amount: voucherAmount,
Extra: &paych.ModVerifyParams{
Extra: &v0paych.ModVerifyParams{
Actor: tutils.NewActorAddr(t, "act"),
},
}
@ -1083,13 +1006,13 @@ type mockBestSpendableAPI struct {
mgr *Manager
}
func (m *mockBestSpendableAPI) PaychVoucherList(ctx context.Context, ch address.Address) ([]*paych.SignedVoucher, error) {
func (m *mockBestSpendableAPI) PaychVoucherList(ctx context.Context, ch address.Address) ([]*v0paych.SignedVoucher, error) {
vi, err := m.mgr.ListVouchers(ctx, ch)
if err != nil {
return nil, err
}
out := make([]*paych.SignedVoucher, len(vi))
out := make([]*v0paych.SignedVoucher, len(vi))
for k, v := range vi {
out[k] = v.Voucher
}
@ -1097,7 +1020,7 @@ func (m *mockBestSpendableAPI) PaychVoucherList(ctx context.Context, ch address.
return out, nil
}
func (m *mockBestSpendableAPI) PaychVoucherCheckSpendable(ctx context.Context, ch address.Address, voucher *paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
func (m *mockBestSpendableAPI) PaychVoucherCheckSpendable(ctx context.Context, ch address.Address, voucher *v0paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
return m.mgr.CheckVoucherSpendable(ctx, ch, voucher, secret, proof)
}

View File

@ -6,29 +6,22 @@ import (
"testing"
"time"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
cborrpc "github.com/filecoin-project/go-cbor-util"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
tutils "github.com/filecoin-project/specs-actors/support/testing"
"github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
ds_sync "github.com/ipfs/go-datastore/sync"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
tutils "github.com/filecoin-project/specs-actors/support/testing"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
paychmock "github.com/filecoin-project/lotus/chain/actors/builtin/paych/mock"
"github.com/filecoin-project/lotus/chain/types"
)
func testChannelResponse(t *testing.T, ch address.Address) types.MessageReceipt {
@ -976,25 +969,15 @@ func TestPaychAvailableFunds(t *testing.T) {
require.EqualValues(t, 0, av.VoucherReedeemedAmt.Int64())
// Create channel in state
arr, err := adt.MakeEmptyArray(mock.store).Root()
require.NoError(t, err)
mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to})
mock.setAccountAddress(fromAcct, from)
mock.setAccountAddress(toAcct, to)
act := &types.Actor{
Code: builtin.AccountActorCodeID,
Head: cid.Cid{},
Nonce: 0,
Balance: createAmt,
}
mock.setPaychState(ch, act, paych.State{
From: fromAcct,
To: toAcct,
ToSend: big.NewInt(0),
SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0),
LaneStates: arr,
})
mock.setPaychState(ch, act, paychmock.NewMockPayChState(fromAcct, toAcct, abi.ChainEpoch(0), big.NewInt(0), make(map[uint64]paych.LaneState)))
// Send create channel response
response := testChannelResponse(t, ch)
mock.receiveMsgResponse(createMsgCid, response)

View File

@ -6,20 +6,17 @@ import (
"fmt"
"sync"
"github.com/filecoin-project/lotus/api"
"golang.org/x/sync/errgroup"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/ipfs/go-cid"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
v0paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
@ -320,7 +317,7 @@ func (ca *channelAccessor) currentAvailableFunds(channelID string, queuedAmt typ
}
for _, ls := range laneStates {
totalRedeemed = types.BigAdd(totalRedeemed, ls.Redeemed)
totalRedeemed = types.BigAdd(totalRedeemed, ls.Redeemed())
}
}
@ -385,7 +382,7 @@ func (ca *channelAccessor) processTask(ctx context.Context, amt types.BigInt) *p
// createPaych sends a message to create the channel and returns the message cid
func (ca *channelAccessor) createPaych(ctx context.Context, amt types.BigInt) (cid.Cid, error) {
params, aerr := actors.SerializeParams(&paych.ConstructorParams{From: ca.from, To: ca.to})
params, aerr := actors.SerializeParams(&v0paych.ConstructorParams{From: ca.from, To: ca.to})
if aerr != nil {
return cid.Undef, aerr
}

View File

@ -3,13 +3,9 @@ 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"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/types"
)
@ -17,14 +13,8 @@ type stateAccessor struct {
sm stateManagerAPI
}
func (ca *stateAccessor) loadPaychActorState(ctx context.Context, ch address.Address) (*types.Actor, *paych.State, error) {
var pcast paych.State
act, err := ca.sm.LoadActorState(ctx, ch, &pcast, nil)
if err != nil {
return nil, nil, err
}
return act, &pcast, nil
func (ca *stateAccessor) loadPaychActorState(ctx context.Context, ch address.Address) (*types.Actor, paych.State, error) {
return ca.sm.GetPaychState(ctx, ch, nil)
}
func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Address, dir uint64) (*ChannelInfo, error) {
@ -33,17 +23,15 @@ func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Ad
return nil, err
}
var account account.State
_, err = ca.sm.LoadActorState(ctx, st.From, &account, nil)
// Load channel "From" account actor state
from, err := ca.sm.ResolveToKeyAddress(ctx, st.From(), nil)
if err != nil {
return nil, err
}
from := account.Address
_, err = ca.sm.LoadActorState(ctx, st.To, &account, nil)
to, err := ca.sm.ResolveToKeyAddress(ctx, st.To(), nil)
if err != nil {
return nil, err
}
to := account.Address
nextLane, err := ca.nextLaneFromState(ctx, st)
if err != nil {
@ -67,25 +55,24 @@ func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Ad
return ci, nil
}
func (ca *stateAccessor) nextLaneFromState(ctx context.Context, st *paych.State) (uint64, error) {
store := ca.sm.AdtStore(ctx)
laneStates, err := adt.AsArray(store, st.LaneStates)
func (ca *stateAccessor) nextLaneFromState(ctx context.Context, st paych.State) (uint64, error) {
laneCount, err := st.LaneCount()
if err != nil {
return 0, err
}
if laneStates.Length() == 0 {
if laneCount == 0 {
return 0, nil
}
maxID := int64(0)
if err := laneStates.ForEach(nil, func(i int64) error {
if i > maxID {
maxID = i
maxID := uint64(0)
if err := st.ForEachLaneState(func(idx uint64, _ paych.LaneState) error {
if idx > maxID {
maxID = idx
}
return nil
}); err != nil {
return 0, err
}
return uint64(maxID + 1), nil
return maxID + 1, nil
}