input+lnwallet+contractcourt: define SignDetails for HTLC resolutions

This commit is contained in:
Johan T. Halseth
2020-12-09 12:24:01 +01:00
parent eb8d22e194
commit 1e68cdc8cf
7 changed files with 334 additions and 2 deletions

View File

@@ -6,7 +6,9 @@ import (
"fmt"
"io"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/kvdb"
@@ -275,6 +277,13 @@ var (
// the full set of resolutions for a channel.
resolutionsKey = []byte("resolutions")
// resolutionsSignDetailsKey is the key under the logScope where we
// will store input.SignDetails for each HTLC resolution. If this is
// not found under the logScope, it means it was written before
// SignDetails was introduced, and should be set nil for each HTLC
// resolution.
resolutionsSignDetailsKey = []byte("resolutions-sign-details")
// anchorResolutionKey is the key under the logScope that we'll use to
// store the anchor resolution, if any.
anchorResolutionKey = []byte("anchor-resolution")
@@ -656,6 +665,10 @@ func (b *boltArbitratorLog) LogContractResolutions(c *ContractResolutions) error
}
}
// As we write the HTLC resolutions, we'll serialize the sign
// details for each, to store under a new key.
var signDetailsBuf bytes.Buffer
// With the output for the commitment transaction written, we
// can now write out the resolutions for the incoming and
// outgoing HTLC's.
@@ -668,6 +681,11 @@ func (b *boltArbitratorLog) LogContractResolutions(c *ContractResolutions) error
if err != nil {
return err
}
err = encodeSignDetails(&signDetailsBuf, htlc.SignDetails)
if err != nil {
return err
}
}
numOutgoing := uint32(len(c.HtlcResolutions.OutgoingHTLCs))
if err := binary.Write(&b, endian, numOutgoing); err != nil {
@@ -678,13 +696,28 @@ func (b *boltArbitratorLog) LogContractResolutions(c *ContractResolutions) error
if err != nil {
return err
}
err = encodeSignDetails(&signDetailsBuf, htlc.SignDetails)
if err != nil {
return err
}
}
// Put the resolutions under the resolutionsKey.
err = scopeBucket.Put(resolutionsKey, b.Bytes())
if err != nil {
return err
}
// We'll put the serialized sign details under its own key to
// stay backwards compatible.
err = scopeBucket.Put(
resolutionsSignDetailsKey, signDetailsBuf.Bytes(),
)
if err != nil {
return err
}
// Write out the anchor resolution if present.
if c.AnchorResolution != nil {
var b bytes.Buffer
@@ -779,6 +812,33 @@ func (b *boltArbitratorLog) FetchContractResolutions() (*ContractResolutions, er
}
}
// Now we attempt to get the sign details for our HTLC
// resolutions. If not present the channel is of a type that
// doesn't need them. If present there will be SignDetails
// encoded for each HTLC resolution.
signDetailsBytes := scopeBucket.Get(resolutionsSignDetailsKey)
if signDetailsBytes != nil {
r := bytes.NewReader(signDetailsBytes)
// They will be encoded in the same order as the
// resolutions: firs incoming HTLCs, then outgoing.
for i := uint32(0); i < numIncoming; i++ {
htlc := &c.HtlcResolutions.IncomingHTLCs[i]
htlc.SignDetails, err = decodeSignDetails(r)
if err != nil {
return err
}
}
for i := uint32(0); i < numOutgoing; i++ {
htlc := &c.HtlcResolutions.OutgoingHTLCs[i]
htlc.SignDetails, err = decodeSignDetails(r)
if err != nil {
return err
}
}
}
anchorResBytes := scopeBucket.Get(anchorResolutionKey)
if anchorResBytes != nil {
c.AnchorResolution = &lnwallet.AnchorResolution{}
@@ -941,6 +1001,11 @@ func (b *boltArbitratorLog) WipeHistory() error {
return err
}
err = scopeBucket.Delete(resolutionsSignDetailsKey)
if err != nil {
return err
}
// We'll delete any chain actions that are still stored by
// removing the enclosing bucket.
err = scopeBucket.DeleteNestedBucket(actionsBucketKey)
@@ -980,6 +1045,79 @@ func (b *boltArbitratorLog) checkpointContract(c ContractResolver,
}, func() {})
}
// encodeSignDetails encodes the gived SignDetails struct to the writer.
// SignDetails is allowed to be nil, in which we will encode that it is not
// present.
func encodeSignDetails(w io.Writer, s *input.SignDetails) error {
// If we don't have sign details, write false and return.
if s == nil {
return binary.Write(w, endian, false)
}
// Otherwise write true, and the contents of the SignDetails.
if err := binary.Write(w, endian, true); err != nil {
return err
}
err := input.WriteSignDescriptor(w, &s.SignDesc)
if err != nil {
return err
}
err = binary.Write(w, endian, uint32(s.SigHashType))
if err != nil {
return err
}
// Write the DER-encoded signature.
b := s.PeerSig.Serialize()
if err := wire.WriteVarBytes(w, 0, b); err != nil {
return err
}
return nil
}
// decodeSignDetails extracts a single SignDetails from the reader. It is
// allowed to return nil in case the SignDetails were empty.
func decodeSignDetails(r io.Reader) (*input.SignDetails, error) {
var present bool
if err := binary.Read(r, endian, &present); err != nil {
return nil, err
}
// Simply return nil if the next SignDetails was not present.
if !present {
return nil, nil
}
// Otherwise decode the elements of the SignDetails.
s := input.SignDetails{}
err := input.ReadSignDescriptor(r, &s.SignDesc)
if err != nil {
return nil, err
}
var sigHash uint32
err = binary.Read(r, endian, &sigHash)
if err != nil {
return nil, err
}
s.SigHashType = txscript.SigHashType(sigHash)
// Read DER-encoded signature.
rawSig, err := wire.ReadVarBytes(r, 0, 200, "signature")
if err != nil {
return nil, err
}
sig, err := btcec.ParseDERSignature(rawSig, btcec.S256())
if err != nil {
return nil, err
}
s.PeerSig = sig
return &s, nil
}
func encodeIncomingResolution(w io.Writer, i *lnwallet.IncomingHtlcResolution) error {
if _, err := w.Write(i.Preimage[:]); err != nil {
return err