mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 11:59:30 +00:00
paynym refactor to tx v2
This commit is contained in:
parent
e9300c9208
commit
a69b4f8ed5
10 changed files with 220 additions and 128 deletions
|
@ -44,6 +44,7 @@ class OutpointV2 {
|
||||||
@Embedded()
|
@Embedded()
|
||||||
class InputV2 {
|
class InputV2 {
|
||||||
late final String? scriptSigHex;
|
late final String? scriptSigHex;
|
||||||
|
late final String? scriptSigAsm;
|
||||||
late final int? sequence;
|
late final int? sequence;
|
||||||
late final OutpointV2? outpoint;
|
late final OutpointV2? outpoint;
|
||||||
late final List<String> addresses;
|
late final List<String> addresses;
|
||||||
|
@ -63,6 +64,7 @@ class InputV2 {
|
||||||
|
|
||||||
static InputV2 isarCantDoRequiredInDefaultConstructor({
|
static InputV2 isarCantDoRequiredInDefaultConstructor({
|
||||||
required String? scriptSigHex,
|
required String? scriptSigHex,
|
||||||
|
String? scriptSigAsm,
|
||||||
required int? sequence,
|
required int? sequence,
|
||||||
required OutpointV2? outpoint,
|
required OutpointV2? outpoint,
|
||||||
required List<String> addresses,
|
required List<String> addresses,
|
||||||
|
@ -74,6 +76,7 @@ class InputV2 {
|
||||||
}) =>
|
}) =>
|
||||||
InputV2()
|
InputV2()
|
||||||
..scriptSigHex = scriptSigHex
|
..scriptSigHex = scriptSigHex
|
||||||
|
..scriptSigAsm = scriptSigAsm
|
||||||
..sequence = sequence
|
..sequence = sequence
|
||||||
..outpoint = outpoint
|
..outpoint = outpoint
|
||||||
..addresses = List.unmodifiable(addresses)
|
..addresses = List.unmodifiable(addresses)
|
||||||
|
@ -85,6 +88,7 @@ class InputV2 {
|
||||||
|
|
||||||
InputV2 copyWith({
|
InputV2 copyWith({
|
||||||
String? scriptSigHex,
|
String? scriptSigHex,
|
||||||
|
String? scriptSigAsm,
|
||||||
int? sequence,
|
int? sequence,
|
||||||
OutpointV2? outpoint,
|
OutpointV2? outpoint,
|
||||||
List<String>? addresses,
|
List<String>? addresses,
|
||||||
|
@ -96,6 +100,7 @@ class InputV2 {
|
||||||
}) {
|
}) {
|
||||||
return InputV2.isarCantDoRequiredInDefaultConstructor(
|
return InputV2.isarCantDoRequiredInDefaultConstructor(
|
||||||
scriptSigHex: scriptSigHex ?? this.scriptSigHex,
|
scriptSigHex: scriptSigHex ?? this.scriptSigHex,
|
||||||
|
scriptSigAsm: scriptSigAsm ?? this.scriptSigAsm,
|
||||||
sequence: sequence ?? this.sequence,
|
sequence: sequence ?? this.sequence,
|
||||||
outpoint: outpoint ?? this.outpoint,
|
outpoint: outpoint ?? this.outpoint,
|
||||||
addresses: addresses ?? this.addresses,
|
addresses: addresses ?? this.addresses,
|
||||||
|
@ -111,6 +116,7 @@ class InputV2 {
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'InputV2(\n'
|
return 'InputV2(\n'
|
||||||
' scriptSigHex: $scriptSigHex,\n'
|
' scriptSigHex: $scriptSigHex,\n'
|
||||||
|
' scriptSigAsm: $scriptSigAsm,\n'
|
||||||
' sequence: $sequence,\n'
|
' sequence: $sequence,\n'
|
||||||
' outpoint: $outpoint,\n'
|
' outpoint: $outpoint,\n'
|
||||||
' addresses: $addresses,\n'
|
' addresses: $addresses,\n'
|
||||||
|
|
|
@ -6,6 +6,7 @@ part 'output_v2.g.dart';
|
||||||
@Embedded()
|
@Embedded()
|
||||||
class OutputV2 {
|
class OutputV2 {
|
||||||
late final String scriptPubKeyHex;
|
late final String scriptPubKeyHex;
|
||||||
|
late final String? scriptPubKeyAsm;
|
||||||
late final String valueStringSats;
|
late final String valueStringSats;
|
||||||
late final List<String> addresses;
|
late final List<String> addresses;
|
||||||
|
|
||||||
|
@ -18,24 +19,28 @@ class OutputV2 {
|
||||||
|
|
||||||
static OutputV2 isarCantDoRequiredInDefaultConstructor({
|
static OutputV2 isarCantDoRequiredInDefaultConstructor({
|
||||||
required String scriptPubKeyHex,
|
required String scriptPubKeyHex,
|
||||||
|
String? scriptPubKeyAsm,
|
||||||
required String valueStringSats,
|
required String valueStringSats,
|
||||||
required List<String> addresses,
|
required List<String> addresses,
|
||||||
required bool walletOwns,
|
required bool walletOwns,
|
||||||
}) =>
|
}) =>
|
||||||
OutputV2()
|
OutputV2()
|
||||||
..scriptPubKeyHex = scriptPubKeyHex
|
..scriptPubKeyHex = scriptPubKeyHex
|
||||||
|
..scriptPubKeyAsm = scriptPubKeyAsm
|
||||||
..valueStringSats = valueStringSats
|
..valueStringSats = valueStringSats
|
||||||
..walletOwns = walletOwns
|
..walletOwns = walletOwns
|
||||||
..addresses = List.unmodifiable(addresses);
|
..addresses = List.unmodifiable(addresses);
|
||||||
|
|
||||||
OutputV2 copyWith({
|
OutputV2 copyWith({
|
||||||
String? scriptPubKeyHex,
|
String? scriptPubKeyHex,
|
||||||
|
String? scriptPubKeyAsm,
|
||||||
String? valueStringSats,
|
String? valueStringSats,
|
||||||
List<String>? addresses,
|
List<String>? addresses,
|
||||||
bool? walletOwns,
|
bool? walletOwns,
|
||||||
}) {
|
}) {
|
||||||
return OutputV2.isarCantDoRequiredInDefaultConstructor(
|
return OutputV2.isarCantDoRequiredInDefaultConstructor(
|
||||||
scriptPubKeyHex: scriptPubKeyHex ?? this.scriptPubKeyHex,
|
scriptPubKeyHex: scriptPubKeyHex ?? this.scriptPubKeyHex,
|
||||||
|
scriptPubKeyAsm: scriptPubKeyAsm ?? this.scriptPubKeyAsm,
|
||||||
valueStringSats: valueStringSats ?? this.valueStringSats,
|
valueStringSats: valueStringSats ?? this.valueStringSats,
|
||||||
addresses: addresses ?? this.addresses,
|
addresses: addresses ?? this.addresses,
|
||||||
walletOwns: walletOwns ?? this.walletOwns,
|
walletOwns: walletOwns ?? this.walletOwns,
|
||||||
|
@ -61,6 +66,7 @@ class OutputV2 {
|
||||||
|
|
||||||
return OutputV2.isarCantDoRequiredInDefaultConstructor(
|
return OutputV2.isarCantDoRequiredInDefaultConstructor(
|
||||||
scriptPubKeyHex: json["scriptPubKey"]["hex"] as String,
|
scriptPubKeyHex: json["scriptPubKey"]["hex"] as String,
|
||||||
|
scriptPubKeyAsm: json["scriptPubKey"]["asm"] as String?,
|
||||||
valueStringSats: parseOutputAmountString(
|
valueStringSats: parseOutputAmountString(
|
||||||
json["value"].toString(),
|
json["value"].toString(),
|
||||||
decimalPlaces: decimalPlaces,
|
decimalPlaces: decimalPlaces,
|
||||||
|
@ -100,27 +106,10 @@ class OutputV2 {
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'OutputV2(\n'
|
return 'OutputV2(\n'
|
||||||
' scriptPubKeyHex: $scriptPubKeyHex,\n'
|
' scriptPubKeyHex: $scriptPubKeyHex,\n'
|
||||||
|
' scriptPubKeyAsm: $scriptPubKeyAsm,\n'
|
||||||
' value: $value,\n'
|
' value: $value,\n'
|
||||||
' walletOwns: $walletOwns,\n'
|
' walletOwns: $walletOwns,\n'
|
||||||
' addresses: $addresses,\n'
|
' addresses: $addresses,\n'
|
||||||
')';
|
')';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _listEquals<T, U>(List<T> a, List<U> b) {
|
|
||||||
if (T != U) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.length != b.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < a.length; i++) {
|
|
||||||
if (a[i] != b[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,12 +11,12 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:bip47/src/util.dart';
|
import 'package:bip47/src/util.dart';
|
||||||
import 'package:stackwallet/models/isar/models/blockchain_data/output.dart';
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
|
||||||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||||
|
|
||||||
abstract class Bip47Utils {
|
abstract class Bip47Utils {
|
||||||
/// looks at tx outputs and returns a blinded payment code if found
|
/// looks at tx outputs and returns a blinded payment code if found
|
||||||
static Uint8List? getBlindedPaymentCodeBytesFrom(Transaction transaction) {
|
static Uint8List? getBlindedPaymentCodeBytesFrom(TransactionV2 transaction) {
|
||||||
for (int i = 0; i < transaction.outputs.length; i++) {
|
for (int i = 0; i < transaction.outputs.length; i++) {
|
||||||
final bytes = getBlindedPaymentCodeBytesFromOutput(
|
final bytes = getBlindedPaymentCodeBytesFromOutput(
|
||||||
transaction.outputs.elementAt(i));
|
transaction.outputs.elementAt(i));
|
||||||
|
@ -28,7 +28,7 @@ abstract class Bip47Utils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint8List? getBlindedPaymentCodeBytesFromOutput(Output output) {
|
static Uint8List? getBlindedPaymentCodeBytesFromOutput(OutputV2 output) {
|
||||||
Uint8List? blindedCodeBytes;
|
Uint8List? blindedCodeBytes;
|
||||||
|
|
||||||
List<String>? scriptChunks = output.scriptPubKeyAsm?.split(" ");
|
List<String>? scriptChunks = output.scriptPubKeyAsm?.split(" ");
|
||||||
|
|
|
@ -6,9 +6,10 @@ import 'package:stackwallet/utilities/default_nodes.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
import 'package:stackwallet/wallets/crypto_currency/interfaces/paynym_currency_interface.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||||
|
|
||||||
class Bitcoin extends Bip39HDCurrency {
|
class Bitcoin extends Bip39HDCurrency with PaynymCurrencyInterface {
|
||||||
Bitcoin(super.network) {
|
Bitcoin(super.network) {
|
||||||
switch (network) {
|
switch (network) {
|
||||||
case CryptoCurrencyNetwork.main:
|
case CryptoCurrencyNetwork.main:
|
||||||
|
@ -49,11 +50,6 @@ class Bitcoin extends Bip39HDCurrency {
|
||||||
fractionDigits: fractionDigits,
|
fractionDigits: fractionDigits,
|
||||||
);
|
);
|
||||||
|
|
||||||
Amount get dustLimitP2PKH => Amount(
|
|
||||||
rawValue: BigInt.from(546),
|
|
||||||
fractionDigits: fractionDigits,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
coinlib.NetworkParams get networkParams {
|
coinlib.NetworkParams get networkParams {
|
||||||
switch (network) {
|
switch (network) {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||||
|
|
||||||
|
mixin PaynymCurrencyInterface on Bip39HDCurrency {
|
||||||
|
Amount get dustLimitP2PKH => Amount(
|
||||||
|
rawValue: BigInt.from(546),
|
||||||
|
fractionDigits: fractionDigits,
|
||||||
|
);
|
||||||
|
}
|
|
@ -9,17 +9,18 @@ import 'package:stackwallet/utilities/extensions/extensions.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart';
|
import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||||
|
import 'package:stackwallet/wallets/crypto_currency/interfaces/paynym_currency_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
||||||
|
|
||||||
class BitcoinWallet extends Bip39HDWallet
|
class BitcoinWallet<T extends PaynymCurrencyInterface> extends Bip39HDWallet<T>
|
||||||
with ElectrumXInterface, CoinControlInterface, PaynymInterface {
|
with ElectrumXInterface<T>, CoinControlInterface, PaynymInterface<T> {
|
||||||
@override
|
@override
|
||||||
int get isarTransactionVersion => 2;
|
int get isarTransactionVersion => 2;
|
||||||
|
|
||||||
BitcoinWallet(CryptoCurrencyNetwork network) : super(Bitcoin(network));
|
BitcoinWallet(CryptoCurrencyNetwork network) : super(Bitcoin(network) as T);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FilterOperation? get changeAddressFilterOperation =>
|
FilterOperation? get changeAddressFilterOperation =>
|
||||||
|
@ -153,6 +154,7 @@ class BitcoinWallet extends Bip39HDWallet
|
||||||
|
|
||||||
InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
|
InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
|
||||||
scriptSigHex: map["scriptSig"]?["hex"] as String?,
|
scriptSigHex: map["scriptSig"]?["hex"] as String?,
|
||||||
|
scriptSigAsm: map["scriptSig"]?["asm"] as String?,
|
||||||
sequence: map["sequence"] as int?,
|
sequence: map["sequence"] as int?,
|
||||||
outpoint: outpoint,
|
outpoint: outpoint,
|
||||||
valueStringSats: valueStringSats,
|
valueStringSats: valueStringSats,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import 'package:stackwallet/utilities/default_nodes.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/utilities/paynym_is_api.dart';
|
||||||
import 'package:stackwallet/utilities/prefs.dart';
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||||
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
||||||
|
@ -45,6 +46,7 @@ import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_int
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
||||||
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||||
|
|
||||||
|
@ -471,6 +473,24 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||||
|
final Set<String> codesToCheck = {};
|
||||||
|
if (this is PaynymInterface) {
|
||||||
|
// isSegwit does not matter here at all
|
||||||
|
final myCode =
|
||||||
|
await (this as PaynymInterface).getPaymentCode(isSegwit: false);
|
||||||
|
|
||||||
|
final nym = await PaynymIsApi().nym(myCode.toString());
|
||||||
|
if (nym.value != null) {
|
||||||
|
for (final follower in nym.value!.followers) {
|
||||||
|
codesToCheck.add(follower.code);
|
||||||
|
}
|
||||||
|
for (final following in nym.value!.following) {
|
||||||
|
codesToCheck.add(following.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId));
|
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.0, walletId));
|
||||||
await updateChainHeight();
|
await updateChainHeight();
|
||||||
|
|
||||||
|
@ -505,6 +525,14 @@ abstract class Wallet<T extends CryptoCurrency> {
|
||||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId));
|
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId));
|
||||||
|
|
||||||
await fetchFuture;
|
await fetchFuture;
|
||||||
|
|
||||||
|
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||||
|
if (this is PaynymInterface && codesToCheck.isNotEmpty) {
|
||||||
|
await (this as PaynymInterface)
|
||||||
|
.checkForNotificationTransactionsTo(codesToCheck);
|
||||||
|
// check utxos again for notification outputs
|
||||||
|
await updateUTXOs();
|
||||||
|
}
|
||||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.80, walletId));
|
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.80, walletId));
|
||||||
|
|
||||||
// await getAllTxsToWatch();
|
// await getAllTxsToWatch();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||||
|
|
||||||
mixin CoinControlInterface on Bip39HDWallet {
|
mixin CoinControlInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
||||||
// any required here?
|
// any required here?
|
||||||
// currently only used to id which wallets support coin control
|
// currently only used to id which wallets support coin control
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,13 @@ import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart';
|
import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart';
|
||||||
|
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
mixin ElectrumXInterface on Bip39HDWallet {
|
mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
||||||
late ElectrumXClient electrumXClient;
|
late ElectrumXClient electrumXClient;
|
||||||
late CachedElectrumXClient electrumXCachedClient;
|
late CachedElectrumXClient electrumXCachedClient;
|
||||||
|
|
||||||
|
@ -533,20 +534,29 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
||||||
final address = await mainDB.getAddress(walletId, sd.utxo.address!);
|
final address = await mainDB.getAddress(walletId, sd.utxo.address!);
|
||||||
if (address?.derivationPath != null) {
|
if (address?.derivationPath != null) {
|
||||||
if (address!.subType == AddressSubType.paynymReceive) {
|
if (address!.subType == AddressSubType.paynymReceive) {
|
||||||
// TODO paynym
|
if (this is PaynymInterface) {
|
||||||
// final code = await paymentCodeStringByKey(address.otherData!);
|
final code = await (this as PaynymInterface)
|
||||||
//
|
.paymentCodeStringByKey(address.otherData!);
|
||||||
// final bip47base = await getBip47BaseNode();
|
|
||||||
//
|
final bip47base =
|
||||||
// final privateKey = await getPrivateKeyForPaynymReceivingAddress(
|
await (this as PaynymInterface).getBip47BaseNode();
|
||||||
// paymentCodeString: code!,
|
|
||||||
// index: address.derivationIndex,
|
final privateKey = await (this as PaynymInterface)
|
||||||
// );
|
.getPrivateKeyForPaynymReceivingAddress(
|
||||||
//
|
paymentCodeString: code!,
|
||||||
// keys = coinlib.HDPrivateKey.fromKeyAndChainCode(
|
index: address.derivationIndex,
|
||||||
// privateKey,
|
);
|
||||||
// bip47base.chainCode,
|
|
||||||
// );
|
keys = coinlib.HDPrivateKey.fromKeyAndChainCode(
|
||||||
|
coinlib.ECPrivateKey.fromHex(privateKey.toHex),
|
||||||
|
bip47base.chainCode,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw Exception(
|
||||||
|
"$runtimeType tried to fetchBuildTxData for a paynym address"
|
||||||
|
" in a non PaynymInterface wallet",
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
keys = root.derivePath(address.derivationPath!.value);
|
keys = root.derivePath(address.derivationPath!.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import 'package:isar/isar.dart';
|
||||||
import 'package:pointycastle/digests/sha256.dart';
|
import 'package:pointycastle/digests/sha256.dart';
|
||||||
import 'package:stackwallet/exceptions/wallet/insufficient_balance_exception.dart';
|
import 'package:stackwallet/exceptions/wallet/insufficient_balance_exception.dart';
|
||||||
import 'package:stackwallet/exceptions/wallet/paynym_send_exception.dart';
|
import 'package:stackwallet/exceptions/wallet/paynym_send_exception.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
|
||||||
|
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
import 'package:stackwallet/models/signing_data.dart';
|
import 'package:stackwallet/models/signing_data.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
@ -20,6 +22,7 @@ import 'package:stackwallet/utilities/bip47_utils.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/wallets/crypto_currency/interfaces/paynym_currency_interface.dart';
|
||||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||||
|
@ -43,12 +46,8 @@ String _sendPaynymAddressDerivationPath(
|
||||||
}) =>
|
}) =>
|
||||||
"${_basePaynymDerivePath(testnet: testnet)}/0/$index";
|
"${_basePaynymDerivePath(testnet: testnet)}/0/$index";
|
||||||
|
|
||||||
mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
mixin PaynymInterface<T extends PaynymCurrencyInterface>
|
||||||
Amount get _dustLimitP2PKH => Amount(
|
on Bip39HDWallet<T>, ElectrumXInterface<T> {
|
||||||
rawValue: BigInt.from(546),
|
|
||||||
fractionDigits: cryptoCurrency.fractionDigits,
|
|
||||||
);
|
|
||||||
|
|
||||||
btc_dart.NetworkType get networkType => btc_dart.NetworkType(
|
btc_dart.NetworkType get networkType => btc_dart.NetworkType(
|
||||||
messagePrefix: cryptoCurrency.networkParams.messagePrefix,
|
messagePrefix: cryptoCurrency.networkParams.messagePrefix,
|
||||||
bech32: cryptoCurrency.networkParams.bech32Hrp,
|
bech32: cryptoCurrency.networkParams.bech32Hrp,
|
||||||
|
@ -447,9 +446,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
List<UTXO>? utxos,
|
List<UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
// final amountToSend = cryptoCurrency.dustLimitP2PKH;
|
final amountToSend = cryptoCurrency.dustLimitP2PKH;
|
||||||
|
|
||||||
final amountToSend = _dustLimitP2PKH;
|
|
||||||
final List<UTXO> availableOutputs =
|
final List<UTXO> availableOutputs =
|
||||||
utxos ?? await mainDB.getUTXOs(walletId).findAll();
|
utxos ?? await mainDB.getUTXOs(walletId).findAll();
|
||||||
final List<UTXO> spendableOutputs = [];
|
final List<UTXO> spendableOutputs = [];
|
||||||
|
@ -550,14 +547,14 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (satoshisBeingUsed - amountToSend.raw >
|
if (satoshisBeingUsed - amountToSend.raw >
|
||||||
feeForNoChange + _dustLimitP2PKH.raw) {
|
feeForNoChange + cryptoCurrency.dustLimitP2PKH.raw) {
|
||||||
// try to add change output due to "left over" amount being greater than
|
// try to add change output due to "left over" amount being greater than
|
||||||
// the estimated fee + the dust limit
|
// the estimated fee + the dust limit
|
||||||
BigInt changeAmount =
|
BigInt changeAmount =
|
||||||
satoshisBeingUsed - amountToSend.raw - feeForWithChange;
|
satoshisBeingUsed - amountToSend.raw - feeForWithChange;
|
||||||
|
|
||||||
// check estimates are correct and build notification tx
|
// check estimates are correct and build notification tx
|
||||||
if (changeAmount >= _dustLimitP2PKH.raw &&
|
if (changeAmount >= cryptoCurrency.dustLimitP2PKH.raw &&
|
||||||
satoshisBeingUsed - amountToSend.raw - changeAmount ==
|
satoshisBeingUsed - amountToSend.raw - changeAmount ==
|
||||||
feeForWithChange) {
|
feeForWithChange) {
|
||||||
var txn = await _createNotificationTx(
|
var txn = await _createNotificationTx(
|
||||||
|
@ -746,7 +743,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
|
|
||||||
txb.addOutput(
|
txb.addOutput(
|
||||||
notificationAddress,
|
notificationAddress,
|
||||||
(overrideAmountForTesting ?? _dustLimitP2PKH.raw).toInt(),
|
(overrideAmountForTesting ?? cryptoCurrency.dustLimitP2PKH.raw).toInt(),
|
||||||
);
|
);
|
||||||
txb.addOutput(opReturnScript, 0);
|
txb.addOutput(opReturnScript, 0);
|
||||||
|
|
||||||
|
@ -854,44 +851,67 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
|
|
||||||
final myNotificationAddress = await getMyNotificationAddress();
|
final myNotificationAddress = await getMyNotificationAddress();
|
||||||
|
|
||||||
final txns = await mainDB
|
final txns = await mainDB.isar.transactionV2s
|
||||||
.getTransactions(walletId)
|
.where()
|
||||||
|
.walletIdEqualTo(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
.subTypeEqualTo(TransactionSubType.bip47Notification)
|
.subTypeEqualTo(TransactionSubType.bip47Notification)
|
||||||
.findAll();
|
.findAll();
|
||||||
|
|
||||||
for (final tx in txns) {
|
for (final tx in txns) {
|
||||||
if (tx.type == TransactionType.incoming &&
|
switch (tx.type) {
|
||||||
tx.address.value?.value == myNotificationAddress.value) {
|
case TransactionType.incoming:
|
||||||
final unBlindedPaymentCode = await unBlindedPaymentCodeFromTransaction(
|
for (final output in tx.outputs) {
|
||||||
transaction: tx,
|
for (final outputAddress in output.addresses) {
|
||||||
);
|
if (outputAddress == myNotificationAddress.value) {
|
||||||
|
final unBlindedPaymentCode =
|
||||||
|
await unBlindedPaymentCodeFromTransaction(
|
||||||
|
transaction: tx,
|
||||||
|
);
|
||||||
|
|
||||||
if (unBlindedPaymentCode != null &&
|
if (unBlindedPaymentCode != null &&
|
||||||
paymentCodeString == unBlindedPaymentCode.toString()) {
|
paymentCodeString == unBlindedPaymentCode.toString()) {
|
||||||
// await _setConnectedCache(paymentCodeString, true);
|
// await _setConnectedCache(paymentCodeString, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final unBlindedPaymentCodeBad =
|
final unBlindedPaymentCodeBad =
|
||||||
await unBlindedPaymentCodeFromTransactionBad(
|
await unBlindedPaymentCodeFromTransactionBad(
|
||||||
transaction: tx,
|
transaction: tx,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (unBlindedPaymentCodeBad != null &&
|
if (unBlindedPaymentCodeBad != null &&
|
||||||
paymentCodeString == unBlindedPaymentCodeBad.toString()) {
|
paymentCodeString == unBlindedPaymentCodeBad.toString()) {
|
||||||
// await _setConnectedCache(paymentCodeString, true);
|
// await _setConnectedCache(paymentCodeString, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (tx.type == TransactionType.outgoing) {
|
}
|
||||||
if (tx.address.value?.otherData != null) {
|
}
|
||||||
final code =
|
|
||||||
await paymentCodeStringByKey(tx.address.value!.otherData!);
|
|
||||||
if (code == paymentCodeString) {
|
|
||||||
// await _setConnectedCache(paymentCodeString, true);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
case TransactionType.outgoing:
|
||||||
|
for (final output in tx.outputs) {
|
||||||
|
for (final outputAddress in output.addresses) {
|
||||||
|
final address = await mainDB.isar.addresses
|
||||||
|
.where()
|
||||||
|
.walletIdEqualTo(walletId)
|
||||||
|
.filter()
|
||||||
|
.subTypeEqualTo(AddressSubType.paynymNotification)
|
||||||
|
.and()
|
||||||
|
.valueEqualTo(outputAddress)
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
if (address?.otherData != null) {
|
||||||
|
final code = await paymentCodeStringByKey(address!.otherData!);
|
||||||
|
if (code == paymentCodeString) {
|
||||||
|
// await _setConnectedCache(paymentCodeString, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,7 +920,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List? _pubKeyFromInput(Input input) {
|
Uint8List? _pubKeyFromInput(InputV2 input) {
|
||||||
final scriptSigComponents = input.scriptSigAsm?.split(" ") ?? [];
|
final scriptSigComponents = input.scriptSigAsm?.split(" ") ?? [];
|
||||||
if (scriptSigComponents.length > 1) {
|
if (scriptSigComponents.length > 1) {
|
||||||
return scriptSigComponents[1].fromHex;
|
return scriptSigComponents[1].fromHex;
|
||||||
|
@ -919,7 +939,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PaymentCode?> unBlindedPaymentCodeFromTransaction({
|
Future<PaymentCode?> unBlindedPaymentCodeFromTransaction({
|
||||||
required Transaction transaction,
|
required TransactionV2 transaction,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final blindedCodeBytes =
|
final blindedCodeBytes =
|
||||||
|
@ -932,8 +952,8 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
|
|
||||||
final designatedInput = transaction.inputs.first;
|
final designatedInput = transaction.inputs.first;
|
||||||
|
|
||||||
final txPoint = designatedInput.txid.fromHex.reversed.toList();
|
final txPoint = designatedInput.outpoint!.txid.fromHex.reversed.toList();
|
||||||
final txPointIndex = designatedInput.vout;
|
final txPointIndex = designatedInput.outpoint!.vout;
|
||||||
|
|
||||||
final rev = Uint8List(txPoint.length + 4);
|
final rev = Uint8List(txPoint.length + 4);
|
||||||
Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
|
Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
|
||||||
|
@ -970,7 +990,7 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PaymentCode?> unBlindedPaymentCodeFromTransactionBad({
|
Future<PaymentCode?> unBlindedPaymentCodeFromTransactionBad({
|
||||||
required Transaction transaction,
|
required TransactionV2 transaction,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final blindedCodeBytes =
|
final blindedCodeBytes =
|
||||||
|
@ -983,8 +1003,8 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
|
|
||||||
final designatedInput = transaction.inputs.first;
|
final designatedInput = transaction.inputs.first;
|
||||||
|
|
||||||
final txPoint = designatedInput.txid.fromHex.toList();
|
final txPoint = designatedInput.outpoint!.txid.fromHex.toList();
|
||||||
final txPointIndex = designatedInput.vout;
|
final txPointIndex = designatedInput.outpoint!.vout;
|
||||||
|
|
||||||
final rev = Uint8List(txPoint.length + 4);
|
final rev = Uint8List(txPoint.length + 4);
|
||||||
Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
|
Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
|
||||||
|
@ -1022,8 +1042,9 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
|
|
||||||
Future<List<PaymentCode>>
|
Future<List<PaymentCode>>
|
||||||
getAllPaymentCodesFromNotificationTransactions() async {
|
getAllPaymentCodesFromNotificationTransactions() async {
|
||||||
final txns = await mainDB
|
final txns = await mainDB.isar.transactionV2s
|
||||||
.getTransactions(walletId)
|
.where()
|
||||||
|
.walletIdEqualTo(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
.subTypeEqualTo(TransactionSubType.bip47Notification)
|
.subTypeEqualTo(TransactionSubType.bip47Notification)
|
||||||
.findAll();
|
.findAll();
|
||||||
|
@ -1032,18 +1053,33 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
|
|
||||||
for (final tx in txns) {
|
for (final tx in txns) {
|
||||||
// tx is sent so we can check the address's otherData for the code String
|
// tx is sent so we can check the address's otherData for the code String
|
||||||
if (tx.type == TransactionType.outgoing &&
|
if (tx.type == TransactionType.outgoing) {
|
||||||
tx.address.value?.otherData != null) {
|
for (final output in tx.outputs) {
|
||||||
final codeString =
|
for (final outputAddress
|
||||||
await paymentCodeStringByKey(tx.address.value!.otherData!);
|
in output.addresses.where((e) => e.isNotEmpty)) {
|
||||||
if (codeString != null &&
|
final address = await mainDB.isar.addresses
|
||||||
codes.where((e) => e.toString() == codeString).isEmpty) {
|
.where()
|
||||||
codes.add(
|
.walletIdEqualTo(walletId)
|
||||||
PaymentCode.fromPaymentCode(
|
.filter()
|
||||||
codeString,
|
.subTypeEqualTo(AddressSubType.paynymNotification)
|
||||||
networkType: networkType,
|
.and()
|
||||||
),
|
.valueEqualTo(outputAddress)
|
||||||
);
|
.findFirst();
|
||||||
|
|
||||||
|
if (address?.otherData != null) {
|
||||||
|
final codeString =
|
||||||
|
await paymentCodeStringByKey(address!.otherData!);
|
||||||
|
if (codeString != null &&
|
||||||
|
codes.where((e) => e.toString() == codeString).isEmpty) {
|
||||||
|
codes.add(
|
||||||
|
PaymentCode.fromPaymentCode(
|
||||||
|
codeString,
|
||||||
|
networkType: networkType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// otherwise we need to un blind the code
|
// otherwise we need to un blind the code
|
||||||
|
@ -1072,8 +1108,9 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
|
|
||||||
Future<void> checkForNotificationTransactionsTo(
|
Future<void> checkForNotificationTransactionsTo(
|
||||||
Set<String> otherCodeStrings) async {
|
Set<String> otherCodeStrings) async {
|
||||||
final sentNotificationTransactions = await mainDB
|
final sentNotificationTransactions = await mainDB.isar.transactionV2s
|
||||||
.getTransactions(walletId)
|
.where()
|
||||||
|
.walletIdEqualTo(walletId)
|
||||||
.filter()
|
.filter()
|
||||||
.subTypeEqualTo(TransactionSubType.bip47Notification)
|
.subTypeEqualTo(TransactionSubType.bip47Notification)
|
||||||
.and()
|
.and()
|
||||||
|
@ -1087,23 +1124,37 @@ mixin PaynymInterface on Bip39HDWallet, ElectrumXInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final tx in sentNotificationTransactions) {
|
for (final tx in sentNotificationTransactions) {
|
||||||
if (tx.address.value != null && tx.address.value!.otherData == null) {
|
for (final output in tx.outputs) {
|
||||||
final oldAddress =
|
for (final outputAddress in output.addresses) {
|
||||||
await mainDB.getAddress(walletId, tx.address.value!.value);
|
if (outputAddress.isNotEmpty) {
|
||||||
for (final code in codes) {
|
for (final code in codes) {
|
||||||
final notificationAddress = code.notificationAddressP2PKH();
|
final notificationAddress = code.notificationAddressP2PKH();
|
||||||
if (notificationAddress == oldAddress!.value) {
|
|
||||||
final address = Address(
|
if (outputAddress == notificationAddress) {
|
||||||
walletId: walletId,
|
Address? storedAddress =
|
||||||
value: notificationAddress,
|
await mainDB.getAddress(walletId, outputAddress);
|
||||||
publicKey: [],
|
if (storedAddress == null) {
|
||||||
derivationIndex: 0,
|
// most likely not mine
|
||||||
derivationPath: oldAddress.derivationPath,
|
storedAddress = Address(
|
||||||
type: oldAddress.type,
|
walletId: walletId,
|
||||||
subType: AddressSubType.paynymNotification,
|
value: notificationAddress,
|
||||||
otherData: await storeCode(code.toString()),
|
publicKey: [],
|
||||||
);
|
derivationIndex: 0,
|
||||||
await mainDB.updateAddress(oldAddress, address);
|
derivationPath: null,
|
||||||
|
type: AddressType.unknown,
|
||||||
|
subType: AddressSubType.paynymNotification,
|
||||||
|
otherData: await storeCode(code.toString()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
storedAddress = storedAddress.copyWith(
|
||||||
|
subType: AddressSubType.paynymNotification,
|
||||||
|
otherData: await storeCode(code.toString()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await mainDB.updateOrPutAddresses([storedAddress]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue