diff --git a/node/impl/paych/paych.go b/node/impl/paych/paych.go index 156a9837a..b9f8005d7 100644 --- a/node/impl/paych/paych.go +++ b/node/impl/paych/paych.go @@ -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) } diff --git a/paychmgr/manager.go b/paychmgr/manager.go index 6244d9104..fe41100ae 100644 --- a/paychmgr/manager.go +++ b/paychmgr/manager.go @@ -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 { diff --git a/paychmgr/mock_test.go b/paychmgr/mock_test.go index 2ccb6e1d7..1c8674ae4 100644 --- a/paychmgr/mock_test.go +++ b/paychmgr/mock_test.go @@ -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{}{} +} diff --git a/paychmgr/paych.go b/paychmgr/paych.go index d4b02ec11..43e4c128b 100644 --- a/paychmgr/paych.go +++ b/paychmgr/paych.go @@ -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 diff --git a/paychmgr/paych_test.go b/paychmgr/paych_test.go index 126e73702..0b8cd0f6f 100644 --- a/paychmgr/paych_test.go +++ b/paychmgr/paych_test.go @@ -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, diff --git a/paychmgr/state.go b/paychmgr/state.go index db1b8ded2..00fe2adce 100644 --- a/paychmgr/state.go +++ b/paychmgr/state.go @@ -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 } diff --git a/paychmgr/store.go b/paychmgr/store.go index 62c4cf9b2..09790d311 100644 --- a/paychmgr/store.go +++ b/paychmgr/store.go @@ -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) } } diff --git a/paychmgr/store_test.go b/paychmgr/store_test.go index 65be6f1b1..36ff7a5b0 100644 --- a/paychmgr/store_test.go +++ b/paychmgr/store_test.go @@ -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