mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-31 17:51:33 +02:00
watchtower/blob: add justiceKitPacketV1 encoding
In preparation for the new Taproot commitment type being supported by watchtowers, we add a new justice packet type.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
@@ -35,6 +36,17 @@ const (
|
||||
// commit to-remote sig: 64 bytes, maybe blank
|
||||
V0PlaintextSize = 274
|
||||
|
||||
// V1PlaintextSize is the plaintext size of a version 1 encoded blob.
|
||||
// sweep address length: 1 byte
|
||||
// padded sweep address: 42 bytes
|
||||
// revocation pubkey: 32 bytes
|
||||
// local delay pubkey: 32 bytes
|
||||
// commit to-local revocation sig: 64 bytes
|
||||
// hash of to-local delay script: 32 bytes
|
||||
// commit to-remote pubkey: 33 bytes, maybe blank
|
||||
// commit to-remote sig: 64 bytes, maybe blank
|
||||
V1PlaintextSize = 300
|
||||
|
||||
// MaxSweepAddrSize defines the maximum sweep address size that can be
|
||||
// encoded in a blob.
|
||||
MaxSweepAddrSize = 42
|
||||
@@ -78,6 +90,9 @@ func Size(kit JusticeKit) int {
|
||||
return NonceSize + kit.PlainTextSize() + CiphertextExpansion
|
||||
}
|
||||
|
||||
// schnorrPubKey is a 32-byte serialized x-only public key.
|
||||
type schnorrPubKey [32]byte
|
||||
|
||||
// pubKey is a 33-byte, serialized compressed public key.
|
||||
type pubKey [33]byte
|
||||
|
||||
@@ -400,3 +415,222 @@ func (b *justiceKitPacketV0) decode(r io.Reader) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// justiceKitPacketV1 is the Blob of Justice for taproot channels.
|
||||
type justiceKitPacketV1 struct {
|
||||
// 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
|
||||
// correlated across sessions and/or towers.
|
||||
//
|
||||
// NOTE: This is chosen to be the length of a maximally sized witness
|
||||
// program.
|
||||
sweepAddress []byte
|
||||
|
||||
// revocationPubKey is the x-only pubkey that guards the revocation
|
||||
// clause of the remote party's to-local output.
|
||||
revocationPubKey schnorrPubKey
|
||||
|
||||
// localDelayPubKey is the x-only pubkey in the to-local script of
|
||||
// the remote party, which guards the path where the remote party
|
||||
// claims their commitment output.
|
||||
localDelayPubKey schnorrPubKey
|
||||
|
||||
// delayScriptHash is the hash of the to_local delay script that is used
|
||||
// in the TapTree.
|
||||
delayScriptHash [chainhash.HashSize]byte
|
||||
|
||||
// commitToLocalSig is a signature under revocationPubKey using
|
||||
// SIGHASH_DEFAULT.
|
||||
commitToLocalSig lnwire.Sig
|
||||
|
||||
// commitToRemotePubKey is the public key in the to-remote output of the
|
||||
// revoked commitment transaction. This uses a 33-byte compressed pubkey
|
||||
// encoding unlike the other public keys because it will not always be
|
||||
// present and so this gives us an easy way to check if it is present or
|
||||
// not.
|
||||
//
|
||||
// NOTE: This value is only used if it contains a valid compressed
|
||||
// public key.
|
||||
commitToRemotePubKey pubKey
|
||||
|
||||
// commitToRemoteSig is a signature under commitToRemotePubKey using
|
||||
// SIGHASH_DEFAULT.
|
||||
//
|
||||
// NOTE: This value is only used if commitToRemotePubKey contains a
|
||||
// valid compressed public key.
|
||||
commitToRemoteSig lnwire.Sig
|
||||
}
|
||||
|
||||
// encode encodes the justiceKitPacketV1 to the provided io.Writer. The encoding
|
||||
// supports sweeping of the commit to-local output, and optionally the commit
|
||||
// to-remote output. The encoding produces a constant-size plaintext size of
|
||||
// 300 bytes.
|
||||
//
|
||||
// blob version 1 plaintext encoding:
|
||||
//
|
||||
// sweep address length: 1 byte
|
||||
// padded sweep address: 42 bytes
|
||||
// revocation pubkey: 32 bytes
|
||||
// local delay pubkey: 32 bytes
|
||||
// commit to-local revocation sig: 64 bytes
|
||||
// hash of to-local delay script: 32 bytes
|
||||
// commit to-remote pubkey: 33 bytes, maybe blank
|
||||
// commit to-remote sig: 64 bytes, maybe blank
|
||||
func (t *justiceKitPacketV1) encode(w io.Writer) error {
|
||||
// Assert the sweep address length is sane.
|
||||
if len(t.sweepAddress) > MaxSweepAddrSize {
|
||||
return ErrSweepAddressToLong
|
||||
}
|
||||
|
||||
// Write the actual length of the sweep address as a single byte.
|
||||
err := binary.Write(w, byteOrder, uint8(len(t.sweepAddress)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Pad the sweep address to our maximum length of 42 bytes.
|
||||
var sweepAddressBuf [MaxSweepAddrSize]byte
|
||||
copy(sweepAddressBuf[:], t.sweepAddress)
|
||||
|
||||
// Write padded 42-byte sweep address.
|
||||
_, err = w.Write(sweepAddressBuf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write 32-byte revocation public key.
|
||||
_, err = w.Write(t.revocationPubKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write 32-byte local delay public key.
|
||||
_, err = w.Write(t.localDelayPubKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write 64-byte revocation signature for commit to-local output.
|
||||
_, err = w.Write(t.commitToLocalSig.RawBytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write 32-byte hash of the to-local delay script.
|
||||
_, err = w.Write(t.delayScriptHash[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write 33-byte commit to-remote public key, which may be blank.
|
||||
_, err = w.Write(t.commitToRemotePubKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write 64-byte commit to-remote signature, which may be blank.
|
||||
_, err = w.Write(t.commitToRemoteSig.RawBytes())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// decode reconstructs a justiceKitPacketV1 from the io.Reader, using version 1
|
||||
// encoding scheme. This will parse a constant size input stream of 300 bytes to
|
||||
// recover information for the commit to-local output, and possibly the commit
|
||||
// to-remote output.
|
||||
//
|
||||
// blob version 1 plaintext encoding:
|
||||
//
|
||||
// sweep address length: 1 byte
|
||||
// padded sweep address: 42 bytes
|
||||
// revocation pubkey: 32 bytes
|
||||
// local delay pubkey: 32 bytes
|
||||
// commit to-local revocation sig: 64 bytes
|
||||
// hash of to-local delay script: 32 bytes
|
||||
// commit to-remote pubkey: 33 bytes, maybe blank
|
||||
// commit to-remote sig: 64 bytes, maybe blank
|
||||
func (t *justiceKitPacketV1) decode(r io.Reader) error {
|
||||
// Read the sweep address length as a single byte.
|
||||
var sweepAddrLen uint8
|
||||
err := binary.Read(r, byteOrder, &sweepAddrLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Assert the sweep address length is sane.
|
||||
if sweepAddrLen > MaxSweepAddrSize {
|
||||
return ErrSweepAddressToLong
|
||||
}
|
||||
// Read padded 42-byte sweep address.
|
||||
var sweepAddressBuf [MaxSweepAddrSize]byte
|
||||
_, err = io.ReadFull(r, sweepAddressBuf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse sweep address from padded buffer.
|
||||
t.sweepAddress = make([]byte, sweepAddrLen)
|
||||
copy(t.sweepAddress, sweepAddressBuf[:])
|
||||
|
||||
// Read 32-byte revocation public key.
|
||||
_, err = io.ReadFull(r, t.revocationPubKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read 32-byte local delay public key.
|
||||
_, err = io.ReadFull(r, t.localDelayPubKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read 64-byte revocation signature for commit to-local output.
|
||||
var localSig [64]byte
|
||||
_, err = io.ReadFull(r, localSig[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read 32-byte to-local delay script hash.
|
||||
_, err = io.ReadFull(r, t.delayScriptHash[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.commitToLocalSig, err = lnwire.NewSigFromSchnorrRawSignature(
|
||||
localSig[:],
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
commitToRemotePubkey pubKey
|
||||
commitToRemoteSig [64]byte
|
||||
)
|
||||
// Read 33-byte commit to-remote public key, which may be discarded.
|
||||
_, err = io.ReadFull(r, commitToRemotePubkey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Read 64-byte commit to-remote signature, which may be discarded.
|
||||
_, err = io.ReadFull(r, commitToRemoteSig[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only populate the commit to-remote fields in the decoded blob if a
|
||||
// valid compressed public key was read from the reader.
|
||||
if !btcec.IsCompressedPubKey(commitToRemotePubkey[:]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
t.commitToRemotePubKey = commitToRemotePubkey
|
||||
t.commitToRemoteSig, err = lnwire.NewSigFromSchnorrRawSignature(
|
||||
commitToRemoteSig[:],
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user