diff --git a/examples/graphql/graphql.go b/examples/graphql/graphql.go index 06882e07..6cf75a86 100644 --- a/examples/graphql/graphql.go +++ b/examples/graphql/graphql.go @@ -1,7 +1,7 @@ package main import ( - "github.com/BuxOrg/go-buxclient/xkeys" + "github.com/BuxOrg/go-buxclient/xpriv" "log" "github.com/BuxOrg/go-buxclient" @@ -10,14 +10,14 @@ import ( func main() { // Generate keys - keys, resErr := xkeys.Generate() + keys, resErr := xpriv.Generate() if resErr != nil { log.Fatalln(resErr.Error()) } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(keys.Xpriv.String()), + buxclient.WithXPriv(keys.XPub().String()), buxclient.WithGraphQL("localhost:3001"), buxclient.WithDebugging(true), buxclient.WithSignRequest(true), diff --git a/examples/http/http.go b/examples/http/http.go index 5fc40de5..e5159d09 100644 --- a/examples/http/http.go +++ b/examples/http/http.go @@ -1,7 +1,7 @@ package main import ( - "github.com/BuxOrg/go-buxclient/xkeys" + "github.com/BuxOrg/go-buxclient/xpriv" "log" "github.com/BuxOrg/go-buxclient" @@ -10,14 +10,14 @@ import ( func main() { // Generate keys - keys, resErr := xkeys.Generate() + keys, resErr := xpriv.Generate() if resErr != nil { log.Fatalln(resErr.Error()) } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(keys.Xpriv.String()), + buxclient.WithXPriv(keys.String()), 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..132e4c73 --- /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.String()) + 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.String()) + fmt.Println("XPub: ", xpriv3.XPub().String()) + fmt.Println("Mnemonic: ", xpriv3.Mnemonic()) + + //Generate keys from string + xpriv2, err := xpriv.FromString(keys.String()) + if err != nil { + panic(err) + } + + fmt.Println("<-- FromString method") + fmt.Println("XPriv: ", xpriv2.String()) + 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 67c04a3f..8102995d 100644 --- a/examples/new_paymail/new_paymail.go +++ b/examples/new_paymail/new_paymail.go @@ -2,7 +2,7 @@ package main import ( "context" - "github.com/BuxOrg/go-buxclient/xkeys" + "github.com/BuxOrg/go-buxclient/xpriv" "log" "github.com/BuxOrg/go-buxclient" @@ -11,14 +11,14 @@ import ( func main() { // Generate keys - keys, resErr := xkeys.Generate() + keys, resErr := xpriv.Generate() if resErr != nil { log.Fatalln(resErr.Error()) } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(keys.Xpriv.String()), + buxclient.WithXPriv(keys.String()), 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(), keys.Xpub.String(), "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 24e33939..58eb17d0 100644 --- a/examples/register_xpub/register_xpub.go +++ b/examples/register_xpub/register_xpub.go @@ -2,7 +2,7 @@ package main import ( "context" - "github.com/BuxOrg/go-buxclient/xkeys" + "github.com/BuxOrg/go-buxclient/xpriv" "log" buxmodels "github.com/BuxOrg/bux-models" @@ -11,14 +11,14 @@ import ( func main() { // Generate keys - keys, resErr := xkeys.Generate() + keys, resErr := xpriv.Generate() if resErr != nil { log.Fatalln(resErr.Error()) } // Create a client buxClient, err := buxclient.New( - buxclient.WithXPriv(keys.Xpriv.String()), + buxclient.WithXPriv(keys.String()), buxclient.WithHTTP("localhost:3001"), buxclient.WithDebugging(true), buxclient.WithSignRequest(true), @@ -28,10 +28,10 @@ func main() { } if err = buxClient.NewXpub( - context.Background(), keys.Xpub.String(), &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: " + keys.Xpub.String()) + log.Println("registered xPub: " + keys.XPub().String()) } diff --git a/xkeys/xkeys.go b/xkeys/xkeys.go deleted file mode 100644 index 44019d8b..00000000 --- a/xkeys/xkeys.go +++ /dev/null @@ -1,58 +0,0 @@ -package xkeys - -import ( - "github.com/BuxOrg/go-buxclient/transports" - "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 *bip32.ExtendedKey - Xpub *bip32.ExtendedKey - Mnemonic string -} - -// Generate generates a random set of keys - xpriv, xpb and mnemonic -func Generate() (*Keys, transports.ResponseError) { - entropy, err := bip39.GenerateEntropy(160) - if err != nil { - return nil, transports.WrapError(err) - } - - mnemonic, seed, err := bip39.Mnemonic(entropy, "") - - if err != nil { - return nil, transports.WrapError(err) - } - - hdXpriv, err := bip32.NewMaster(seed, &chaincfg.MainNet) - - if err != nil { - return nil, transports.WrapError(err) - } - - hdXpub, err := hdXpriv.Neuter() - if err != nil { - return nil, transports.WrapError(err) - } - - keys := &Keys{ - Xpriv: hdXpriv, - Xpub: hdXpub, - Mnemonic: mnemonic, - } - - return keys, nil -} - -// GetPublicKeyFromHDPrivateKey returns the public key from the HD private key -func GetPublicKeyFromHDPrivateKey(hdXpriv string) (*bip32.ExtendedKey, transports.ResponseError) { - hdKey, err := bip32.NewKeyFromString(hdXpriv) - hdXpub, err := hdKey.Neuter() - if err != nil { - return nil, transports.WrapError(err) - } - return hdXpub, nil -} diff --git a/xpriv/xpriv.go b/xpriv/xpriv.go new file mode 100644 index 00000000..396bfbbb --- /dev/null +++ b/xpriv/xpriv.go @@ -0,0 +1,139 @@ +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 struct { + Xpub string +} + +// Key represents basic key methods +type Key interface { + String() 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 +} + +// String return hierarchical deterministic private key +func (k *Keys) String() 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 k.Xpub +} + +// 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) (*bip32.ExtendedKey, *bip32.ExtendedKey, 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 +}