From c3cd34da94eac8159294bd56c9f3102ee29696a8 Mon Sep 17 00:00:00 2001 From: DenioD <41270280+DenioD@users.noreply.github.com> Date: Sat, 20 Jun 2020 22:39:04 +0200 Subject: [PATCH] add send Hush to a friend --- src/DataStore/ChatDataStore.cpp | 40 ++++ src/DataStore/ChatDataStore.h | 2 + src/mainwindow.cpp | 311 +++++++++++++++++++++++++++++++- src/mainwindow.h | 7 +- src/sendHushTransactionChat.ui | 4 +- 5 files changed, 353 insertions(+), 11 deletions(-) diff --git a/src/DataStore/ChatDataStore.cpp b/src/DataStore/ChatDataStore.cpp index f8335a6..2818b3a 100644 --- a/src/DataStore/ChatDataStore.cpp +++ b/src/DataStore/ChatDataStore.cpp @@ -116,6 +116,46 @@ std::map ChatDataStore::getAllOldContactRequests() return filteredItems; } +std::map ChatDataStore::getAllCashMemosIncoming() +{ + std::map filteredItems; + + for(auto &c: this->data) + { + if ( + (c.second.isOutgoing() == false) && + (c.second.getType() == "Money") && + (c.second.getMemo().startsWith("{")) + + ) + + { + filteredItems[c.first] = c.second; + } + } + return filteredItems; +} + +std::map ChatDataStore::getAllCashMemosOutgoing() +{ + std::map filteredItems; + + for(auto &c: this->data) + { + if ( + (c.second.isOutgoing() == true) && + (c.second.getType() == "Money") && + (c.second.getMemo().startsWith("{")) + + ) + + { + filteredItems[c.first] = c.second; + } + } + return filteredItems; +} + std::map ChatDataStore::getAllMemos() { std::map filteredItems; diff --git a/src/DataStore/ChatDataStore.h b/src/DataStore/ChatDataStore.h index f31ce7b..276ed53 100644 --- a/src/DataStore/ChatDataStore.h +++ b/src/DataStore/ChatDataStore.h @@ -22,6 +22,8 @@ class ChatDataStore ChatItem getData(QString key); std::map getAllRawChatItems(); std::map getAllNewContactRequests(); + std::map getAllCashMemosOutgoing(); + std::map getAllCashMemosIncoming(); std::map getAllOldContactRequests(); std::map getAllMemos(); QString getPassword(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6ed707a..27c1327 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -606,6 +606,18 @@ void MainWindow::setPassword(QString password) _password = password; } +QString MainWindow::getAmt() +{ + + return _amt; +} + +void MainWindow::setAmt(QString amt) +{ + + _amt = amt; +} + void MainWindow::setupStatusBar() { // Status Bar loadingLabel = new QLabel(); @@ -1464,9 +1476,9 @@ void MainWindow::setupchatTab() { QAction* requestHushAction; QAction* subatomicAction; contextMenu = new QMenu(ui->listContactWidget); - requestAction = new QAction("Send a contact request - coming soon",contextMenu); + HushAction = new QAction("Send or Request some Hush ",contextMenu); editAction = new QAction("Delete this contact",contextMenu); - HushAction = new QAction("Send or Request some Hush - coming soon",contextMenu); + requestAction = new QAction("Send a contact request - coming soon",contextMenu); subatomicAction = new QAction("Make a subatomic swap with a friend- coming soon",contextMenu); @@ -1497,15 +1509,16 @@ void MainWindow::setupchatTab() { QModelIndex index = ui->listContactWidget->currentIndex(); QString label_contact = index.data(Qt::DisplayRole).toString(); - QDialog transactionDialog(this); Ui_transactionHush transaction; + QDialog transactionDialog(this); transaction.setupUi(&transactionDialog); + Settings::saveRestore(&transactionDialog); for(auto &p : AddressBook::getInstance()->getAllAddressLabels()) if (label_contact == p.getName()) { - // Settings::saveRestore(&transactionDialog); + QStandardItemModel* contact = new QStandardItemModel(); QString avatar = p.getAvatar(); QStandardItem* Items1 = new QStandardItem(p.getName()); @@ -1517,11 +1530,17 @@ void MainWindow::setupchatTab() { transaction.contactName->setDragDropMode(QAbstractItemView::DropOnly); transaction.contactName->show(); - - QString amt = transaction.amountChat->text(); - qDebug()<<"AMT : " << amt; - } + + QObject::connect(transaction.sendHush, &QPushButton::clicked, [&] (){ + QString amt = transaction.amountChat->text(); + this->setAmt(amt); + transactionDialog.close(); + }); + + QObject::connect(transaction.sendHush, &QPushButton::clicked, this , &MainWindow::sendMoneyChat); + + transactionDialog.exec(); @@ -1554,6 +1573,282 @@ ui->memoTxtChat->setLenDisplayLabelChat(ui->memoSizeChat); } +// Create a Tx from the current state of the Chat page. +Tx MainWindow::createTxFromSendChatPage() { + Tx tx; + CAmount totalAmt; + // For each addr/amt in the Chat tab + { + + QString amtStr = this->getAmt(); + CAmount amt; + CAmount amtHm; + + amt = CAmount::fromDecimalString(amtStr); + amtHm = CAmount::fromDecimalString("0"); + totalAmt = totalAmt + amt; + + QModelIndex index = ui->listContactWidget->currentIndex(); + QString label_contact = index.data(Qt::DisplayRole).toString(); + + for(auto &c : AddressBook::getInstance()->getAllAddressLabels()) + + if (label_contact == c.getName()) { + + QString cid = c.getCid(); + QString myAddr = c.getMyAddress(); + QString type = "Money"; + QString addr = c.getPartnerAddress(); + + /////////User input for chatmemos + QString memoplain = QString("You have received/sent ") + amtStr + QString(" HUSH") ; + + /////////We convert the user input from QString to unsigned char*, so we can encrypt it later + int lengthmemo = memoplain.length(); + + char *memoplainchar = NULL; + memoplainchar = new char[lengthmemo+2]; + strncpy(memoplainchar, memoplain.toUtf8(), lengthmemo +1); + + QString pubkey = this->getPubkeyByAddress(addr); + QString passphraseHash = DataStore::getChatDataStore()->getPassword(); + int length = passphraseHash.length(); + + ////////////////Generate the secretkey for our message encryption + + char *hashEncryptionKeyraw = NULL; + hashEncryptionKeyraw = new char[length+1]; + strncpy(hashEncryptionKeyraw, passphraseHash.toUtf8(), length+1); + + #define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw) + #define MESSAGEAS1_LEN length + + + unsigned char sk[crypto_kx_SECRETKEYBYTES]; + unsigned char pk[crypto_kx_PUBLICKEYBYTES]; + unsigned char server_rx[crypto_kx_SESSIONKEYBYTES], server_tx[crypto_kx_SESSIONKEYBYTES]; + + if (crypto_kx_seed_keypair(pk,sk, + MESSAGEAS1) !=0) { + + this->logger->write("Suspicious keypair, bail out "); + } + ////////////////Get the pubkey from Bob, so we can create the share key + + const QByteArray pubkeyBobArray = QByteArray::fromHex(pubkey.toLatin1()); + const unsigned char *pubkeyBob = reinterpret_cast(pubkeyBobArray.constData()); + /////Create the shared key for sending the message + + if (crypto_kx_server_session_keys(server_rx, server_tx, + pk, sk, pubkeyBob) != 0) { + this->logger->write("Suspicious client public send key, bail out "); + } + + + // Let's try to preserve Unicode characters + QByteArray ba_memo = memoplain.toUtf8(); + int ba_memo_length = ba_memo.size(); + + #define MESSAGEMoney (const unsigned char *) ba_memo.data() + #define MESSAGE_LENMoney ba_memo_length + + + ////////////Now lets encrypt the message Alice send to Bob////////////////////////////// + //#define MESSAGE (const unsigned char *) memoplainchar + //#define MESSAGE_LEN lengthmemo + #define CIPHERTEXT_LEN (crypto_secretstream_xchacha20poly1305_ABYTES + MESSAGE_LENMoney) + unsigned char ciphertext[CIPHERTEXT_LEN]; + unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; + + crypto_secretstream_xchacha20poly1305_state state; + + /* Set up a new stream: initialize the state and create the header */ + crypto_secretstream_xchacha20poly1305_init_push(&state, header, server_tx); + + + /* Now, encrypt the first chunk. `c1` will contain an encrypted, + * authenticated representation of `MESSAGE_PART1`. */ + crypto_secretstream_xchacha20poly1305_push + (&state, ciphertext, NULL, MESSAGEMoney, MESSAGE_LENMoney, NULL, 0, crypto_secretstream_xchacha20poly1305_TAG_FINAL); + + ////Create the HM for this message + QString headerbytes = QByteArray(reinterpret_cast(header), crypto_secretstream_xchacha20poly1305_HEADERBYTES).toHex(); + QString publickeyAlice = QByteArray(reinterpret_cast(pk), crypto_kx_PUBLICKEYBYTES).toHex(); + + + QString hmemo= createHeaderMemo(type,cid,myAddr,headerbytes,publickeyAlice,1,0); + + /////Ciphertext Memo + QString memo = QByteArray(reinterpret_cast(ciphertext), CIPHERTEXT_LEN).toHex(); + + + tx.toAddrs.push_back(ToFields{addr, amtHm, hmemo}); + tx.toAddrs.push_back(ToFields{addr, amt, memo}); + + } + } + + tx.fee = Settings::getMinerFee(); + + return tx; + +} + +void MainWindow::sendMoneyChat() { + +////////////////////////////Todo: Check if a Contact is selected////////// + + // Create a Tx from the values on the send tab. Note that this Tx object + // might not be valid yet. + + /* QString Name = ui->contactNameMemo->text(); + + if ((ui->contactNameMemo->text().isEmpty()) || (ui->memoTxtChat->toPlainText().trimmed().isEmpty())) { + + QMessageBox msg(QMessageBox::Critical, tr("You have to select a contact and insert a Memo"), + tr("You have selected no Contact from Contactlist,\n") + tr("\nor your Memo is empty"), + QMessageBox::Ok, this); + + msg.exec(); + return; + }*/ + + + Tx tx = createTxFromSendChatPage(); + + QString error = doSendChatMoneyTxValidations(tx); + + if (!error.isEmpty()) { + // Something went wrong, so show an error and exit + QMessageBox msg(QMessageBox::Critical, tr("Message Error"), error, + QMessageBox::Ok, this); + + msg.exec(); + + // abort the Tx + return; + } + + auto movie = new QMovie(this); + auto movie1 = new QMovie(this); + movie->setFileName(":/img/res/loaderblack.gif"); + movie1->setFileName(":/img/res/loaderwhite.gif"); + + auto theme = Settings::getInstance()->get_theme_name(); + if (theme == "Dark" || theme == "Midnight") { + + connect(movie, &QMovie::frameChanged, [=]{ + ui->sendChatButton->setIcon(movie->currentPixmap()); + }); + movie->start(); + ui->sendChatButton->show(); + ui->sendChatButton->setEnabled(false); + + } else { + + connect(movie1, &QMovie::frameChanged, [=]{ + ui->sendChatButton->setIcon(movie1->currentPixmap()); + }); + movie1->start(); + ui->sendChatButton->show(); + ui->sendChatButton->setEnabled(false); + } + + ui->memoTxtChat->clear(); + + // And send the Tx + rpc->executeTransaction(tx, + [=] (QString txid) { + ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); + + + QTimer::singleShot(1000, [=]() { + + if (theme == "Dark" || theme == "Midnight") { + QPixmap send(":/icons/res/send-white.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie->stop(); + ui->sendChatButton->setEnabled(true); + }else{ + + QPixmap send(":/icons/res/sendBlack.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie1->stop(); + ui->sendChatButton->setEnabled(true); + } + + }); + + // Force a UI update so we get the unconfirmed Tx + rpc->refresh(true); + ui->memoTxtChat->clear(); + + }, + // Errored out + [=] (QString opid, QString errStr) { + ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000); + + if (!opid.isEmpty()) + errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr; + + QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok); + movie->stop(); + + + if (theme == "Dark" || theme == "Midnight") { + QPixmap send(":/icons/res/send-white.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie->stop(); + ui->sendChatButton->setEnabled(true); + }else{ + + QPixmap send(":/icons/res/sendBlack.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie1->stop(); + ui->sendChatButton->setEnabled(true); + } + + + + } + ); + + } + +QString MainWindow::doSendChatMoneyTxValidations(Tx tx) { + // Check to see if we have enough verified funds to send the Tx. + + CAmount total; + for (auto toAddr : tx.toAddrs) { + if (!Settings::isValidAddress(toAddr.addr)) { + QString addr = (toAddr.addr.length() > 100 ? toAddr.addr.left(100) + "..." : toAddr.addr); + return QString(tr("Recipient Address ")) % addr % tr(" is Invalid"); + } + + // This technically shouldn't be possible, but issue #62 seems to have discovered a bug + // somewhere, so just add a check to make sure. + if (toAddr.amount.toqint64() < 0) { + return QString(tr("Amount for address '%1' is invalid!").arg(toAddr.addr)); + } + + total = total + toAddr.amount; + } + total = total + tx.fee; + + auto available = rpc->getModel()->getAvailableBalance(); + + if (available < total) { + return tr("Not enough available funds to send this transaction\n\nHave: %1\nNeed: %2\n\nNote: Funds need 1 confirmations before they can be spent") + .arg(available.toDecimalhushString(), total.toDecimalhushString()); + } + + return ""; +} + void MainWindow::updateChat() { rpc->refreshChat(ui->listChat,ui->memoSizeChat); diff --git a/src/mainwindow.h b/src/mainwindow.h index 3550b5a..2cdf9ce 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -49,12 +49,15 @@ public: QString doSendTxValidations(Tx tx); QString doSendChatTxValidations(Tx tx); + QString doSendChatMoneyTxValidations(Tx tx); QString doSendRequestTxValidations(Tx tx); QString getCid(); + QString getAmt(); QString getPassword(); std::map pubkeyMap; QString getPubkeyByAddress(QString requestZaddr); void setPassword(QString Password); + void setAmt(QString Amt); void addPubkey(QString requestZaddr, QString pubkey); @@ -111,6 +114,7 @@ private: void closeEvent(QCloseEvent* event); void closeEventpw(QCloseEvent* event); QString _password; + QString _amt; void setupSendTab(); @@ -135,7 +139,7 @@ private: Tx createTxFromChatPage(); Tx createTxForSafeContactRequest(); - + Tx createTxFromSendChatPage(); void encryptWallet(); void removeWalletEncryption(); @@ -144,6 +148,7 @@ private: void cancelButton(); void sendButton(); void sendChat(); + void sendMoneyChat(); void addContact(); void ContactRequest(); diff --git a/src/sendHushTransactionChat.ui b/src/sendHushTransactionChat.ui index 936e38f..1d403c6 100644 --- a/src/sendHushTransactionChat.ui +++ b/src/sendHushTransactionChat.ui @@ -159,14 +159,14 @@ 6 - + Request Hush - + 16777215