fix: _address vs address, missing p2sh

This commit is contained in:
Rafael Saes 2024-02-09 11:19:55 -03:00
parent 2e6ba71e57
commit f8cadfa112
9 changed files with 87 additions and 47 deletions

View file

@ -11,6 +11,8 @@ String addressFromOutputScript(Script script, BasedUtxoNetwork network) {
return P2shAddress.fromScriptPubkey(script: script).toAddress(network);
case SegwitAddresType.p2wpkh:
return P2wpkhAddress.fromScriptPubkey(script: script).toAddress(network);
case P2shAddressType.p2pkhInP2sh:
return P2shAddress.fromScriptPubkey(script: script).toAddress(network);
case SegwitAddresType.p2wsh:
return P2wshAddress.fromScriptPubkey(script: script).toAddress(network);
case SegwitAddresType.p2tr:

View file

@ -12,7 +12,7 @@ class BitcoinAddressRecord {
int balance = 0,
String name = '',
bool isUsed = false,
this.type,
required this.type,
}) : _txCount = txCount,
_balance = balance,
_name = name,
@ -32,7 +32,7 @@ class BitcoinAddressRecord {
type: decoded['type'] != null && decoded['type'] != ''
? BitcoinAddressType.values
.firstWhere((type) => type.toString() == decoded['type'] as String)
: null,
: SegwitAddresType.p2wpkh,
);
}
@ -67,7 +67,7 @@ class BitcoinAddressRecord {
String get cashAddr => bitbox.Address.toCashAddress(address);
BitcoinAddressType? type;
BitcoinAddressType type;
String toJSON() => json.encode({
'address': address,
@ -77,6 +77,6 @@ class BitcoinAddressRecord {
'txCount': txCount,
'name': name,
'balance': balance,
'type': type?.toString() ?? '',
'type': type.toString(),
});
}

View file

@ -3,6 +3,7 @@ import 'package:cw_core/receive_page_option.dart';
class BitcoinReceivePageOption implements ReceivePageOption {
static const p2wpkh = BitcoinReceivePageOption._('Segwit (P2WPKH)');
static const p2sh = BitcoinReceivePageOption._('Segwit-Compatible (P2SH)');
static const p2tr = BitcoinReceivePageOption._('Taproot (P2TR)');
static const p2wsh = BitcoinReceivePageOption._('Segwit (P2WSH)');
static const p2pkh = BitcoinReceivePageOption._('Legacy (P2PKH)');
@ -17,6 +18,7 @@ class BitcoinReceivePageOption implements ReceivePageOption {
static const all = [
BitcoinReceivePageOption.p2wpkh,
BitcoinReceivePageOption.p2sh,
BitcoinReceivePageOption.p2tr,
BitcoinReceivePageOption.p2wsh,
BitcoinReceivePageOption.p2pkh
@ -30,6 +32,8 @@ class BitcoinReceivePageOption implements ReceivePageOption {
return BitcoinReceivePageOption.p2wsh;
case P2pkhAddressType.p2pkh:
return BitcoinReceivePageOption.p2pkh;
case P2shAddressType.p2wpkhInP2sh:
return BitcoinReceivePageOption.p2pkh;
case SegwitAddresType.p2wpkh:
default:
return BitcoinReceivePageOption.p2wpkh;

View file

@ -32,6 +32,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
if (addressType == SegwitAddresType.p2wsh)
return generateP2WSHAddress(hd: hd, index: index, network: network);
if (addressType == P2shAddressType.p2wpkhInP2sh)
return generateP2SHAddress(hd: hd, index: index, network: network);
return generateP2WPKHAddress(hd: hd, index: index, network: network);
}
}

View file

@ -157,7 +157,7 @@ abstract class ElectrumWalletBase
Future<void> startSync() async {
try {
syncStatus = AttemptingSyncStatus();
await walletAddresses.discoverAddresses();
await walletAddresses.discoverAddressesAll();
await updateTransactions();
_subscribeForUpdates();
await updateUnspent();
@ -849,6 +849,8 @@ class EstimatedTxResult {
BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) {
if (P2pkhAddress.regex.hasMatch(address)) {
return P2pkhAddress.fromAddress(address: address, network: network);
} else if (P2shAddress.regex.hasMatch(address)) {
return P2shAddress.fromAddress(address: address, network: network);
} else if (P2wshAddress.regex.hasMatch(address)) {
return P2wshAddress.fromAddress(address: address, network: network);
} else if (P2trAddress.regex.hasMatch(address)) {
@ -861,6 +863,8 @@ BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network)
BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
if (type is P2pkhAddress) {
return P2pkhAddressType.p2pkh;
} else if (type is P2shAddress) {
return P2shAddressType.p2wpkhInP2sh;
} else if (type is P2wshAddress) {
return SegwitAddresType.p2wsh;
} else if (type is P2trAddress) {

View file

@ -65,7 +65,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
String get addressPageTypeStr => addressPageType.toString();
@computed
List<BitcoinAddressRecord> get addresses => _addresses.where(_isAddressTypeMatch).toList();
List<BitcoinAddressRecord> get addresses => _addresses.where(_isAddressPageTypeMatch).toList();
@computed
List<BitcoinAddressRecord> get allAddresses => _addresses;
@ -80,7 +80,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
String get address {
String receiveAddress;
final typeMatchingReceiveAddresses = receiveAddresses.where(_isAddressTypeMatch);
final typeMatchingReceiveAddresses = receiveAddresses.where(_isAddressPageTypeMatch);
if ((isEnabledAutoGenerateSubaddress && receiveAddresses.isEmpty) ||
typeMatchingReceiveAddresses.isEmpty) {
@ -151,15 +151,28 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return acc;
});
Future<void> discoverAddresses() async {
Future<void> discoverAddressesAll() async {
await _discoverAddresses(false);
await _discoverAddresses(true);
await _discoverAddresses(false, type: P2pkhAddressType.p2pkh);
await _discoverAddresses(true, type: P2pkhAddressType.p2pkh);
await _discoverAddresses(false, type: P2shAddressType.p2wpkhInP2sh);
await _discoverAddresses(true, type: P2shAddressType.p2wpkhInP2sh);
await _discoverAddresses(false, type: SegwitAddresType.p2tr);
await _discoverAddresses(true, type: SegwitAddresType.p2tr);
await _discoverAddresses(false, type: SegwitAddresType.p2wsh);
await _discoverAddresses(true, type: SegwitAddresType.p2wsh);
updateReceiveAddresses();
await updateAddressesInBox();
}
@override
Future<void> init() async {
await _generateInitialAddresses();
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
updateReceiveAddresses();
updateChangeAddresses();
await updateAddressesInBox();
@ -199,11 +212,11 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
0, (int acc, addressRecord) => addressRecord.isHidden == false ? acc + 1 : acc);
final address = BitcoinAddressRecord(
getAddress(index: newAddressIndex, hd: mainHd, addressType: addressPageType),
index: newAddressIndex,
isHidden: false,
name: label,
);
getAddress(index: newAddressIndex, hd: mainHd, addressType: addressPageType),
index: newAddressIndex,
isHidden: false,
name: label,
type: addressPageType);
addresses.add(address);
return address;
}
@ -240,31 +253,36 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
void updateReceiveAddresses() {
receiveAddresses.removeRange(0, receiveAddresses.length);
final newAddresses =
addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
_addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
receiveAddresses.addAll(newAddresses);
}
@action
void updateChangeAddresses() {
changeAddresses.removeRange(0, changeAddresses.length);
final newAddresses =
addresses.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
final newAddresses = _addresses.where((addressRecord) =>
addressRecord.isHidden &&
!addressRecord.isUsed &&
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
addressRecord.type == SegwitAddresType.p2wpkh);
changeAddresses.addAll(newAddresses);
}
@action
Future<void> _discoverAddresses(bool isHidden) async {
Future<void> _discoverAddresses(bool isHidden,
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
var hasAddrUse = true;
List<BitcoinAddressRecord> addrs;
final matchingAddresses = _addresses.where((addr) => _addressMatchHidden(addr, isHidden, type));
if (addresses.where((addr) => addr.isHidden == isHidden).isNotEmpty) {
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
if (matchingAddresses.isNotEmpty) {
addrs = matchingAddresses.toList();
} else {
addrs = await _createNewAddresses(
isHidden ? defaultChangeAddressesCount : defaultReceiveAddressesCount,
startIndex: 0,
isHidden: isHidden,
);
isHidden ? defaultChangeAddressesCount : defaultReceiveAddressesCount,
startIndex: 0,
isHidden: isHidden,
type: type);
}
while (hasAddrUse) {
@ -277,51 +295,55 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final start = addrs.length;
final count = start + gap;
final batch = await _createNewAddresses(count, startIndex: start, isHidden: isHidden);
final batch =
await _createNewAddresses(count, startIndex: start, isHidden: isHidden, type: type);
addrs.addAll(batch);
}
addAddresses(addrs);
}
Future<void> _generateInitialAddresses() async {
Future<void> _generateInitialAddresses(
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
var countOfReceiveAddresses = 0;
var countOfHiddenAddresses = 0;
addresses.forEach((addr) {
if (addr.isHidden) {
countOfHiddenAddresses += 1;
return;
}
_addresses.forEach((addr) {
if (_isAddressByType(addr, type)) {
if (addr.isHidden) {
countOfHiddenAddresses += 1;
return;
}
countOfReceiveAddresses += 1;
countOfReceiveAddresses += 1;
}
});
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
final newAddresses = await _createNewAddresses(addressesCount,
startIndex: countOfReceiveAddresses, isHidden: false);
addresses.addAll(newAddresses);
startIndex: countOfReceiveAddresses, isHidden: false, type: type);
_addresses.addAll(newAddresses);
}
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
final newAddresses = await _createNewAddresses(addressesCount,
startIndex: countOfHiddenAddresses, isHidden: true);
addresses.addAll(newAddresses);
startIndex: countOfHiddenAddresses, isHidden: true, type: type);
_addresses.addAll(newAddresses);
}
}
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
{int startIndex = 0, bool isHidden = false}) async {
{int startIndex = 0, bool isHidden = false, BitcoinAddressType? type}) async {
final list = <BitcoinAddressRecord>[];
for (var i = startIndex; i < count + startIndex; i++) {
final address = BitcoinAddressRecord(
getAddress(index: i, hd: _getHd(isHidden), addressType: addressPageType),
getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType),
index: i,
isHidden: isHidden,
type: addressPageType,
type: type ?? addressPageType,
);
list.add(address);
}
@ -346,17 +368,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@action
Future<void> setAddressType(BitcoinAddressType type) async {
_addressPageType = type;
await discoverAddresses();
await saveAddressesInBox();
}
bool _isAddressTypeMatch(BitcoinAddressRecord addressRecord) {
// Old wallets before address types were introduced will have an empty address record type
return addressPageType == SegwitAddresType.p2wpkh
? addressRecord.type == null || addressRecord.type == addressPageType
: addressRecord.type == addressPageType;
bool _isAddressPageTypeMatch(BitcoinAddressRecord addressRecord) {
return _isAddressByType(addressRecord, addressPageType);
}
bitcoin.HDWallet _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
addr.type == type || (addr.type == null && type == SegwitAddresType.p2wpkh);
bool _addressMatchHidden(BitcoinAddressRecord addr, bool isHidden, BitcoinAddressType type) =>
_isAddressByType(addr, type) && addr.isHidden == isHidden;
}

View file

@ -16,6 +16,10 @@ String generateP2WPKHAddress(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPublic.fromHex(hd.derive(index).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 generateP2WSHAddress(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wshAddress().toAddress(network);

View file

@ -80,7 +80,7 @@ packages:
description:
path: "."
ref: cake-update-v1
resolved-ref: "318986da9cb03e4c18da2e6c0daf3e62aef7eb72"
resolved-ref: "9611e9db77e92a8434e918cdfb620068f6fcb1aa"
url: "https://github.com/cake-tech/bitcoin_base.git"
source: git
version: "4.0.0"

View file

@ -233,6 +233,9 @@ class AddressPage extends BasePage {
case BitcoinReceivePageOption.p2pkh:
addressListViewModel.setAddressType(P2pkhAddressType.p2pkh);
break;
case BitcoinReceivePageOption.p2sh:
addressListViewModel.setAddressType(P2shAddressType.p2wpkhInP2sh);
break;
case BitcoinReceivePageOption.p2wpkh:
addressListViewModel.setAddressType(SegwitAddresType.p2wpkh);
break;