[skip ci] lots of fixes, still testing

This commit is contained in:
Matthew Fosse 2024-06-24 12:48:42 -07:00
parent 01ea1f16b9
commit e595d2d6cd
3 changed files with 155 additions and 93 deletions

View file

@ -51,18 +51,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex,
int? initialMwebHeight,
}) : mwebHd =
bitcoin.HDWallet.fromSeed(seedBytes, network: litecoinNetwork).derivePath("m/1000'"),
super(
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
networkType: litecoinNetwork,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.ltc) {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
networkType: litecoinNetwork,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
seedBytes: seedBytes,
currency: CryptoCurrency.ltc,
) {
walletAddresses = LitecoinWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
@ -81,6 +83,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
final bitcoin.HDWallet mwebHd;
late final Box<MwebUtxo> mwebUtxosBox;
Timer? _syncTimer;
// late int lastMwebUtxosHeight;
int mwebUtxosHeight = 0;
static Future<LitecoinWallet> create(
{required String mnemonic,
@ -143,10 +147,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
);
}
// final Map<String, Utxo> mwebUtxos = {};
int mwebUtxosHeight = 0;
int lastMwebUtxosHeight = 2699272;
@action
@override
Future<void> startSync() async {
@ -158,7 +158,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
final height = await electrumClient.getCurrentBlockChainTip() ?? 0;
final resp = await stub.status(StatusRequest());
// print("stats:");
// print(resp.mwebHeaderHeight);
// print("???????????????????");
// print(resp.blockHeaderHeight);
// print(resp.mwebUtxosHeight);
// print(height);
if (resp.blockHeaderHeight < height) {
@ -183,6 +184,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
final confirmations = mwebUtxosHeight - transaction.height + 1;
if (transaction.confirmations == confirmations) continue;
transaction.confirmations = confirmations;
print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@4");
transactionHistory.addOne(transaction);
}
await transactionHistory.save();
@ -206,24 +208,16 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
Future<void> processMwebUtxos() async {
final stub = await CwMweb.stub();
final scanSecret = mwebHd.derive(0x80000000).privKey!;
final req = UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: lastMwebUtxosHeight);
var initDone = false;
await for (Utxo sUtxo in stub.utxos(req)) {
if (sUtxo.address.isEmpty) {
await updateUnspent();
await updateBalance();
print("SCANNING FROM HEIGHT: ${walletInfo.restoreHeight}");
final req =
UtxosRequest(scanSecret: hex.decode(scanSecret), fromHeight: walletInfo.restoreHeight);
bool initDone = false;
for (final utxo in mwebUtxosBox.values) {
if (utxo.address.isEmpty) {
initDone = true;
continue;
}
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
if (!mwebAddrs.contains(sUtxo.address)) continue;
final utxo = MwebUtxo(
address: sUtxo.address,
blockTime: sUtxo.blockTime,
height: sUtxo.height,
outputId: sUtxo.outputId,
value: sUtxo.value.toInt(),
);
mwebUtxosBox.put(utxo.outputId, utxo);
final status = await stub.status(StatusRequest());
var date = DateTime.now();
@ -234,26 +228,33 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
}
var tx = transactionHistory.transactions.values
.firstWhereOrNull((tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false);
if (tx == null)
tx = ElectrumTransactionInfo(WalletType.litecoin,
id: utxo.outputId,
height: utxo.height,
amount: utxo.value.toInt(),
fee: 0,
direction: TransactionDirection.incoming,
isPending: utxo.height == 0,
date: date,
confirmations: confirmations,
inputAddresses: [],
outputAddresses: [utxo.outputId]);
if (tx == null) {
tx = ElectrumTransactionInfo(
WalletType.litecoin,
id: utxo.outputId,
height: utxo.height,
amount: utxo.value.toInt(),
fee: 0,
direction: TransactionDirection.incoming,
isPending: utxo.height == 0,
date: date,
confirmations: confirmations,
inputAddresses: [],
outputAddresses: [utxo.outputId],
);
}
tx.height = utxo.height;
tx.isPending = utxo.height == 0;
tx.confirmations = confirmations;
var isNew = transactionHistory.transactions[tx.id] == null;
bool isNew = transactionHistory.transactions[tx.id] == null;
if (!(tx.outputAddresses?.contains(utxo.address) ?? false)) {
tx.outputAddresses?.add(utxo.address);
isNew = true;
}
if (isNew) {
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
@ -261,13 +262,39 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
addressRecord.balance += utxo.value.toInt();
addressRecord.setAsUsed();
}
print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@");
transactionHistory.addOne(tx);
if (initDone) {
await updateUnspent();
await updateBalance();
}
lastMwebUtxosHeight = utxo.height;
print("latest mweb utxo height: $lastMwebUtxosHeight");
if (utxo.height > walletInfo.restoreHeight) {
walletInfo.updateRestoreHeight(utxo.height);
}
}
await for (Utxo sUtxo in stub.utxos(req)) {
final utxo = MwebUtxo(
address: sUtxo.address,
blockTime: sUtxo.blockTime,
height: sUtxo.height,
outputId: sUtxo.outputId,
value: sUtxo.value.toInt(),
);
if (utxo.address.isEmpty) {
await updateUnspent();
await updateBalance();
initDone = true;
}
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
if (!mwebAddrs.contains(utxo.address) && utxo.address.isNotEmpty) {
continue;
}
await mwebUtxosBox.put(utxo.outputId, utxo);
}
}
@ -277,7 +304,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
.map(checkPendingTransaction)))
.any((x) => x));
final outputIds =
mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId);
mwebUtxosBox.values.where((utxo) => utxo.height > 0).map((utxo) => utxo.outputId).toList();
final stub = await CwMweb.stub();
final resp = await stub.spent(SpentRequest(outputId: outputIds));
@ -293,6 +320,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
var input = sha256.startChunkedConversion(output);
for (final outputId in spent) {
final utxo = mwebUtxosBox.get(outputId);
await mwebUtxosBox.delete(outputId);
if (utxo == null) continue;
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
@ -300,7 +328,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
addressRecord.balance -= utxo.value.toInt();
amount += utxo.value.toInt();
inputAddresses.add(utxo.address);
mwebUtxosBox.delete(outputId);
input.add(hex.decode(outputId));
}
if (inputAddresses.isEmpty) return;
@ -317,6 +344,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
confirmations: 1,
inputAddresses: inputAddresses.toList(),
outputAddresses: []);
print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@2");
transactionHistory.addOne(tx);
await transactionHistory.save();
}
@ -356,9 +385,25 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
mwebUtxosBox.keys.forEach((dynamic oId) {
final String outputId = oId as String;
final utxo = mwebUtxosBox.get(outputId);
if (utxo == null) return;
if (utxo == null) {
return;
}
if (utxo.address.isEmpty) {
// not sure if a bug or a special case but we definitely ignore these
return;
}
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
.firstWhereOrNull((addressRecord) => addressRecord.address == utxo.address);
// print("^^^^^^^^^^^^^^^^^^");
// print(utxo.address);
// for (var a in walletAddresses.allAddresses) {
// print(a.address);
// }
if (addressRecord == null) {
print("addressRecord is null! TODO: handle this case");
return;
}
final unspent = BitcoinUnspent(
addressRecord, outputId, utxo.value.toInt(), mwebAddrs.indexOf(utxo.address));
if (unspent.vout == 0) unspent.isChange = true;
@ -378,6 +423,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
unconfirmed += utxo.value.toInt();
}
});
print("confirmed: $confirmed, unconfirmed: $unconfirmed");
return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen);
}
@ -465,53 +511,63 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction;
final stub = await CwMweb.stub();
final resp = await stub.create(CreateRequest(
try {
final tx = await super.createTransaction(credentials) as PendingBitcoinTransaction;
final stub = await CwMweb.stub();
final resp = await stub.create(CreateRequest(
rawTx: hex.decode(tx.hex),
scanSecret: hex.decode(mwebHd.derive(0x80000000).privKey!),
spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!),
feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000));
final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx));
tx.hexOverride = tx2
.copyWith(
witnesses: tx2.inputs.asMap().entries.map((e) {
final utxo = unspentCoins
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
final key = generateECPrivate(
hd: utxo.bitcoinAddressRecord.isHidden
? walletAddresses.sideHd
: walletAddresses.mainHd,
index: utxo.bitcoinAddressRecord.index,
network: network);
final digest = tx2.getTransactionSegwitDigit(
feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000,
));
final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx));
tx.hexOverride = tx2
.copyWith(
witnesses: tx2.inputs.asMap().entries.map((e) {
final utxo = unspentCoins
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
final key = generateECPrivate(
hd: utxo.bitcoinAddressRecord.isHidden
? walletAddresses.sideHd
: walletAddresses.mainHd,
index: utxo.bitcoinAddressRecord.index,
network: network);
final digest = tx2.getTransactionSegwitDigit(
txInIndex: e.key,
script: key.getPublic().toP2pkhAddress().toScriptPubKey(),
amount: BigInt.from(utxo.value));
return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]);
}).toList())
.toHex();
tx.outputs = resp.outputId;
return tx
..addListener((transaction) async {
final addresses = <String>{};
transaction.inputAddresses?.forEach((id) {
final utxo = mwebUtxosBox.get(id);
if (utxo == null) return;
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
if (!addresses.contains(utxo.address)) {
addressRecord.txCount++;
addresses.add(utxo.address);
}
addressRecord.balance -= utxo.value.toInt();
mwebUtxosBox.delete(id);
amount: BigInt.from(utxo.value),
);
return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]);
}).toList())
.toHex();
tx.outputs = resp.outputId;
return tx
..addListener((transaction) async {
final addresses = <String>{};
transaction.inputAddresses?.forEach((id) async {
final utxo = mwebUtxosBox.get(id);
await mwebUtxosBox.delete(id);
if (utxo == null) return;
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
if (!addresses.contains(utxo.address)) {
addressRecord.txCount++;
addresses.add(utxo.address);
}
addressRecord.balance -= utxo.value.toInt();
});
transaction.inputAddresses?.addAll(addresses);
print("BEING ADDED HERE@@@@@@@@@@@@@@@@@@@@@@@3");
transactionHistory.addOne(transaction);
await updateUnspent();
await updateBalance();
});
transaction.inputAddresses?.addAll(addresses);
transactionHistory.addOne(transaction);
await updateUnspent();
await updateBalance();
});
} catch (e, s) {
print(e);
print(s);
rethrow;
}
}
@override

View file

@ -22,7 +22,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
super.initialAddresses,
super.initialRegularAddressIndex,
super.initialChangeAddressIndex,
}) : super(walletInfo);
}) : super(walletInfo) {
topUpMweb(0);
}
final HDWallet mwebHd;
List<String> mwebAddrs = [];
@ -49,13 +51,14 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
String getAddress({required int index, required HDWallet hd, BitcoinAddressType? addressType}) {
if (addressType == SegwitAddresType.mweb) {
topUpMweb(index);
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index+1];
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
}
return generateP2WPKHAddress(hd: hd, index: index, network: network);
}
@override
Future<String> getAddressAsync({required int index, required HDWallet hd, BitcoinAddressType? addressType}) async {
Future<String> getAddressAsync(
{required int index, required HDWallet hd, BitcoinAddressType? addressType}) async {
if (addressType == SegwitAddresType.mweb) {
await topUpMweb(index);
}
@ -65,6 +68,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
@action
@override
Future<String> getChangeAddress() async {
updateChangeAddresses();
// this means all change addresses used will be mweb addresses!:
await topUpMweb(0);
return mwebAddrs[0];
}

View file

@ -103,6 +103,7 @@ class PendingBitcoinTransaction with PendingTransaction {
await _commit();
}
_listeners.forEach((listener) => listener(transactionInfo()));
}