Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

psbt script spend [3/3]: allow signing for asset script path spends through PSBTs #245

Merged
merged 11 commits into from
Feb 22, 2023
Merged
141 changes: 141 additions & 0 deletions commitment/encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package commitment

import (
"bytes"
"io"

"github.com/lightninglabs/taro/mssmt"
"github.com/lightningnetwork/lnd/tlv"
)

func ProofEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(*Proof); ok {
return t.Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "*Proof")
}

func ProofDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
if typ, ok := val.(*Proof); ok {
var proofBytes []byte
if err := tlv.DVarBytes(r, &proofBytes, buf, l); err != nil {
return err
}
var proof Proof
if err := proof.Decode(bytes.NewReader(proofBytes)); err != nil {
return err
}
*typ = proof
return nil
}
return tlv.NewTypeForEncodingErr(val, "*Proof")
}

func AssetProofEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(**AssetProof); ok {
records := []tlv.Record{
AssetProofVersionRecord(&(*t).Version),
AssetProofAssetIDRecord(&(*t).AssetID),
AssetProofRecord(&(*t).Proof),
}
stream, err := tlv.NewStream(records...)
if err != nil {
return err
}
return stream.Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "*commitment.AssetProof")
}

func AssetProofDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
if typ, ok := val.(**AssetProof); ok {
var streamBytes []byte
if err := tlv.DVarBytes(r, &streamBytes, buf, l); err != nil {
return err
}
var proof AssetProof
records := []tlv.Record{
AssetProofVersionRecord(&proof.Version),
AssetProofAssetIDRecord(&proof.AssetID),
AssetProofRecord(&proof.Proof),
}
stream, err := tlv.NewStream(records...)
if err != nil {
return err
}
if err := stream.Decode(bytes.NewReader(streamBytes)); err != nil {
return err
}
*typ = &proof
return nil
}
return tlv.NewTypeForEncodingErr(val, "*commitment.AssetProof")
}

func TaroProofEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(*TaroProof); ok {
records := []tlv.Record{
TaroProofVersionRecord(&(*t).Version),
TaroProofRecord(&(*t).Proof),
}
stream, err := tlv.NewStream(records...)
if err != nil {
return err
}
return stream.Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "commitment.TaroProof")
}

func TaroProofDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
if typ, ok := val.(*TaroProof); ok {
var streamBytes []byte
if err := tlv.DVarBytes(r, &streamBytes, buf, l); err != nil {
return err
}
var proof TaroProof
records := []tlv.Record{
TaroProofVersionRecord(&proof.Version),
TaroProofRecord(&proof.Proof),
}
stream, err := tlv.NewStream(records...)
if err != nil {
return err
}
if err := stream.Decode(bytes.NewReader(streamBytes)); err != nil {
return err
}
*typ = proof
return nil
}
return tlv.NewTypeForEncodingErr(val, "commitment.TaroProof")
}

func TreeProofEncoder(w io.Writer, val any, buf *[8]byte) error {
if t, ok := val.(*mssmt.Proof); ok {
return t.Compress().Encode(w)
}
return tlv.NewTypeForEncodingErr(val, "mssmt.Proof")
}

func TreeProofDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
if typ, ok := val.(*mssmt.Proof); ok {
var proofBytes []byte
if err := tlv.DVarBytes(r, &proofBytes, buf, l); err != nil {
return err
}
var proof mssmt.CompressedProof
if err := proof.Decode(bytes.NewReader(proofBytes)); err != nil {
return err
}

fullProof, err := proof.Decompress()
if err != nil {
return err
}

*typ = *fullProof
return nil
}
return tlv.NewTypeForEncodingErr(val, "mssmt.Proof")
}
38 changes: 38 additions & 0 deletions commitment/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package commitment

import (
"errors"
"io"

"github.com/lightninglabs/taro/asset"
"github.com/lightninglabs/taro/mssmt"
"github.com/lightningnetwork/lnd/tlv"
)

var (
Expand Down Expand Up @@ -55,6 +57,42 @@ type Proof struct {
TaroProof TaroProof
}

// EncodeRecords returns the encoding records for the Proof.
func (p Proof) EncodeRecords() []tlv.Record {
records := make([]tlv.Record, 0, 3)
if p.AssetProof != nil {
records = append(records, ProofAssetProofRecord(&p.AssetProof))
}
records = append(records, ProofTaroProofRecord(&p.TaroProof))
return records
}

// DecodeRecords returns the decoding records for the CommitmentProof.
func (p *Proof) DecodeRecords() []tlv.Record {
return []tlv.Record{
ProofAssetProofRecord(&p.AssetProof),
ProofTaroProofRecord(&p.TaroProof),
}
}

// Encode attempts to encode the CommitmentProof into the passed io.Writer.
func (p Proof) Encode(w io.Writer) error {
stream, err := tlv.NewStream(p.EncodeRecords()...)
if err != nil {
return err
}
return stream.Encode(w)
}

// Decode attempts to decode the CommitmentProof from the passed io.Reader.
func (p *Proof) Decode(r io.Reader) error {
stream, err := tlv.NewStream(p.DecodeRecords()...)
if err != nil {
return err
}
return stream.Decode(r)
}

// DeriveByAssetInclusion derives the Taro commitment containing the provided
// asset. This consists of proving that an asset exists within the inner MS-SMT
// with the AssetProof, also known as the AssetCommitment. With the
Expand Down
97 changes: 97 additions & 0 deletions commitment/records.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package commitment

import (
"bytes"

"github.com/lightninglabs/taro/asset"
"github.com/lightninglabs/taro/mssmt"
"github.com/lightningnetwork/lnd/tlv"
)

const (
AssetProofVersionType tlv.Type = 0
AssetProofAssetIDType tlv.Type = 1
AssetProofType tlv.Type = 2

TaroProofVersionType tlv.Type = 0
TaroProofType tlv.Type = 1

ProofAssetProofType tlv.Type = 0
ProofTaroProofType tlv.Type = 1
)

func ProofAssetProofRecord(proof **AssetProof) tlv.Record {
sizeFunc := func() uint64 {
var buf bytes.Buffer
err := AssetProofEncoder(&buf, proof, &[8]byte{})
if err != nil {
panic(err)
}
return uint64(len(buf.Bytes()))
}
return tlv.MakeDynamicRecord(
ProofAssetProofType, proof, sizeFunc,
AssetProofEncoder, AssetProofDecoder,
)
}

func ProofTaroProofRecord(proof *TaroProof) tlv.Record {
sizeFunc := func() uint64 {
var buf bytes.Buffer
err := TaroProofEncoder(&buf, proof, &[8]byte{})
if err != nil {
panic(err)
}
return uint64(len(buf.Bytes()))
}
return tlv.MakeDynamicRecord(
ProofTaroProofType, proof, sizeFunc, TaroProofEncoder,
TaroProofDecoder,
)
}

func AssetProofVersionRecord(version *asset.Version) tlv.Record {
return tlv.MakeStaticRecord(
AssetProofVersionType, version, 1, asset.VersionEncoder,
asset.VersionDecoder,
)
}

func AssetProofAssetIDRecord(assetID *[32]byte) tlv.Record {
return tlv.MakePrimitiveRecord(AssetProofAssetIDType, assetID)
}

func AssetProofRecord(proof *mssmt.Proof) tlv.Record {
sizeFunc := func() uint64 {
var buf bytes.Buffer
if err := proof.Compress().Encode(&buf); err != nil {
panic(err)
}
return uint64(len(buf.Bytes()))
}
return tlv.MakeDynamicRecord(
AssetProofType, proof, sizeFunc, TreeProofEncoder,
TreeProofDecoder,
)
}

func TaroProofVersionRecord(version *asset.Version) tlv.Record {
return tlv.MakeStaticRecord(
TaroProofVersionType, version, 1, asset.VersionEncoder,
asset.VersionDecoder,
)
}

func TaroProofRecord(proof *mssmt.Proof) tlv.Record {
sizeFunc := func() uint64 {
var buf bytes.Buffer
if err := proof.Compress().Encode(&buf); err != nil {
panic(err)
}
return uint64(len(buf.Bytes()))
}
return tlv.MakeDynamicRecord(
TaroProofType, proof, sizeFunc, TreeProofEncoder,
TreeProofDecoder,
)
}
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ type Config struct {

ProofArchive proof.Archiver

AssetWallet tarofreighter.Wallet

ChainPorter tarofreighter.Porter

// LogWriter is the root logger that all of the daemon's subloggers are
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/btcsuite/btcd v0.23.4
github.com/btcsuite/btcd/btcec/v2 v2.2.2
github.com/btcsuite/btcd/btcutil v1.1.3
github.com/btcsuite/btcd/btcutil/psbt v1.1.5
github.com/btcsuite/btcd/btcutil/psbt v1.1.7
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcwallet v0.16.6-0.20221203002441-6c7480c8a46b
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ github.com/btcsuite/btcd/btcutil v1.1.1/go.mod h1:nbKlBMNm9FGsdvKvu0essceubPiAcI
github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ=
github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0=
github.com/btcsuite/btcd/btcutil/psbt v1.1.4/go.mod h1:9AyU6EQVJ9Iw9zPyNT1lcdHd6cnEZdno5wLu5FY74os=
github.com/btcsuite/btcd/btcutil/psbt v1.1.5 h1:x0ZRrYY8j75ThV6xBz86CkYAG82F5bzay4H5D1c8b/U=
github.com/btcsuite/btcd/btcutil/psbt v1.1.5/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U=
github.com/btcsuite/btcd/btcutil/psbt v1.1.7 h1:NWvSWKvXsl+rilY0BjqgiYtQBMqDJX98TQAdT6XqnCk=
github.com/btcsuite/btcd/btcutil/psbt v1.1.7/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM=
Expand Down
18 changes: 18 additions & 0 deletions internal/test/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/stretchr/testify/require"
"golang.org/x/exp/constraints"
)
Expand Down Expand Up @@ -69,6 +70,23 @@ func PubToKeyDesc(p *btcec.PublicKey) keychain.KeyDescriptor {
}
}

func ParseRPCKeyDescriptor(t testing.TB,
rpcDesc *signrpc.KeyDescriptor) keychain.KeyDescriptor {

pubKey, err := btcec.ParsePubKey(rpcDesc.RawKeyBytes)
require.NoError(t, err)

require.NotNil(t, rpcDesc.KeyLoc)

return keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamily(rpcDesc.KeyLoc.KeyFamily),
Index: uint32(rpcDesc.KeyLoc.KeyIndex),
},
PubKey: pubKey,
}
}

func ComputeTaprootScript(t testing.TB, taprootKey *btcec.PublicKey) []byte {
script, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_1).
Expand Down
Loading