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) {
_ = a.PaychMgr.TrackInboundChannel(ctx, ch) // TODO: expose those calls
return a.PaychMgr.AddVoucher(ctx, ch, sv, proof, minDelta)
return a.PaychMgr.AddVoucherInbound(ctx, ch, sv, proof, minDelta)
}
// 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
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)
}

View File

@ -45,8 +45,10 @@ type stateManagerAPI interface {
// paychAPI defines the API methods needed by the payment channel manager
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)
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
@ -127,26 +129,6 @@ func (pm *Manager) Stop() error {
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) {
chanAccessor, err := pm.accessorByFromTo(from, to)
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)
}
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)
if err != nil {
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)
}
// 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) {
ca, err := pm.accessorByAddress(ch)
if err != nil {

View File

@ -111,6 +111,7 @@ type mockPaychAPI struct {
messages map[cid.Cid]*types.SignedMessage
waitingCalls map[cid.Cid]*waitingCall
waitingResponses map[cid.Cid]*waitingResponse
wallet map[address.Address]struct{}
}
func newMockPaychAPI() *mockPaychAPI {
@ -118,6 +119,7 @@ func newMockPaychAPI() *mockPaychAPI {
messages: make(map[cid.Cid]*types.SignedMessage),
waitingCalls: make(map[cid.Cid]*waitingCall),
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)
}
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)
}
act, pchState, err := ca.sa.loadPaychState(ctx, ch)
// Load payment channel actor state
act, pchState, err := ca.sa.loadPaychActorState(ctx, ch)
if err != nil {
return nil, err
}
// Load channel "From" account actor state
var actState account.State
_, err = ca.api.LoadActorState(ctx, pchState.From, &actState, nil)
if err != nil {
@ -76,7 +78,7 @@ func (ca *channelAccessor) checkVoucherValidUnlocked(ctx context.Context, ch add
}
from := actState.Address
// verify signature
// verify voucher signature
vb, err := sv.SigningBytes()
if err != nil {
return nil, err

View File

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

View File

@ -17,7 +17,7 @@ type stateAccessor struct {
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
act, err := ca.sm.LoadActorState(ctx, ch, &pcast, 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) {
_, st, err := ca.loadPaychState(ctx, ch)
_, st, err := ca.loadPaychActorState(ctx, ch)
if err != nil {
return nil, err
}

View File

@ -84,15 +84,20 @@ type ChannelInfo struct {
// TrackChannel stores a channel, returning an error if the channel was already
// being tracked
func (ps *Store) TrackChannel(ci *ChannelInfo) error {
func (ps *Store) TrackChannel(ci *ChannelInfo) (*ChannelInfo, error) {
_, err := ps.ByAddress(*ci.Channel)
switch err {
default:
return err
return nil, err
case nil:
return fmt.Errorf("already tracking channel: %s", ci.Channel)
return nil, fmt.Errorf("already tracking channel: %s", ci.Channel)
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
err = store.TrackChannel(ci)
_, err = store.TrackChannel(ci)
require.NoError(t, err)
// Tracking same channel again should error
err = store.TrackChannel(ci)
_, err = store.TrackChannel(ci)
require.Error(t, err)
// Track another channel
err = store.TrackChannel(ci2)
_, err = store.TrackChannel(ci2)
require.NoError(t, err)
// List channels should include all channels