Skip to content

Commit 4a15eb2

Browse files
authored
Merge pull request #1245 from lightninglabs/decimal-display-funding-blob
[custom channels]: add decimal display to channel funding blob
2 parents 8113fa8 + 565ee3c commit 4a15eb2

File tree

18 files changed

+464
-276
lines changed

18 files changed

+464
-276
lines changed

address/address.go

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ import (
1414
"github.com/btcsuite/btcd/chaincfg/chainhash"
1515
"github.com/btcsuite/btcd/txscript"
1616
"github.com/btcsuite/btcd/wire"
17-
"github.com/decred/dcrd/dcrec/secp256k1/v4"
1817
"github.com/lightninglabs/taproot-assets/asset"
1918
"github.com/lightninglabs/taproot-assets/commitment"
2019
"github.com/lightninglabs/taproot-assets/fn"
20+
"github.com/lightninglabs/taproot-assets/taprpc"
2121
"github.com/lightningnetwork/lnd/tlv"
2222
)
2323

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

541-
// GenChallengeNUMS generates a variant of the NUMS script key that is modified
542-
// by the provided challenge.
543-
//
544-
// The resulting scriptkey is:
545-
// res := NUMS + challenge*G
546-
func GenChallengeNUMS(challengeBytesOpt fn.Option[[32]byte]) asset.ScriptKey {
547-
var (
548-
nums, g, res btcec.JacobianPoint
549-
challenge secp256k1.ModNScalar
550-
)
551-
552-
if challengeBytesOpt.IsNone() {
553-
return asset.NUMSScriptKey
554-
}
555-
556-
var challengeBytes [32]byte
557-
558-
challengeBytesOpt.WhenSome(func(b [32]byte) {
559-
challengeBytes = b
560-
})
561-
562-
// Convert the NUMS key to a Jacobian point.
563-
asset.NUMSPubKey.AsJacobian(&nums)
564-
565-
// Multiply G by 1 to get G as a Jacobian point.
566-
secp256k1.ScalarBaseMultNonConst(
567-
new(secp256k1.ModNScalar).SetInt(1), &g,
568-
)
541+
// UnmarshalVersion parses an address version from the RPC variant.
542+
func UnmarshalVersion(version taprpc.AddrVersion) (Version, error) {
543+
// For now, we'll only support two address versions. The ones in the
544+
// future should be reserved for future use, so we disallow unknown
545+
// versions.
546+
switch version {
547+
case taprpc.AddrVersion_ADDR_VERSION_UNSPECIFIED:
548+
return V1, nil
569549

570-
// Convert the challenge to a scalar.
571-
challenge.SetByteSlice(challengeBytes[:])
550+
case taprpc.AddrVersion_ADDR_VERSION_V0:
551+
return V0, nil
572552

573-
// Calculate res = challenge * G.
574-
secp256k1.ScalarMultNonConst(&challenge, &g, &res)
553+
case taprpc.AddrVersion_ADDR_VERSION_V1:
554+
return V1, nil
575555

576-
// Calculate res = nums + res.
577-
secp256k1.AddNonConst(&nums, &res, &res)
556+
default:
557+
return 0, fmt.Errorf("unknown address version: %v", version)
558+
}
559+
}
578560

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

581-
resultPubKey := btcec.NewPublicKey(&res.X, &res.Y)
570+
case V1:
571+
return taprpc.AddrVersion_ADDR_VERSION_V1, nil
582572

583-
return asset.NewScriptKey(resultPubKey)
573+
default:
574+
return 0, fmt.Errorf("unknown address version: %v", version)
575+
}
584576
}

address/address_test.go

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/btcsuite/btcd/chaincfg"
1111
"github.com/btcsuite/btcd/txscript"
1212
"github.com/btcsuite/btcd/wire"
13-
"github.com/decred/dcrd/dcrec/secp256k1/v4"
1413
"github.com/lightninglabs/taproot-assets/asset"
1514
"github.com/lightninglabs/taproot-assets/commitment"
1615
"github.com/lightninglabs/taproot-assets/fn"
@@ -502,64 +501,6 @@ func TestBIPTestVectors(t *testing.T) {
502501
}
503502
}
504503

505-
// TestGenChallengeNUMS tests the generation of NUMS challenges.
506-
func TestGenChallengeNUMS(t *testing.T) {
507-
t.Parallel()
508-
509-
gx, gy := secp256k1.Params().Gx, secp256k1.Params().Gy
510-
511-
// addG is a helper function that adds G to the given public key.
512-
addG := func(p *btcec.PublicKey) *btcec.PublicKey {
513-
x, y := secp256k1.S256().Add(p.X(), p.Y(), gx, gy)
514-
var xFieldVal, yFieldVal secp256k1.FieldVal
515-
xFieldVal.SetByteSlice(x.Bytes())
516-
yFieldVal.SetByteSlice(y.Bytes())
517-
return btcec.NewPublicKey(&xFieldVal, &yFieldVal)
518-
}
519-
520-
testCases := []struct {
521-
name string
522-
challenge fn.Option[[32]byte]
523-
expectedKey asset.ScriptKey
524-
}{
525-
{
526-
name: "no challenge",
527-
challenge: fn.None[[32]byte](),
528-
expectedKey: asset.NUMSScriptKey,
529-
},
530-
{
531-
name: "challenge is scalar 1",
532-
challenge: fn.Some([32]byte{
533-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
534-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
535-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
536-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
537-
}),
538-
expectedKey: asset.NewScriptKey(addG(asset.NUMSPubKey)),
539-
},
540-
{
541-
name: "challenge is scalar 2",
542-
challenge: fn.Some([32]byte{
543-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
544-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
545-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
546-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
547-
}),
548-
expectedKey: asset.NewScriptKey(
549-
addG(addG(asset.NUMSPubKey)),
550-
),
551-
},
552-
}
553-
554-
for _, tc := range testCases {
555-
result := GenChallengeNUMS(tc.challenge)
556-
require.Equal(
557-
t, tc.expectedKey.PubKey.SerializeCompressed(),
558-
result.PubKey.SerializeCompressed(),
559-
)
560-
}
561-
}
562-
563504
// runBIPTestVector runs the tests in a single BIP test vector file.
564505
func runBIPTestVector(t *testing.T, testVectors *TestVectors) {
565506
for _, validCase := range testVectors.ValidTestCases {

address/book.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/lightninglabs/taproot-assets/asset"
1616
"github.com/lightninglabs/taproot-assets/commitment"
1717
"github.com/lightninglabs/taproot-assets/fn"
18+
"github.com/lightninglabs/taproot-assets/proof"
1819
"github.com/lightningnetwork/lnd/keychain"
1920
)
2021

@@ -23,6 +24,10 @@ var (
2324
// This means an address can't be created until a Universe bootstrap or
2425
// manual issuance proof insertion.
2526
ErrAssetGroupUnknown = fmt.Errorf("asset group is unknown")
27+
28+
// ErrAssetMetaNotFound is returned when an asset meta is not found in
29+
// the database.
30+
ErrAssetMetaNotFound = fmt.Errorf("asset meta not found")
2631
)
2732

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

108+
// FetchAssetMetaByHash attempts to fetch an asset meta based on an
109+
// asset hash.
110+
FetchAssetMetaByHash(ctx context.Context,
111+
metaHash [asset.MetaHashLen]byte) (*proof.MetaReveal, error)
112+
113+
// FetchAssetMetaForAsset attempts to fetch an asset meta based on an
114+
// asset ID.
115+
FetchAssetMetaForAsset(ctx context.Context,
116+
assetID asset.ID) (*proof.MetaReveal, error)
117+
103118
// AddrByTaprootOutput returns a single address based on its Taproot
104119
// output key or a sql.ErrNoRows error if no such address exists.
105120
AddrByTaprootOutput(ctx context.Context,
@@ -218,7 +233,7 @@ func (b *Book) QueryAssetInfo(ctx context.Context,
218233
return nil, err
219234
}
220235

221-
log.Debugf("asset %v is unknown, attempting to bootstrap", id.String())
236+
log.Debugf("Asset %v is unknown, attempting to bootstrap", id.String())
222237

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

236-
log.Debugf("bootstrap succeeded for asset %v", id.String())
251+
log.Debugf("Bootstrap succeeded for asset %v", id.String())
237252

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

271+
// FetchAssetMetaByHash attempts to fetch an asset meta based on an asset hash.
272+
func (b *Book) FetchAssetMetaByHash(ctx context.Context,
273+
metaHash [asset.MetaHashLen]byte) (*proof.MetaReveal, error) {
274+
275+
return b.cfg.Store.FetchAssetMetaByHash(ctx, metaHash)
276+
}
277+
278+
// FetchAssetMetaForAsset attempts to fetch an asset meta based on an asset ID.
279+
func (b *Book) FetchAssetMetaForAsset(ctx context.Context,
280+
assetID asset.ID) (*proof.MetaReveal, error) {
281+
282+
// Check if we know of this meta hash already.
283+
meta, err := b.cfg.Store.FetchAssetMetaForAsset(ctx, assetID)
284+
switch {
285+
case meta != nil:
286+
return meta, nil
287+
288+
// Asset lookup failed gracefully; continue to asset lookup using the
289+
// AssetSyncer if enabled.
290+
case errors.Is(err, ErrAssetMetaNotFound):
291+
if b.cfg.Syncer == nil {
292+
return nil, ErrAssetMetaNotFound
293+
}
294+
295+
case err != nil:
296+
return nil, err
297+
}
298+
299+
log.Debugf("Asset %v is unknown, attempting to bootstrap",
300+
assetID.String())
301+
302+
// Use the AssetSyncer to query our universe federation for the asset.
303+
err = b.cfg.Syncer.SyncAssetInfo(ctx, &assetID)
304+
if err != nil {
305+
return nil, err
306+
}
307+
308+
// The asset meta info may have been synced from a universe server;
309+
// query for the asset ID again.
310+
meta, err = b.cfg.Store.FetchAssetMetaForAsset(ctx, assetID)
311+
if err != nil {
312+
return nil, err
313+
}
314+
315+
log.Debugf("Bootstrap succeeded for asset %v", assetID.String())
316+
317+
return meta, nil
318+
}
319+
256320
// NewAddress creates a new Taproot Asset address based on the input parameters.
257321
func (b *Book) NewAddress(ctx context.Context, addrVersion Version,
258322
assetID asset.ID, amount uint64,

asset/witness.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package asset
22

3-
import "github.com/btcsuite/btcd/btcec/v2"
3+
import (
4+
"github.com/btcsuite/btcd/btcec/v2"
5+
"github.com/decred/dcrd/dcrec/secp256k1/v4"
6+
"github.com/lightninglabs/taproot-assets/fn"
7+
)
48

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

3539
return scriptKey.IsEqual(DeriveBurnKey(prevID))
3640
}
41+
42+
// GenChallengeNUMS generates a variant of the NUMS script key that is modified
43+
// by the provided challenge.
44+
//
45+
// The resulting scriptkey is:
46+
// res := NUMS + challenge*G
47+
func GenChallengeNUMS(challengeBytesOpt fn.Option[[32]byte]) ScriptKey {
48+
var (
49+
nums, g, res btcec.JacobianPoint
50+
challenge secp256k1.ModNScalar
51+
)
52+
53+
if challengeBytesOpt.IsNone() {
54+
return NUMSScriptKey
55+
}
56+
57+
var challengeBytes [32]byte
58+
59+
challengeBytesOpt.WhenSome(func(b [32]byte) {
60+
challengeBytes = b
61+
})
62+
63+
// Convert the NUMS key to a Jacobian point.
64+
NUMSPubKey.AsJacobian(&nums)
65+
66+
// Multiply G by 1 to get G as a Jacobian point.
67+
secp256k1.ScalarBaseMultNonConst(
68+
new(secp256k1.ModNScalar).SetInt(1), &g,
69+
)
70+
71+
// Convert the challenge to a scalar.
72+
challenge.SetByteSlice(challengeBytes[:])
73+
74+
// Calculate res = challenge * G.
75+
secp256k1.ScalarMultNonConst(&challenge, &g, &res)
76+
77+
// Calculate res = nums + res.
78+
secp256k1.AddNonConst(&nums, &res, &res)
79+
80+
res.ToAffine()
81+
82+
resultPubKey := btcec.NewPublicKey(&res.X, &res.Y)
83+
84+
return NewScriptKey(resultPubKey)
85+
}

asset/witness_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package asset
2+
3+
import (
4+
"testing"
5+
6+
"github.com/btcsuite/btcd/btcec/v2"
7+
"github.com/decred/dcrd/dcrec/secp256k1/v4"
8+
"github.com/lightninglabs/taproot-assets/fn"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// TestGenChallengeNUMS tests the generation of NUMS challenges.
13+
func TestGenChallengeNUMS(t *testing.T) {
14+
t.Parallel()
15+
16+
gx, gy := secp256k1.Params().Gx, secp256k1.Params().Gy
17+
18+
// addG is a helper function that adds G to the given public key.
19+
addG := func(p *btcec.PublicKey) *btcec.PublicKey {
20+
x, y := secp256k1.S256().Add(p.X(), p.Y(), gx, gy)
21+
var xFieldVal, yFieldVal secp256k1.FieldVal
22+
xFieldVal.SetByteSlice(x.Bytes())
23+
yFieldVal.SetByteSlice(y.Bytes())
24+
return btcec.NewPublicKey(&xFieldVal, &yFieldVal)
25+
}
26+
27+
testCases := []struct {
28+
name string
29+
challenge fn.Option[[32]byte]
30+
expectedKey ScriptKey
31+
}{
32+
{
33+
name: "no challenge",
34+
challenge: fn.None[[32]byte](),
35+
expectedKey: NUMSScriptKey,
36+
},
37+
{
38+
name: "challenge is scalar 1",
39+
challenge: fn.Some([32]byte{
40+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
44+
}),
45+
expectedKey: NewScriptKey(addG(NUMSPubKey)),
46+
},
47+
{
48+
name: "challenge is scalar 2",
49+
challenge: fn.Some([32]byte{
50+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
54+
}),
55+
expectedKey: NewScriptKey(addG(addG(NUMSPubKey))),
56+
},
57+
}
58+
59+
for _, tc := range testCases {
60+
result := GenChallengeNUMS(tc.challenge)
61+
require.Equal(
62+
t, tc.expectedKey.PubKey.SerializeCompressed(),
63+
result.PubKey.SerializeCompressed(),
64+
)
65+
}
66+
}

0 commit comments

Comments
 (0)