updates & fixes

This commit is contained in:
Matthew Fosse 2024-04-25 18:36:28 -07:00
parent 2cc115e557
commit d39ffe5ce1
8 changed files with 126 additions and 111 deletions

View file

@ -6,7 +6,7 @@ Map<DerivationType, List<DerivationInfo>> bitcoin_derivations = {
derivationType: DerivationType.electrum,
derivationPath: "m/0'/0",
description: "Electrum",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
],
DerivationType.bip39: [
@ -14,79 +14,79 @@ Map<DerivationType, List<DerivationInfo>> bitcoin_derivations = {
derivationType: DerivationType.bip39,
derivationPath: "m/44'/0'/0'",
description: "Standard BIP44",
script_type: "p2pkh",
scriptType: "p2pkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/49'/0'/0'",
description: "Standard BIP49 compatibility segwit",
script_type: "p2wpkh-p2sh",
scriptType: "p2wpkh-p2sh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/0'",
description: "Standard BIP84 native segwit",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/0'",
description: "Non-standard legacy",
script_type: "p2pkh",
scriptType: "p2pkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/0'",
description: "Non-standard compatibility segwit",
script_type: "p2wpkh-p2sh",
scriptType: "p2wpkh-p2sh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/0'",
description: "Non-standard native segwit",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/44'/0'/0'",
description: "Copay native segwit",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/2147483644'",
description: "Samourai Bad Bank (toxic change)",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/2147483645'",
description: "Samourai Whirlpool Pre Mix",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/2147483646'",
description: "Samourai Whirlpool Post Mix",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/44'/0'/2147483647'",
description: "Samourai Ricochet legacy",
script_type: "p2pkh",
scriptType: "p2pkh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/49'/0'/2147483647'",
description: "Samourai Ricochet compatibility segwit",
script_type: "p2wpkh-p2sh",
scriptType: "p2wpkh-p2sh",
),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/2147483647'",
description: "Samourai Ricochet native segwit",
script_type: "p2wpkh",
scriptType: "p2wpkh",
),
],
};

View file

@ -97,9 +97,6 @@ Future<String> generateElectrumMnemonic({int strength = 264, String prefix = seg
var result = '';
do {
// final originalBytes = await secRandom(byteCount);
// // create a modifiable copy, however I'm not sure why this is necessary
// final bytes = Uint8List.fromList(originalBytes);
final bytes = await secRandom(byteCount);
maskBytes(bytes, strength);
result = encode(bytes);

View file

@ -45,23 +45,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.btc) {
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
// the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here)
String derivationPath = walletInfo.derivationInfo!.derivationPath!;
String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
walletAddresses = BitcoinWalletAddresses(
walletInfo,
electrumClient: electrumClient,
initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
// mainHd: hd,
// sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
mainHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
.derivePath(walletInfo.derivationInfo!.derivationPath!),
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath(walletInfo
.derivationInfo!.derivationPath!
.substring(0, walletInfo.derivationInfo!.derivationPath!.length - 1) +
"1"),
mainHd: hd.derivePath(derivationPath),
sideHd: hd.derivePath(sideDerivationPath),
network: networkParam ?? network,
);
autorun((_) {
@ -69,7 +65,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
});
}
static Future<BitcoinWallet> create({
required String mnemonic,
required String password,
@ -82,7 +77,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex,
}) async {
late Uint8List seedBytes;
switch (walletInfo.derivationInfo?.derivationType) {

View file

@ -5,29 +5,58 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
import 'package:hex/hex.dart';
bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, required int index}) =>
PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!)));
bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, int? index}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return PaymentData(pubkey: Uint8List.fromList(HEX.decode(pubKey)));
}
ECPrivate generateECPrivate(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPrivate.fromWif(hd.derive(index).wif!, netVersion: network.wifNetVer);
{required bitcoin.HDWallet hd, required BasedUtxoNetwork network, int? index}) {
final wif = index != null ? hd.derive(index).wif! : hd.wif!;
return ECPrivate.fromWif(wif, netVersion: network.wifNetVer);
}
String generateP2WPKHAddress(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhAddress().toAddress(network);
String generateP2WPKHAddress({
required bitcoin.HDWallet hd,
required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2wpkhAddress().toAddress(network);
}
String generateP2SHAddress(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhInP2sh().toAddress(network);
String generateP2SHAddress({
required bitcoin.HDWallet hd,
required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2wpkhInP2sh().toAddress(network);
}
String generateP2WSHAddress(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wshAddress().toAddress(network);
String generateP2WSHAddress({
required bitcoin.HDWallet hd,
required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2wshAddress().toAddress(network);
}
String generateP2PKHAddress(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPublic.fromHex(hd.derive(index).pubKey!).toP2pkhAddress().toAddress(network);
String generateP2PKHAddress({
required bitcoin.HDWallet hd,
required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2pkhAddress().toAddress(network);
}
String generateP2TRAddress(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPublic.fromHex(hd.derive(index).pubKey!).toTaprootAddress().toAddress(network);
String generateP2TRAddress({
required bitcoin.HDWallet hd,
required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toTaprootAddress().toAddress(network);
}

View file

@ -27,18 +27,32 @@ class DerivationInfo extends HiveObject {
this.balance = "",
this.address = "",
this.transactionsCount = 0,
this.script_type,
this.scriptType,
this.description,
});
static const typeId = DERIVATION_INFO_TYPE_ID;
String balance;
@HiveField(0)
String address;
@HiveField(1)
String balance;
@HiveField(2)
int transactionsCount;
@HiveField(3)
DerivationType? derivationType;
@HiveField(4)
String? derivationPath;
final String? script_type;
@HiveField(5)
final String? scriptType;
@HiveField(6)
final String? description;
}

View file

@ -284,6 +284,20 @@ class CWBitcoin extends Bitcoin {
final electrumClient = ElectrumClient();
await electrumClient.connectToUri(node.uri);
late BasedUtxoNetwork network;
btc.NetworkType networkType;
switch (node.type) {
case WalletType.litecoin:
network = LitecoinNetwork.mainnet;
networkType = litecoinNetwork;
break;
case WalletType.bitcoin:
default:
network = BitcoinNetwork.mainnet;
networkType = btc.bitcoin;
break;
}
for (DerivationType dType in bitcoin_derivations.keys) {
late Uint8List seedBytes;
if (dType == DerivationType.electrum) {
@ -298,80 +312,47 @@ class CWBitcoin extends Bitcoin {
derivationType: dInfo.derivationType,
derivationPath: dInfo.derivationPath,
description: dInfo.description,
script_type: dInfo.script_type,
scriptType: dInfo.scriptType,
);
var hd = bip32.BIP32.fromSeed(seedBytes);
String derivationPath = dInfoCopy.derivationPath!;
int derivationDepth = _countOccurrences(derivationPath, "/");
// the correct derivation depth is dependant on the derivation type:
// the derivation paths defined in bitcoin_derivations are at the ROOT level, i.e.:
// electrum's format doesn't specify subaddresses, just subaccounts:
// for BIP44
if (derivationDepth == 3) {
// we add "/0/0" so that we generate account 0, index 0 and correctly get balance
derivationPath += "/0/0";
// we don't support sub-ACCOUNTS in bitcoin like we do monero, and so the path dInfoCopy
// expects should be ACCOUNT 0, index unspecified:
dInfoCopy.derivationPath = dInfoCopy.derivationPath! + "/0";
}
hd = hd.derivePath(derivationPath);
// var hd = btc.HDWallet.fromSeed(
// seedBytes,
// network: node.type == WalletType.bitcoin ? btc.bitcoin : litecoinNetwork,
// ).derivePath(dInfoCopy.derivationPath!);
// if (addressType == P2pkhAddressType.p2pkh)
// return generateP2PKHAddress(hd: hd, index: index, network: network);
//
// if (addressType == SegwitAddresType.p2tr)
// return generateP2TRAddress(hd: hd, index: index, network: network);
//
// if (addressType == SegwitAddresType.p2wsh)
// return generateP2WSHAddress(hd: hd, index: index, network: network);
//
// if (addressType == P2shAddressType.p2wpkhInP2sh)
// return generateP2SHAddress(hd: hd, index: index, network: network);
// var hd = bip32.BIP32.fromSeed(seedBytes).derivePath(derivationPath);
final hd = btc.HDWallet.fromSeed(
seedBytes,
network: networkType,
).derivePath(derivationPath);
String? address;
switch (dInfoCopy.script_type) {
switch (dInfoCopy.scriptType) {
case "p2wpkh":
// address = generateP2WPKHAddress(
// hd: hd,
// index: 0,
// network: node.type == WalletType.bitcoin
// ? BitcoinNetwork.mainnet
// : LitecoinNetwork.mainnet);
address = btc
.P2WPKH(
data: new btc.PaymentData(pubkey: hd.publicKey),
network: btc.bitcoin,
)
.data
.address;
address = generateP2WPKHAddress(hd: hd, network: network);
break;
case "p2pkh":
// address = generateP2PKHAddress(
// hd: hd,
// index: 0,
// network: node.type == WalletType.bitcoin
// ? BitcoinNetwork.mainnet
// : LitecoinNetwork.mainnet);
address = btc
.P2PKH(
data: new btc.PaymentData(pubkey: hd.publicKey),
network: btc.bitcoin,
)
.data
.address;
address = generateP2PKHAddress(hd: hd, network: network);
break;
case "p2wpkh-p2sh":
address = generateP2SHAddress(hd: hd, network: network);
break;
// case "p2wpkh-p2sh":
// address = generateP2SHAddress(
// hd: hd,
// index: 0,
// network: node.type == WalletType.bitcoin
// ? BitcoinNetwork.mainnet
// : LitecoinNetwork.mainnet);
// break;
default:
continue;
}
final sh = scriptHash(address!, network: BitcoinNetwork.mainnet);
final sh = scriptHash(address, network: network);
final history = await electrumClient.getHistory(sh);
final balance = await electrumClient.getBalance(sh);
@ -388,7 +369,6 @@ class CWBitcoin extends Bitcoin {
// sort the list such that derivations with the most transactions are first:
list.sort((a, b) => b.transactionsCount.compareTo(a.transactionsCount));
return list;
}

View file

@ -113,10 +113,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
name: name, mnemonic: seed, password: password);
case WalletType.nano:
return nano!.createNanoRestoreWalletFromSeedCredentials(
name: name,
mnemonic: seed,
password: password,
derivationType: derivationInfo!.derivationType!,
name: name,
mnemonic: seed,
password: password,
derivationType: derivationInfo!.derivationType!,
);
case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromSeedCredentials(
@ -274,7 +274,6 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
if (mode == WalletRestoreMode.keys) {
return walletCreationService.restoreFromKeys(credentials, isTestnet: useTestnet);
}
return walletCreationService.restoreFromSeed(credentials, isTestnet: useTestnet);
}
}

View file

@ -82,6 +82,8 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:hive/hive.dart';
""";
const bitcoinCWHeaders = """
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_bitcoin/litecoin_network.dart';
import 'package:cw_bitcoin/bitcoin_derivations.dart';
import 'package:cw_bitcoin/electrum.dart';
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';