Skip to content

Commit

Permalink
add support 12-word seed and 24-word seed
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinBeBoy committed Mar 29, 2024
1 parent 579bc3f commit a8da03a
Show file tree
Hide file tree
Showing 18 changed files with 467 additions and 187 deletions.
4 changes: 2 additions & 2 deletions libwallet/assets/btc/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func hardenedKey(key uint32) uint32 {
}

// DeriveAccountXpub derives the xpub for the given account.
func (asset *Asset) DeriveAccountXpub(seedMnemonic string, account uint32, params *chaincfg.Params) (xpub string, err error) {
seed, err := sharedW.DecodeSeedMnemonic(seedMnemonic, asset.Type)
func (asset *Asset) DeriveAccountXpub(seedMnemonic string, wordSeedType sharedW.WordSeedType, account uint32, params *chaincfg.Params) (xpub string, err error) {
seed, err := sharedW.DecodeSeedMnemonic(seedMnemonic, asset.Type, wordSeedType)
if err != nil {
return "", err
}
Expand Down
11 changes: 9 additions & 2 deletions libwallet/assets/ltc/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ltcsuite/ltcd/ltcutil"
"github.com/ltcsuite/ltcd/ltcutil/hdkeychain"
"github.com/ltcsuite/ltcwallet/waddrmgr"
"github.com/tyler-smith/go-bip39"
)

const (
Expand Down Expand Up @@ -56,8 +57,14 @@ func (asset *Asset) ToAmount(v int64) sharedW.AssetAmount {
}

// DeriveAccountXpub derives the xpub for the given account.
func (asset *Asset) DeriveAccountXpub(seedMnemonic string, account uint32, params *chaincfg.Params) (xpub string, err error) {
seed, err := walletseed.DecodeUserInput(seedMnemonic)
func (asset *Asset) DeriveAccountXpub(seedMnemonic string, wordSeedType sharedW.WordSeedType, account uint32, params *chaincfg.Params) (xpub string, err error) {
var seed []byte
if wordSeedType == sharedW.WordSeed33 {
seed, err = walletseed.DecodeUserInput(seedMnemonic)
} else {
seed, err = bip39.EntropyFromMnemonic(seedMnemonic)
}
// seed, err := walletseed.DecodeUserInput(seedMnemonic)
if err != nil {
return "", err
}
Expand Down
23 changes: 23 additions & 0 deletions libwallet/assets/wallet/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/asdine/storm"
btchdkeychain "github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/crypto-power/cryptopower/libwallet/assets/wallet/wordlist"
"github.com/crypto-power/cryptopower/libwallet/utils"
"github.com/decred/dcrd/dcrutil/v4"
)
Expand Down Expand Up @@ -52,6 +53,7 @@ type AuthInfo struct {
Name string
PrivatePass string
PrivatePassType int32
WordSeedType WordSeedType
}

type BlockInfo struct {
Expand Down Expand Up @@ -422,3 +424,24 @@ type UnspentOutput struct {
ReceiveTime time.Time
Tree int8
}

type WordSeedType int

const (
WordSeed12 WordSeedType = 12
WordSeed24 WordSeedType = 24
WordSeed33 WordSeedType = 33
)

func (s WordSeedType) ToInt() int {
return int(s)
}

func (s WordSeedType) AllSeeds() []string {
switch s {
case WordSeed24, WordSeed12:
return wordlist.BIP39WordList()
default:
return wordlist.PGPWordList()
}
}
23 changes: 16 additions & 7 deletions libwallet/assets/wallet/wallet_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package wallet

import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
Expand All @@ -27,7 +28,8 @@ type Wallet struct {
db *storm.DB
logDir string

EncryptedSeed []byte
EncryptedSeed []byte
// SeedType WordSeedType
IsRestored bool
HasDiscoveredAccounts bool
PrivatePassphraseType int32
Expand Down Expand Up @@ -328,11 +330,17 @@ func (wallet *Wallet) SetBirthday(birthday time.Time) {
func CreateNewWallet(pass *AuthInfo, loader loader.AssetLoader,
params *InitParams, assetType utils.AssetType,
) (*Wallet, error) {
seed, err := generateSeed(assetType)
if pass.WordSeedType.ToInt() == 0 {
return nil, errors.New("Please select word seed type.")

Check warning on line 334 in libwallet/assets/wallet/wallet_shared.go

View workflow job for this annotation

GitHub Actions / Build

error-strings: error strings should not be capitalized or end with punctuation or a newline (revive)
}

seed, err := generateSeed(assetType, pass.WordSeedType)
if err != nil {
return nil, err
}

fmt.Println("-----------generateSeed--------->", seed)

encryptedSeed, err := encryptWalletSeed([]byte(pass.PrivatePass), seed)
if err != nil {
return nil, err
Expand All @@ -346,7 +354,7 @@ func CreateNewWallet(pass *AuthInfo, loader loader.AssetLoader,
logDir: params.LogDir,
CreatedAt: time.Now(),
EncryptedSeed: encryptedSeed,

// SeedType: pass.WordSeedType,
PrivatePassphraseType: pass.PrivatePassType,
HasDiscoveredAccounts: true,
Type: assetType,
Expand All @@ -359,7 +367,7 @@ func CreateNewWallet(pass *AuthInfo, loader loader.AssetLoader,
if err != nil {
return err
}
return wallet.createWallet(pass.PrivatePass, seed)
return wallet.createWallet(pass.PrivatePass, seed, pass.WordSeedType)
}); err != nil {
return nil, err
}
Expand All @@ -373,13 +381,13 @@ func CreateNewWallet(pass *AuthInfo, loader loader.AssetLoader,
return wallet, nil
}

func (wallet *Wallet) createWallet(privatePassphrase, seedMnemonic string) error {
func (wallet *Wallet) createWallet(privatePassphrase, seedMnemonic string, wordSeedType WordSeedType) error {
log.Info("Creating Wallet")
if len(seedMnemonic) == 0 {
return errors.New(utils.ErrEmptySeed)
}

seed, err := DecodeSeedMnemonic(seedMnemonic, wallet.Type)
seed, err := DecodeSeedMnemonic(seedMnemonic, wallet.Type, wordSeedType)
if err != nil {
log.Error(err)
return err
Expand Down Expand Up @@ -482,7 +490,7 @@ func RestoreWallet(seedMnemonic string, pass *AuthInfo, loader loader.AssetLoade
if err != nil {
return err
}
return wallet.createWallet(pass.PrivatePass, seedMnemonic)
return wallet.createWallet(pass.PrivatePass, seedMnemonic, pass.WordSeedType)
}); err != nil {
return nil, err
}
Expand Down Expand Up @@ -695,6 +703,7 @@ func (wallet *Wallet) ChangePrivatePassphraseForWallet(oldPrivatePassphrase, new
oldPassphrase := []byte(oldPrivatePassphrase)
newPassphrase := []byte(newPrivatePassphrase)
encryptedSeed := wallet.EncryptedSeed

if encryptedSeed != nil {
decryptedSeed, err := decryptWalletSeed(oldPassphrase, encryptedSeed)
if err != nil {
Expand Down
71 changes: 60 additions & 11 deletions libwallet/assets/wallet/wallet_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/kevinburke/nacl"
"github.com/kevinburke/nacl/secretbox"
ltchdkeychain "github.com/ltcsuite/ltcd/ltcutil/hdkeychain"
"github.com/tyler-smith/go-bip39"
"golang.org/x/crypto/scrypt"
)

Expand Down Expand Up @@ -122,6 +123,8 @@ func (wallet *Wallet) DecryptSeed(privatePassphrase string) (string, error) {
return "", errors.New(utils.ErrInvalid)
}

fmt.Println("-------EncryptedSeed------>", string(wallet.EncryptedSeed))

return decryptWalletSeed([]byte(privatePassphrase), wallet.EncryptedSeed)
}

Expand Down Expand Up @@ -177,28 +180,60 @@ func decryptWalletSeed(pass []byte, encryptedSeed []byte) (string, error) {

// For use with gomobile bind,
// doesn't support the alternative `GenerateSeed` function because it returns more than 2 types.
func generateSeed(assetType utils.AssetType) (v string, err error) {
var seed []byte
func generateSeed(assetType utils.AssetType, wordSeedType WordSeedType) (v string, err error) {
var entropy []byte
var length uint8 = 32
if wordSeedType == WordSeed12 {
length = 16
}
switch assetType {
case utils.BTCWalletAsset:
seed, err = btchdkeychain.GenerateSeed(btchdkeychain.RecommendedSeedLen)
entropy, err = btchdkeychain.GenerateSeed(length)
if err != nil {
return "", err
}
case utils.DCRWalletAsset:
seed, err = dcrhdkeychain.GenerateSeed(dcrhdkeychain.RecommendedSeedLen)
entropy, err = dcrhdkeychain.GenerateSeed(length)
if err != nil {
return "", err
}
case utils.LTCWalletAsset:
seed, err = ltchdkeychain.GenerateSeed(ltchdkeychain.RecommendedSeedLen)
entropy, err = ltchdkeychain.GenerateSeed(length)
if err != nil {
return "", err
}
}

if len(seed) > 0 {
return walletseed.EncodeMnemonic(seed), nil
// Tạo entropy với 128 bits cho 12 từ seed phrase
// entropy, err := bip39.NewEntropy(128)
// if err != nil {
// fmt.Println("-----NewEntropy------error---->", err)
// return "", err
// }

// Create Seed phrase from entropy
// seedPhrase, err := bip39.NewMnemonic(entropy)
// if err != nil {
// fmt.Println("-----NewMnemonic------error---->", err)
// return "", err
// }

// fmt.Println("12 từ seed phrase:", seedPhrase)

if len(entropy) > 0 {
//Use bip39 for 12-word seeds and 24-word seeds
if wordSeedType == WordSeed33 {
return walletseed.EncodeMnemonic(entropy), nil
} else {

Check warning on line 227 in libwallet/assets/wallet/wallet_utils.go

View workflow job for this annotation

GitHub Actions / Build

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)
// Create Seed phrase from entropy
seedPhrase, err := bip39.NewMnemonic(entropy)
if err != nil {
fmt.Println("-----NewMnemonic------error---->", err)
return "", err
}
fmt.Println("12 từ seed phrase:", seedPhrase)
return seedPhrase, nil
}
}

// Execution should never get here but error added as a safeguard to
Expand All @@ -207,15 +242,29 @@ func generateSeed(assetType utils.AssetType) (v string, err error) {
return "", fmt.Errorf("%v: (%v)", utils.ErrAssetUnknown, assetType)
}

func VerifySeed(seedMnemonic string, assetType utils.AssetType) bool {
_, err := DecodeSeedMnemonic(seedMnemonic, assetType)
func VerifySeed(seedMnemonic string, assetType utils.AssetType, seedType WordSeedType) bool {
_, err := DecodeSeedMnemonic(seedMnemonic, assetType, seedType)
return err == nil
}

func DecodeSeedMnemonic(seedMnemonic string, assetType utils.AssetType) (hashedSeed []byte, err error) {
// func DecodeSeedMnemonic1(seedMnemonic string, assetType utils.AssetType) (hashedSeed []byte, err error) {
// switch assetType {
// case utils.BTCWalletAsset, utils.DCRWalletAsset, utils.LTCWalletAsset:
// hashedSeed, err = bip39.EntropyFromMnemonic(seedMnemonic)
// default:
// err = fmt.Errorf("%v: (%v)", utils.ErrAssetUnknown, assetType)
// }
// return
// }

func DecodeSeedMnemonic(seedMnemonic string, assetType utils.AssetType, seedType WordSeedType) (hashedSeed []byte, err error) {
switch assetType {
case utils.BTCWalletAsset, utils.DCRWalletAsset, utils.LTCWalletAsset:
hashedSeed, err = walletseed.DecodeUserInput(seedMnemonic)
if seedType == WordSeed33 {
hashedSeed, err = walletseed.DecodeUserInput(seedMnemonic)
} else {
hashedSeed, err = bip39.EntropyFromMnemonic(seedMnemonic)
}
default:
err = fmt.Errorf("%v: (%v)", utils.ErrAssetUnknown, assetType)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

package dcr
package wordlist

import "strings"
import (
"strings"

"github.com/tyler-smith/go-bip39/wordlists"
)

func BIP39WordList() []string {
return wordlists.English
}

func PGPWordList() []string {
return strings.Split(AlternatingWords, "\n")
Expand Down
16 changes: 8 additions & 8 deletions libwallet/assets_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,28 +611,28 @@ func (mgr *AssetsManager) RootDirFileSizeInBytes(dataDir string) (int64, error)

// WalletWithSeed returns the ID of the wallet with the given seed. If a wallet
// with the given seed does not exist, it returns -1.
func (mgr *AssetsManager) WalletWithSeed(walletType utils.AssetType, seedMnemonic string) (int, error) {
func (mgr *AssetsManager) WalletWithSeed(walletType utils.AssetType, seedMnemonic string, wordSeedType sharedW.WordSeedType) (int, error) {
switch walletType {
case utils.BTCWalletAsset:
return mgr.BTCWalletWithSeed(seedMnemonic)
return mgr.BTCWalletWithSeed(seedMnemonic, wordSeedType)
case utils.DCRWalletAsset:
return mgr.DCRWalletWithSeed(seedMnemonic)
return mgr.DCRWalletWithSeed(seedMnemonic, wordSeedType)
case utils.LTCWalletAsset:
return mgr.LTCWalletWithSeed(seedMnemonic)
return mgr.LTCWalletWithSeed(seedMnemonic, wordSeedType)
default:
return -1, utils.ErrAssetUnknown
}
}

// RestoreWallet restores a wallet from the given seed.
func (mgr *AssetsManager) RestoreWallet(walletType utils.AssetType, walletName, seedMnemonic, privatePassphrase string, privatePassphraseType int32) (sharedW.Asset, error) {
func (mgr *AssetsManager) RestoreWallet(walletType utils.AssetType, walletName, seedMnemonic, privatePassphrase string, privatePassphraseType int32, wordSeedType sharedW.WordSeedType) (sharedW.Asset, error) {
switch walletType {
case utils.BTCWalletAsset:
return mgr.RestoreBTCWallet(walletName, seedMnemonic, privatePassphrase, privatePassphraseType)
return mgr.RestoreBTCWallet(walletName, seedMnemonic, privatePassphrase, wordSeedType, privatePassphraseType)
case utils.DCRWalletAsset:
return mgr.RestoreDCRWallet(walletName, seedMnemonic, privatePassphrase, privatePassphraseType)
return mgr.RestoreDCRWallet(walletName, seedMnemonic, privatePassphrase, wordSeedType, privatePassphraseType)
case utils.LTCWalletAsset:
return mgr.RestoreLTCWallet(walletName, seedMnemonic, privatePassphrase, privatePassphraseType)
return mgr.RestoreLTCWallet(walletName, seedMnemonic, privatePassphrase, wordSeedType, privatePassphraseType)
default:
return nil, utils.ErrAssetUnknown
}
Expand Down
10 changes: 6 additions & 4 deletions libwallet/btc.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ func initializeBTCWalletParameters(netType utils.NetworkType) (*chaincfg.Params,
}

// CreateNewBTCWallet creates a new BTC wallet and returns it.
func (mgr *AssetsManager) CreateNewBTCWallet(walletName, privatePassphrase string, privatePassphraseType int32) (sharedW.Asset, error) {
func (mgr *AssetsManager) CreateNewBTCWallet(walletName, privatePassphrase string, privatePassphraseType int32, wordSeedType sharedW.WordSeedType) (sharedW.Asset, error) {
pass := &sharedW.AuthInfo{
Name: walletName,
PrivatePass: privatePassphrase,
PrivatePassType: privatePassphraseType,
WordSeedType: wordSeedType,
}
wallet, err := btc.CreateNewWallet(pass, mgr.params)
if err != nil {
Expand All @@ -51,11 +52,12 @@ func (mgr *AssetsManager) CreateNewBTCWatchOnlyWallet(walletName, extendedPublic
}

// RestoreBTCWallet restores a BTC wallet from a seed and returns it.
func (mgr *AssetsManager) RestoreBTCWallet(walletName, seedMnemonic, privatePassphrase string, privatePassphraseType int32) (sharedW.Asset, error) {
func (mgr *AssetsManager) RestoreBTCWallet(walletName, seedMnemonic, privatePassphrase string, wordSeedType sharedW.WordSeedType, privatePassphraseType int32) (sharedW.Asset, error) {
pass := &sharedW.AuthInfo{
Name: walletName,
PrivatePass: privatePassphrase,
PrivatePassType: privatePassphraseType,
WordSeedType: wordSeedType,
}
wallet, err := btc.RestoreWallet(seedMnemonic, pass, mgr.params)
if err != nil {
Expand Down Expand Up @@ -100,7 +102,7 @@ func (mgr *AssetsManager) BTCWalletWithXPub(xpub string) (int, error) {
// BTCWalletWithSeed returns the ID of the BTC wallet that was created or restored
// using the same seed as the one provided. Returns -1 if no wallet uses the
// provided seed.
func (mgr *AssetsManager) BTCWalletWithSeed(seedMnemonic string) (int, error) {
func (mgr *AssetsManager) BTCWalletWithSeed(seedMnemonic string, wordSeedType sharedW.WordSeedType) (int, error) {
if len(seedMnemonic) == 0 {
return -1, errors.New(utils.ErrEmptySeed)
}
Expand All @@ -124,7 +126,7 @@ func (mgr *AssetsManager) BTCWalletWithSeed(seedMnemonic string) (int, error) {
if accs.AccountNumber == waddrmgr.ImportedAddrAccount {
continue
}
xpub, err := asset.DeriveAccountXpub(seedMnemonic,
xpub, err := asset.DeriveAccountXpub(seedMnemonic, wordSeedType,
accs.AccountNumber, wallet.Internal().BTC.ChainParams())
if err != nil {
return -1, err
Expand Down
Loading

0 comments on commit a8da03a

Please sign in to comment.