Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

watchtower/wtclient: reliable, asynchronous pipeline for revoked state backups #2618

Merged
merged 21 commits into from
Mar 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9c70f49
watchtower/wtwire/create_session_reply: remove extra Reject from code
cfromknecht Mar 15, 2019
99dbbf4
watchtower/wtwire/error_code: add human-readable descriptors
cfromknecht Mar 15, 2019
247978d
watchtower/wtdb/tower: store wt pk and addrs
cfromknecht Mar 15, 2019
9177358
watchtower/wtdb/client_session: add ClientSession
cfromknecht Mar 15, 2019
04bbf39
watchtower/wtclient/log: adds wtclient logging
cfromknecht Mar 15, 2019
b190345
watchtower/wtclient/interface: add DB ifaces
cfromknecht Mar 15, 2019
4642954
watchtower/wtclient/backup_task: bind to ClientSession instead of Ses…
cfromknecht Mar 15, 2019
b23bff6
watchtower/wtclient/errors
cfromknecht Mar 15, 2019
a8721bc
watchtower/wtclient/tower_candidate_iterator: linked-list iterator
cfromknecht Mar 15, 2019
95fa765
watchtower/wtclient/session_negotiator: add session negotiation
cfromknecht Mar 15, 2019
65d09fc
watchtower/wtclient/task_pipeline: add reliable task aggregator
cfromknecht Mar 15, 2019
aa2b211
watchtower/wtclient/session_queue: batch upload state updates
cfromknecht Mar 15, 2019
abef9e0
watchtower/wtclient/stats: adds clientStats
cfromknecht Mar 15, 2019
f00b4c5
watchtower/wtclient/client: hook up full client pipeline
cfromknecht Mar 15, 2019
87e8700
watchtower/wtmock/client_db: add mock client db
cfromknecht Mar 15, 2019
81497ec
watchtower/wtmock/peer: create mock net.Conn using bidi MockPeer
cfromknecht Mar 15, 2019
8b0cc48
watchtower/wtdb+wtserver: allow retransmission of last update
cfromknecht Mar 15, 2019
a222a63
watchtower/wtserver/server: no ack updates
cfromknecht Mar 15, 2019
e1e805d
watchtower/wtserver/server: fix race condition on Stop
cfromknecht Mar 15, 2019
80040d9
watchtower/wtclient/client_test: adds client-server upload test
cfromknecht Mar 15, 2019
05e3a7f
watchtower/wtmock/peer: set local pubkey
cfromknecht Mar 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions watchtower/wtclient/backup_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ import (
// necessary components are stripped out and encrypted before being sent to
// the tower in a StateUpdate.
type backupTask struct {
chanID lnwire.ChannelID
commitHeight uint64
breachInfo *lnwallet.BreachRetribution
id wtdb.BackupID
breachInfo *lnwallet.BreachRetribution

// state-dependent variables

Expand Down Expand Up @@ -96,8 +95,10 @@ func newBackupTask(chanID *lnwire.ChannelID,
}

return &backupTask{
chanID: *chanID,
commitHeight: breachInfo.RevokedStateNum,
id: wtdb.BackupID{
ChanID: *chanID,
CommitHeight: breachInfo.RevokedStateNum,
},
breachInfo: breachInfo,
toLocalInput: toLocalInput,
toRemoteInput: toRemoteInput,
Expand Down Expand Up @@ -125,7 +126,7 @@ func (t *backupTask) inputs() map[wire.OutPoint]input.Input {
// SessionInfo's policy. If no error is returned, the task has been bound to the
// session and can be queued to upload to the tower. Otherwise, the bind failed
// and should be rescheduled with a different session.
func (t *backupTask) bindSession(session *wtdb.SessionInfo) error {
func (t *backupTask) bindSession(session *wtdb.ClientSession) error {

// First we'll begin by deriving a weight estimate for the justice
// transaction. The final weight can be different depending on whether
Expand Down Expand Up @@ -154,7 +155,7 @@ func (t *backupTask) bindSession(session *wtdb.SessionInfo) error {
// in the current session's policy.
outputs, err := session.Policy.ComputeJusticeTxOuts(
t.totalAmt, int64(weightEstimate.Weight()),
t.sweepPkScript, session.RewardAddress,
t.sweepPkScript, session.RewardPkScript,
)
if err != nil {
return err
Expand Down
16 changes: 8 additions & 8 deletions watchtower/wtclient/backup_task_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type backupTaskTest struct {
expSweepAmt int64
expRewardAmt int64
expRewardScript []byte
session *wtdb.SessionInfo
session *wtdb.ClientSession
bindErr error
expSweepScript []byte
signer input.Signer
Expand Down Expand Up @@ -205,13 +205,13 @@ func genTaskTest(
expSweepAmt: expSweepAmt,
expRewardAmt: expRewardAmt,
expRewardScript: rewardScript,
session: &wtdb.SessionInfo{
session: &wtdb.ClientSession{
Policy: wtpolicy.Policy{
BlobType: blobType,
SweepFeeRate: sweepFeeRate,
RewardRate: 10000,
},
RewardAddress: rewardScript,
RewardPkScript: rewardScript,
},
bindErr: bindErr,
expSweepScript: makeAddrSlice(22),
Expand Down Expand Up @@ -379,7 +379,7 @@ var backupTaskTests = []backupTaskTest{
}

// TestBackupTaskBind tests the initialization and binding of a backupTask to a
// SessionInfo. After a succesfful bind, all parameters of the justice
// ClientSession. After a successful bind, all parameters of the justice
// transaction should be solidified, so we assert there correctness. In an
// unsuccessful bind, the session-dependent parameters should be unmodified so
// that the backup task can be rescheduled if necessary. Finally, we assert that
Expand All @@ -401,14 +401,14 @@ func testBackupTask(t *testing.T, test backupTaskTest) {

// Assert that all parameters set during initialization are properly
// populated.
if task.chanID != test.chanID {
if task.id.ChanID != test.chanID {
t.Fatalf("channel id mismatch, want: %s, got: %s",
test.chanID, task.chanID)
test.chanID, task.id.ChanID)
}

if task.commitHeight != test.breachInfo.RevokedStateNum {
if task.id.CommitHeight != test.breachInfo.RevokedStateNum {
t.Fatalf("commit height mismatch, want: %d, got: %d",
test.breachInfo.RevokedStateNum, task.commitHeight)
test.breachInfo.RevokedStateNum, task.id.CommitHeight)
}

if task.totalAmt != test.expTotalAmt {
Expand Down
82 changes: 82 additions & 0 deletions watchtower/wtclient/candidate_iterator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package wtclient

import (
"container/list"
"sync"

"github.com/lightningnetwork/lnd/watchtower/wtdb"
)

// TowerCandidateIterator provides an abstraction for iterating through possible
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
// watchtower addresses when attempting to create a new session.
type TowerCandidateIterator interface {
// Reset clears any internal iterator state, making previously taken
// candidates available as long as they remain in the set.
Reset() error

// Next returns the next candidate tower. The iterator is not required
// to return results in any particular order. If no more candidates are
// available, ErrTowerCandidatesExhausted is returned.
Next() (*wtdb.Tower, error)
}

// towerListIterator is a linked-list backed TowerCandidateIterator.
type towerListIterator struct {
mu sync.Mutex
candidates *list.List
nextCandidate *list.Element
}

// Compile-time constraint to ensure *towerListIterator implements the
// TowerCandidateIterator interface.
var _ TowerCandidateIterator = (*towerListIterator)(nil)

// newTowerListIterator initializes a new towerListIterator from a variadic list
// of lnwire.NetAddresses.
func newTowerListIterator(candidates ...*wtdb.Tower) *towerListIterator {
iter := &towerListIterator{
candidates: list.New(),
}

for _, candidate := range candidates {
iter.candidates.PushBack(candidate)
}
iter.Reset()

return iter
}

// Reset clears the iterators state, and makes the address at the front of the
// list the next item to be returned..
func (t *towerListIterator) Reset() error {
t.mu.Lock()
defer t.mu.Unlock()

// Reset the next candidate to the front of the linked-list.
t.nextCandidate = t.candidates.Front()

return nil
}

// Next returns the next candidate tower. This iterator will always return
// candidates in the order given when the iterator was instantiated. If no more
// candidates are available, ErrTowerCandidatesExhausted is returned.
func (t *towerListIterator) Next() (*wtdb.Tower, error) {
t.mu.Lock()
defer t.mu.Unlock()

// If the next candidate is nil, we've exhausted the list.
if t.nextCandidate == nil {
return nil, ErrTowerCandidatesExhausted
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
}

// Propose the tower at the front of the list.
tower := t.nextCandidate.Value.(*wtdb.Tower)

// Set the next candidate to the subsequent element.
t.nextCandidate = t.nextCandidate.Next()

return tower, nil
}

// TODO(conner): implement graph-backed candidate iterator for public towers.
Loading