This commit is contained in:
fosse 2023-08-25 11:59:24 -04:00
parent e72f196c23
commit 3053ea4d10
5 changed files with 49 additions and 41 deletions

View file

@ -65,7 +65,8 @@ String bufferToBin(Uint8List data) {
return q2;
}
String encode(Uint8List data) {
String encode(Uint8List originalData) {
final data = Uint8List.fromList(originalData); // Create a modifiable copy
final dataBitLen = data.length * 8;
final wordBitLen = logBase(wordlist.length, 2).ceil();
final wordCount = (dataBitLen / wordBitLen).floor();
@ -90,15 +91,16 @@ List<bool> prefixMatches(String source, List<String> prefixes) {
return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList();
}
Future<String> generateMnemonic(
{int strength = 264, String prefix = segwit}) async {
Future<String> generateElectrumMnemonic({int strength = 264, String prefix = segwit}) async {
final wordBitlen = logBase(wordlist.length, 2).ceil();
final wordCount = strength / wordBitlen;
final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil();
var result = '';
do {
final bytes = await secRandom(byteCount);
final originalBytes = await secRandom(byteCount);
// create a modifiable copy, however I'm not sure why this is necessary
final bytes = Uint8List.fromList(originalBytes);
maskBytes(bytes, strength);
result = encode(bytes);
} while (!prefixMatches(result, [prefix]).first);
@ -107,21 +109,17 @@ Future<String> generateMnemonic(
}
Future<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async {
final pbkdf2 = cryptography.Pbkdf2(
macAlgorithm: cryptography.Hmac.sha512(),
iterations: 2048,
bits: 512);
final pbkdf2 =
cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512);
final text = normalizeText(mnemonic);
// pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce)
final key = await pbkdf2.deriveKey(
secretKey: cryptography.SecretKey(text.codeUnits),
nonce: 'electrum'.codeUnits);
secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits);
final bytes = await key.extractBytes();
return Uint8List.fromList(bytes);
}
bool matchesAnyPrefix(String mnemonic) =>
prefixMatches(mnemonic, [segwit]).any((el) => el);
bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit]).any((el) => el);
bool validateMnemonic(String mnemonic, {String prefix = segwit}) {
try {
@ -208,10 +206,8 @@ String removeCJKSpaces(String source) {
}
String normalizeText(String source) {
final res = removeCombiningCharacters(unorm.nfkd(source).toLowerCase())
.trim()
.split('/\s+/')
.join(' ');
final res =
removeCombiningCharacters(unorm.nfkd(source).toLowerCase()).trim().split('/\s+/').join(' ');
return removeCJKSpaces(res);
}

View file

@ -37,28 +37,33 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.btc) {
walletAddresses = BitcoinWalletAddresses(
walletInfo,
walletAddresses = BitcoinWalletAddresses(walletInfo,
electrumClient: electrumClient,
initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
mainHd: hd,
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
.derivePath(walletInfo.derivationPath!),
.derivePath(walletInfo.derivationPath!),
networkType: networkType);
}
static Future<BitcoinWallet> create({
required String mnemonic,
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0
}) async {
static Future<BitcoinWallet> create(
{required String mnemonic,
required String password,
required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance,
int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0}) async {
late Uint8List seedBytes;
if (walletInfo.derivationType == DerivationType.electrum2) {
seedBytes = await mnemonicToSeedBytes(mnemonic);
} else {
// TODO: add bip39 seed
seedBytes = await mnemonicToSeedBytes(mnemonic);
}
return BitcoinWallet(
mnemonic: mnemonic,
password: password,
@ -66,7 +71,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: await mnemonicToSeedBytes(mnemonic),
seedBytes: seedBytes,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex);
}
@ -79,7 +84,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
}) async {
final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password);
walletInfo.derivationType = snp.derivationType;
walletInfo.derivationPath = snp.derivationPath;
@ -87,6 +91,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
if (walletInfo.derivationPath == null) {
walletInfo.derivationPath = "m/0'/1";
}
if (walletInfo.derivationType == null) {
walletInfo.derivationType = DerivationType.electrum2;
}
late Uint8List seedBytes;
if (walletInfo.derivationType == DerivationType.electrum2) {
seedBytes = await mnemonicToSeedBytes(snp.mnemonic);
} else {
// TODO: add bip39 seed
seedBytes = await mnemonicToSeedBytes(snp.mnemonic);
}
return BitcoinWallet(
mnemonic: snp.mnemonic,
@ -95,7 +110,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses,
initialBalance: snp.balance,
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
seedBytes: seedBytes,
initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex);
}

View file

@ -34,12 +34,10 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
@override
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
// default derivation type/path for bitcoin wallets:
// TODO: figure out what the default derivation type is
// credentials.walletInfo!.derivationType = DerivationType.bip39;
// TODO: figure out what the default derivation path is
credentials.walletInfo!.derivationPath = "m/0'/1";
final wallet = await BitcoinWalletBase.create(
mnemonic: await generateMnemonic(),
mnemonic: await generateElectrumMnemonic(strength: 132),
password: credentials.password!,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource);
@ -115,9 +113,9 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
static Future<List<DerivationType>> compareDerivationMethods(
{required String mnemonic, required Node node}) async {
// if the mnemonic is 12 words, then it could be electrum 1.0,
// if the mnemonic is 24 words, then it could be electrum 2.0
// bip39 is possible with any number of words
// if the mnemonic is 12 words, then it could be electrum 1.0,
// if the mnemonic is 24 words, then it could be electrum 2.0
// bip39 is possible with any number of words
int wordCount = mnemonic.split(" ").length;
if (wordCount == 24) {
return [DerivationType.bip39, DerivationType.electrum1];

View file

@ -27,7 +27,7 @@ class LitecoinWalletService extends WalletService<
@override
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials) async {
final wallet = await LitecoinWalletBase.create(
mnemonic: await generateMnemonic(),
mnemonic: await generateElectrumMnemonic(),
password: credentials.password!,
walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource);

View file

@ -50,7 +50,6 @@ abstract class WalletCreationVMBase with Store {
walletCreationService.checkIfExists(name);
final dirPath = await pathForWalletDir(name: name, type: type);
final path = await pathForWallet(name: name, type: type);
print("options: $options");
final credentials = restoreWallet != null
? getCredentialsFromRestoredWallet(options, restoreWallet)
: getCredentials(options);