diff --git a/examples/graphql/graphql.go b/examples/graphql/graphql.go index a6419727..ad3c8f89 100644 --- a/examples/graphql/graphql.go +++ b/examples/graphql/graphql.go @@ -1,21 +1,23 @@ package main import ( + "github.com/BuxOrg/go-buxclient/xpriv" "log" "github.com/BuxOrg/go-buxclient" - "github.com/bitcoinschema/go-bitcoin/v2" ) func main() { - // Example xPub - masterKey, _ := bitcoin.GenerateHDKey(bitcoin.SecureSeedLength) - // rawXPub, _ := bitcoin.GetExtendedPublicKey(masterKey) + // Generate keys + keys, resErr := xpriv.Generate() + if resErr != nil { + log.Fatalln(resErr.Error()) + } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(masterKey.String()), + buxclient.WithXPriv(keys.XPriv()), buxclient.WithGraphQL("localhost:3001"), buxclient.WithDebugging(true), buxclient.WithSignRequest(true), diff --git a/examples/http/http.go b/examples/http/http.go index 1f38302e..c4e988c9 100644 --- a/examples/http/http.go +++ b/examples/http/http.go @@ -1,21 +1,23 @@ package main import ( + "github.com/BuxOrg/go-buxclient/xpriv" "log" "github.com/BuxOrg/go-buxclient" - "github.com/bitcoinschema/go-bitcoin/v2" ) func main() { - // Example xPub - masterKey, _ := bitcoin.GenerateHDKey(bitcoin.SecureSeedLength) - // rawXPub, _ := bitcoin.GetExtendedPublicKey(masterKey) + // Generate keys + keys, resErr := xpriv.Generate() + if resErr != nil { + log.Fatalln(resErr.Error()) + } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(masterKey.String()), + buxclient.WithXPriv(keys.XPriv()), buxclient.WithHTTP("localhost:3001"), buxclient.WithDebugging(true), buxclient.WithSignRequest(true), diff --git a/examples/keys/keys.go b/examples/keys/keys.go new file mode 100644 index 00000000..23ac492f --- /dev/null +++ b/examples/keys/keys.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "github.com/BuxOrg/go-buxclient/xpriv" +) + +func main() { + //Generate keys + keys, err := xpriv.Generate() + if err != nil { + panic(err) + } + + fmt.Println("<-- Generate method") + fmt.Println("XPriv: ", keys.XPriv()) + fmt.Println("XPub: ", keys.XPub().String()) + fmt.Println("Mnemonic: ", keys.Mnemonic()) + + //Generate keys from mnemonic string + xpriv3, err := xpriv.FromMnemonic(keys.Mnemonic()) + if err != nil { + panic(err) + } + + fmt.Println("<-- FromMnemonic method") + fmt.Println("XPriv: ", xpriv3.XPriv()) + fmt.Println("XPub: ", xpriv3.XPub().String()) + fmt.Println("Mnemonic: ", xpriv3.Mnemonic()) + + //Generate keys from string + xpriv2, err := xpriv.FromString(keys.XPriv()) + if err != nil { + panic(err) + } + + fmt.Println("<-- FromString method") + fmt.Println("XPriv: ", xpriv2.XPriv()) + fmt.Println("XPub: ", xpriv2.XPub().String()) + fmt.Println("Can not get mnemonic from keys generated from string") +} diff --git a/examples/new_paymail/new_paymail.go b/examples/new_paymail/new_paymail.go index 1415d605..013a9922 100644 --- a/examples/new_paymail/new_paymail.go +++ b/examples/new_paymail/new_paymail.go @@ -2,23 +2,23 @@ package main import ( "context" + "github.com/BuxOrg/go-buxclient/xpriv" "log" - "github.com/bitcoinschema/go-bitcoin/v2" - "github.com/BuxOrg/go-buxclient" ) func main() { - // Example xPub - masterKey, _ := bitcoin.GenerateHDKey(bitcoin.SecureSeedLength) - - rawXPub, _ := bitcoin.GetExtendedPublicKey(masterKey) + // Generate keys + keys, resErr := xpriv.Generate() + if resErr != nil { + log.Fatalln(resErr.Error()) + } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(masterKey.String()), + buxclient.WithXPriv(keys.XPriv()), buxclient.WithHTTP("localhost:3001"), buxclient.WithDebugging(true), buxclient.WithSignRequest(true), @@ -28,7 +28,7 @@ func main() { } log.Printf("client loaded - bux debug: %v", buxClient.IsDebug()) - err = buxClient.NewPaymail(context.Background(), rawXPub, "foo@domain.com", "", "Foo", nil) + err = buxClient.NewPaymail(context.Background(), keys.XPub().String(), "foo@domain.com", "", "Foo", nil) if err != nil { log.Fatalln(err.Error()) diff --git a/examples/register_xpub/register_xpub.go b/examples/register_xpub/register_xpub.go index 5a1c60dd..fb667dc7 100644 --- a/examples/register_xpub/register_xpub.go +++ b/examples/register_xpub/register_xpub.go @@ -2,21 +2,23 @@ package main import ( "context" + "github.com/BuxOrg/go-buxclient/xpriv" "log" buxmodels "github.com/BuxOrg/bux-models" "github.com/BuxOrg/go-buxclient" - "github.com/bitcoinschema/go-bitcoin/v2" ) func main() { - // Example xPub - masterKey, _ := bitcoin.GenerateHDKey(bitcoin.SecureSeedLength) - rawXPub, _ := bitcoin.GetExtendedPublicKey(masterKey) + // Generate keys + keys, resErr := xpriv.Generate() + if resErr != nil { + log.Fatalln(resErr.Error()) + } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(masterKey.String()), + buxclient.WithXPriv(keys.XPriv()), buxclient.WithHTTP("localhost:3001"), buxclient.WithDebugging(true), buxclient.WithSignRequest(true), @@ -26,10 +28,10 @@ func main() { } if err = buxClient.NewXpub( - context.Background(), rawXPub, &buxmodels.Metadata{"example_field": "example_data"}, + context.Background(), keys.XPub().String(), &buxmodels.Metadata{"example_field": "example_data"}, ); err != nil { log.Fatalln(err.Error()) } - log.Println("registered xPub: " + rawXPub) + log.Println("registered xPub: " + keys.XPub().String()) } diff --git a/xpriv/xpriv.go b/xpriv/xpriv.go new file mode 100644 index 00000000..a73b001d --- /dev/null +++ b/xpriv/xpriv.go @@ -0,0 +1,137 @@ +package xpriv + +import ( + "fmt" + + "github.com/libsv/go-bk/bip32" + "github.com/libsv/go-bk/bip39" + "github.com/libsv/go-bk/chaincfg" +) + +// Keys is a struct containing the xpriv, xpub and mnemonic +type Keys struct { + xpriv string + xpub PublicKey + mnemonic string +} + +// PublicKey is a struct containing public key information +type PublicKey string + +// Key represents basic key methods +type Key interface { + XPriv() string + XPub() PubKey +} + +// PubKey represents public key methods +type PubKey interface { + String() string +} + +// KeyWithMnemonic represents methods for generated keys +type KeyWithMnemonic interface { + Key + Mnemonic() string +} + +// XPub return hierarchical struct which contain xpub info +func (k *Keys) XPub() PubKey { + return k.xpub +} + +// XPriv return hierarchical deterministic private key +func (k *Keys) XPriv() string { + return k.xpriv +} + +// Mnemonic return mnemonic from which keys where generated +func (k *Keys) Mnemonic() string { + return k.mnemonic +} + +// String return hierarchical deterministic publick ey +func (k PublicKey) String() string { + return string(k) +} + +// Generate generates a random set of keys - xpriv, xpb and mnemonic +func Generate() (KeyWithMnemonic, error) { + entropy, err := bip39.GenerateEntropy(160) + if err != nil { + return nil, fmt.Errorf("generate method: key generation error when creating entropy: %w", err) + } + + mnemonic, seed, err := bip39.Mnemonic(entropy, "") + + if err != nil { + return nil, fmt.Errorf("generate method: key generation error when creating mnemonic: %w", err) + } + + hdXpriv, hdXpub, err := createXPrivAndXPub(seed) + if err != nil { + return nil, err + } + + keys := &Keys{ + xpriv: hdXpriv.String(), + xpub: PublicKey(hdXpub.String()), + mnemonic: mnemonic, + } + + return keys, nil +} + +// FromMnemonic generates Keys based on given mnemonic +func FromMnemonic(mnemonic string) (KeyWithMnemonic, error) { + seed, err := bip39.MnemonicToSeed(mnemonic, "") + if err != nil { + return nil, fmt.Errorf("FromMnemonic method: error when creating seed: %w", err) + } + + hdXpriv, hdXpub, err := createXPrivAndXPub(seed) + if err != nil { + return nil, fmt.Errorf("FromMnemonic method: %w", err) + } + + keys := &Keys{ + xpriv: hdXpriv.String(), + xpub: PublicKey(hdXpub.String()), + mnemonic: mnemonic, + } + + return keys, nil +} + +// FromString generates keys from given xpriv +func FromString(xpriv string) (Key, error) { + hdXpriv, err := bip32.NewKeyFromString(xpriv) + if err != nil { + return nil, fmt.Errorf("FromString method: key generation error when creating hd private key: %w", err) + } + + hdXpub, err := hdXpriv.Neuter() + if err != nil { + return nil, fmt.Errorf("FromString method: key generation error when creating hd public hey: %w", err) + } + + keys := &Keys{ + xpriv: hdXpriv.String(), + xpub: PublicKey(hdXpub.String()), + } + + return keys, nil +} + +func createXPrivAndXPub(seed []byte) (hdXpriv *bip32.ExtendedKey, hdXpub *bip32.ExtendedKey, err error) { + hdXpriv, err = bip32.NewMaster(seed, &chaincfg.MainNet) + if err != nil { + return nil, nil, fmt.Errorf("key generation error when creating hd private key: %w", err) + } + + hdXpub, err = hdXpriv.Neuter() + if err != nil { + return nil, nil, fmt.Errorf("key generation error when creating hd public hey: %w", err) + } + return hdXpriv, hdXpub, nil +}