feat: new rust lib

This commit is contained in:
Rafael Saes 2024-04-12 15:28:01 -03:00
parent 465c7efa73
commit a4f8cdfa99
7 changed files with 160 additions and 71 deletions

View file

@ -42,6 +42,7 @@ import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:http/http.dart' as http;
import 'package:sp_scanner/sp_scanner.dart';
part 'electrum_wallet.g.dart';
@ -223,6 +224,10 @@ abstract class ElectrumWalletBase
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
node: ScanNode(node!.uri, node!.useSSL),
labels: walletAddresses.labels,
labelIndexes: walletAddresses.silentAddresses
.where((addr) => addr.type == SilentPaymentsAddresType.p2sp && addr.index >= 1)
.map((addr) => addr.index)
.toList(),
isSingleScan: doSingleScan ?? false,
));
@ -238,6 +243,10 @@ abstract class ElectrumWalletBase
final existingTxInfo = transactionHistory.transactions[txid];
if (existingTxInfo != null) {
final addressRecord =
walletAddresses.silentAddresses.firstWhere((addr) => addr.address == tx.to);
addressRecord.txCount += 1;
existingTxInfo.amount = tx.amount;
existingTxInfo.confirmations = tx.confirmations;
existingTxInfo.height = tx.height;
@ -1616,6 +1625,7 @@ class ScanData {
final ElectrumClient electrumClient;
final List<String> transactionHistoryIds;
final Map<String, String> labels;
final List<int> labelIndexes;
final bool isSingleScan;
ScanData({
@ -1628,6 +1638,7 @@ class ScanData {
required this.electrumClient,
required this.transactionHistoryIds,
required this.labels,
required this.labelIndexes,
required this.isSingleScan,
});
@ -1642,6 +1653,7 @@ class ScanData {
transactionHistoryIds: scanData.transactionHistoryIds,
electrumClient: scanData.electrumClient,
labels: scanData.labels,
labelIndexes: scanData.labelIndexes,
isSingleScan: scanData.isSingleScan,
);
}
@ -1714,8 +1726,9 @@ Future<void> startRefresh(ScanData scanData) async {
try {
final electrumClient = await getElectrumConnection();
// TODO: hardcoded values, if timed out decrease amount of blocks per request?
final scanningBlockCount =
scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 50 : 10);
scanData.isSingleScan ? 1 : (scanData.network == BitcoinNetwork.testnet ? 25 : 10);
Map<String, dynamic>? tweaks;
try {
@ -1752,14 +1765,16 @@ Future<void> startRefresh(ScanData scanData) async {
final tweak = details["tweak"].toString();
try {
final spb = SilentPaymentBuilder(receiverTweak: tweak);
final addToWallet = spb.scanOutputs(
scanData.silentAddress.b_scan,
scanData.silentAddress.B_spend,
outputPubkeys.values
.map((o) => getScriptFromOutput(o[0].toString(), int.parse(o[1].toString())))
.toList(),
precomputedLabels: scanData.labels,
final addToWallet = scanOutputs(
outputPubkeys.values.map((o) => o[0].toString()).toList(),
tweak,
Receiver(
scanData.silentAddress.b_scan.toHex(),
scanData.silentAddress.B_spend.toHex(),
scanData.network == BitcoinNetwork.testnet,
scanData.labelIndexes,
scanData.labelIndexes.length,
),
);
if (addToWallet.isEmpty) {
@ -1767,64 +1782,72 @@ Future<void> startRefresh(ScanData scanData) async {
continue;
}
addToWallet.forEach((key, value) async {
final t_k = value.tweak;
addToWallet.forEach((label, value) async {
(value as Map<String, dynamic>).forEach((output, tweak) async {
final t_k = tweak.toString();
final addressRecord = BitcoinSilentPaymentAddressRecord(
value.output.address.toAddress(scanData.network),
index: 0,
isHidden: false,
isUsed: true,
network: scanData.network,
silentPaymentTweak: t_k,
type: SegwitAddresType.p2tr,
);
final receivingOutputAddress = ECPublic.fromHex(output)
.toTaprootAddress(tweak: false)
.toAddress(scanData.network);
int? amount;
int? pos;
outputPubkeys.entries.firstWhere((k) {
final matches = k.value[0] == key;
if (matches) {
amount = int.parse(k.value[1].toString());
pos = int.parse(k.key.toString());
return true;
}
return false;
final receivedAddressRecord = BitcoinSilentPaymentAddressRecord(
receivingOutputAddress,
index: 0,
isHidden: false,
isUsed: true,
network: scanData.network,
silentPaymentTweak: t_k,
type: SegwitAddresType.p2tr,
);
int? amount;
int? pos;
outputPubkeys.entries.firstWhere((k) {
final matches = k.value[0] == output;
if (matches) {
amount = int.parse(k.value[1].toString());
pos = int.parse(k.key.toString());
return true;
}
return false;
});
final json = <String, dynamic>{
'address_record': receivedAddressRecord.toJSON(),
'tx_hash': txid,
'value': amount!,
'tx_pos': pos!,
'silent_payment_tweak': t_k,
};
final tx = BitcoinUnspent.fromJSON(receivedAddressRecord, json);
final silentPaymentAddress = SilentPaymentAddress(
version: scanData.silentAddress.version,
B_scan: scanData.silentAddress.B_scan,
B_spend: label == "None"
? scanData.silentAddress.B_spend
: scanData.silentAddress.B_spend
.tweakAdd(BigintUtils.fromBytes(BytesUtils.fromHexString(label))),
hrp: scanData.silentAddress.hrp,
);
final txInfo = ElectrumTransactionInfo(
WalletType.bitcoin,
id: tx.hash,
height: syncHeight,
amount: amount!,
fee: 0,
direction: TransactionDirection.incoming,
isPending: false,
date: DateTime.now(),
confirmations: await getUpdatedNodeHeight() - int.parse(blockHeight) + 1,
to: silentPaymentAddress.toString(),
unspents: [tx],
);
scanData.sendPort.send({txInfo.id: txInfo});
});
final json = <String, dynamic>{
'address_record': addressRecord.toJSON(),
'tx_hash': txid,
'value': amount!,
'tx_pos': pos!,
'silent_payment_tweak': t_k,
};
final tx = BitcoinUnspent.fromJSON(addressRecord, json);
final txInfo = ElectrumTransactionInfo(
WalletType.bitcoin,
id: tx.hash,
height: syncHeight,
amount: amount!,
fee: 0,
direction: TransactionDirection.incoming,
isPending: false,
date: DateTime.now(),
confirmations: await getUpdatedNodeHeight() - int.parse(blockHeight) + 1,
to: value.label != null
? SilentPaymentAddress(
version: scanData.silentAddress.version,
B_scan: scanData.silentAddress.B_scan.tweakAdd(
BigintUtils.fromBytes(BytesUtils.fromHexString(value.tweak))),
B_spend: scanData.silentAddress.B_spend,
hrp: scanData.silentAddress.hrp,
).toString()
: scanData.silentAddress.toString(),
unspents: [tx],
);
scanData.sendPort.send({txInfo.id: txInfo});
});
} catch (_) {}
}

View file

@ -60,7 +60,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (silentAddresses.length == 0)
silentAddresses.add(BitcoinSilentPaymentAddressRecord(
silentAddress.toString(),
index: 1,
index: 0,
isHidden: false,
name: "",
silentPaymentTweak: null,
@ -268,7 +268,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
final currentSilentAddressIndex = silentAddresses
.where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr)
.length +
.length -
1;
this.currentSilentAddressIndex = currentSilentAddressIndex;

View file

@ -80,7 +80,7 @@ packages:
description:
path: "."
ref: cake-update-v3
resolved-ref: fb4c0a0b6cf24628ddad7d3cdc58e4c918eff714
resolved-ref: "3ddad3d1a9b78f49c9ef542962758400315d64a7"
url: "https://github.com/cake-tech/bitcoin_base"
source: git
version: "4.0.0"
@ -198,6 +198,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.1"
clock:
dependency: transitive
description:
@ -285,6 +293,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
ffigen:
dependency: transitive
description:
name: ffigen
sha256: d3e76c2ad48a4e7f93a29a162006f00eba46ce7c08194a77bb5c5e97d1b5ff0a
url: "https://pub.dev"
source: hosted
version: "8.0.2"
file:
dependency: transitive
description:
@ -607,6 +623,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.3"
quiver:
dependency: transitive
description:
name: quiver
sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47
url: "https://pub.dev"
source: hosted
version: "3.2.1"
rxdart:
dependency: "direct main"
description:
@ -668,6 +692,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
sp_scanner:
dependency: "direct main"
description:
path: "."
ref: master
resolved-ref: de90b20f4250647d0f55f6bd5e7203710d0d5678
url: "https://github.com/rafael-xmr/sp_scanner"
source: git
version: "0.0.1"
stack_trace:
dependency: transitive
description:
@ -740,6 +773,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
source: hosted
version: "3.0.7"
vector_math:
dependency: transitive
description:
@ -788,6 +829,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
yaml_edit:
dependency: transitive
description:
name: yaml_edit
sha256: c566f4f804215d84a7a2c377667f546c6033d5b34b4f9e60dfb09d17c4e97826
url: "https://pub.dev"
source: hosted
version: "2.2.0"
sdks:
dart: ">=3.0.0 <4.0.0"
dart: ">=3.0.6 <4.0.0"
flutter: ">=3.10.0"

View file

@ -38,6 +38,10 @@ dependencies:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v1
sp_scanner:
git:
url: https://github.com/rafael-xmr/sp_scanner
ref: master
dev_dependencies:
flutter_test:

View file

@ -297,6 +297,7 @@ class CWBitcoin extends Bitcoin {
);
}
@override
List<BitcoinSilentPaymentAddressRecord> getSilentPaymentAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.silentAddresses
@ -304,6 +305,7 @@ class CWBitcoin extends Bitcoin {
.toList();
}
@override
List<BitcoinSilentPaymentAddressRecord> getSilentPaymentReceivedAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.silentAddresses
@ -311,10 +313,12 @@ class CWBitcoin extends Bitcoin {
.toList();
}
@override
bool isBitcoinReceivePageOption(ReceivePageOption option) {
return option is BitcoinReceivePageOption;
}
@override
BitcoinAddressType getOptionToType(ReceivePageOption option) {
return (option as BitcoinReceivePageOption).toType();
}
@ -326,6 +330,7 @@ class CWBitcoin extends Bitcoin {
return bitcoinWallet.silentPaymentsScanningActive;
}
@override
Future<void> setScanningActive(Object wallet, SettingsStore settingsStore, bool active) async {
final bitcoinWallet = wallet as ElectrumWallet;
// TODO: always when setting to scanning active, will force switch nodes. Remove when not needed anymore
@ -338,6 +343,7 @@ class CWBitcoin extends Bitcoin {
bitcoinWallet.setSilentPaymentsScanning(active);
}
@override
bool isTestnet(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.isTestnet ?? false;
@ -346,6 +352,7 @@ class CWBitcoin extends Bitcoin {
@override
int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date);
@override
Future<void> rescan(Object wallet, SettingsStore settingsStore,
{required int height, bool? doSingleScan}) async {
final bitcoinWallet = wallet as ElectrumWallet;
@ -359,6 +366,7 @@ class CWBitcoin extends Bitcoin {
bitcoinWallet.rescan(height: height, doSingleScan: doSingleScan);
}
@override
bool getNodeIsCakeElectrs(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
final node = bitcoinWallet.node;
@ -366,6 +374,7 @@ class CWBitcoin extends Bitcoin {
return node?.uri.host == '198.58.111.154' && node?.uri.port == 50002;
}
@override
void deleteSilentPaymentAddress(Object wallet, String address) {
final bitcoinWallet = wallet as ElectrumWallet;
bitcoinWallet.walletAddresses.deleteSilentPaymentAddress(address);

View file

@ -120,7 +120,7 @@ dev_dependencies:
mobx_codegen: ^2.1.1
build_resolvers: ^2.0.9
hive_generator: ^1.1.3
flutter_launcher_icons: ^0.11.0
# flutter_launcher_icons: ^0.11.0
# check flutter_launcher_icons for usage
pedantic: ^1.8.0
# replace https://github.com/dart-lang/lints#migrating-from-packagepedantic

View file

@ -89,6 +89,7 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_core/node.dart';
import 'package:mobx/mobx.dart';
""";
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
@ -160,7 +161,7 @@ abstract class Bitcoin {
BitcoinAddressType getOptionToType(ReceivePageOption option);
bool hasTaprootInput(PendingTransaction pendingTransaction);
bool getScanningActive(Object wallet);
void setScanningActive(Object wallet, bool active);
Future<void> setScanningActive(Object wallet, SettingsStore settingsStore, bool active);
bool isTestnet(Object wallet);
Future<PendingTransaction> replaceByFee(Object wallet, String transactionHash, String fee);
@ -169,7 +170,10 @@ abstract class Bitcoin {
int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size});
int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size});
int getHeightByDate({required DateTime date});
void rescan(Object wallet, {required int height, bool? doSingleScan});
Future<void> rescan(Object wallet, SettingsStore settingsStore,
{required int height, bool? doSingleScan});
bool getNodeIsCakeElectrs(Object wallet);
void deleteSilentPaymentAddress(Object wallet, String address);
}
""";