@@ -10,6 +10,7 @@ import (
1010 "github.com/btcsuite/btcd/blockchain"
1111 "github.com/btcsuite/btcd/btcec/v2"
1212 "github.com/btcsuite/btcd/btcec/v2/schnorr"
13+ "github.com/btcsuite/btcd/btcutil/hdkeychain"
1314 "github.com/btcsuite/btcd/chaincfg/chainhash"
1415 "github.com/btcsuite/btcd/txscript"
1516 "github.com/btcsuite/btcd/wire"
@@ -1296,3 +1297,91 @@ func TestCopySpendTemplate(t *testing.T) {
12961297
12971298 require .True (t , newAsset .DeepEqual (spendTemplate ))
12981299}
1300+
1301+ // TestExternalKeyPubKey tests that the public key can be derived from an
1302+ // external key.
1303+ func TestExternalKeyPubKey (t * testing.T ) {
1304+ t .Parallel ()
1305+
1306+ dummyXPub := func () hdkeychain.ExtendedKey {
1307+ xpubStr := "xpub6BynCcnXLYNnnMUZARkHxbP9pG6h5rES8Zb8aHtGwmFX" +
1308+ "9DdjJiyT9PNwkSMZfS3CvGRpvV21SkLRM6xhtshvA3DnJbQsvjD" +
1309+ "yySWGArynQNf"
1310+ xpub , err := hdkeychain .NewKeyFromString (xpubStr )
1311+ require .NoError (t , err , "failed to create xpub from string" )
1312+ return * xpub
1313+ }
1314+
1315+ testCases := []struct {
1316+ name string
1317+ externalKey ExternalKey
1318+ expectError bool
1319+ expectedError string
1320+ }{
1321+ {
1322+ name : "valid BIP-86 external key" ,
1323+ externalKey : ExternalKey {
1324+ XPub : dummyXPub (),
1325+ MasterFingerprint : 0x12345678 ,
1326+ DerivationPath : []uint32 {
1327+ 86 + hdkeychain .HardenedKeyStart ,
1328+ 0 + hdkeychain .HardenedKeyStart ,
1329+ 0 + hdkeychain .HardenedKeyStart , 0 , 0 ,
1330+ },
1331+ },
1332+ expectError : false ,
1333+ },
1334+ {
1335+ name : "invalid derivation path length" ,
1336+ externalKey : ExternalKey {
1337+ XPub : dummyXPub (),
1338+ MasterFingerprint : 0x12345678 ,
1339+ DerivationPath : []uint32 {
1340+ 86 + hdkeychain .HardenedKeyStart ,
1341+ 0 + hdkeychain .HardenedKeyStart ,
1342+ 0 + hdkeychain .HardenedKeyStart ,
1343+ },
1344+ },
1345+ expectError : true ,
1346+ expectedError : "derivation path must have exactly 5 " +
1347+ "components" ,
1348+ },
1349+ {
1350+ name : "invalid BIP-86 derivation path" ,
1351+ externalKey : ExternalKey {
1352+ XPub : dummyXPub (),
1353+ MasterFingerprint : 0x12345678 ,
1354+ DerivationPath : []uint32 {
1355+ 44 + hdkeychain .HardenedKeyStart ,
1356+ 0 + hdkeychain .HardenedKeyStart ,
1357+ 0 + hdkeychain .HardenedKeyStart , 0 , 0 ,
1358+ },
1359+ },
1360+ expectError : true ,
1361+ expectedError : "xpub must be derived from BIP-0086 " +
1362+ "(Taproot) derivation path" ,
1363+ },
1364+
1365+ // TODO(ffranr): Add test(s) for positive case: valid xpub and
1366+ // valid known correct corresponding public key.
1367+ }
1368+
1369+ for _ , tc := range testCases {
1370+ t .Run (tc .name , func (tt * testing.T ) {
1371+ pubKey , err := tc .externalKey .PubKey ()
1372+
1373+ if tc .expectError {
1374+ require .Error (tt , err , tc .name )
1375+ if tc .expectedError != "" {
1376+ require .Contains (
1377+ tt , err .Error (),
1378+ tc .expectedError ,
1379+ )
1380+ }
1381+ } else {
1382+ require .NoError (tt , err )
1383+ require .IsType (tt , btcec.PublicKey {}, pubKey )
1384+ }
1385+ })
1386+ }
1387+ }
0 commit comments