Showing with 3,459 additions and 1,233 deletions.
  1. +4 −4 breacharbiter_test.go
  2. +46 −3 channeldb/channel.go
  3. +28 −0 channeldb/codec.go
  4. +36 −2 channeldb/invoices.go
  5. +4 −0 channeldb/payments.go
  6. +1 −1 contractcourt/chain_watcher_test.go
  7. +1 −0 go.mod
  8. +2 −0 go.sum
  9. +33 −8 htlcswitch/link.go
  10. +3 −2 htlcswitch/link_isolated_test.go
  11. +94 −0 htlcswitch/link_ptlc_test.go
  12. +47 −43 htlcswitch/link_test.go
  13. +21 −0 htlcswitch/mock.go
  14. +5 −5 htlcswitch/switch_test.go
  15. +45 −12 htlcswitch/test_utils.go
  16. +509 −0 input/script_utils_ptlc.go
  17. +12 −0 input/signdescriptor.go
  18. +21 −0 input/test_utils.go
  19. +34 −1 lnrpc/invoicesrpc/addinvoice.go
  20. +4 −0 lnrpc/invoicesrpc/invoices.swagger.json
  21. +1 −0 lnrpc/routerrpc/router_backend.go
  22. +4 −1 lnrpc/routerrpc/router_server.go
  23. +878 −868 lnrpc/rpc.pb.go
  24. +2 −0 lnrpc/rpc.proto
  25. +4 −0 lnrpc/rpc.swagger.json
  26. +156 −0 lntest/itest/lnd_ptlc_test.go
  27. +4 −0 lntest/itest/lnd_test.go
  28. +640 −104 lnwallet/channel.go
  29. +139 −139 lnwallet/channel_test.go
  30. +150 −4 lnwallet/commitment.go
  31. +21 −0 lnwallet/dcrwallet/signer.go
  32. +177 −0 lnwallet/ptlc_test.go
  33. +21 −0 lnwallet/remotedcrwallet/signer.go
  34. +25 −4 lnwallet/sigpool.go
  35. +21 −7 lnwallet/test_utils.go
  36. +36 −0 lnwallet/transactions.go
  37. +4 −4 lnwallet/transactions_test.go
  38. +14 −0 lnwallet/wallet.go
  39. +21 −0 lnwire/commit_sig.go
  40. +43 −0 lnwire/lnwire.go
  41. +14 −0 lnwire/lnwire_test.go
  42. +2 −0 lnwire/message.go
  43. +18 −0 lnwire/signature.go
  44. +34 −1 lnwire/update_add_htlc.go
  45. +7 −2 peer/brontide.go
  46. +14 −9 routing/payment_lifecycle.go
  47. +11 −2 routing/router.go
  48. +6 −6 routing/router_test.go
  49. +5 −1 rpcserver.go
  50. +8 −0 zpay32/decode.go
  51. +19 −0 zpay32/encode.go
  52. +10 −0 zpay32/invoice.go
8 changes: 4 additions & 4 deletions breacharbiter_test.go
Expand Up @@ -2009,19 +2009,19 @@ func createHTLC(data int, amount lnwire.MilliAtom) (*lnwire.UpdateAddHTLC, [32]b
// pending updates.
// TODO(conner) remove code duplication
func forceStateTransition(chanA, chanB *lnwallet.LightningChannel) error {
aliceSig, aliceHtlcSigs, _, err := chanA.SignNextCommitment()
aliceSig, aliceHtlcSigs, alicePtlcSigs, _, err := chanA.SignNextCommitment()
if err != nil {
return fmt.Errorf("error on chanA.SignNextCommitment: %v", err)
}
if err = chanB.ReceiveNewCommitment(aliceSig, aliceHtlcSigs); err != nil {
if err = chanB.ReceiveNewCommitment(aliceSig, aliceHtlcSigs, alicePtlcSigs); err != nil {
return fmt.Errorf("error on chanB.ReceiveNewCommitment: %v", err)
}

bobRevocation, _, err := chanB.RevokeCurrentCommitment()
if err != nil {
return fmt.Errorf("error on chanB.RevokeCurrentCommitment: %v", err)
}
bobSig, bobHtlcSigs, _, err := chanB.SignNextCommitment()
bobSig, bobHtlcSigs, bobPtlcSigs, _, err := chanB.SignNextCommitment()
if err != nil {
return fmt.Errorf("error on chanB.SignNextCommitment: %v", err)
}
Expand All @@ -2030,7 +2030,7 @@ func forceStateTransition(chanA, chanB *lnwallet.LightningChannel) error {
if err != nil {
return err
}
if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil {
if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs, bobPtlcSigs); err != nil {
return fmt.Errorf("error on chanA.ReceiveNewCommitment: %v", err)
}

Expand Down
49 changes: 46 additions & 3 deletions channeldb/channel.go
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/decred/dcrlnd/keychain"
"github.com/decred/dcrlnd/lnwire"
"github.com/decred/dcrlnd/shachain"
"github.com/matheusd/dcr_adaptor_sigs"
)

const (
Expand Down Expand Up @@ -340,6 +341,12 @@ type ChannelConfig struct {
// from the per-commitment point) is used within the "to self" clause
// within any HTLC output scripts.
HtlcBasePoint keychain.KeyDescriptor

// RandomKey is the random key used to generate random nonces for PTLC
// adaptor signatures.
//
// This will be filled only for the local chan config.
RandomKey secp256k1.PrivateKey
}

// ChannelCommitment is a snapshot of the commitment state at a particular
Expand Down Expand Up @@ -1581,6 +1588,11 @@ type HTLC struct {
// RHash is the payment hash of the HTLC.
RHash [32]byte

PaymentPoint *secp256k1.PublicKey

AdaptorSigSuccess *dcr_adaptor_sigs.AdaptorSignature
AdaptorSigSuccessNoDelay *dcr_adaptor_sigs.AdaptorSignature

// Amt is the amount of milli-atoms this HTLC escrows.
Amt lnwire.MilliAtom

Expand Down Expand Up @@ -1624,10 +1636,22 @@ func SerializeHtlcs(b io.Writer, htlcs ...HTLC) error {
}

for _, htlc := range htlcs {
var ppBytes, asBytes, assBytes []byte
if htlc.PaymentPoint != nil {
ppBytes = htlc.PaymentPoint.SerializeCompressed()
}
if htlc.AdaptorSigSuccess != nil {
asBytes = htlc.AdaptorSigSuccess.Serialize()
}
if htlc.AdaptorSigSuccessNoDelay != nil {
assBytes = htlc.AdaptorSigSuccessNoDelay.Serialize()
}

if err := WriteElements(b,
htlc.Signature, htlc.RHash, htlc.Amt, htlc.RefundTimeout,
htlc.OutputIndex, htlc.Incoming, htlc.OnionBlob,
htlc.HtlcIndex, htlc.LogIndex,
htlc.HtlcIndex, htlc.LogIndex, ppBytes, asBytes,
assBytes,
); err != nil {
return err
}
Expand Down Expand Up @@ -1655,14 +1679,33 @@ func DeserializeHtlcs(r io.Reader) ([]HTLC, error) {

htlcs = make([]HTLC, numHtlcs)
for i := uint16(0); i < numHtlcs; i++ {
var ppBytes, asBytes, assBytes []byte
if err := ReadElements(r,
&htlcs[i].Signature, &htlcs[i].RHash, &htlcs[i].Amt,
&htlcs[i].RefundTimeout, &htlcs[i].OutputIndex,
&htlcs[i].Incoming, &htlcs[i].OnionBlob,
&htlcs[i].HtlcIndex, &htlcs[i].LogIndex,
&ppBytes, &asBytes, &assBytes,
); err != nil {
return htlcs, err
}

var err error
if len(ppBytes) == 33 {
if htlcs[i].PaymentPoint, err = secp256k1.ParsePubKey(ppBytes); err != nil {
return htlcs, err
}
}
if len(asBytes) == dcr_adaptor_sigs.AdaptorSignatureSerializeLen {
if htlcs[i].AdaptorSigSuccess, err = dcr_adaptor_sigs.ParseAdaptorSignature(asBytes); err != nil {
return htlcs, err
}
}
if len(assBytes) == dcr_adaptor_sigs.AdaptorSignatureSerializeLen {
if htlcs[i].AdaptorSigSuccessNoDelay, err = dcr_adaptor_sigs.ParseAdaptorSignature(assBytes); err != nil {
return htlcs, err
}
}
}

return htlcs, nil
Expand Down Expand Up @@ -3079,7 +3122,7 @@ func writeChanConfig(b io.Writer, c *ChannelConfig) error {
c.DustLimit, c.MaxPendingAmount, c.ChanReserve, c.MinHTLC,
c.MaxAcceptedHtlcs, c.CsvDelay, c.MultiSigKey,
c.RevocationBasePoint, c.PaymentBasePoint, c.DelayBasePoint,
c.HtlcBasePoint,
c.HtlcBasePoint, &c.RandomKey,
)
}

Expand Down Expand Up @@ -3258,7 +3301,7 @@ func readChanConfig(b io.Reader, c *ChannelConfig) error {
&c.MinHTLC, &c.MaxAcceptedHtlcs, &c.CsvDelay,
&c.MultiSigKey, &c.RevocationBasePoint,
&c.PaymentBasePoint, &c.DelayBasePoint,
&c.HtlcBasePoint,
&c.HtlcBasePoint, &c.RandomKey,
)
}

Expand Down
28 changes: 28 additions & 0 deletions channeldb/codec.go
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/decred/dcrlnd/keychain"
"github.com/decred/dcrlnd/lnwire"
"github.com/decred/dcrlnd/shachain"
"github.com/matheusd/dcr_adaptor_sigs"
)

// writeOutpoint writes an outpoint to the passed writer using the minimal
Expand Down Expand Up @@ -164,6 +165,12 @@ func WriteElement(w io.Writer, element interface{}) error {
return err
}

case *dcr_adaptor_sigs.AdaptorSignature:
b := e.Serialize()
if _, err := w.Write(b); err != nil {
return err
}

case shachain.Producer:
return e.Encode(w)

Expand Down Expand Up @@ -344,6 +351,15 @@ func ReadElement(r io.Reader, element interface{}) error {
priv := secp256k1.PrivKeyFromBytes(b[:])
*e = priv

case *secp256k1.PrivateKey:
var b [secp256k1.PrivKeyBytesLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil {
return err
}

priv := secp256k1.PrivKeyFromBytes(b[:])
*e = *priv

case **secp256k1.PublicKey:
var b [secp256k1.PubKeyBytesLenCompressed]byte
if _, err := io.ReadFull(r, b[:]); err != nil {
Expand All @@ -356,6 +372,18 @@ func ReadElement(r io.Reader, element interface{}) error {
}
*e = pubKey

case **dcr_adaptor_sigs.AdaptorSignature:
var b [dcr_adaptor_sigs.AdaptorSignatureSerializeLen]byte
if _, err := io.ReadFull(r, b[:]); err != nil {
return err
}

asig, err := dcr_adaptor_sigs.ParseAdaptorSignature(b[:])
if err != nil {
return err
}
*e = asig

case *shachain.Producer:
var root [32]byte
if _, err := io.ReadFull(r, root[:]); err != nil {
Expand Down
38 changes: 36 additions & 2 deletions channeldb/invoices.go
Expand Up @@ -2,12 +2,14 @@ package channeldb

import (
"bytes"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"time"

"github.com/decred/dcrd/dcrec/secp256k1/v3"
"github.com/decred/dcrlnd/channeldb/kvdb"
"github.com/decred/dcrlnd/htlcswitch/hop"
"github.com/decred/dcrlnd/lntypes"
Expand Down Expand Up @@ -157,6 +159,8 @@ const (
invStateType tlv.Type = 12
amtPaidType tlv.Type = 13
hodlInvoiceType tlv.Type = 14

isPtlcInvoiceType tlv.Type = 99
)

// InvoiceRef is a composite identifier for invoices. Invoices can be referenced
Expand Down Expand Up @@ -271,6 +275,10 @@ type ContractTerm struct {
// extended. Set to nil if the preimage isn't known yet.
PaymentPreimage *lntypes.Preimage

// IsPTLC indicates whether the preimage corresponds to a secret scalar
// instead of a hash preimage.
IsPTLC bool

// Value is the expected amount of milli-atoms to be paid to an HTLC
// which can be satisfied by the above preimage.
Value lnwire.MilliAtom
Expand Down Expand Up @@ -1150,6 +1158,11 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
hodlInvoice = 1
}

var isPtlc uint8
if i.Terms.IsPTLC {
isPtlc = 1
}

tlvStream, err := tlv.NewStream(
// Memo and payreq.
tlv.MakePrimitiveRecord(memoType, &i.Memo),
Expand All @@ -1174,6 +1187,7 @@ func serializeInvoice(w io.Writer, i *Invoice) error {
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),

tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
tlv.MakePrimitiveRecord(isPtlcInvoiceType, &isPtlc),
)
if err != nil {
return err
Expand Down Expand Up @@ -1276,6 +1290,7 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
amtPaid uint64
state uint8
hodlInvoice uint8
isPtlc uint8

creationDateBytes []byte
settleDateBytes []byte
Expand Down Expand Up @@ -1307,6 +1322,7 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
tlv.MakePrimitiveRecord(amtPaidType, &amtPaid),

tlv.MakePrimitiveRecord(hodlInvoiceType, &hodlInvoice),
tlv.MakePrimitiveRecord(isPtlcInvoiceType, &isPtlc),
)
if err != nil {
return i, err
Expand Down Expand Up @@ -1338,6 +1354,10 @@ func deserializeInvoice(r io.Reader) (Invoice, error) {
i.HodlInvoice = true
}

if isPtlc != 0 {
i.Terms.IsPTLC = true
}

err = i.CreationDate.UnmarshalBinary(creationDateBytes)
if err != nil {
return i, err
Expand Down Expand Up @@ -1649,14 +1669,28 @@ func updateInvoiceState(invoice *Invoice, hash lntypes.Hash,
case ContractOpen:
if update.NewState == ContractSettled {
// Validate preimage.
isPTLC := invoice.Terms.IsPTLC
log.Debugf("JJJJJJJJJJJJJ attempting to settle contract, preimage %v %v %v", isPTLC, update.Preimage, invoice.Terms.PaymentPreimage)
switch {
case update.Preimage != nil:
case isPTLC && update.Preimage != nil:
privKey := secp256k1.PrivKeyFromBytes(update.Preimage[:])
pubKey := privKey.PubKey()
gotHash := sha256.Sum256(pubKey.SerializeCompressed())
if gotHash != hash {
// TODO: PTLC-specific error.
return ErrInvoicePreimageMismatch
}

case isPTLC && invoice.Terms.PaymentPreimage == nil:
return errors.New("unknown preimage")

case !isPTLC && update.Preimage != nil:
if update.Preimage.Hash() != hash {
return ErrInvoicePreimageMismatch
}
invoice.Terms.PaymentPreimage = update.Preimage

case invoice.Terms.PaymentPreimage == nil:
case !isPTLC && invoice.Terms.PaymentPreimage == nil:
return errors.New("unknown preimage")
}
}
Expand Down
4 changes: 4 additions & 0 deletions channeldb/payments.go
Expand Up @@ -9,6 +9,7 @@ import (
"sort"
"time"

"github.com/decred/dcrd/dcrec/secp256k1/v3"
"github.com/decred/dcrd/wire"
"github.com/decred/dcrlnd/channeldb/kvdb"
"github.com/decred/dcrlnd/lntypes"
Expand Down Expand Up @@ -219,6 +220,9 @@ type PaymentCreationInfo struct {
// PaymentHash is the hash this payment is paying to.
PaymentHash lntypes.Hash

// TODO: needs to be serialized/deserialized.
PaymentPoint *secp256k1.PublicKey

// Value is the amount we are paying.
Value lnwire.MilliAtom

Expand Down
2 changes: 1 addition & 1 deletion contractcourt/chain_watcher_test.go
Expand Up @@ -212,7 +212,7 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) {

// With the HTLC added, we'll now manually initiate a state transition
// from Alice to Bob.
_, _, _, err = aliceChannel.SignNextCommitment()
_, _, _, _, err = aliceChannel.SignNextCommitment()
if err != nil {
t.Fatal(err)
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -50,6 +50,7 @@ require (
github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d // indirect
github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect
github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec
github.com/matheusd/dcr_adaptor_sigs v0.0.0-20210202215049-f7396951ad6a
github.com/matheusd/etcd v0.5.0-alpha.5.0.20201012144914-e63dcc3d7528
github.com/matheusd/google-protobuf-protos v0.0.0-20200707194502-ef6ec5c2266f
github.com/matheusd/protobuf-hex-display v1.3.3-0.20201012153224-75fb8d4840f1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -311,6 +311,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matheusd/dcr_adaptor_sigs v0.0.0-20210202215049-f7396951ad6a h1:Xvq54Uje6AuKT3WyVT4FfeNR3ai7ilw/nO6jv2fzGV4=
github.com/matheusd/dcr_adaptor_sigs v0.0.0-20210202215049-f7396951ad6a/go.mod h1:05SCRl+QSF7dJ/L6dBHiJ6UQ4otSKjnS/nMVbWSkNXU=
github.com/matheusd/etcd v0.5.0-alpha.5.0.20201012144914-e63dcc3d7528 h1:E8Ten1XgpETC4YSO2FDB6/ZvnDiEV2MW0/EvampmIdk=
github.com/matheusd/etcd v0.5.0-alpha.5.0.20201012144914-e63dcc3d7528/go.mod h1:SoOfCmPwMftuPacDfthltuksjynBOjkpAL0ERJQT0mQ=
github.com/matheusd/google-protobuf-protos v0.0.0-20200707194502-ef6ec5c2266f h1:C4oKaubQkUb0IdljKC7TQ2ERNrRs0S9vWm75LwyameY=
Expand Down