From 1db9bf2fd4b1e8e3e25aeb7fd594c18af412d04b Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 23 May 2019 20:47:49 -0700 Subject: [PATCH] watchtower/wtdb: create embedded ClientSessionBody This commit splits out the portions of the ClientSession into an embedded ClientSessionBody, since these fields will be serialized together on-disk. --- watchtower/wtclient/backup_task.go | 3 +- .../wtclient/backup_task_internal_test.go | 4 +- watchtower/wtclient/session_negotiator.go | 11 ++-- watchtower/wtclient/session_queue.go | 2 +- watchtower/wtdb/client_session.go | 60 ++++++++++++------- watchtower/wtmock/client_db.go | 16 ++--- 6 files changed, 59 insertions(+), 37 deletions(-) diff --git a/watchtower/wtclient/backup_task.go b/watchtower/wtclient/backup_task.go index c88bfd0f9..72e149348 100644 --- a/watchtower/wtclient/backup_task.go +++ b/watchtower/wtclient/backup_task.go @@ -126,8 +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.ClientSession) error { - +func (t *backupTask) bindSession(session *wtdb.ClientSessionBody) error { // First we'll begin by deriving a weight estimate for the justice // transaction. The final weight can be different depending on whether // the watchtower is taking a reward. diff --git a/watchtower/wtclient/backup_task_internal_test.go b/watchtower/wtclient/backup_task_internal_test.go index 2c25c9a02..869c40426 100644 --- a/watchtower/wtclient/backup_task_internal_test.go +++ b/watchtower/wtclient/backup_task_internal_test.go @@ -69,7 +69,7 @@ type backupTaskTest struct { expSweepAmt int64 expRewardAmt int64 expRewardScript []byte - session *wtdb.ClientSession + session *wtdb.ClientSessionBody bindErr error expSweepScript []byte signer input.Signer @@ -205,7 +205,7 @@ func genTaskTest( expSweepAmt: expSweepAmt, expRewardAmt: expRewardAmt, expRewardScript: rewardScript, - session: &wtdb.ClientSession{ + session: &wtdb.ClientSessionBody{ Policy: wtpolicy.Policy{ BlobType: blobType, SweepFeeRate: sweepFeeRate, diff --git a/watchtower/wtclient/session_negotiator.go b/watchtower/wtclient/session_negotiator.go index 355701bda..e3d58c1f1 100644 --- a/watchtower/wtclient/session_negotiator.go +++ b/watchtower/wtclient/session_negotiator.go @@ -417,14 +417,15 @@ func (n *sessionNegotiator) tryAddress(privKey *btcec.PrivateKey, privKey.PubKey(), ) clientSession := &wtdb.ClientSession{ - TowerID: tower.ID, + ClientSessionBody: wtdb.ClientSessionBody{ + TowerID: tower.ID, + KeyIndex: keyIndex, + Policy: n.cfg.Policy, + RewardPkScript: rewardPkScript, + }, Tower: tower, - KeyIndex: keyIndex, SessionPrivKey: privKey, ID: sessionID, - Policy: n.cfg.Policy, - SeqNum: 0, - RewardPkScript: rewardPkScript, } err = n.cfg.DB.CreateClientSession(clientSession) diff --git a/watchtower/wtclient/session_queue.go b/watchtower/wtclient/session_queue.go index 258e274a3..39ab0a431 100644 --- a/watchtower/wtclient/session_queue.go +++ b/watchtower/wtclient/session_queue.go @@ -215,7 +215,7 @@ func (q *sessionQueue) AcceptTask(task *backupTask) (reserveStatus, bool) { // // TODO(conner): queue backups and retry with different session params. case reserveAvailable: - err := task.bindSession(q.cfg.ClientSession) + err := task.bindSession(&q.cfg.ClientSession.ClientSessionBody) if err != nil { q.queueCond.L.Unlock() log.Debugf("SessionQueue %s rejected backup chanid=%s "+ diff --git a/watchtower/wtdb/client_session.go b/watchtower/wtdb/client_session.go index 9eed54f45..5b2d39d72 100644 --- a/watchtower/wtdb/client_session.go +++ b/watchtower/wtdb/client_session.go @@ -46,8 +46,48 @@ var ( type ClientSession struct { // ID is the client's public key used when authenticating with the // tower. + // + // NOTE: This value is not serialized with the body of the struct, it + // should be set and recovered as the ClientSession's key. ID SessionID + ClientSessionBody + + // CommittedUpdates is a sorted list of unacked updates. These updates + // can be resent after a restart if the updates failed to send or + // receive an acknowledgment. + // + // NOTE: This list is serialized in it's own bucket, separate from the + // body of the ClientSession. The representation on disk is a key value + // map from sequence number to CommittedUpdateBody to allow efficient + // insertion and retrieval. + CommittedUpdates []CommittedUpdate + + // AckedUpdates is a map from sequence number to backup id to record + // which revoked states were uploaded via this session. + // + // NOTE: This map is serialized in it's own bucket, separate from the + // body of the ClientSession. + AckedUpdates map[uint16]BackupID + + // Tower holds the pubkey and address of the watchtower. + // + // NOTE: This value is not serialized. It is recovered by looking up the + // tower with TowerID. + Tower *Tower + + // SessionPrivKey is the ephemeral secret key used to connect to the + // watchtower. + // + // NOTE: This value is not serialized. It is derived using the KeyIndex + // on startup to avoid storing private keys on disk. + SessionPrivKey *btcec.PrivateKey +} + +// ClientSessionBody represents the primary components of a ClientSession that +// are serialized together within the database. The CommittedUpdates and +// AckedUpdates are serialized in buckets separate from the body. +type ClientSessionBody struct { // SeqNum is the next unallocated sequence number that can be sent to // the tower. SeqNum uint16 @@ -59,25 +99,12 @@ type ClientSession struct { // Tower with which the session is negotiated. TowerID TowerID - // Tower holds the pubkey and address of the watchtower. - // - // NOTE: This value is not serialized. It is recovered by looking up the - // tower with TowerID. - Tower *Tower - // KeyIndex is the index of key locator used to derive the client's // session key so that it can authenticate with the tower to update its // session. In order to rederive the private key, the key locator should // use the keychain.KeyFamilyTowerSession key family. KeyIndex uint32 - // SessionPrivKey is the ephemeral secret key used to connect to the - // watchtower. - // - // NOTE: This value is not serialized. It is derived using the KeyIndex - // on startup to avoid storing private keys on disk. - SessionPrivKey *btcec.PrivateKey - // Policy holds the negotiated session parameters. Policy wtpolicy.Policy @@ -86,14 +113,7 @@ type ClientSession struct { // specifies a reward output. RewardPkScript []byte - // CommittedUpdates is a sorted list of unacked updates. These updates - // can be resent after a restart if the updates failed to send or - // receive an acknowledgment. - CommittedUpdates []CommittedUpdate - // AckedUpdates is a map from sequence number to backup id to record - // which revoked states were uploaded via this session. - AckedUpdates map[uint16]BackupID } // BackupID identifies a particular revoked, remote commitment by channel id and diff --git a/watchtower/wtmock/client_db.go b/watchtower/wtmock/client_db.go index e4e13c83c..32898e4e0 100644 --- a/watchtower/wtmock/client_db.go +++ b/watchtower/wtmock/client_db.go @@ -122,13 +122,15 @@ func (m *ClientDB) CreateClientSession(session *wtdb.ClientSession) error { delete(m.indexes, session.TowerID) m.activeSessions[session.ID] = &wtdb.ClientSession{ - TowerID: session.TowerID, - KeyIndex: session.KeyIndex, - ID: session.ID, - Policy: session.Policy, - SeqNum: session.SeqNum, - TowerLastApplied: session.TowerLastApplied, - RewardPkScript: cloneBytes(session.RewardPkScript), + ID: session.ID, + ClientSessionBody: wtdb.ClientSessionBody{ + SeqNum: session.SeqNum, + TowerLastApplied: session.TowerLastApplied, + TowerID: session.TowerID, + KeyIndex: session.KeyIndex, + Policy: session.Policy, + RewardPkScript: cloneBytes(session.RewardPkScript), + }, CommittedUpdates: make([]wtdb.CommittedUpdate, 0), AckedUpdates: make(map[uint16]wtdb.BackupID), }