Merge pull request #3111 from filecoin-project/fix/paych-addvchr-chk

paych: check To address is owned by wallet for inbound channels
This commit is contained in:
Łukasz Magiera 2020-08-24 23:02:10 +02:00 committed by GitHub
commit 2f6f978cd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 257 additions and 160 deletions

View File

@ -124,9 +124,7 @@ func (a *PaychAPI) PaychVoucherCheckSpendable(ctx context.Context, ch address.Ad
} }
func (a *PaychAPI) PaychVoucherAdd(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) { func (a *PaychAPI) PaychVoucherAdd(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
_ = a.PaychMgr.TrackInboundChannel(ctx, ch) // TODO: expose those calls return a.PaychMgr.AddVoucherInbound(ctx, ch, sv, proof, minDelta)
return a.PaychMgr.AddVoucher(ctx, ch, sv, proof, minDelta)
} }
// PaychVoucherCreate creates a new signed voucher on the given payment channel // PaychVoucherCreate creates a new signed voucher on the given payment channel
@ -164,7 +162,7 @@ func (a *PaychAPI) paychVoucherCreate(ctx context.Context, pch address.Address,
sv.Signature = sig sv.Signature = sig
if _, err := a.PaychMgr.AddVoucher(ctx, pch, sv, nil, types.NewInt(0)); err != nil { if _, err := a.PaychMgr.AddVoucherOutbound(ctx, pch, sv, nil, types.NewInt(0)); err != nil {
return nil, xerrors.Errorf("failed to persist voucher: %w", err) return nil, xerrors.Errorf("failed to persist voucher: %w", err)
} }

View File

@ -45,8 +45,10 @@ type stateManagerAPI interface {
// paychAPI defines the API methods needed by the payment channel manager // paychAPI defines the API methods needed by the payment channel manager
type paychAPI interface { type paychAPI interface {
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error)
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
MpoolPushMessage(ctx context.Context, msg *types.Message, maxFee *api.MessageSendSpec) (*types.SignedMessage, error) MpoolPushMessage(ctx context.Context, msg *types.Message, maxFee *api.MessageSendSpec) (*types.SignedMessage, error)
WalletHas(ctx context.Context, addr address.Address) (bool, error)
} }
// managerAPI defines all methods needed by the manager // managerAPI defines all methods needed by the manager
@ -127,26 +129,6 @@ func (pm *Manager) Stop() error {
return nil return nil
} }
func (pm *Manager) TrackOutboundChannel(ctx context.Context, ch address.Address) error {
return pm.trackChannel(ctx, ch, DirOutbound)
}
func (pm *Manager) TrackInboundChannel(ctx context.Context, ch address.Address) error {
return pm.trackChannel(ctx, ch, DirInbound)
}
func (pm *Manager) trackChannel(ctx context.Context, ch address.Address, dir uint64) error {
pm.lk.Lock()
defer pm.lk.Unlock()
ci, err := pm.sa.loadStateChannelInfo(ctx, ch, dir)
if err != nil {
return err
}
return pm.store.TrackChannel(ci)
}
func (pm *Manager) GetPaych(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, cid.Cid, error) { func (pm *Manager) GetPaych(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, cid.Cid, error) {
chanAccessor, err := pm.accessorByFromTo(from, to) chanAccessor, err := pm.accessorByFromTo(from, to)
if err != nil { if err != nil {
@ -218,7 +200,9 @@ func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address
return ca.checkVoucherSpendable(ctx, ch, sv, secret, proof) return ca.checkVoucherSpendable(ctx, ch, sv, secret, proof)
} }
func (pm *Manager) AddVoucher(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) { // 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) {
ca, err := pm.accessorByAddress(ch) ca, err := pm.accessorByAddress(ch)
if err != nil { if err != nil {
return types.NewInt(0), err return types.NewInt(0), err
@ -226,6 +210,70 @@ func (pm *Manager) AddVoucher(ctx context.Context, ch address.Address, sv *paych
return ca.addVoucher(ctx, ch, sv, proof, minDelta) return ca.addVoucher(ctx, ch, sv, proof, minDelta)
} }
// 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) {
// Make sure channel is in store, or can be fetched from state, and that
// the channel To address is owned by the wallet
ci, err := pm.trackInboundChannel(ctx, ch)
if err != nil {
return types.BigInt{}, err
}
// This is an inbound channel, so To is the Control address (this node)
from := ci.Target
to := ci.Control
ca, err := pm.accessorByFromTo(from, to)
if err != nil {
return types.BigInt{}, err
}
return ca.addVoucher(ctx, ch, sv, proof, minDelta)
}
func (pm *Manager) trackInboundChannel(ctx context.Context, ch address.Address) (*ChannelInfo, error) {
// Need to take an exclusive lock here so that channel operations can't run
// in parallel (see channelLock)
pm.lk.Lock()
defer pm.lk.Unlock()
// Check if channel is in store
ci, err := pm.store.ByAddress(ch)
if err == nil {
// Channel is in store, so it's already being tracked
return ci, nil
}
// If there's an error (besides channel not in store) return err
if err != ErrChannelNotTracked {
return nil, err
}
// Channel is not in store, so get channel from state
stateCi, err := pm.sa.loadStateChannelInfo(ctx, ch, DirInbound)
if err != nil {
return nil, err
}
// Check that channel To address is in wallet
to := stateCi.Control // Inbound channel so To addr is Control (this node)
toKey, err := pm.pchapi.StateAccountKey(ctx, to, types.EmptyTSK)
if err != nil {
return nil, err
}
has, err := pm.pchapi.WalletHas(ctx, toKey)
if err != nil {
return nil, err
}
if !has {
msg := "cannot add voucher for channel %s: wallet does not have key for address %s"
return nil, xerrors.Errorf(msg, ch, to)
}
// Save channel to store
return pm.store.TrackChannel(stateCi)
}
func (pm *Manager) AllocateLane(ch address.Address) (uint64, error) { func (pm *Manager) AllocateLane(ch address.Address) (uint64, error) {
ca, err := pm.accessorByAddress(ch) ca, err := pm.accessorByAddress(ch)
if err != nil { if err != nil {

View File

@ -111,6 +111,7 @@ type mockPaychAPI struct {
messages map[cid.Cid]*types.SignedMessage messages map[cid.Cid]*types.SignedMessage
waitingCalls map[cid.Cid]*waitingCall waitingCalls map[cid.Cid]*waitingCall
waitingResponses map[cid.Cid]*waitingResponse waitingResponses map[cid.Cid]*waitingResponse
wallet map[address.Address]struct{}
} }
func newMockPaychAPI() *mockPaychAPI { func newMockPaychAPI() *mockPaychAPI {
@ -118,6 +119,7 @@ func newMockPaychAPI() *mockPaychAPI {
messages: make(map[cid.Cid]*types.SignedMessage), messages: make(map[cid.Cid]*types.SignedMessage),
waitingCalls: make(map[cid.Cid]*waitingCall), waitingCalls: make(map[cid.Cid]*waitingCall),
waitingResponses: make(map[cid.Cid]*waitingResponse), waitingResponses: make(map[cid.Cid]*waitingResponse),
wallet: make(map[address.Address]struct{}),
} }
} }
@ -199,3 +201,22 @@ func (pchapi *mockPaychAPI) pushedMessageCount() int {
return len(pchapi.messages) return len(pchapi.messages)
} }
func (pchapi *mockPaychAPI) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
return addr, nil
}
func (pchapi *mockPaychAPI) WalletHas(ctx context.Context, addr address.Address) (bool, error) {
pchapi.lk.Lock()
defer pchapi.lk.Unlock()
_, ok := pchapi.wallet[addr]
return ok, nil
}
func (pchapi *mockPaychAPI) addWalletAddress(addr address.Address) {
pchapi.lk.Lock()
defer pchapi.lk.Unlock()
pchapi.wallet[addr] = struct{}{}
}

View File

@ -64,11 +64,13 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
return nil, xerrors.Errorf("voucher ChannelAddr doesn't match channel address, got %s, expected %s", sv.ChannelAddr, ch) return nil, xerrors.Errorf("voucher ChannelAddr doesn't match channel address, got %s, expected %s", sv.ChannelAddr, ch)
} }
act, pchState, err := ca.sa.loadPaychState(ctx, ch) // Load payment channel actor state
act, pchState, err := ca.sa.loadPaychActorState(ctx, ch)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Load channel "From" account actor state
var actState account.State var actState account.State
_, err = ca.api.LoadActorState(ctx, pchState.From, &actState, nil) _, err = ca.api.LoadActorState(ctx, pchState.From, &actState, nil)
if err != nil { if err != nil {
@ -76,7 +78,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
} }
from := actState.Address from := actState.Address
// verify signature // verify voucher signature
vb, err := sv.SigningBytes() vb, err := sv.SigningBytes()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -29,95 +29,16 @@ import (
ds_sync "github.com/ipfs/go-datastore/sync" ds_sync "github.com/ipfs/go-datastore/sync"
) )
func TestPaychOutbound(t *testing.T) {
ctx := context.Background()
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
ch := tutils.NewIDAddr(t, 100)
from := tutils.NewIDAddr(t, 101)
to := tutils.NewIDAddr(t, 102)
fromAcct := tutils.NewIDAddr(t, 201)
toAcct := tutils.NewIDAddr(t, 202)
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.setPaychState(ch, nil, paych.State{
From: fromAcct,
To: toAcct,
ToSend: big.NewInt(0),
SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0),
LaneStates: arr,
})
mgr, err := newManager(store, mock)
require.NoError(t, err)
err = mgr.TrackOutboundChannel(ctx, ch)
require.NoError(t, err)
ci, err := mgr.GetChannelInfo(ch)
require.NoError(t, err)
require.Equal(t, *ci.Channel, ch)
require.Equal(t, ci.Control, from)
require.Equal(t, ci.Target, to)
require.EqualValues(t, ci.Direction, DirOutbound)
require.EqualValues(t, ci.NextLane, 0)
require.Len(t, ci.Vouchers, 0)
}
func TestPaychInbound(t *testing.T) {
ctx := context.Background()
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
ch := tutils.NewIDAddr(t, 100)
from := tutils.NewIDAddr(t, 101)
to := tutils.NewIDAddr(t, 102)
fromAcct := tutils.NewIDAddr(t, 201)
toAcct := tutils.NewIDAddr(t, 202)
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.setPaychState(ch, nil, paych.State{
From: fromAcct,
To: toAcct,
ToSend: big.NewInt(0),
SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0),
LaneStates: arr,
})
mgr, err := newManager(store, mock)
require.NoError(t, err)
err = mgr.TrackInboundChannel(ctx, ch)
require.NoError(t, err)
ci, err := mgr.GetChannelInfo(ch)
require.NoError(t, err)
require.Equal(t, *ci.Channel, ch)
require.Equal(t, ci.Control, to)
require.Equal(t, ci.Target, from)
require.EqualValues(t, ci.Direction, DirInbound)
require.EqualValues(t, ci.NextLane, 0)
require.Len(t, ci.Vouchers, 0)
}
func TestCheckVoucherValid(t *testing.T) { func TestCheckVoucherValid(t *testing.T) {
ctx := context.Background() ctx := context.Background()
fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t) fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
toKeyPrivate, toKeyPublic := testGenerateKeyPair(t)
randKeyPrivate, _ := testGenerateKeyPair(t) randKeyPrivate, _ := testGenerateKeyPair(t)
ch := tutils.NewIDAddr(t, 100) ch := tutils.NewIDAddr(t, 100)
from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic)) from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic))
to := tutils.NewSECP256K1Addr(t, "secpTo") to := tutils.NewSECP256K1Addr(t, string(toKeyPublic))
fromAcct := tutils.NewActorAddr(t, "fromAct") fromAcct := tutils.NewActorAddr(t, "fromAct")
toAcct := tutils.NewActorAddr(t, "toAct") toAcct := tutils.NewActorAddr(t, "toAct")
@ -155,6 +76,13 @@ func TestCheckVoucherValid(t *testing.T) {
actorBalance: big.NewInt(10), actorBalance: big.NewInt(10),
toSend: big.NewInt(0), toSend: big.NewInt(0),
voucherAmount: big.NewInt(5), voucherAmount: big.NewInt(5),
}, {
name: "fails when signed by channel To account (instead of From account)",
expectError: true,
key: toKeyPrivate,
actorBalance: big.NewInt(10),
toSend: big.NewInt(0),
voucherAmount: big.NewInt(5),
}, { }, {
name: "fails when nonce too low", name: "fails when nonce too low",
expectError: true, expectError: true,
@ -269,6 +197,7 @@ func TestCheckVoucherValid(t *testing.T) {
t.Run(tcase.name, func(t *testing.T) { t.Run(tcase.name, func(t *testing.T) {
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore())) store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
// Create an actor for the channel with the test case balance
act := &types.Actor{ act := &types.Actor{
Code: builtin.AccountActorCodeID, Code: builtin.AccountActorCodeID,
Head: cid.Cid{}, Head: cid.Cid{},
@ -276,6 +205,7 @@ func TestCheckVoucherValid(t *testing.T) {
Balance: tcase.actorBalance, Balance: tcase.actorBalance,
} }
// Set the state of the channel's lanes
laneStates, err := mock.storeLaneStates(tcase.laneStates) laneStates, err := mock.storeLaneStates(tcase.laneStates)
require.NoError(t, err) require.NoError(t, err)
@ -288,14 +218,24 @@ func TestCheckVoucherValid(t *testing.T) {
LaneStates: laneStates, LaneStates: laneStates,
}) })
// Create a manager
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
require.NoError(t, err) require.NoError(t, err)
err = mgr.TrackInboundChannel(ctx, ch) // Create the channel in the manager's store
ci := &ChannelInfo{
Channel: &ch,
Control: toAcct,
Target: fromAcct,
Direction: DirInbound,
}
err = mgr.store.putChannelInfo(ci)
require.NoError(t, err) require.NoError(t, err)
sv := testCreateVoucher(t, ch, tcase.voucherLane, tcase.voucherNonce, tcase.voucherAmount, tcase.key) // Create a signed voucher
sv := createTestVoucher(t, ch, tcase.voucherLane, tcase.voucherNonce, tcase.voucherAmount, tcase.key)
// Check the voucher's validity
err = mgr.CheckVoucherValid(ctx, ch, sv) err = mgr.CheckVoucherValid(ctx, ch, sv)
if tcase.expectError { if tcase.expectError {
require.Error(t, err) require.Error(t, err)
@ -358,7 +298,14 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
require.NoError(t, err) require.NoError(t, err)
err = mgr.TrackInboundChannel(ctx, ch) // Create the channel in the manager's store
ci := &ChannelInfo{
Channel: &ch,
Control: toAcct,
Target: fromAcct,
Direction: DirInbound,
}
err = mgr.store.putChannelInfo(ci)
require.NoError(t, err) require.NoError(t, err)
// //
@ -380,7 +327,7 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
voucherLane := uint64(1) voucherLane := uint64(1)
voucherNonce := uint64(2) voucherNonce := uint64(2)
voucherAmount := big.NewInt(6) voucherAmount := big.NewInt(6)
sv := testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate) sv := createTestVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
err = mgr.CheckVoucherValid(ctx, ch, sv) err = mgr.CheckVoucherValid(ctx, ch, sv)
require.Error(t, err) require.Error(t, err)
@ -398,13 +345,13 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
// actor balance is 10 so total is ok. // actor balance is 10 so total is ok.
// //
voucherAmount = big.NewInt(4) voucherAmount = big.NewInt(4)
sv = testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
err = mgr.CheckVoucherValid(ctx, ch, sv) err = mgr.CheckVoucherValid(ctx, ch, sv)
require.NoError(t, err) require.NoError(t, err)
// Add voucher to lane 1, so Lane 1 effective redeemed // Add voucher to lane 1, so Lane 1 effective redeemed
// (with first voucher) is now 4 // (with first voucher) is now 4
_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
// //
@ -422,7 +369,7 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
// //
voucherNonce++ voucherNonce++
voucherAmount = big.NewInt(6) voucherAmount = big.NewInt(6)
sv = testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
err = mgr.CheckVoucherValid(ctx, ch, sv) err = mgr.CheckVoucherValid(ctx, ch, sv)
require.Error(t, err) require.Error(t, err)
@ -440,7 +387,7 @@ func TestCheckVoucherValidCountingAllLanes(t *testing.T) {
// actor balance is 10 so total is ok. // actor balance is 10 so total is ok.
// //
voucherAmount = big.NewInt(5) voucherAmount = big.NewInt(5)
sv = testCreateVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, voucherNonce, voucherAmount, fromKeyPrivate)
err = mgr.CheckVoucherValid(ctx, ch, sv) err = mgr.CheckVoucherValid(ctx, ch, sv)
require.NoError(t, err) require.NoError(t, err)
} }
@ -457,23 +404,23 @@ func TestAddVoucherDelta(t *testing.T) {
minDelta := big.NewInt(2) minDelta := big.NewInt(2)
nonce := uint64(1) nonce := uint64(1)
voucherAmount := big.NewInt(1) voucherAmount := big.NewInt(1)
sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.Error(t, err) require.Error(t, err)
// Expect success when adding a voucher whose amount is equal to minDelta // Expect success when adding a voucher whose amount is equal to minDelta
nonce++ nonce++
voucherAmount = big.NewInt(2) voucherAmount = big.NewInt(2)
sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
delta, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta) delta, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, delta.Int64(), 2) require.EqualValues(t, delta.Int64(), 2)
// Check that delta is correct when there's an existing voucher // Check that delta is correct when there's an existing voucher
nonce++ nonce++
voucherAmount = big.NewInt(5) voucherAmount = big.NewInt(5)
sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
delta, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta) delta, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, delta.Int64(), 3) require.EqualValues(t, delta.Int64(), 3)
@ -481,8 +428,8 @@ func TestAddVoucherDelta(t *testing.T) {
nonce = uint64(1) nonce = uint64(1)
voucherAmount = big.NewInt(6) voucherAmount = big.NewInt(6)
voucherLane = uint64(2) voucherLane = uint64(2)
sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
delta, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta) delta, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, delta.Int64(), 6) require.EqualValues(t, delta.Int64(), 6)
} }
@ -499,8 +446,8 @@ func TestAddVoucherNextLane(t *testing.T) {
// Add a voucher in lane 2 // Add a voucher in lane 2
nonce := uint64(1) nonce := uint64(1)
voucherLane := uint64(2) voucherLane := uint64(2)
sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
ci, err := mgr.GetChannelInfo(ch) ci, err := mgr.GetChannelInfo(ch)
@ -518,8 +465,8 @@ func TestAddVoucherNextLane(t *testing.T) {
// Add a voucher in lane 1 // Add a voucher in lane 1
voucherLane = uint64(1) voucherLane = uint64(1)
sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
ci, err = mgr.GetChannelInfo(ch) ci, err = mgr.GetChannelInfo(ch)
@ -528,8 +475,8 @@ func TestAddVoucherNextLane(t *testing.T) {
// Add a voucher in lane 7 // Add a voucher in lane 7
voucherLane = uint64(7) voucherLane = uint64(7)
sv = testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
ci, err = mgr.GetChannelInfo(ch) ci, err = mgr.GetChannelInfo(ch)
@ -557,7 +504,7 @@ func TestAllocateLane(t *testing.T) {
func TestAllocateLaneWithExistingLaneState(t *testing.T) { func TestAllocateLaneWithExistingLaneState(t *testing.T) {
ctx := context.Background() ctx := context.Background()
_, fromKeyPublic := testGenerateKeyPair(t) fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
ch := tutils.NewIDAddr(t, 100) ch := tutils.NewIDAddr(t, 100)
from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic)) from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic))
@ -568,17 +515,13 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
mock := newMockManagerAPI() mock := newMockManagerAPI()
mock.setAccountState(fromAcct, account.State{Address: from}) mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to}) mock.setAccountState(toAcct, account.State{Address: to})
mock.addWalletAddress(to)
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore())) store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
// Create a channel that will be retrieved from state
actorBalance := big.NewInt(10) actorBalance := big.NewInt(10)
toSend := big.NewInt(1) toSend := big.NewInt(1)
laneStates := map[uint64]paych.LaneState{
2: {
Nonce: 1,
Redeemed: big.NewInt(4),
},
}
act := &types.Actor{ act := &types.Actor{
Code: builtin.AccountActorCodeID, Code: builtin.AccountActorCodeID,
@ -587,7 +530,7 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
Balance: actorBalance, Balance: actorBalance,
} }
lsCid, err := mock.storeLaneStates(laneStates) arr, err := adt.MakeEmptyArray(mock.store).Root()
require.NoError(t, err) require.NoError(t, err)
mock.setPaychState(ch, act, paych.State{ mock.setPaychState(ch, act, paych.State{
From: fromAcct, From: fromAcct,
@ -595,15 +538,23 @@ func TestAllocateLaneWithExistingLaneState(t *testing.T) {
ToSend: toSend, ToSend: toSend,
SettlingAt: abi.ChainEpoch(0), SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0), MinSettleHeight: abi.ChainEpoch(0),
LaneStates: lsCid, LaneStates: arr,
}) })
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
require.NoError(t, err) require.NoError(t, err)
err = mgr.TrackInboundChannel(ctx, ch) // Create a voucher on lane 2
// (also reads the channel from state and puts it in the store)
voucherLane := uint64(2)
minDelta := big.NewInt(0)
nonce := uint64(2)
voucherAmount := big.NewInt(5)
sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err = mgr.AddVoucherInbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
// Allocate lane should return the next lane (lane 3)
lane, err := mgr.AllocateLane(ch) lane, err := mgr.AllocateLane(ch)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 3, lane) require.EqualValues(t, 3, lane)
@ -623,8 +574,8 @@ func TestAddVoucherProof(t *testing.T) {
// Add a voucher with no proof // Add a voucher with no proof
var proof []byte var proof []byte
sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate) sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
// Expect one voucher with no proof // Expect one voucher with no proof
@ -635,7 +586,7 @@ func TestAddVoucherProof(t *testing.T) {
// Add same voucher with no proof // Add same voucher with no proof
voucherLane = uint64(1) voucherLane = uint64(1)
_, err = mgr.AddVoucher(ctx, ch, sv, proof, minDelta) _, err = mgr.AddVoucherOutbound(ctx, ch, sv, proof, minDelta)
require.NoError(t, err) require.NoError(t, err)
// Expect one voucher with no proof // Expect one voucher with no proof
@ -646,7 +597,7 @@ func TestAddVoucherProof(t *testing.T) {
// Add same voucher with proof // Add same voucher with proof
proof = []byte{1} proof = []byte{1}
_, err = mgr.AddVoucher(ctx, ch, sv, proof, minDelta) _, err = mgr.AddVoucherOutbound(ctx, ch, sv, proof, minDelta)
require.NoError(t, err) require.NoError(t, err)
// Should add proof to existing voucher // Should add proof to existing voucher
@ -656,6 +607,69 @@ func TestAddVoucherProof(t *testing.T) {
require.Len(t, ci.Vouchers[0].Proof, 1) require.Len(t, ci.Vouchers[0].Proof, 1)
} }
func TestAddVoucherInboundWalletKey(t *testing.T) {
ctx := context.Background()
fromKeyPrivate, fromKeyPublic := testGenerateKeyPair(t)
ch := tutils.NewIDAddr(t, 100)
from := tutils.NewSECP256K1Addr(t, string(fromKeyPublic))
to := tutils.NewSECP256K1Addr(t, "secpTo")
fromAcct := tutils.NewActorAddr(t, "fromAct")
toAcct := tutils.NewActorAddr(t, "toAct")
// Create an actor for the channel in state
act := &types.Actor{
Code: builtin.AccountActorCodeID,
Head: cid.Cid{},
Nonce: 0,
Balance: types.NewInt(20),
}
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.setPaychState(ch, act, paych.State{
From: fromAcct,
To: toAcct,
ToSend: types.NewInt(0),
SettlingAt: abi.ChainEpoch(0),
MinSettleHeight: abi.ChainEpoch(0),
LaneStates: arr,
})
// Create a manager
store := NewStore(ds_sync.MutexWrap(ds.NewMapDatastore()))
mgr, err := newManager(store, mock)
require.NoError(t, err)
// Add a voucher
nonce := uint64(1)
voucherLane := uint64(1)
minDelta := big.NewInt(0)
voucherAmount := big.NewInt(2)
sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err = mgr.AddVoucherInbound(ctx, ch, sv, nil, minDelta)
// Should fail because there is no wallet key matching the channel To
// address (ie, the channel is not "owned" by this node)
require.Error(t, err)
// Add wallet key for To address
mock.addWalletAddress(to)
// Add voucher again
sv = createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, fromKeyPrivate)
_, err = mgr.AddVoucherInbound(ctx, ch, sv, nil, minDelta)
// Should now pass because there is a wallet key matching the channel To
// address
require.NoError(t, err)
}
func TestNextNonceForLane(t *testing.T) { func TestNextNonceForLane(t *testing.T) {
ctx := context.Background() ctx := context.Background()
@ -674,19 +688,19 @@ func TestNextNonceForLane(t *testing.T) {
// Add vouchers such that we have // Add vouchers such that we have
// lane 1: nonce 2 // lane 1: nonce 2
// lane 1: nonce 4 // lane 1: nonce 4
// lane 2: nonce 7
voucherLane := uint64(1) voucherLane := uint64(1)
for _, nonce := range []uint64{2, 4} { for _, nonce := range []uint64{2, 4} {
voucherAmount = big.Add(voucherAmount, big.NewInt(1)) voucherAmount = big.Add(voucherAmount, big.NewInt(1))
sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, key) sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, key)
_, err := mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err := mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
} }
// lane 2: nonce 7
voucherLane = uint64(2) voucherLane = uint64(2)
nonce := uint64(7) nonce := uint64(7)
sv := testCreateVoucher(t, ch, voucherLane, nonce, voucherAmount, key) sv := createTestVoucher(t, ch, voucherLane, nonce, voucherAmount, key)
_, err = mgr.AddVoucher(ctx, ch, sv, nil, minDelta) _, err = mgr.AddVoucherOutbound(ctx, ch, sv, nil, minDelta)
require.NoError(t, err) require.NoError(t, err)
// Expect next nonce for lane 1 to be 5 // Expect next nonce for lane 1 to be 5
@ -715,6 +729,7 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, addre
mock.setAccountState(fromAcct, account.State{Address: from}) mock.setAccountState(fromAcct, account.State{Address: from})
mock.setAccountState(toAcct, account.State{Address: to}) mock.setAccountState(toAcct, account.State{Address: to})
// Create channel in state
act := &types.Actor{ act := &types.Actor{
Code: builtin.AccountActorCodeID, Code: builtin.AccountActorCodeID,
Head: cid.Cid{}, Head: cid.Cid{},
@ -734,8 +749,16 @@ func testSetupMgrWithChannel(ctx context.Context, t *testing.T) (*Manager, addre
mgr, err := newManager(store, mock) mgr, err := newManager(store, mock)
require.NoError(t, err) require.NoError(t, err)
err = mgr.TrackInboundChannel(ctx, ch) // Create the channel in the manager's store
ci := &ChannelInfo{
Channel: &ch,
Control: fromAcct,
Target: toAcct,
Direction: DirOutbound,
}
err = mgr.store.putChannelInfo(ci)
require.NoError(t, err) require.NoError(t, err)
return mgr, ch, fromKeyPrivate return mgr, ch, fromKeyPrivate
} }
@ -747,7 +770,7 @@ func testGenerateKeyPair(t *testing.T) ([]byte, []byte) {
return priv, pub return priv, pub
} }
func testCreateVoucher(t *testing.T, ch address.Address, voucherLane uint64, nonce uint64, voucherAmount big.Int, key []byte) *paych.SignedVoucher { func createTestVoucher(t *testing.T, ch address.Address, voucherLane uint64, nonce uint64, voucherAmount big.Int, key []byte) *paych.SignedVoucher {
sv := &paych.SignedVoucher{ sv := &paych.SignedVoucher{
ChannelAddr: ch, ChannelAddr: ch,
Lane: voucherLane, Lane: voucherLane,

View File

@ -17,7 +17,7 @@ type stateAccessor struct {
sm stateManagerAPI sm stateManagerAPI
} }
func (ca *stateAccessor) loadPaychState(ctx context.Context, ch address.Address) (*types.Actor, *paych.State, error) { func (ca *stateAccessor) loadPaychActorState(ctx context.Context, ch address.Address) (*types.Actor, *paych.State, error) {
var pcast paych.State var pcast paych.State
act, err := ca.sm.LoadActorState(ctx, ch, &pcast, nil) act, err := ca.sm.LoadActorState(ctx, ch, &pcast, nil)
if err != nil { if err != nil {
@ -28,7 +28,7 @@ func (ca *stateAccessor) loadPaychState(ctx context.Context, ch address.Address)
} }
func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Address, dir uint64) (*ChannelInfo, error) { func (ca *stateAccessor) loadStateChannelInfo(ctx context.Context, ch address.Address, dir uint64) (*ChannelInfo, error) {
_, st, err := ca.loadPaychState(ctx, ch) _, st, err := ca.loadPaychActorState(ctx, ch)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -84,15 +84,20 @@ type ChannelInfo struct {
// TrackChannel stores a channel, returning an error if the channel was already // TrackChannel stores a channel, returning an error if the channel was already
// being tracked // being tracked
func (ps *Store) TrackChannel(ci *ChannelInfo) error { func (ps *Store) TrackChannel(ci *ChannelInfo) (*ChannelInfo, error) {
_, err := ps.ByAddress(*ci.Channel) _, err := ps.ByAddress(*ci.Channel)
switch err { switch err {
default: default:
return err return nil, err
case nil: case nil:
return fmt.Errorf("already tracking channel: %s", ci.Channel) return nil, fmt.Errorf("already tracking channel: %s", ci.Channel)
case ErrChannelNotTracked: case ErrChannelNotTracked:
return ps.putChannelInfo(ci) err = ps.putChannelInfo(ci)
if err != nil {
return nil, err
}
return ps.ByAddress(*ci.Channel)
} }
} }

View File

@ -38,15 +38,15 @@ func TestStore(t *testing.T) {
} }
// Track the channel // Track the channel
err = store.TrackChannel(ci) _, err = store.TrackChannel(ci)
require.NoError(t, err) require.NoError(t, err)
// Tracking same channel again should error // Tracking same channel again should error
err = store.TrackChannel(ci) _, err = store.TrackChannel(ci)
require.Error(t, err) require.Error(t, err)
// Track another channel // Track another channel
err = store.TrackChannel(ci2) _, err = store.TrackChannel(ci2)
require.NoError(t, err) require.NoError(t, err)
// List channels should include all channels // List channels should include all channels