mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 17:57:40 +00:00
detect paynym notification transactions
This commit is contained in:
parent
6498e1926c
commit
6253652c21
3 changed files with 71 additions and 20 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?,
|
||||||
|
|
Loading…
Reference in a new issue