This commit is contained in:
fossephate 2024-10-24 20:39:11 -07:00
parent 456c856163
commit 8fc980f55d
5 changed files with 85 additions and 32 deletions

View file

@ -464,7 +464,6 @@ class ElectrumClient {
final id = _id; final id = _id;
_registryTask(id, completer); _registryTask(id, completer);
socket!.write(jsonrpc(method: method, id: id, params: params)); socket!.write(jsonrpc(method: method, id: id, params: params));
await socket!.flush();
Timer(Duration(milliseconds: timeout), () { Timer(Duration(milliseconds: timeout), () {
if (!completer.isCompleted) { if (!completer.isCompleted) {
completer.completeError(RequestFailedTimeoutException(method, id)); completer.completeError(RequestFailedTimeoutException(method, id));

View file

@ -85,8 +85,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
alwaysScan: alwaysScan, alwaysScan: alwaysScan,
) { ) {
if (seedBytes != null) { if (seedBytes != null) {
mwebHd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath( mwebHd =
"m/1000'") as Bip32Slip10Secp256k1; Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/1000'") as Bip32Slip10Secp256k1;
mwebEnabled = alwaysScan ?? false; mwebEnabled = alwaysScan ?? false;
} else { } else {
mwebHd = null; mwebHd = null;
@ -380,6 +380,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
print("updating confs ${tx.id} from ${tx.confirmations} -> $confirmations"); print("updating confs ${tx.id} from ${tx.confirmations} -> $confirmations");
// if an outgoing tx is now confirmed, delete the utxo from the box (delete the unspent coin):
if (tx.confirmations >= 2 &&
tx.direction == TransactionDirection.outgoing &&
tx.unspents != null) {
for (var coin in tx.unspents!) {
// print(coin.address);
final utxo = mwebUtxosBox.get(coin.address);
if (utxo != null) {
print("deleting utxo ${coin.address} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
await mwebUtxosBox.delete(coin.address);
}
}
}
tx.confirmations = confirmations; tx.confirmations = confirmations;
tx.isPending = false; tx.isPending = false;
transactionHistory.addOne(tx); transactionHistory.addOne(tx);
@ -614,6 +628,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
await mwebUtxosBox.put(utxo.outputId, utxo); await mwebUtxosBox.put(utxo.outputId, utxo);
await handleIncoming(utxo); await handleIncoming(utxo);
}, onError: (error) {
print("error in utxo stream: $error");
}); });
} }
@ -750,7 +766,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
mwebUtxosBox.keys.forEach((dynamic oId) { mwebUtxosBox.keys.forEach((dynamic oId) {
final String outputId = oId as String; final String outputId = oId as String;
final utxo = mwebUtxosBox.get(outputId); final utxo = mwebUtxosBox.get(outputId);
if (utxo == null) { if (utxo == null || utxo.spent) {
return; return;
} }
if (utxo.address.isEmpty) { if (utxo.address.isEmpty) {
@ -800,15 +816,32 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
int unconfirmedMweb = 0; int unconfirmedMweb = 0;
try { try {
mwebUtxosBox.values.forEach((utxo) { mwebUtxosBox.values.forEach((utxo) {
if (utxo.height > 0) { bool isConfirmed = utxo.height > 0;
print(
"utxo: ${isConfirmed ? "confirmed" : "unconfirmed"} ${utxo.spent ? "spent" : "unspent"} ${utxo.outputId} ${utxo.height} ${utxo.value}");
// print(utxo.)
// old behavior:
// if (isConfirmed) {
// confirmedMweb += utxo.value.toInt();
// } else {
// unconfirmedMweb += utxo.value.toInt();
// }
if (isConfirmed) {
confirmedMweb += utxo.value.toInt(); confirmedMweb += utxo.value.toInt();
} else { }
if (isConfirmed && utxo.spent) {
unconfirmedMweb -= utxo.value.toInt();
}
if (!isConfirmed) {
unconfirmedMweb += utxo.value.toInt(); unconfirmedMweb += utxo.value.toInt();
} }
}); });
if (unconfirmedMweb > 0) {
unconfirmedMweb = -1 * (confirmedMweb - unconfirmedMweb);
}
} catch (_) {} } catch (_) {}
for (var addressRecord in walletAddresses.allAddresses) { for (var addressRecord in walletAddresses.allAddresses) {
@ -959,8 +992,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
if (!mwebEnabled) { if (!mwebEnabled) {
tx.changeAddressOverride = tx.changeAddressOverride =
(await (walletAddresses as LitecoinWalletAddresses) (await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(isPegIn: false))
.getChangeAddress(isPegIn: false))
.address; .address;
return tx; return tx;
} }
@ -1000,10 +1032,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
bool isPegIn = !hasMwebInput && hasMwebOutput; bool isPegIn = !hasMwebInput && hasMwebOutput;
bool isRegular = !hasMwebInput && !hasMwebOutput; bool isRegular = !hasMwebInput && !hasMwebOutput;
tx.changeAddressOverride = tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
(await (walletAddresses as LitecoinWalletAddresses) .getChangeAddress(isPegIn: isPegIn || isRegular))
.getChangeAddress(isPegIn: isPegIn || isRegular)) .address;
.address;
if (!hasMwebInput && !hasMwebOutput) { if (!hasMwebInput && !hasMwebOutput) {
tx.isMweb = false; tx.isMweb = false;
return tx; return tx;
@ -1053,11 +1084,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
return tx return tx
..addListener((transaction) async { ..addListener((transaction) async {
print(
"@@@@@@@@@@@@@@@@@@@@@@@@@############################# transaction: ${transaction.outputAddresses}");
final addresses = <String>{}; final addresses = <String>{};
transaction.inputAddresses?.forEach((id) async { transaction.inputAddresses?.forEach((id) async {
print("@@@@@@@@@@@@@@@@@@@@@@@@@ input address: $id");
final utxo = mwebUtxosBox.get(id); final utxo = mwebUtxosBox.get(id);
await mwebUtxosBox.delete(id); // gets deleted in checkMwebUtxosSpent // await mwebUtxosBox.delete(id); // gets deleted in checkMwebUtxosSpent
if (utxo == null) return; if (utxo == null) return;
// mark utxo as spent so we add it to the unconfirmed balance (as negative):
utxo.spent = true;
print(
"@@@@@@@@@@@@@@@@@@@@@@@@@ spent utxo: ${utxo.outputId} ${utxo.height} ${utxo.value}");
await mwebUtxosBox.put(id, utxo);
final addressRecord = walletAddresses.allAddresses final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address); .firstWhere((addressRecord) => addressRecord.address == utxo.address);
if (!addresses.contains(utxo.address)) { if (!addresses.contains(utxo.address)) {
@ -1250,8 +1289,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
@override @override
void setLedgerConnection(LedgerConnection connection) { void setLedgerConnection(LedgerConnection connection) {
_ledgerConnection = connection; _ledgerConnection = connection;
_litecoinLedgerApp = _litecoinLedgerApp = LitecoinLedgerApp(_ledgerConnection!,
LitecoinLedgerApp(_ledgerConnection!, derivationPath: walletInfo.derivationInfo!.derivationPath!); derivationPath: walletInfo.derivationInfo!.derivationPath!);
} }
@override @override
@ -1287,19 +1326,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
if (maybeChangePath != null) changePath ??= maybeChangePath.derivationPath; if (maybeChangePath != null) changePath ??= maybeChangePath.derivationPath;
} }
final rawHex = await _litecoinLedgerApp!.createTransaction( final rawHex = await _litecoinLedgerApp!.createTransaction(
inputs: readyInputs, inputs: readyInputs,
outputs: outputs outputs: outputs
.map((e) => TransactionOutput.fromBigInt( .map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value,
(e as BitcoinOutput).value, Uint8List.fromList(e.address.toScriptPubKey().toBytes()))) Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
.toList(), .toList(),
changePath: changePath, changePath: changePath,
sigHashType: 0x01, sigHashType: 0x01,
additionals: ["bech32"], additionals: ["bech32"],
isSegWit: true, isSegWit: true,
useTrustedInputForSegwit: true useTrustedInputForSegwit: true);
);
return BtcTransaction.fromRaw(rawHex); return BtcTransaction.fromRaw(rawHex);
} }

View file

@ -118,8 +118,7 @@ class PendingBitcoinTransaction with PendingTransaction {
Future<void> _ltcCommit() async { Future<void> _ltcCommit() async {
try { try {
final stub = await CwMweb.stub(); final resp = await CwMweb.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
final resp = await stub.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
idOverride = resp.txid; idOverride = resp.txid;
} on GrpcError catch (e) { } on GrpcError catch (e) {
throw BitcoinTransactionCommitFailed(errorMessage: e.message); throw BitcoinTransactionCommitFailed(errorMessage: e.message);

View file

@ -11,6 +11,7 @@ class MwebUtxo extends HiveObject {
required this.address, required this.address,
required this.outputId, required this.outputId,
required this.blockTime, required this.blockTime,
this.spent = false,
}); });
static const typeId = MWEB_UTXO_TYPE_ID; static const typeId = MWEB_UTXO_TYPE_ID;
@ -30,4 +31,7 @@ class MwebUtxo extends HiveObject {
@HiveField(4) @HiveField(4)
int blockTime; int blockTime;
@HiveField(5)
bool spent;
} }

View file

@ -197,4 +197,18 @@ class CwMweb {
} }
return null; return null;
} }
static Future<BroadcastResponse> broadcast(BroadcastRequest request) async {
log("mweb.broadcast() called");
try {
_rpcClient = await stub();
return await _rpcClient!.broadcast(request, options: CallOptions(timeout: TIMEOUT_DURATION));
} on GrpcError catch (e) {
log('Caught grpc error: ${e.message}');
rethrow;
} catch (e) {
log("Error getting create: $e");
rethrow;
}
}
} }