kaspad/util/hdkeychain/extendedkey_test.go
stasatdaglabs c1f7ae72e0 [NOD-514] Change dagcoin to kaspa, dagtest to kaspatest, dagreg to kaspareg, and dagsim to kaspasim (#538)
* [NOD-514] Change dagcoin to kaspa, dagtest to kaspatest, etc.

* [NOD-514] Remove no-longer-relevant link in doc.
2019-12-15 18:24:15 +02:00

1071 lines
38 KiB
Go

// Copyright (c) 2014-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package hdkeychain
// References:
// [BIP32]: BIP0032 - Hierarchical Deterministic Wallets
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
import (
"bytes"
"encoding/hex"
"github.com/kaspanet/kaspad/testutil"
"github.com/pkg/errors"
"math"
"reflect"
"testing"
"github.com/kaspanet/kaspad/util"
)
// TestBIP0032Vectors tests the vectors provided by [BIP32] to ensure the
// derivation works as intended.
func TestBIP0032Vectors(t *testing.T) {
// The master seeds for each of the two test vectors in [BIP32].
testVec1MasterHex := "000102030405060708090a0b0c0d0e0f"
testVec2MasterHex := "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
testVec3MasterHex := "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be"
hkStart := uint32(0x80000000)
tests := []struct {
name string
master string
path []uint32
wantPub string
wantPriv string
hdKeyIDPair HDKeyIDPair
}{
// Test vector 1
{
name: "test vector 1 chain m",
master: testVec1MasterHex,
path: []uint32{},
wantPub: "xpub661MyMwAqRbcGTTa4Jdtmttwg6yq1xoxNMPqCMT4iasLkbMDJHwHX6GGExBu9TXWSs2W7YNtQciJWhDuE2yJSDbHKSptjnBNnHMGxg5ybMW",
wantPriv: "xprv9s21ZrQH143K3yP6xH6tQkxD859LcW6718UEPy3TAFLMso24kkd2yHwnPeKuxD5MyK3YvXTJsqgXVp6hdrhYNabhhgxSPNbh3N69w5WrGT1",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 1 chain m/0H",
master: testVec1MasterHex,
path: []uint32{hkStart},
wantPub: "xpub69KEapfQ6NaCMD2hCspTKoAv2or3Ti3Uu5mXf84w5pFskMpPvNKWyBELokM6pCz51Pjw6gLor1troP7eeUgczktnzewPcdXVh9j6aPJhfH8",
wantPriv: "xprv9vKtBK8WG11u8ixE6rHSxfEBUn1Z4FKdXrqvrjfKXUitsZVFNq1GRNurxUUZ5Rhrw5MpM6nbBQJJMnA2qoQD3j2JbbG4BS594GcQsToQaiB",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 1 chain m/0H/1",
master: testVec1MasterHex,
path: []uint32{hkStart, 1},
wantPub: "xpub6AjVQFnusUq1NAzf6s5HAfHnxPZAaXNvCD2dxb5mr68GYMYSTBsGYL1sqe1NERKsdZPA2CQeASih53Ms3XXQgwagHCVCM8jwvMTsrdBTUsi",
wantPriv: "xprv9wk8zkG237Gi9gvBzqYGoXM4QMigB4f4pz73ACgAHkbHfZDHueZ1zXhPzMWScvVYsHvckXUViA3BfUiKiyTJuRnbWEkKCD1LNvzYQkjzAg6",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 1 chain m/0H/1/2H",
master: testVec1MasterHex,
path: []uint32{hkStart, 1, hkStart + 2},
wantPub: "xpub6BqBypWNhDSbEmsBUrkmQhBfpxAQN4HvStjopj7pdQSTHcG5z5yQhxR2qYQQa4J3FAfjK5nzPocGKzkjWxtEKDL5Gom4rfWkqxzR26QnsSk",
wantPriv: "xprv9xqqaJyUrqtJ2HniNqDm3ZEwGvKuxba55fpD2LiD54uUQovwSYfAAA6YzHAPgpojwoc4webEpkbhyzrq6xKY18HrfZYbTb5F1ZchyoNTSJh",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 1 chain m/0H/1/2H/2",
master: testVec1MasterHex,
path: []uint32{hkStart, 1, hkStart + 2, 2},
wantPub: "xpub6EgqrrZKS9A48mXpZtsWQ57Evxzt3sPHwP41p3TmqynQABtGcspvs1ivvHTiQWnuarDsCBPvMK6hf3rnqefXkVTtcs1JZdYSrL5WFtmm92b",
wantPriv: "xprvA1hVTM2RbmbkvHTMTsLW2wAWNwAPeQfSaA8R1f4AHeFRHPZ85LWgKDQT53tf7QeCfrh679CxyJZSasHJHJg6Tqed8qo4gTDmapTY9RQEQLJ",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 1 chain m/0H/1/2H/2/1000000000",
master: testVec1MasterHex,
path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000},
wantPub: "xpub6HJ4G6aN3TxzUM7jp992qnKyJR3eSwvPAL2YASqBmR3GnoxUGFjZWdCv8ZfuY3XxkJiq4ukTdfzMDLDrcSGL9QE51a6pWSNFi579DEGbdqQ",
wantPriv: "xprvA4Jhrb3UD6QhFs3Gi7c2UePEkPDA3VCXo76wN4RaD5WHv1dKiiRJxptSHJYEBqf9DnhBy5Acj9ECL2Fi3vcr78ivPMes5qoELfw9xuENti1",
hdKeyIDPair: HDKeyPairMainNet,
},
// Test vector 2
{
name: "test vector 2 chain m",
master: testVec2MasterHex,
path: []uint32{},
wantPub: "xpub661MyMwAqRbcGgMhBVBjF9Nmm7gem3XyVKNwpxEUVoreyzfksY8hvVjTHvxgNw5fZznRPrGE9xf1WR2vHkBB3Gc8oBDpiaC2vScCKGcdA1d",
wantPriv: "xprv9s21ZrQH143K4CHE5Teit1S3D5rAMap886TM2ZprwUKg7CLcKzpTNhQySeBnY7R7LL6kZyZPjHGkc9ouLY4Dhy6e9UFP2KJ6j3TiBXACRSB",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 2 chain m/0",
master: testVec2MasterHex,
path: []uint32{0},
wantPub: "xpub6912QQoTAjwVLJiaMFhPbu4maaaieUN2FQrSWCYSBtBSeM5yd6eK7pCFFHXLUPeh6iAaGR6ErHjJ8x2VKW7F4Htu9iVWzAtvj88UQKNvxj6",
wantPriv: "xprv9v1fzuGZLNPC7pe7FEAPEm832YkEF1eAtBvqhp8pdYeTmYkq5ZL4a1smPzv3LKVArYkYvPgLgP947tJmH38NqCWcpkzNbXvQ9joq6FQePaw",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 2 chain m/0/2147483647H",
master: testVec2MasterHex,
path: []uint32{0, hkStart + 2147483647},
wantPub: "xpub6B9QvMX5vvu5y1aEfyJHpPymeL81y9cdU3eo8EA55ftMuqaJG6HNaznyxDXX1WaQPWQjRahRz5VsGNtJRRWTkhtxgKHCm4PhTsrYz11cdm7",
wantPriv: "xprv9xA4WqzC6ZLnkXVmZwmHTG336JHXZgtn6pjCKqkTXLMP33F9iYy83CUW6ySkBGQtHhBGqZpqzStP5C3Xq5rvqNrhPCuPEsqUXPNQnTKyiax",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 2 chain m/0/2147483647H/1",
master: testVec2MasterHex,
path: []uint32{0, hkStart + 2147483647, 1},
wantPub: "xpub6CPAoCUuVd4VKDE39r1aGoRndRdBqD9QXWXt4hYxKWDznaR1nJXgjbnb5DQNmzYt83SmvCe9zmGXWRwi1M5rqCUkwNDLsjc4jUJcjqbKgSW",
wantPriv: "xprv9yPpPgx1fFWC6j9a3pUZufV45PnhRkRZAHcHGK9LmAh1un5sEmDSBoU7DwtYAvgsUoewLkJRhXhWZjxJ6TuPDSL3tXSuAkz3nFEw83F9PNX",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 2 chain m/0/2147483647H/1/2147483646H",
master: testVec2MasterHex,
path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646},
wantPub: "xpub6EGnnakCt69xbT83sUN5BhrRr933BtTd4cMUiqzyrkRWkNhnKeMifJN1G9gjbART8QA86KxyYSijp9DpEi12oFnznwS7H1vFjGVcta65Ru1",
wantPriv: "xprvA1HSP5DK3ibfNy3amSq4pZuhJ7CYnRjmhPRsvTbNJQtXsaNdn73U7W3XQszDCEKwRrgGTSe8R2nFcTje7eBeyP9V8264EHeuvt8mXLbdQWQ",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 2 chain m/0/2147483647H/1/2147483646H/2",
master: testVec2MasterHex,
path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646, 2},
wantPub: "xpub6GyY8YKU9JoxnwgqbusBYvJByXBiY2FWE2HMrXQDunVs61Rr5ojSBkeLxwnsQ9mMkMY1JVvRdMtoY5KHSwv8jWypeFX5tWzkhiH4qj5xEhr",
wantPriv: "xprvA3zBj2naJwFfaTcNVtLBBnMTRVME8ZXeroMm48zcMSxtDD6hYGRBdxKs7e1j3oih75fyXXkPLPcCoBt6GmfdgZQqTw3Zt7i18j1CTQnDCYs",
hdKeyIDPair: HDKeyPairMainNet,
},
// Test vector 3
{
name: "test vector 3 chain m",
master: testVec3MasterHex,
path: []uint32{},
wantPub: "xpub661MyMwAqRbcH1Qh3yC3Zk1eESmJ1QpoXt8HK2h3MTA5QTMsKYD4guWkaqDdrmiHFRssCPRaUA9CD2tXhNtmN1aT7eV18q8BoExXbo4mEtZ",
wantPriv: "xprv9s21ZrQH143K4XLDwwf3Cc4ugQvobx6xAfCgWeHRo7d6Xf2imztp97CGjX9Yu7gpv9tfoNNx99bEUTkinzeXKrdafDkuhqwK5TeViwMWZ6i",
hdKeyIDPair: HDKeyPairMainNet,
},
{
name: "test vector 3 chain m/0H",
master: testVec3MasterHex,
path: []uint32{hkStart},
wantPub: "xpub68sNRZc1xTrER4A1q2buYUQyfaRMv8FgXDvR2xQhtEhCzVMaFoXbquRd5JPfCQv3dN48d7Cuq3fCdTXzrxXZxbZoEwhBAz6xKwFUc3vRgg4",
wantPriv: "xprv9ut2245886HwCa5Yj14uBLUF7YasWfXq9zzpEa16KuAE7h2RiGDMJ779E4cv62x55XQNiZ4jaUbes1G9nYoBKAE8b6Ud8mrhABo9fxLRuad",
hdKeyIDPair: HDKeyPairMainNet,
},
// Test vector 1 - Testnet
{
name: "test vector 1 chain m - testnet",
master: testVec1MasterHex,
path: []uint32{},
wantPub: "tpubD6NzVbkrYhZ4YFeRWVcyypEK1E5V1MK1uyz8YuWD4VdEVt1vNWnNfXw6UzGgfxUkEmZQj7tkwiYMCsiWctpYKf3aP3aCRJqcNEwgienHE4w",
wantPriv: "tprv8ZgxMBicQKsPencdcqxPaQaCSCZYr287LgPMGPTueDpqfPm9k7xnV3KEJpVZxaTgLkaKvd553CGKxfeSm53VBdsJELAk3jKjxTqaNj93jhF",
hdKeyIDPair: HDKeyPairTestNet,
},
{
name: "test vector 1 chain m/0H - testnet",
master: testVec1MasterHex,
path: []uint32{hkStart},
wantPub: "tpubD9gs74V5oeXed1DYf4oYXiWHMvwhT6YYSiMq1g85Rj1mVeV6zbAc7cuB3nRtLhwJoJGqiFrgP7iuVZcG3LXrtCM64FghJABjH7KWLRMoCvo",
wantPriv: "tprv8czpxeSqfGqyjYBkmR8x8JrAnuRmHmMdsQm3jA5n1TDNfAELNCM1w8HJseeD5o6BJWtbMCQMLkt6pdhmy1k9rnHu8EUMqnoByNMqKA1yKAG",
hdKeyIDPair: HDKeyPairTestNet,
},
{
name: "test vector 1 chain m/0H/1 - testnet",
master: testVec1MasterHex,
path: []uint32{hkStart, 1},
wantPub: "tpubDB77vVcbaknTdyBWZ44NNadAHWepZusyjqcwK98vBztAHeD9XQiMgmgi5g69kvH7RTv4dmvWhYYjmDrUSPNeaP2yLoEW2fQBWK4HceSHWLM",
wantPriv: "tprv8eR5n5aMSP6nkW9ifQPmyAy3iV8tQah5AY2A2d6cmj5mT9xNu1tmWH4quXg6dHssEjTPkd6FsWcz8LG4rBoFiV4C2sxcrZjPJ2jxrRiSdSz",
hdKeyIDPair: HDKeyPairTestNet,
},
{
name: "test vector 1 chain m/0H/1/2H - testnet",
master: testVec1MasterHex,
path: []uint32{hkStart, 1, hkStart + 2},
wantPub: "tpubDCCpW4L4QVQ3Wa42w3jrccX3A5G4MSnyzXL7BHAxyKCM2tvo4JpVrQ5s5aVC6ZFH35CdvfJrvuSK2BFLupjUCenNLQWNYCAzRvapn4MGur7",
wantPriv: "tprv8fWnMeHpG7iNd72F3Q5GDCrvb3k8C7c5RDjKtm8fZ3PxCQg2RuzufuTzuTL3hCC4KF8qwkCzz7BWSrQaEAfUpBZTCCku7woHvfN8RSEDVA8",
hdKeyIDPair: HDKeyPairTestNet,
},
{
name: "test vector 1 chain m/0H/1/2H/2 - testnet",
master: testVec1MasterHex,
path: []uint32{hkStart, 1, hkStart + 2, 2},
wantPub: "tpubDF4UP6P19R7WQZig25rbbzScG66Y3FtMV1eKAbWvBtYHuUYyh6g21TPmAKYVw1k9NkkmokuntQvkMEMQEWWmdvvBgTkcFACgSHfv1ze714s",
wantPriv: "tprv8iNSEgLm13RqX6gt8SC1CanVh4absvhSui3Xt5Ucmcju4zJD4hrRpxmtzE4K7n2X3JDs7Epj8f9F3iq3QX23GtvDfV1NLowpVvCxb7VMUpG",
hdKeyIDPair: HDKeyPairTestNet,
},
{
name: "test vector 1 chain m/0H/1/2H/2/1000000000 - testnet",
master: testVec1MasterHex,
path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000},
wantPub: "tpubDHfgnLQ3kjvSk9JbGL883hfLdY9JSLRShxcqWztL7KoAY6dBLUaef4skNbkh4YVCYDFjgVGLAmpPuWiU1J7a2qgN5Ar8By2VJ2hYyEPjRX2",
wantPriv: "tprv8kyedvMocNEmrgGoNgTXeJ1E4WdNH1EY8f24EUr2h3zmhcNQi5m4UaFtCUhtCD3TbEDxyAnNtVoznsoTB8xnvBzWuzsAkCXHFmgaQbL7CbH",
hdKeyIDPair: HDKeyPairTestNet,
},
}
tests:
for i, test := range tests {
masterSeed, err := hex.DecodeString(test.master)
if err != nil {
t.Errorf("DecodeString #%d (%s): unexpected error: %v",
i, test.name, err)
continue
}
extKey, err := NewMaster(masterSeed, test.hdKeyIDPair.PrivateKeyID)
if err != nil {
t.Errorf("NewMaster #%d (%s): unexpected error when "+
"creating new master key: %v", i, test.name,
err)
continue
}
for _, childNum := range test.path {
var err error
extKey, err = extKey.Child(childNum)
if err != nil {
t.Errorf("err: %v", err)
continue tests
}
}
if extKey.Depth() != uint8(len(test.path)) {
t.Errorf("Depth of key %d should match fixture path: %v",
extKey.Depth(), len(test.path))
continue
}
privStr := extKey.String()
if privStr != test.wantPriv {
t.Errorf("Serialize #%d (%s): mismatched serialized "+
"private extended key -- got: %s, want: %s", i,
test.name, privStr, test.wantPriv)
continue
}
pubKey, err := extKey.Neuter()
if err != nil {
t.Errorf("Neuter #%d (%s): unexpected error: %v ", i,
test.name, err)
continue
}
// Neutering a second time should have no effect.
pubKey, err = pubKey.Neuter()
if err != nil {
t.Errorf("Neuter #%d (%s): unexpected error: %v", i,
test.name, err)
return
}
pubStr := pubKey.String()
if pubStr != test.wantPub {
t.Errorf("Neuter #%d (%s): mismatched serialized "+
"public extended key -- got: %s, want: %s", i,
test.name, pubStr, test.wantPub)
continue
}
}
}
// TestPrivateDerivation tests several vectors which derive private keys from
// other private keys works as intended.
func TestPrivateDerivation(t *testing.T) {
// The private extended keys for test vectors in [BIP32].
testVec1MasterPrivKey := "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
testVec2MasterPrivKey := "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"
tests := []struct {
name string
master string
path []uint32
wantPriv string
}{
// Test vector 1
{
name: "test vector 1 chain m",
master: testVec1MasterPrivKey,
path: []uint32{},
wantPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
},
{
name: "test vector 1 chain m/0",
master: testVec1MasterPrivKey,
path: []uint32{0},
wantPriv: "xprv9uHRZZhbkedL37eZEnyrNsQPFZYRAvjy5rt6M1nbEkLSo378x1CQQLo2xxBvREwiK6kqf7GRNvsNEchwibzXaV6i5GcsgyjBeRguXhKsi4R",
},
{
name: "test vector 1 chain m/0/1",
master: testVec1MasterPrivKey,
path: []uint32{0, 1},
wantPriv: "xprv9ww7sMFLzJMzy7bV1qs7nGBxgKYrgcm3HcJvGb4yvNhT9vxXC7eX7WVULzCfxucFEn2TsVvJw25hH9d4mchywguGQCZvRgsiRaTY1HCqN8G",
},
{
name: "test vector 1 chain m/0/1/2",
master: testVec1MasterPrivKey,
path: []uint32{0, 1, 2},
wantPriv: "xprv9xrdP7iD2L1YZCgR9AecDgpDMZSTzP5KCfUykGXgjBxLgp1VFHsEeL3conzGAkbc1MigG1o8YqmfEA2jtkPdf4vwMaGJC2YSDbBTPAjfRUi",
},
{
name: "test vector 1 chain m/0/1/2/2",
master: testVec1MasterPrivKey,
path: []uint32{0, 1, 2, 2},
wantPriv: "xprvA2J8Hq4eiP7xCEBP7gzRJGJnd9CHTkEU6eTNMrZ6YR7H5boik8daFtDZxmJDfdMSKHwroCfAfsBKWWidRfBQjpegy6kzXSkQGGoMdWKz5Xh",
},
{
name: "test vector 1 chain m/0/1/2/2/1000000000",
master: testVec1MasterPrivKey,
path: []uint32{0, 1, 2, 2, 1000000000},
wantPriv: "xprvA3XhazxncJqJsQcG85Gg61qwPQKiobAnWjuPpjKhExprZjfse6nErRwTMwGe6uGWXPSykZSTiYb2TXAm7Qhwj8KgRd2XaD21Styu6h6AwFz",
},
// Test vector 2
{
name: "test vector 2 chain m",
master: testVec2MasterPrivKey,
path: []uint32{},
wantPriv: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
},
{
name: "test vector 2 chain m/0",
master: testVec2MasterPrivKey,
path: []uint32{0},
wantPriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
},
{
name: "test vector 2 chain m/0/2147483647",
master: testVec2MasterPrivKey,
path: []uint32{0, 2147483647},
wantPriv: "xprv9wSp6B7cXJWXZRpDbxkFg3ry2fuSyUfvboJ5Yi6YNw7i1bXmq9QwQ7EwMpeG4cK2pnMqEx1cLYD7cSGSCtruGSXC6ZSVDHugMsZgbuY62m6",
},
{
name: "test vector 2 chain m/0/2147483647/1",
master: testVec2MasterPrivKey,
path: []uint32{0, 2147483647, 1},
wantPriv: "xprv9ysS5br6UbWCRCJcggvpUNMyhVWgD7NypY9gsVTMYmuRtZg8izyYC5Ey4T931WgWbfJwRDwfVFqV3b29gqHDbuEpGcbzf16pdomk54NXkSm",
},
{
name: "test vector 2 chain m/0/2147483647/1/2147483646",
master: testVec2MasterPrivKey,
path: []uint32{0, 2147483647, 1, 2147483646},
wantPriv: "xprvA2LfeWWwRCxh4iqigcDMnUf2E3nVUFkntc93nmUYBtb9rpSPYWa8MY3x9ZHSLZkg4G84UefrDruVK3FhMLSJsGtBx883iddHNuH1LNpRrEp",
},
{
name: "test vector 2 chain m/0/2147483647/1/2147483646/2",
master: testVec2MasterPrivKey,
path: []uint32{0, 2147483647, 1, 2147483646, 2},
wantPriv: "xprvA48ALo8BDjcRET68R5RsPzF3H7WeyYYtHcyUeLRGBPHXu6CJSGjwW7dWoeUWTEzT7LG3qk6Eg6x2ZoqD8gtyEFZecpAyvchksfLyg3Zbqam",
},
// Custom tests to trigger specific conditions.
{
// Seed 000000000000000000000000000000da.
name: "Derived privkey with zero high byte m/0",
master: "xprv9s21ZrQH143K4FR6rNeqEK4EBhRgLjWLWhA3pw8iqgAKk82ypz58PXbrzU19opYcxw8JDJQF4id55PwTsN1Zv8Xt6SKvbr2KNU5y8jN8djz",
path: []uint32{0},
wantPriv: "xprv9uC5JqtViMmgcAMUxcsBCBFA7oYCNs4bozPbyvLfddjHou4rMiGEHipz94xNaPb1e4f18TRoPXfiXx4C3cDAcADqxCSRSSWLvMBRWPctSN9",
},
}
tests:
for i, test := range tests {
extKey, err := NewKeyFromString(test.master)
if err != nil {
t.Errorf("NewKeyFromString #%d (%s): unexpected error "+
"creating extended key: %v", i, test.name,
err)
continue
}
for _, childNum := range test.path {
var err error
extKey, err = extKey.Child(childNum)
if err != nil {
t.Errorf("err: %v", err)
continue tests
}
}
privStr := extKey.String()
if privStr != test.wantPriv {
t.Errorf("Child #%d (%s): mismatched serialized "+
"private extended key -- got: %s, want: %s", i,
test.name, privStr, test.wantPriv)
continue
}
}
}
// TestPublicDerivation tests several vectors which derive public keys from
// other public keys works as intended.
func TestPublicDerivation(t *testing.T) {
// The public extended keys for test vectors in [BIP32].
testVec1MasterPubKey := "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
testVec2MasterPubKey := "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"
tests := []struct {
name string
master string
path []uint32
wantPub string
}{
// Test vector 1
{
name: "test vector 1 chain m",
master: testVec1MasterPubKey,
path: []uint32{},
wantPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
},
{
name: "test vector 1 chain m/0",
master: testVec1MasterPubKey,
path: []uint32{0},
wantPub: "xpub68Gmy5EVb2BdFbj2LpWrk1M7obNuaPTpT5oh9QCCo5sRfqSHVYWex97WpDZzszdzHzxXDAzPLVSwybe4uPYkSk4G3gnrPqqkV9RyNzAcNJ1",
},
{
name: "test vector 1 chain m/0/1",
master: testVec1MasterPubKey,
path: []uint32{0, 1},
wantPub: "xpub6AvUGrnEpfvJBbfx7sQ89Q8hEMPM65UteqEX4yUbUiES2jHfjexmfJoxCGSwFMZiPBaKQT1RiKWrKfuDV4vpgVs4Xn8PpPTR2i79rwHd4Zr",
},
{
name: "test vector 1 chain m/0/1/2",
master: testVec1MasterPubKey,
path: []uint32{0, 1, 2},
wantPub: "xpub6BqyndF6rhZqmgktFCBcapkwubGxPqoAZtQaYewJHXVKZcLdnqBVC8N6f6FSHWUghjuTLeubWyQWfJdk2G3tGgvgj3qngo4vLTnnSjAZckv",
},
{
name: "test vector 1 chain m/0/1/2/2",
master: testVec1MasterPubKey,
path: []uint32{0, 1, 2, 2},
wantPub: "xpub6FHUhLbYYkgFQiFrDiXRfQFXBB2msCxKTsNyAExi6keFxQ8sHfwpogY3p3s1ePSpUqLNYks5T6a3JqpCGszt4kxbyq7tUoFP5c8KWyiDtPp",
},
{
name: "test vector 1 chain m/0/1/2/2/1000000000",
master: testVec1MasterPubKey,
path: []uint32{0, 1, 2, 2, 1000000000},
wantPub: "xpub6GX3zWVgSgPc5tgjE6ogT9nfwSADD3tdsxpzd7jJoJMqSY12Be6VQEFwDCp6wAQoZsH2iq5nNocHEaVDxBcobPrkZCjYW3QUmoDYzMFBDu9",
},
// Test vector 2
{
name: "test vector 2 chain m",
master: testVec2MasterPubKey,
path: []uint32{},
wantPub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
},
{
name: "test vector 2 chain m/0",
master: testVec2MasterPubKey,
path: []uint32{0},
wantPub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
},
{
name: "test vector 2 chain m/0/2147483647",
master: testVec2MasterPubKey,
path: []uint32{0, 2147483647},
wantPub: "xpub6ASAVgeWMg4pmutghzHG3BohahjwNwPmy2DgM6W9wGegtPrvNgjBwuZRD7hSDFhYfunq8vDgwG4ah1gVzZysgp3UsKz7VNjCnSUJJ5T4fdD",
},
{
name: "test vector 2 chain m/0/2147483647/1",
master: testVec2MasterPubKey,
path: []uint32{0, 2147483647, 1},
wantPub: "xpub6CrnV7NzJy4VdgP5niTpqWJiFXMAca6qBm5Hfsry77SQmN1HGYHnjsZSujoHzdxf7ZNK5UVrmDXFPiEW2ecwHGWMFGUxPC9ARipss9rXd4b",
},
{
name: "test vector 2 chain m/0/2147483647/1/2147483646",
master: testVec2MasterPubKey,
path: []uint32{0, 2147483647, 1, 2147483646},
wantPub: "xpub6FL2423qFaWzHCvBndkN9cbkn5cysiUeFq4eb9t9kE88jcmY63tNuLNRzpHPdAM4dUpLhZ7aUm2cJ5zF7KYonf4jAPfRqTMTRBNkQL3Tfta",
},
{
name: "test vector 2 chain m/0/2147483647/1/2147483646/2",
master: testVec2MasterPubKey,
path: []uint32{0, 2147483647, 1, 2147483646, 2},
wantPub: "xpub6H7WkJf547AiSwAbX6xsm8Bmq9M9P1Gjequ5SipsjipWmtXSyp4C3uwzewedGEgAMsDy4jEvNTWtxLyqqHY9C12gaBmgUdk2CGmwachwnWK",
},
}
tests:
for i, test := range tests {
extKey, err := NewKeyFromString(test.master)
if err != nil {
t.Errorf("NewKeyFromString #%d (%s): unexpected error "+
"creating extended key: %v", i, test.name,
err)
continue
}
for _, childNum := range test.path {
var err error
extKey, err = extKey.Child(childNum)
if err != nil {
t.Errorf("err: %v", err)
continue tests
}
}
pubStr := extKey.String()
if pubStr != test.wantPub {
t.Errorf("Child #%d (%s): mismatched serialized "+
"public extended key -- got: %s, want: %s", i,
test.name, pubStr, test.wantPub)
continue
}
}
}
// TestGenenerateSeed ensures the GenerateSeed function works as intended.
func TestGenenerateSeed(t *testing.T) {
wantErr := errors.New("seed length must be between 128 and 512 bits")
tests := []struct {
name string
length uint8
err error
}{
// Test various valid lengths.
{name: "16 bytes", length: 16},
{name: "17 bytes", length: 17},
{name: "20 bytes", length: 20},
{name: "32 bytes", length: 32},
{name: "64 bytes", length: 64},
// Test invalid lengths.
{name: "15 bytes", length: 15, err: wantErr},
{name: "65 bytes", length: 65, err: wantErr},
}
for i, test := range tests {
seed, err := GenerateSeed(test.length)
if !testutil.AreErrorsEqual(err, test.err) {
t.Errorf("GenerateSeed #%d (%s): unexpected error -- "+
"want %v, got %v", i, test.name, test.err, err)
continue
}
if test.err == nil && len(seed) != int(test.length) {
t.Errorf("GenerateSeed #%d (%s): length mismatch -- "+
"got %d, want %d", i, test.name, len(seed),
test.length)
continue
}
}
}
// TestExtendedKeyAPI ensures the API on the ExtendedKey type works as intended.
func TestExtendedKeyAPI(t *testing.T) {
tests := []struct {
name string
extKey string
isPrivate bool
parentFP uint32
privKey string
privKeyErr error
pubKey string
address string
}{
{
name: "test vector 1 master node private",
extKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
isPrivate: true,
parentFP: 0,
privKey: "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",
pubKey: "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2",
address: "kaspa:qq6yyxf7rwmsj9hfz32jzukdfckme80czy6wxqe82z",
},
{
name: "test vector 1 chain m/0H/1/2H public",
extKey: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
isPrivate: false,
parentFP: 3203769081,
privKeyErr: ErrNotPrivExtKey,
pubKey: "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2",
address: "kaspa:qrh84wgvmet23s8zhvyx43yhfzudh8wwwgvqz64sfs",
},
}
for i, test := range tests {
key, err := NewKeyFromString(test.extKey)
if err != nil {
t.Errorf("NewKeyFromString #%d (%s): unexpected "+
"error: %v", i, test.name, err)
continue
}
if key.IsPrivate() != test.isPrivate {
t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+
"want private %v, got private %v", i, test.name,
test.isPrivate, key.IsPrivate())
continue
}
parentFP := key.ParentFingerprint()
if parentFP != test.parentFP {
t.Errorf("ParentFingerprint #%d (%s): mismatched "+
"parent fingerprint -- want %d, got %d", i,
test.name, test.parentFP, parentFP)
continue
}
serializedKey := key.String()
if serializedKey != test.extKey {
t.Errorf("String #%d (%s): mismatched serialized key "+
"-- want %s, got %s", i, test.name, test.extKey,
serializedKey)
continue
}
privKey, err := key.ECPrivKey()
if !reflect.DeepEqual(err, test.privKeyErr) {
t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+
"%v, got %v", i, test.name, test.privKeyErr, err)
continue
}
if test.privKeyErr == nil {
privKeyStr := hex.EncodeToString(privKey.Serialize())
if privKeyStr != test.privKey {
t.Errorf("ECPrivKey #%d (%s): mismatched "+
"private key -- want %s, got %s", i,
test.name, test.privKey, privKeyStr)
continue
}
}
pubKey, err := key.ECPubKey()
if err != nil {
t.Errorf("ECPubKey #%d (%s): unexpected error: %v", i,
test.name, err)
continue
}
pubKeyStr := hex.EncodeToString(pubKey.SerializeCompressed())
if pubKeyStr != test.pubKey {
t.Errorf("ECPubKey #%d (%s): mismatched public key -- "+
"want %s, got %s", i, test.name, test.pubKey,
pubKeyStr)
continue
}
addr, err := key.Address(util.Bech32PrefixKaspa)
if err != nil {
t.Errorf("Address #%d (%s): unexpected error: %v", i,
test.name, err)
continue
}
if addr.EncodeAddress() != test.address {
t.Errorf("Address #%d (%s): mismatched address -- want "+
"%s, got %s", i, test.name, test.address,
addr.EncodeAddress())
continue
}
}
}
// TestNet ensures the network related APIs work as intended.
func TestNet(t *testing.T) {
tests := []struct {
name string
key string
origHDKeyIDPair HDKeyIDPair
newHDKeyIDPair HDKeyIDPair
newPriv string
newPub string
isPrivate bool
}{
// Private extended keys.
{
name: "mainnet -> simnet",
key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
origHDKeyIDPair: HDKeyPairMainNet,
newHDKeyIDPair: HDKeyPairSimNet,
newPriv: "sprv8Erh3X3hFeKunvVdAGQQtambRPapECWiTDtvsTGdyrhzhbYgnSZajRRWbihzvq4AM4ivm6uso31VfKaukwJJUs3GYihXP8ebhMb3F2AHu3P",
newPub: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk",
isPrivate: true,
},
{
name: "simnet -> mainnet",
key: "sprv8Erh3X3hFeKunvVdAGQQtambRPapECWiTDtvsTGdyrhzhbYgnSZajRRWbihzvq4AM4ivm6uso31VfKaukwJJUs3GYihXP8ebhMb3F2AHu3P",
origHDKeyIDPair: HDKeyPairSimNet,
newHDKeyIDPair: HDKeyPairMainNet,
newPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
isPrivate: true,
},
{
name: "mainnet -> regtest",
key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
origHDKeyIDPair: HDKeyPairMainNet,
newHDKeyIDPair: HDKeyPairRegressionNet,
newPriv: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m",
newPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp",
isPrivate: true,
},
{
name: "regtest -> mainnet",
key: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m",
origHDKeyIDPair: HDKeyPairRegressionNet,
newHDKeyIDPair: HDKeyPairMainNet,
newPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
isPrivate: true,
},
// Public extended keys.
{
name: "mainnet -> simnet",
key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
origHDKeyIDPair: HDKeyPairMainNet,
newHDKeyIDPair: HDKeyPairSimNet,
newPub: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk",
isPrivate: false,
},
{
name: "simnet -> mainnet",
key: "spub4Tr3T2ab61tD1Qa6GHwRFiiKyRRJdfEZpSpXfqgFYCEyaPsqKysqHDjzSzMJSiUEGbcsG3w2SLMoTqn44B8x6u3MLRRkYfACTUBnHK79THk",
origHDKeyIDPair: HDKeyPairSimNet,
newHDKeyIDPair: HDKeyPairMainNet,
newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
isPrivate: false,
},
{
name: "mainnet -> regtest",
key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
origHDKeyIDPair: HDKeyPairMainNet,
newHDKeyIDPair: HDKeyPairRegressionNet,
newPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp",
isPrivate: false,
},
{
name: "regtest -> mainnet",
key: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp",
origHDKeyIDPair: HDKeyPairRegressionNet,
newHDKeyIDPair: HDKeyPairMainNet,
newPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
isPrivate: false,
},
}
for i, test := range tests {
extKey, err := NewKeyFromString(test.key)
if err != nil {
t.Errorf("NewKeyFromString #%d (%s): unexpected error "+
"creating extended key: %v", i, test.name,
err)
continue
}
if !extKey.IsForNet(test.origHDKeyIDPair) {
t.Errorf("IsForNet #%d (%s): key is not for expected "+
"network %v", i, test.name, test.origHDKeyIDPair)
continue
}
extKey.SetNet(test.newHDKeyIDPair)
if !extKey.IsForNet(test.newHDKeyIDPair) {
t.Errorf("SetNet/IsForNet #%d (%s): key is not for "+
"expected network %v", i, test.name,
test.newHDKeyIDPair)
continue
}
if test.isPrivate {
privStr := extKey.String()
if privStr != test.newPriv {
t.Errorf("Serialize #%d (%s): mismatched serialized "+
"private extended key -- got: %s, want: %s", i,
test.name, privStr, test.newPriv)
continue
}
extKey, err = extKey.Neuter()
if err != nil {
t.Errorf("Neuter #%d (%s): unexpected error: %v ", i,
test.name, err)
continue
}
}
pubStr := extKey.String()
if pubStr != test.newPub {
t.Errorf("Neuter #%d (%s): mismatched serialized "+
"public extended key -- got: %s, want: %s", i,
test.name, pubStr, test.newPub)
continue
}
}
}
// TestErrors performs some negative tests for various invalid cases to ensure
// the errors are handled properly.
func TestErrors(t *testing.T) {
// Should get an error when seed has too few bytes.
hdKeyIDPair := HDKeyPairMainNet
_, err := NewMaster(bytes.Repeat([]byte{0x00}, 15), hdKeyIDPair.PrivateKeyID)
if err != ErrInvalidSeedLen {
t.Fatalf("NewMaster: mismatched error -- got: %v, want: %v",
err, ErrInvalidSeedLen)
}
// Should get an error when seed has too many bytes.
_, err = NewMaster(bytes.Repeat([]byte{0x00}, 65), hdKeyIDPair.PrivateKeyID)
if err != ErrInvalidSeedLen {
t.Fatalf("NewMaster: mismatched error -- got: %v, want: %v",
err, ErrInvalidSeedLen)
}
// Generate a new key and neuter it to a public extended key.
seed, err := GenerateSeed(RecommendedSeedLen)
if err != nil {
t.Fatalf("GenerateSeed: unexpected error: %v", err)
}
extKey, err := NewMaster(seed, hdKeyIDPair.PrivateKeyID)
if err != nil {
t.Fatalf("NewMaster: unexpected error: %v", err)
}
pubKey, err := extKey.Neuter()
if err != nil {
t.Fatalf("Neuter: unexpected error: %v", err)
}
// Deriving a hardened child extended key should fail from a public key.
_, err = pubKey.Child(HardenedKeyStart)
if err != ErrDeriveHardFromPublic {
t.Fatalf("Child: mismatched error -- got: %v, want: %v",
err, ErrDeriveHardFromPublic)
}
// NewKeyFromString failure tests.
tests := []struct {
name string
key string
err error
neuter bool
neuterErr error
}{
{
name: "invalid key length",
key: "xpub1234",
err: ErrInvalidKeyLen,
},
{
name: "bad checksum",
key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EBygr15",
err: ErrBadChecksum,
},
{
name: "pubkey not on curve",
key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ1hr9Rwbk95YadvBkQXxzHBSngB8ndpW6QH7zhhsXZ2jHyZqPjk",
err: errors.New("invalid square root"),
},
{
name: "unsupported version",
key: "xbad4LfUL9eKmA66w2GJdVMqhvDmYGJpTGjWRAtjHqoUY17sGaymoMV9Cm3ocn9Ud6Hh2vLFVC7KSKCRVVrqc6dsEdsTjRV1WUmkK85YEUujAPX",
err: nil,
neuter: true,
neuterErr: ErrUnknownHDKeyID,
},
}
for i, test := range tests {
extKey, err := NewKeyFromString(test.key)
if !testutil.AreErrorsEqual(err, test.err) {
t.Errorf("NewKeyFromString #%d (%s): mismatched error "+
"-- got: %v, want: %v", i, test.name, err,
test.err)
continue
}
if test.neuter {
_, err := extKey.Neuter()
if !reflect.DeepEqual(err, test.neuterErr) {
t.Errorf("Neuter #%d (%s): mismatched error "+
"-- got: %v, want: %v", i, test.name,
err, test.neuterErr)
continue
}
}
}
}
// TestZero ensures that zeroing an extended key works as intended.
func TestZero(t *testing.T) {
tests := []struct {
name string
master string
extKey string
hdKeyIDPair HDKeyIDPair
}{
// Test vector 1
{
name: "test vector 1 chain m",
master: "000102030405060708090a0b0c0d0e0f",
extKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
hdKeyIDPair: HDKeyPairMainNet,
},
// Test vector 2
{
name: "test vector 2 chain m",
master: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
extKey: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
hdKeyIDPair: HDKeyPairMainNet,
},
}
// Use a closure to test that a key is zeroed since the tests create
// keys in different ways and need to test the same things multiple
// times.
testZeroed := func(i int, testName string, key *ExtendedKey) bool {
// Zeroing a key should result in it no longer being private
if key.IsPrivate() {
t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+
"want private %v, got private %v", i, testName,
false, key.IsPrivate())
return false
}
parentFP := key.ParentFingerprint()
if parentFP != 0 {
t.Errorf("ParentFingerprint #%d (%s): mismatched "+
"parent fingerprint -- want %d, got %d", i,
testName, 0, parentFP)
return false
}
wantKey := "zeroed extended key"
serializedKey := key.String()
if serializedKey != wantKey {
t.Errorf("String #%d (%s): mismatched serialized key "+
"-- want %s, got %s", i, testName, wantKey,
serializedKey)
return false
}
wantErr := ErrNotPrivExtKey
_, err := key.ECPrivKey()
if !reflect.DeepEqual(err, wantErr) {
t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+
"%v, got %v", i, testName, wantErr, err)
return false
}
wantErr = errors.New("pubkey string is empty")
_, err = key.ECPubKey()
if !testutil.AreErrorsEqual(err, wantErr) {
t.Errorf("ECPubKey #%d (%s): mismatched error: want "+
"%v, got %v", i, testName, wantErr, err)
return false
}
wantAddr := "kaspa:qz689gnx6z7cnsfhq6jpxtx0k9hhcwulev2ematvwr"
addr, err := key.Address(util.Bech32PrefixKaspa)
if err != nil {
t.Errorf("Addres s #%d (%s): unexpected error: %v", i,
testName, err)
return false
}
if addr.EncodeAddress() != wantAddr {
t.Errorf("Address #%d (%s): mismatched address -- want "+
"%s, got %s", i, testName, wantAddr,
addr.EncodeAddress())
return false
}
return true
}
for i, test := range tests {
// Create new key from seed and get the neutered version.
masterSeed, err := hex.DecodeString(test.master)
if err != nil {
t.Errorf("DecodeString #%d (%s): unexpected error: %v",
i, test.name, err)
continue
}
key, err := NewMaster(masterSeed, test.hdKeyIDPair.PrivateKeyID)
if err != nil {
t.Errorf("NewMaster #%d (%s): unexpected error when "+
"creating new master key: %v", i, test.name,
err)
continue
}
neuteredKey, err := key.Neuter()
if err != nil {
t.Errorf("Neuter #%d (%s): unexpected error: %v", i,
test.name, err)
continue
}
// Ensure both non-neutered and neutered keys are zeroed
// properly.
key.Zero()
if !testZeroed(i, test.name+" from seed not neutered", key) {
continue
}
neuteredKey.Zero()
if !testZeroed(i, test.name+" from seed neutered", key) {
continue
}
// Deserialize key and get the neutered version.
key, err = NewKeyFromString(test.extKey)
if err != nil {
t.Errorf("NewKeyFromString #%d (%s): unexpected "+
"error: %v", i, test.name, err)
continue
}
neuteredKey, err = key.Neuter()
if err != nil {
t.Errorf("Neuter #%d (%s): unexpected error: %v", i,
test.name, err)
continue
}
// Ensure both non-neutered and neutered keys are zeroed
// properly.
key.Zero()
if !testZeroed(i, test.name+" deserialized not neutered", key) {
continue
}
neuteredKey.Zero()
if !testZeroed(i, test.name+" deserialized neutered", key) {
continue
}
}
}
// TestMaximumDepth ensures that attempting to retrieve a child key when already
// at the maximum depth is not allowed. The serialization of a BIP32 key uses
// uint8 to encode the depth. This implicitly bounds the depth of the tree to
// 255 derivations. Here we test that an error is returned after 'max uint8'.
func TestMaximumDepth(t *testing.T) {
extKey, err := NewMaster([]byte(`abcd1234abcd1234abcd1234abcd1234`), HDKeyPairMainNet.PrivateKeyID)
if err != nil {
t.Fatalf("NewMaster: unexpected error: %v", err)
}
for i := uint8(0); i < math.MaxUint8; i++ {
if extKey.Depth() != i {
t.Fatalf("extendedkey depth %d should match expected value %d",
extKey.Depth(), i)
}
newKey, err := extKey.Child(1)
if err != nil {
t.Fatalf("Child: unexpected error: %v", err)
}
extKey = newKey
}
noKey, err := extKey.Child(1)
if err != ErrDeriveBeyondMaxDepth {
t.Fatalf("Child: mismatched error: want %v, got %v",
ErrDeriveBeyondMaxDepth, err)
}
if noKey != nil {
t.Fatal("Child: deriving 256th key should not succeed")
}
}