detect paynym notification transactions

This commit is contained in:
julian 2023-01-23 16:11:24 -06:00
parent 6498e1926c
commit 6253652c21
3 changed files with 71 additions and 20 deletions

View file

@ -28,7 +28,7 @@ extension PayNym on DogecoinWallet {
return root; return root;
} }
// fetch or generate this wallet's bip47 payment code /// fetch or generate this wallet's bip47 payment code
Future<PaymentCode> getPaymentCode() async { Future<PaymentCode> getPaymentCode() async {
final address = await db final address = await db
.getAddresses(walletId) .getAddresses(walletId)
@ -75,7 +75,7 @@ extension PayNym on DogecoinWallet {
} }
void preparePaymentCodeSend(PaymentCode pCode) async { void preparePaymentCodeSend(PaymentCode pCode) async {
if (!hasConnected(pCode.notificationAddress())) { if (!(await hasConnected(pCode.notificationAddress()))) {
throw PaynymSendException("No notification transaction sent to $pCode"); throw PaynymSendException("No notification transaction sent to $pCode");
} else { } else {
final root = await getRootNode(mnemonic: await mnemonic); final root = await getRootNode(mnemonic: await mnemonic);
@ -433,12 +433,56 @@ extension PayNym on DogecoinWallet {
} }
} }
bool hasConnected(String paymentCodeString) { // TODO optimize
return db Future<bool> hasConnected(String paymentCodeString) async {
.getTransactions(walletId) final myCode = await getPaymentCode();
.filter() final myNotificationAddress = myCode.notificationAddress();
.address((q) => q.valueEqualTo(paymentCodeString))
.countSync() > final txns = await db
0; .getTransactions(walletId)
.filter()
.subTypeEqualTo(TransactionSubType.bip47Notification)
.findAll();
for (final tx in txns) {
if (tx.address.value?.value == myNotificationAddress) {
return true;
}
final blindedCode =
tx.outputs.elementAt(1).scriptPubKeyAsm!.split(" ")[1];
final designatedInput = tx.inputs.first;
final txPoint = designatedInput.txid.fromHex.toList();
final txPointIndex = designatedInput.vout;
final rev = Uint8List(txPoint.length + 4);
Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
final buffer = rev.buffer.asByteData();
buffer.setUint32(txPoint.length, txPointIndex, Endian.little);
final pubKey = designatedInput.scriptSigAsm!.split(" ")[1].fromHex;
final root = await getRootNode(mnemonic: await mnemonic);
final myPrivateKey =
root.derivePath(kPaynymDerivePath).derive(0).privateKey!;
final S = SecretPoint(myPrivateKey, pubKey);
final mask = PaymentCode.getMask(S.ecdhSecret(), rev);
final unBlindedPayload = PaymentCode.blind(blindedCode.fromHex, mask);
final unBlindedPaymentCode =
PaymentCode.initFromPayload(unBlindedPayload);
if (paymentCodeString == unBlindedPaymentCode.toString()) {
return true;
}
}
// otherwise return no
return false;
} }
} }

View file

@ -1162,10 +1162,8 @@ class DogecoinWallet extends CoinServiceAPI
.not() .not()
.typeEqualTo(isar_models.AddressType.nonWallet) .typeEqualTo(isar_models.AddressType.nonWallet)
.and() .and()
.group((q) => q .not()
.subTypeEqualTo(isar_models.AddressSubType.receiving) .subTypeEqualTo(isar_models.AddressSubType.nonWallet)
.or()
.subTypeEqualTo(isar_models.AddressSubType.change))
.findAll(); .findAll();
return allAddresses; return allAddresses;
} }

View file

@ -1,3 +1,4 @@
import 'package:bip47/src/util.dart';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -148,10 +149,6 @@ mixin ElectrumXParsing {
amount = amountReceivedInWallet; amount = amountReceivedInWallet;
} }
bool isNotificationTx = coin.hasPaynymSupport &&
type == TransactionType.incoming &&
transactionAddress.subType == AddressSubType.paynymNotification;
List<Output> outs = []; List<Output> outs = [];
List<Input> ins = []; List<Input> ins = [];
@ -188,15 +185,27 @@ mixin ElectrumXParsing {
outs.add(output); outs.add(output);
} }
TransactionSubType txSubType = TransactionSubType.none;
if (coin.hasPaynymSupport && outs.length > 1) {
List<String>? scriptChunks = outs[1].scriptPubKeyAsm?.split(" ");
if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") {
final blindedPaymentCode = scriptChunks![1];
final bytes = blindedPaymentCode.fromHex;
// https://en.bitcoin.it/wiki/BIP_0047#Sending
if (bytes.length == 80 && bytes.first == 1) {
txSubType = TransactionSubType.bip47Notification;
}
}
}
final tx = Transaction( final tx = Transaction(
walletId: walletId, walletId: walletId,
txid: txData["txid"] as String, txid: txData["txid"] as String,
timestamp: txData["blocktime"] as int? ?? timestamp: txData["blocktime"] as int? ??
(DateTime.now().millisecondsSinceEpoch ~/ 1000), (DateTime.now().millisecondsSinceEpoch ~/ 1000),
type: type, type: type,
subType: isNotificationTx subType: txSubType,
? TransactionSubType.bip47Notification
: TransactionSubType.none,
amount: amount, amount: amount,
fee: fee, fee: fee,
height: txData["height"] as int?, height: txData["height"] as int?,