Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TA-3386]: NewWalletFromMnemonic #30

Merged
merged 9 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/xrpl-go-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main, 'v*' ]
branches: [ '**' ]
GuillemGarciaDev marked this conversation as resolved.
Show resolved Hide resolved
jobs:
build:
name: Lint and Test
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ test-all:

test-binary-codec:
@echo "Running Go tests for binary codec package..."
@go test -v ./codec/binary/...
@go test -v ./binary-codec/...
@echo "Tests complete!"

test-address-codec:
@echo "Running Go tests for address codec package..."
@go test -v ./codec/address/...
@go test -v ./address-codec/...
@echo "Tests complete!"

test-keypairs:
Expand Down
9 changes: 6 additions & 3 deletions address-codec/address_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ const (
NodePublicKeyPrefix = 0x1C
// ED25519 prefix - value is 237
ED25519Prefix = 0xED
// SECP256K1 prefix - value is 0
SECP256K1Prefix = 0x00
)

type CryptoAlgorithm uint8

const (
Undefined CryptoAlgorithm = iota
// Undefined is the default value for the CryptoAlgorithm type (max uint8 value).
Undefined CryptoAlgorithm = 64
ED25519 = ED25519Prefix
SECP256K1 = FamilySeedPrefix
SECP256K1 = SECP256K1Prefix
)

func (c CryptoAlgorithm) String() string {
Expand Down Expand Up @@ -150,7 +153,7 @@ func EncodeSeed(entropy []byte, encodingType CryptoAlgorithm) (string, error) {
prefix := []byte{0x01, 0xe1, 0x4b}
return Encode(entropy, prefix, FamilySeedLength), nil
case SECP256K1:
prefix := []byte{SECP256K1}
prefix := []byte{FamilySeedPrefix}
return Encode(entropy, prefix, FamilySeedLength), nil
default:
return "", errors.New("encoding type must be `ed25519` or `secp256k1`")
Expand Down
39 changes: 37 additions & 2 deletions examples/wallet/generate_wallet_and_sign_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ const (
)

func main() {
mnemonicWallet, err := xrpl.NewWalletFromMnemonic("monster march exile fee forget response seven push dragon oil clinic attack black miss craft surface patient stomach tank float cabbage visual image resource")
if err != nil {
panic(err)
}

fmt.Println("Wallet generated from mnemonic")

fmt.Printf("Private key: %s\n", mnemonicWallet.PrivateKey)
fmt.Printf("Public key: %s\n", mnemonicWallet.PublicKey)
fmt.Printf("Classic address: %s\n", mnemonicWallet.ClassicAddress)
fmt.Printf("Seed: %s\n", mnemonicWallet.Seed)

wallet, err := xrpl.NewWallet(addresscodec.ED25519)
if err != nil {
panic(err)
Expand Down Expand Up @@ -48,8 +60,6 @@ func main() {
fmt.Printf("Classic address: %s\n", walletFromSecret.ClassicAddress)
fmt.Printf("Seed: %s\n", walletFromSecret.Seed)

fmt.Println("\nSigning a transaction")

tx := transactions.Payment{
BaseTx: transactions.BaseTx{
Account: types.Address(wallet.ClassicAddress),
Expand Down Expand Up @@ -80,11 +90,36 @@ func main() {

fmt.Println(tx.Flatten())

fmt.Println("\nSigning a transaction with wallet generated from seed")

txBlob, hash, err := wallet.Sign(tx.Flatten())
if err != nil {
panic(err)
}

fmt.Printf("txBlob: %s\n", txBlob)
fmt.Printf("hash: %s\n", hash)

fmt.Println("\nSigning a transaction with wallet generated from mnemonic")

mnemonicTx := transactions.Payment{
BaseTx: transactions.BaseTx{
TransactionType: "Payment",
Account: types.Address(mnemonicWallet.ClassicAddress),
},
Amount: types.IssuedCurrencyAmount{
Issuer: Issuer,
Currency: Currency,
Value: Value,
},
Destination: types.Address(DestinationAddress),
}

mnemonicTxBlob, mnemonicHash, err := mnemonicWallet.Sign(mnemonicTx.Flatten())
if err != nil {
panic(err)
}

fmt.Printf("txBlob: %s\n", mnemonicTxBlob)
fmt.Printf("hash: %s\n", mnemonicHash)
}
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ require (
github.com/gorilla/websocket v1.5.0
github.com/mitchellh/mapstructure v1.5.0
github.com/stretchr/testify v1.8.4
github.com/tyler-smith/go-bip32 v1.0.0
github.com/tyler-smith/go-bip39 v1.1.0
github.com/ugorji/go/codec v1.2.11
)

require (
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
)
Expand Down
20 changes: 20 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw=
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -26,15 +32,29 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE=
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
4 changes: 4 additions & 0 deletions keypairs/keypairs.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ func getCryptoImplementation(alg addresscodec.CryptoAlgorithm) CryptoImplementat
switch alg {
case addresscodec.ED25519:
return &ed25519Alg{}
case addresscodec.SECP256K1:
return &secp256k1Alg{}
default:
return nil
}
Expand All @@ -92,6 +94,8 @@ func getCryptoImplementationFromKey(k string) CryptoImplementation {
switch deformatKey(k)[0] {
case addresscodec.ED25519:
return &ed25519Alg{}
case addresscodec.SECP256K1:
return &secp256k1Alg{}
default:
return nil
}
Expand Down
103 changes: 54 additions & 49 deletions xrpl/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package xrpl

import (
"encoding/hex"
"errors"
"fmt"
"strings"

addresscodec "github.com/Peersyst/xrpl-go/address-codec"
binarycodec "github.com/Peersyst/xrpl-go/binary-codec"
"github.com/Peersyst/xrpl-go/keypairs"
"github.com/Peersyst/xrpl-go/xrpl/hash"
"github.com/tyler-smith/go-bip32"
"github.com/tyler-smith/go-bip39"
)

// A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
Expand Down Expand Up @@ -65,55 +70,55 @@ func NewWalletFromSecret(seed string) (Wallet, error) {

// // Derives a wallet from a bip39 or RFC1751 mnemonic (Defaults to bip39).
// // Returns a Wallet object. If an error occurs, it will be returned.
// func NewWalletFromMnemonic(mnemonic string) (*Wallet, error) {
// // Validate the mnemonic
// if !bip39.IsMnemonicValid(mnemonic) {
// return nil, errors.New("invalid mnemonic")
// }

// // Generate seed from mnemonic
// seed := bip39.NewSeed(mnemonic, "")

// // Derive the master key
// masterKey, err := bip32.NewMasterKey(seed)
// if err != nil {
// return nil, err
// }

// // Derive the key using the path m/44'/144'/0'/0/0
// path := []uint32{
// 44 + bip32.FirstHardenedChild,
// 144 + bip32.FirstHardenedChild,
// bip32.FirstHardenedChild,
// 0,
// 0,
// }

// key := masterKey
// for _, childNum := range path {
// key, err = key.NewChildKey(childNum)
// if err != nil {
// return nil, err
// }
// }

// // Convert the private key to the format expected by the XRPL library
// privKey := strings.ToUpper(hex.EncodeToString(key.Key))
// pubKey := strings.ToUpper(hex.EncodeToString(key.PublicKey().Key))

// // Derive classic address
// classicAddr, err := keypairs.DeriveClassicAddress(pubKey)
// if err != nil {
// return nil, err
// }

// return &Wallet{
// PublicKey: pubKey,
// PrivateKey: fmt.Sprintf("00%s", privKey),
// ClassicAddress: classicAddr,
// Seed: "", // We don't have the seed in this case
// }, nil
// }
func NewWalletFromMnemonic(mnemonic string) (*Wallet, error) {
// Validate the mnemonic
if !bip39.IsMnemonicValid(mnemonic) {
return nil, errors.New("invalid mnemonic")
}

// Generate seed from mnemonic
seed := bip39.NewSeed(mnemonic, "")

// Derive the master key
masterKey, err := bip32.NewMasterKey(seed)
if err != nil {
return nil, err
}

// Derive the key using the path m/44'/144'/0'/0/0
path := []uint32{
44 + bip32.FirstHardenedChild,
144 + bip32.FirstHardenedChild,
bip32.FirstHardenedChild,
0,
0,
}

key := masterKey
for _, childNum := range path {
key, err = key.NewChildKey(childNum)
if err != nil {
return nil, err
}
}

// Convert the private key to the format expected by the XRPL library
privKey := strings.ToUpper(hex.EncodeToString(key.Key))
pubKey := strings.ToUpper(hex.EncodeToString(key.PublicKey().Key))

// Derive classic address
classicAddr, err := keypairs.DeriveClassicAddress(pubKey)
if err != nil {
return nil, err
}

return &Wallet{
PublicKey: pubKey,
PrivateKey: fmt.Sprintf("00%s", privKey),
GuillemGarciaDev marked this conversation as resolved.
Show resolved Hide resolved
ClassicAddress: classicAddr,
Seed: "", // We don't have the seed in this case
}, nil
}

// Signs a transaction offline.
// In order for a transaction to be validated, it must be signed by the account sending the transaction to prove
Expand Down
Loading
Loading