From ffe15e2820f1dee944260606573fd93e7b29c202 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Tue, 15 Sep 2020 12:43:37 -0400 Subject: [PATCH] watchtower/blob/justice_kit: add BlobType to JusticeKit struct This is preparation for later commits where the values returned by member methods will need to be conditioned on the blob type used during decryption. --- watchtower/blob/justice_kit.go | 39 ++++++++++++++++++++++++----- watchtower/blob/justice_kit_test.go | 3 ++- watchtower/lookout/lookout_test.go | 7 ++++-- watchtower/wtclient/backup_task.go | 3 ++- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/watchtower/blob/justice_kit.go b/watchtower/blob/justice_kit.go index dd0213fb..20c44cef 100644 --- a/watchtower/blob/justice_kit.go +++ b/watchtower/blob/justice_kit.go @@ -100,6 +100,15 @@ type PubKey [33]byte // and for a watchtower to later decrypt if action must be taken. The encoding // format is versioned to allow future extensions. type JusticeKit struct { + // BlobType encodes a bitfield that inform the tower of various features + // requested by the client when resolving a breach. Examples include + // whether the justice transaction contains a reward for the tower, or + // whether the channel is a legacy or anchor channel. + // + // NOTE: This value is not serialized in the encrypted payload. It is + // stored separately and added to the JusticeKit after decryption. + BlobType Type + // SweepAddress is the witness program of the output where the client's // fund will be deposited. This value is included in the blobs, as // opposed to the session info, such that the sweep addresses can't be @@ -187,17 +196,33 @@ func (b *JusticeKit) HasCommitToRemoteOutput() bool { } // CommitToRemoteWitnessScript returns the witness script for the commitment -// to-remote p2wkh output, which is the pubkey itself. +// to-remote output given the blob type. The script returned will either be for +// a p2wpkh to-remote output or an p2wsh anchor to-remote output which includes +// a CSV delay. func (b *JusticeKit) CommitToRemoteWitnessScript() ([]byte, error) { if !btcec.IsCompressedPubKey(b.CommitToRemotePubKey[:]) { return nil, ErrNoCommitToRemoteOutput } + // If this is a blob for an anchor channel, we'll return the p2wsh + // output containing a CSV delay of 1. + if b.BlobType.IsAnchorChannel() { + pk, err := btcec.ParsePubKey( + b.CommitToRemotePubKey[:], btcec.S256(), + ) + if err != nil { + return nil, err + } + + return input.CommitScriptToRemoteConfirmed(pk) + } + return b.CommitToRemotePubKey[:], nil } // CommitToRemoteWitnessStack returns a witness stack spending the commitment -// to-remote output, which is a regular p2wkh. +// to-remote output, which consists of a single signature satisfying either the +// legacy or anchor witness scripts. // func (b *JusticeKit) CommitToRemoteWitnessStack() ([][]byte, error) { toRemoteSig, err := b.CommitToRemoteSig.ToSignature() @@ -218,11 +243,11 @@ func (b *JusticeKit) CommitToRemoteWitnessStack() ([][]byte, error) { // // NOTE: It is the caller's responsibility to ensure that this method is only // called once for a given (nonce, key) pair. -func (b *JusticeKit) Encrypt(key BreachKey, blobType Type) ([]byte, error) { +func (b *JusticeKit) Encrypt(key BreachKey) ([]byte, error) { // Encode the plaintext using the provided version, to obtain the // plaintext bytes. var ptxtBuf bytes.Buffer - err := b.encode(&ptxtBuf, blobType) + err := b.encode(&ptxtBuf, b.BlobType) if err != nil { return nil, err } @@ -236,7 +261,7 @@ func (b *JusticeKit) Encrypt(key BreachKey, blobType Type) ([]byte, error) { // Allocate the ciphertext, which will contain the nonce, encrypted // plaintext and MAC. plaintext := ptxtBuf.Bytes() - ciphertext := make([]byte, Size(blobType)) + ciphertext := make([]byte, Size(b.BlobType)) // Generate a random 24-byte nonce in the ciphertext's prefix. nonce := ciphertext[:NonceSize] @@ -284,7 +309,9 @@ func Decrypt(key BreachKey, ciphertext []byte, // If decryption succeeded, we will then decode the plaintext bytes // using the specified blob version. - boj := &JusticeKit{} + boj := &JusticeKit{ + BlobType: blobType, + } err = boj.decode(bytes.NewReader(plaintext), blobType) if err != nil { return nil, err diff --git a/watchtower/blob/justice_kit_test.go b/watchtower/blob/justice_kit_test.go index 3bec48ee..69b12b09 100644 --- a/watchtower/blob/justice_kit_test.go +++ b/watchtower/blob/justice_kit_test.go @@ -150,6 +150,7 @@ func TestBlobJusticeKitEncryptDecrypt(t *testing.T) { func testBlobJusticeKitEncryptDecrypt(t *testing.T, test descriptorTest) { boj := &blob.JusticeKit{ + BlobType: test.encVersion, SweepAddress: test.sweepAddr, RevocationPubKey: test.revPubKey, LocalDelayPubKey: test.delayPubKey, @@ -170,7 +171,7 @@ func testBlobJusticeKitEncryptDecrypt(t *testing.T, test descriptorTest) { // Encrypt the blob plaintext using the generated key and // target version for this test. - ctxt, err := boj.Encrypt(key, test.encVersion) + ctxt, err := boj.Encrypt(key) if err != test.encErr { t.Fatalf("unable to encrypt blob: %v", err) } else if test.encErr != nil { diff --git a/watchtower/lookout/lookout_test.go b/watchtower/lookout/lookout_test.go index 157e3042..da2159c1 100644 --- a/watchtower/lookout/lookout_test.go +++ b/watchtower/lookout/lookout_test.go @@ -137,7 +137,9 @@ func TestLookoutBreachMatching(t *testing.T) { } // Construct a justice kit for each possible breach transaction. + blobType := blob.FlagCommitOutputs.Type() blob1 := &blob.JusticeKit{ + BlobType: blobType, SweepAddress: makeAddrSlice(22), RevocationPubKey: makePubKey(1), LocalDelayPubKey: makePubKey(1), @@ -145,6 +147,7 @@ func TestLookoutBreachMatching(t *testing.T) { CommitToLocalSig: makeArray64(1), } blob2 := &blob.JusticeKit{ + BlobType: blobType, SweepAddress: makeAddrSlice(22), RevocationPubKey: makePubKey(2), LocalDelayPubKey: makePubKey(2), @@ -156,13 +159,13 @@ func TestLookoutBreachMatching(t *testing.T) { key2 := blob.NewBreachKeyFromHash(&hash2) // Encrypt the first justice kit under breach key one. - encBlob1, err := blob1.Encrypt(key1, blob.FlagCommitOutputs.Type()) + encBlob1, err := blob1.Encrypt(key1) if err != nil { t.Fatalf("unable to encrypt sweep detail 1: %v", err) } // Encrypt the second justice kit under breach key two. - encBlob2, err := blob2.Encrypt(key2, blob.FlagCommitOutputs.Type()) + encBlob2, err := blob2.Encrypt(key2) if err != nil { t.Fatalf("unable to encrypt sweep detail 2: %v", err) } diff --git a/watchtower/wtclient/backup_task.go b/watchtower/wtclient/backup_task.go index 302a6bc3..9994f7d0 100644 --- a/watchtower/wtclient/backup_task.go +++ b/watchtower/wtclient/backup_task.go @@ -194,6 +194,7 @@ func (t *backupTask) craftSessionPayload( // to-local script, and the remote CSV delay. keyRing := t.breachInfo.KeyRing justiceKit := &blob.JusticeKit{ + BlobType: t.blobType, SweepAddress: t.sweepPkScript, RevocationPubKey: toBlobPubKey(keyRing.RevocationKey), LocalDelayPubKey: toBlobPubKey(keyRing.ToLocalKey), @@ -299,7 +300,7 @@ func (t *backupTask) craftSessionPayload( // Then, we'll encrypt the computed justice kit using the full breach // transaction id, which will allow the tower to recover the contents // after the transaction is seen in the chain or mempool. - encBlob, err := justiceKit.Encrypt(key, t.blobType) + encBlob, err := justiceKit.Encrypt(key) if err != nil { return hint, nil, err }