Skip to content

Commit

Permalink
Merge pull request #1245 from lightninglabs/decimal-display-funding-blob
Browse files Browse the repository at this point in the history
[custom channels]: add decimal display to channel funding blob
  • Loading branch information
guggero authored Dec 9, 2024
2 parents 8113fa8 + 565ee3c commit 4a15eb2
Show file tree
Hide file tree
Showing 18 changed files with 464 additions and 276 deletions.
68 changes: 30 additions & 38 deletions address/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightningnetwork/lnd/tlv"
)

Expand Down Expand Up @@ -538,47 +538,39 @@ func DecodeAddress(addr string, net *ChainParams) (*Tap, error) {
return &a, nil
}

// GenChallengeNUMS generates a variant of the NUMS script key that is modified
// by the provided challenge.
//
// The resulting scriptkey is:
// res := NUMS + challenge*G
func GenChallengeNUMS(challengeBytesOpt fn.Option[[32]byte]) asset.ScriptKey {
var (
nums, g, res btcec.JacobianPoint
challenge secp256k1.ModNScalar
)

if challengeBytesOpt.IsNone() {
return asset.NUMSScriptKey
}

var challengeBytes [32]byte

challengeBytesOpt.WhenSome(func(b [32]byte) {
challengeBytes = b
})

// Convert the NUMS key to a Jacobian point.
asset.NUMSPubKey.AsJacobian(&nums)

// Multiply G by 1 to get G as a Jacobian point.
secp256k1.ScalarBaseMultNonConst(
new(secp256k1.ModNScalar).SetInt(1), &g,
)
// UnmarshalVersion parses an address version from the RPC variant.
func UnmarshalVersion(version taprpc.AddrVersion) (Version, error) {
// For now, we'll only support two address versions. The ones in the
// future should be reserved for future use, so we disallow unknown
// versions.
switch version {
case taprpc.AddrVersion_ADDR_VERSION_UNSPECIFIED:
return V1, nil

// Convert the challenge to a scalar.
challenge.SetByteSlice(challengeBytes[:])
case taprpc.AddrVersion_ADDR_VERSION_V0:
return V0, nil

// Calculate res = challenge * G.
secp256k1.ScalarMultNonConst(&challenge, &g, &res)
case taprpc.AddrVersion_ADDR_VERSION_V1:
return V1, nil

// Calculate res = nums + res.
secp256k1.AddNonConst(&nums, &res, &res)
default:
return 0, fmt.Errorf("unknown address version: %v", version)
}
}

res.ToAffine()
// MarshalVersion marshals the native address version into the RPC variant.
func MarshalVersion(version Version) (taprpc.AddrVersion, error) {
// For now, we'll only support two address versions. The ones in the
// future should be reserved for future use, so we disallow unknown
// versions.
switch version {
case V0:
return taprpc.AddrVersion_ADDR_VERSION_V0, nil

resultPubKey := btcec.NewPublicKey(&res.X, &res.Y)
case V1:
return taprpc.AddrVersion_ADDR_VERSION_V1, nil

return asset.NewScriptKey(resultPubKey)
default:
return 0, fmt.Errorf("unknown address version: %v", version)
}
}
59 changes: 0 additions & 59 deletions address/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/fn"
Expand Down Expand Up @@ -502,64 +501,6 @@ func TestBIPTestVectors(t *testing.T) {
}
}

// TestGenChallengeNUMS tests the generation of NUMS challenges.
func TestGenChallengeNUMS(t *testing.T) {
t.Parallel()

gx, gy := secp256k1.Params().Gx, secp256k1.Params().Gy

// addG is a helper function that adds G to the given public key.
addG := func(p *btcec.PublicKey) *btcec.PublicKey {
x, y := secp256k1.S256().Add(p.X(), p.Y(), gx, gy)
var xFieldVal, yFieldVal secp256k1.FieldVal
xFieldVal.SetByteSlice(x.Bytes())
yFieldVal.SetByteSlice(y.Bytes())
return btcec.NewPublicKey(&xFieldVal, &yFieldVal)
}

testCases := []struct {
name string
challenge fn.Option[[32]byte]
expectedKey asset.ScriptKey
}{
{
name: "no challenge",
challenge: fn.None[[32]byte](),
expectedKey: asset.NUMSScriptKey,
},
{
name: "challenge is scalar 1",
challenge: fn.Some([32]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
}),
expectedKey: asset.NewScriptKey(addG(asset.NUMSPubKey)),
},
{
name: "challenge is scalar 2",
challenge: fn.Some([32]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
}),
expectedKey: asset.NewScriptKey(
addG(addG(asset.NUMSPubKey)),
),
},
}

for _, tc := range testCases {
result := GenChallengeNUMS(tc.challenge)
require.Equal(
t, tc.expectedKey.PubKey.SerializeCompressed(),
result.PubKey.SerializeCompressed(),
)
}
}

// runBIPTestVector runs the tests in a single BIP test vector file.
func runBIPTestVector(t *testing.T, testVectors *TestVectors) {
for _, validCase := range testVectors.ValidTestCases {
Expand Down
68 changes: 66 additions & 2 deletions address/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/lightninglabs/taproot-assets/asset"
"github.com/lightninglabs/taproot-assets/commitment"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/proof"
"github.com/lightningnetwork/lnd/keychain"
)

Expand All @@ -23,6 +24,10 @@ var (
// This means an address can't be created until a Universe bootstrap or
// manual issuance proof insertion.
ErrAssetGroupUnknown = fmt.Errorf("asset group is unknown")

// ErrAssetMetaNotFound is returned when an asset meta is not found in
// the database.
ErrAssetMetaNotFound = fmt.Errorf("asset meta not found")
)

// AddrWithKeyInfo wraps a normal Taproot Asset struct with key descriptor
Expand Down Expand Up @@ -100,6 +105,16 @@ type Storage interface {
// (genesis + group key) associated with a given asset.
QueryAssetGroup(context.Context, asset.ID) (*asset.AssetGroup, error)

// FetchAssetMetaByHash attempts to fetch an asset meta based on an
// asset hash.
FetchAssetMetaByHash(ctx context.Context,
metaHash [asset.MetaHashLen]byte) (*proof.MetaReveal, error)

// FetchAssetMetaForAsset attempts to fetch an asset meta based on an
// asset ID.
FetchAssetMetaForAsset(ctx context.Context,
assetID asset.ID) (*proof.MetaReveal, error)

// AddrByTaprootOutput returns a single address based on its Taproot
// output key or a sql.ErrNoRows error if no such address exists.
AddrByTaprootOutput(ctx context.Context,
Expand Down Expand Up @@ -218,7 +233,7 @@ func (b *Book) QueryAssetInfo(ctx context.Context,
return nil, err
}

log.Debugf("asset %v is unknown, attempting to bootstrap", id.String())
log.Debugf("Asset %v is unknown, attempting to bootstrap", id.String())

// Use the AssetSyncer to query our universe federation for the asset.
err = b.cfg.Syncer.SyncAssetInfo(ctx, &id)
Expand All @@ -233,7 +248,7 @@ func (b *Book) QueryAssetInfo(ctx context.Context,
return nil, err
}

log.Debugf("bootstrap succeeded for asset %v", id.String())
log.Debugf("Bootstrap succeeded for asset %v", id.String())

// If the asset was found after sync, and has an asset group, update our
// universe sync config to ensure that we sync future issuance proofs.
Expand All @@ -253,6 +268,55 @@ func (b *Book) QueryAssetInfo(ctx context.Context,
return assetGroup, nil
}

// FetchAssetMetaByHash attempts to fetch an asset meta based on an asset hash.
func (b *Book) FetchAssetMetaByHash(ctx context.Context,
metaHash [asset.MetaHashLen]byte) (*proof.MetaReveal, error) {

return b.cfg.Store.FetchAssetMetaByHash(ctx, metaHash)
}

// FetchAssetMetaForAsset attempts to fetch an asset meta based on an asset ID.
func (b *Book) FetchAssetMetaForAsset(ctx context.Context,
assetID asset.ID) (*proof.MetaReveal, error) {

// Check if we know of this meta hash already.
meta, err := b.cfg.Store.FetchAssetMetaForAsset(ctx, assetID)
switch {
case meta != nil:
return meta, nil

// Asset lookup failed gracefully; continue to asset lookup using the
// AssetSyncer if enabled.
case errors.Is(err, ErrAssetMetaNotFound):
if b.cfg.Syncer == nil {
return nil, ErrAssetMetaNotFound
}

case err != nil:
return nil, err
}

log.Debugf("Asset %v is unknown, attempting to bootstrap",
assetID.String())

// Use the AssetSyncer to query our universe federation for the asset.
err = b.cfg.Syncer.SyncAssetInfo(ctx, &assetID)
if err != nil {
return nil, err
}

// The asset meta info may have been synced from a universe server;
// query for the asset ID again.
meta, err = b.cfg.Store.FetchAssetMetaForAsset(ctx, assetID)
if err != nil {
return nil, err
}

log.Debugf("Bootstrap succeeded for asset %v", assetID.String())

return meta, nil
}

// NewAddress creates a new Taproot Asset address based on the input parameters.
func (b *Book) NewAddress(ctx context.Context, addrVersion Version,
assetID asset.ID, amount uint64,
Expand Down
51 changes: 50 additions & 1 deletion asset/witness.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package asset

import "github.com/btcsuite/btcd/btcec/v2"
import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lightninglabs/taproot-assets/fn"
)

// IsSplitCommitWitness returns true if the witness is a split-commitment
// witness.
Expand Down Expand Up @@ -34,3 +38,48 @@ func IsBurnKey(scriptKey *btcec.PublicKey, witness Witness) bool {

return scriptKey.IsEqual(DeriveBurnKey(prevID))
}

// GenChallengeNUMS generates a variant of the NUMS script key that is modified
// by the provided challenge.
//
// The resulting scriptkey is:
// res := NUMS + challenge*G
func GenChallengeNUMS(challengeBytesOpt fn.Option[[32]byte]) ScriptKey {
var (
nums, g, res btcec.JacobianPoint
challenge secp256k1.ModNScalar
)

if challengeBytesOpt.IsNone() {
return NUMSScriptKey
}

var challengeBytes [32]byte

challengeBytesOpt.WhenSome(func(b [32]byte) {
challengeBytes = b
})

// Convert the NUMS key to a Jacobian point.
NUMSPubKey.AsJacobian(&nums)

// Multiply G by 1 to get G as a Jacobian point.
secp256k1.ScalarBaseMultNonConst(
new(secp256k1.ModNScalar).SetInt(1), &g,
)

// Convert the challenge to a scalar.
challenge.SetByteSlice(challengeBytes[:])

// Calculate res = challenge * G.
secp256k1.ScalarMultNonConst(&challenge, &g, &res)

// Calculate res = nums + res.
secp256k1.AddNonConst(&nums, &res, &res)

res.ToAffine()

resultPubKey := btcec.NewPublicKey(&res.X, &res.Y)

return NewScriptKey(resultPubKey)
}
66 changes: 66 additions & 0 deletions asset/witness_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package asset

import (
"testing"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/stretchr/testify/require"
)

// TestGenChallengeNUMS tests the generation of NUMS challenges.
func TestGenChallengeNUMS(t *testing.T) {
t.Parallel()

gx, gy := secp256k1.Params().Gx, secp256k1.Params().Gy

// addG is a helper function that adds G to the given public key.
addG := func(p *btcec.PublicKey) *btcec.PublicKey {
x, y := secp256k1.S256().Add(p.X(), p.Y(), gx, gy)
var xFieldVal, yFieldVal secp256k1.FieldVal
xFieldVal.SetByteSlice(x.Bytes())
yFieldVal.SetByteSlice(y.Bytes())
return btcec.NewPublicKey(&xFieldVal, &yFieldVal)
}

testCases := []struct {
name string
challenge fn.Option[[32]byte]
expectedKey ScriptKey
}{
{
name: "no challenge",
challenge: fn.None[[32]byte](),
expectedKey: NUMSScriptKey,
},
{
name: "challenge is scalar 1",
challenge: fn.Some([32]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
}),
expectedKey: NewScriptKey(addG(NUMSPubKey)),
},
{
name: "challenge is scalar 2",
challenge: fn.Some([32]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
}),
expectedKey: NewScriptKey(addG(addG(NUMSPubKey))),
},
}

for _, tc := range testCases {
result := GenChallengeNUMS(tc.challenge)
require.Equal(
t, tc.expectedKey.PubKey.SerializeCompressed(),
result.PubKey.SerializeCompressed(),
)
}
}
Loading

0 comments on commit 4a15eb2

Please sign in to comment.