From 889f9da962f1a4df770385d0cae8214a37a07f21 Mon Sep 17 00:00:00 2001 From: justanwar <42809091+justanwar@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:55:07 +0800 Subject: [PATCH 1/8] Move python dependency into its own line (#1491) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 627c6acea0..f421ef0201 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Bootstrappable builds can [be achieved with Guix.](contrib/guix/README.md) ```sh sudo apt-get update -sudo apt-get install git curl python build-essential libtool automake pkg-config cmake +sudo apt-get install python; sudo apt-get install git curl build-essential libtool automake pkg-config cmake # Also needed for GUI wallet only: sudo apt-get install qttools5-dev qttools5-dev-tools libxcb-xkb-dev bison ``` From bdfb76ee45d2b163618acad2be112dc24528f2fc Mon Sep 17 00:00:00 2001 From: firstcryptoman <86235719+firstcryptoman@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:36:36 +0400 Subject: [PATCH 2/8] Update macOS Build Instructions for Dependency and Configuration IssuesMac os build fix (#1463) * Updated macOS build instructions to include dependency setup * Update build-macos.md * Reorganized macOS build instructions to enhance clarity * Updated macOS build instructions to include troubleshooting steps for m4 detection issues. * Update macOS build instructions to include brew link --force for keg-only m4. --- doc/build-macos.md | 136 +++++++++++++++++++++++++++++---------------- 1 file changed, 87 insertions(+), 49 deletions(-) diff --git a/doc/build-macos.md b/doc/build-macos.md index 9def34e759..154c4fca32 100644 --- a/doc/build-macos.md +++ b/doc/build-macos.md @@ -3,56 +3,92 @@ macOS Build Instructions and Notes The commands in this guide should be executed in a Terminal application. The built-in one is located in `/Applications/Utilities/Terminal.app`. -Preparation ------------ -Install the macOS command line tools: - -`xcode-select --install` - -When the popup appears, click `Install`. - -Then install [Homebrew](http://brew.sh). - -Dependencies ----------------------- - - brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent qrencode - -In case you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG - - brew install librsvg - -Berkley DB ------------------------- +## Preparation +1. Install macOS Command Line Tools (if not already installed): + ```bash + xcode-select --install + ``` + When the popup appears, click `Install`. + + +2. Install Homebrew (if not already installed): + ```bash + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + ``` + + +## Dependencies +Install the required dependencies using Homebrew: +```bash +brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent qrencode python-setuptools m4 +``` + +In case you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG: +```bash +brew install librsvg +``` + +### Ensure `m4` is Found +After installing `m4`, it is important to note that `m4` is a `keg-only` formula in Homebrew. This means it is not symlinked into `/usr/local` by default. To make sure `m4` is available in your PATH, you'll need to link it manually with the `--force` flag: +```bash +brew link m4 --force +``` + +You can verify that `m4` is properly linked and available by running: +```bash +which m4 +``` +This should output the path to the `m4` binary, typically `/opt/homebrew/bin/m4` on Apple Silicon Macs. If you do not use the `--force` flag, `which m4` will likely output `/usr/bin/m4`, which is the system version and not the one installed via Homebrew. + +### Troubleshooting `m4` Issues +If `m4` is not found even after installation and linking with `--force`, you may need to install Xcode to ensure that `m4` is recognized: + +1. Install Xcode from the Mac App Store. +2. Once installed, open Xcode at least once to complete the setup. + +#### Berkeley DB It is recommended to use Berkeley DB 4.8. If you have to build it yourself, you can use [the installation script included in contrib/](https://github.com/bitcoin/bitcoin/blob/master/contrib/install_db4.sh) like so: - ./contrib/install_db4.sh . - +```bash +./contrib/install_db4.sh +``` from the root of the repository. -Note: You only need Berkeley DB if the wallet is enabled (see Disable-wallet mode). - - -Build Firo Core ------------------------- -1. Build Firo-core: - - Configure and build the headless Firo binaries as well as the GUI (if Qt is found). - - In case you want to build the disk image with `make deploy` (.dmg / optional), by passing `--with-gui` to configure. - - You can disable the GUI build by passing `--without-gui` to configure. - - ./autogen.sh - ./configure - make - -2. It is recommended to build and run the unit tests: - - ` make check` +*Note*: You only need Berkeley DB if the wallet is enabled (see Disable-wallet mode). + +## Build Instructions + +#### Download the Source +Before building, download the Firo source code: +```bash +git clone https://github.com/firoorg/firo +cd firo +``` + +#### Build Firo Core +1. **Prepare the build environment**: + ```bash + cd depends + make + cd .. + ``` + +2. **Configure and build Firo-core**: + ```bash + ./autogen.sh + ./configure --prefix=`pwd`/depends/`depends/config.guess` + make + ``` + +3. (optional) **It is recommended to build and run the unit tests**: + ```bash + ./configure --prefix=`pwd`/depends/`depends/config.guess` --enable-tests + make check + ``` -3. You can also create a .dmg that contains the .app bundle (optional): - - ` make deploy` +4. (optional) **You can also create a .dmg that contains the .app bundle**: + ```bash + make deploy + ``` Running @@ -86,7 +122,10 @@ Download and install the community edition of [Qt Creator](https://www.qt.io/dow Uncheck everything except Qt Creator during the installation process. 1. Make sure you installed everything through Homebrew mentioned above -2. Do a proper `./configure --enable-debug` +2. Properly configure the build environment: + ```bash + ./configure --prefix=`pwd`/depends/`depends/config.guess` --enable-debug + ``` 3. In Qt Creator do "New Project" -> Import Project -> Import Existing Project 4. Enter "bitcoin-qt" as project name, enter `src/qt` as location 5. Leave the file selection as it is @@ -99,7 +138,6 @@ Uncheck everything except Qt Creator during the installation process. Notes ----- -* Tested on macOS 10.11 through 10.14 on 64-bit Intel processors only. +* Tested on macOS 10.11 through 10.14 on 64-bit Intel processors, and on macOS 14.5 on an M2 chip. * Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714) - From a09abf06b46effd8c712d734f16712e18a873fff Mon Sep 17 00:00:00 2001 From: levoncrypto <95240473+levoncrypto@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:48:36 +0400 Subject: [PATCH 3/8] Fix block index issue with wallet scan (#1495) --- src/wallet/wallet.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4273885fac..787a739f0d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2382,7 +2382,11 @@ CBlockIndex* CWallet::GetBlockByDate(CBlockIndex* pindexStart, const std::string while (pindex) { if (pindex->GetBlockTime() > targetTimestamp) { - return chainActive[pindex->nHeight - 200]; + if (pindex->nHeight >= 200) { + return chainActive[pindex->nHeight - 200]; + } else { + return chainActive[0]; + } } pindex = chainActive.Next(pindex); } @@ -2411,7 +2415,6 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex *pindexStart, bool f // If you are recovering wallet with mnemonics, start rescan from the block when mnemonics were implemented in Firo. // If the user provides a date, start scanning from the block that corresponds to that date. // If no date is provided, start scanning from the mnemonic start block. - std::string wcdate = GetArg("-wcdate", ""); CBlockIndex* mnemonicStartBlock = chainActive[chainParams.GetConsensus().nMnemonicBlock]; if (mnemonicStartBlock == NULL) From 9d6ad89b4a2a141ca2614380a9979884a8beca16 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 <45027856+levonpetrosyan93@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:51:11 +0400 Subject: [PATCH 4/8] Added ISLocked info for mobile (#1488) --- src/rpc/misc.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 8f73698fa0..59705edb9f 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -24,7 +24,7 @@ #include "masternode-sync.h" #include "evo/deterministicmns.h" - +#include "llmq/quorums_instantsend.h" #include #include @@ -1583,6 +1583,9 @@ UniValue getmempoolsparktxs(const JSONRPCRequest& request) serialized_json.push_backV(serialized_coins); data.push_back(Pair("coins", serialized_json)); + bool fLLMQLocked = llmq::quorumInstantSendManager->IsLocked(txid); + data.push_back(Pair("isLocked", fLLMQLocked)); + result.push_back(Pair(EncodeBase64(txid.begin(), txid.size()), data)); } From 2422a070b25e6e1c9d48bf2a4b91f9cc75947064 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 <45027856+levonpetrosyan93@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:52:14 +0400 Subject: [PATCH 5/8] Show spark output addresses (#1485) --- src/wallet/rpcwallet.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a5ade6b12f..9ed20ecbd8 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1682,6 +1682,17 @@ void ListTransactions(CWallet * const pwallet, const CWalletTx& wtx, const std:: } entry.push_back(Pair("account", strSentAccount)); MaybePushAddress(entry, s.destination, addr); + + CSparkOutputTx output; + if (wtx.tx->IsSparkTransaction()) { + const CTxOut& txout = wtx.tx->vout[s.vout]; + if (txout.scriptPubKey.IsSparkMint() || txout.scriptPubKey.IsSparkSMint()) { + if(pwallet->GetSparkOutputTx(txout.scriptPubKey, output)) { + entry.push_back(Pair("address", output.address)); + } + } + } + if (wtx.tx->HasNoRegularInputs()) { entry.push_back(Pair("category", "spend")); } @@ -1691,7 +1702,12 @@ void ListTransactions(CWallet * const pwallet, const CWalletTx& wtx, const std:: else { entry.push_back(Pair("category", "send")); } - entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); + + if (!output.address.empty()) + entry.push_back(Pair("amount", ValueFromAmount(-output.amount))); + else + entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); + if (pwallet->mapAddressBook.count(s.destination)) { entry.push_back(Pair("label", pwallet->mapAddressBook[s.destination].name)); } From 60351b68850175dcce2fc8d2fa2c6394572217c2 Mon Sep 17 00:00:00 2001 From: levoncrypto <95240473+levoncrypto@users.noreply.github.com> Date: Fri, 29 Nov 2024 09:11:13 +0400 Subject: [PATCH 6/8] Fix mac action build (#1498) * Fix mac action build * Fix mac action build --- .github/workflows/ci-master.yml | 9 ++++++++- doc/build-macos.md | 2 +- doc/build-osx.md | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml index a42c390baf..0413f679d0 100644 --- a/.github/workflows/ci-master.yml +++ b/.github/workflows/ci-master.yml @@ -174,7 +174,14 @@ jobs: - name: Use Xcode instead of Command Line Tools run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer - name: Install Required Packages - run: brew install automake coreutils pkg-config python-setuptools + run: | + if ! command -v pkg-config &> /dev/null; then + echo "pkg-config not found, installing..." + brew install pkg-config + else + echo "pkg-config is already installed" + fi + brew install automake coreutils python-setuptools # Workaround for macOS: https://github.com/actions/runner/issues/2958 - name: Install setuptools run: sudo -H pip install setuptools diff --git a/doc/build-macos.md b/doc/build-macos.md index 154c4fca32..6d22eb4aee 100644 --- a/doc/build-macos.md +++ b/doc/build-macos.md @@ -20,7 +20,7 @@ The built-in one is located in `/Applications/Utilities/Terminal.app`. ## Dependencies Install the required dependencies using Homebrew: ```bash -brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf python qt libevent qrencode python-setuptools m4 +brew install automake berkeley-db4 libtool boost miniupnpc openssl protobuf python qt libevent qrencode python-setuptools m4 ``` In case you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG: diff --git a/doc/build-osx.md b/doc/build-osx.md index 32d7dbd69e..b6e583ff21 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -16,7 +16,7 @@ Then install [Homebrew](https://brew.sh). Dependencies ---------------------- - brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf qt libevent + brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl protobuf qt libevent If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG From c03c847c45c41fd07d309a2a2f356698cc26861d Mon Sep 17 00:00:00 2001 From: levoncrypto <95240473+levoncrypto@users.noreply.github.com> Date: Fri, 29 Nov 2024 16:14:54 +0400 Subject: [PATCH 7/8] Fix build and run issues with --disable-wallet configuration (#1492) --- src/qt/bitcoinaddressvalidator.h | 4 ++++ src/qt/bitcoingui.cpp | 27 ++++++++++++++------------- src/wallet/coincontrol.h | 1 + 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index f1d106dee6..2a08e483e2 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -6,7 +6,11 @@ #define BITCOIN_QT_BITCOINADDRESSVALIDATOR_H #include + +#ifdef ENABLE_WALLET #include "../spark/sparkwallet.h" +#endif + #include "../spark/state.h" /** Base58 entry widget validator, checks for valid characters and diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6e14a99593..47babf2ab7 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1481,19 +1481,20 @@ void BitcoinGUI::resizeEvent(QResizeEvent* event) { // Retrieve new dimensions from the resize event int newWidth = event->size().width(); - int newHeight = event->size().height(); int actionWidth = newWidth / 6; - // Set widths for each action dynamically - QWidget* overviewWidget = toolbar->widgetForAction(overviewAction); - QWidget* receiveWidget = toolbar->widgetForAction(receiveCoinsAction); - QWidget* historyWidget = toolbar->widgetForAction(historyAction); - QWidget* sendCoinsWidget = toolbar->widgetForAction(sendCoinsAction); - QWidget* masternodeWidget = toolbar->widgetForAction(masternodeAction); - - overviewWidget->setMinimumWidth(actionWidth); - receiveWidget->setMinimumWidth(actionWidth); - historyWidget->setMinimumWidth(actionWidth); - sendCoinsWidget->setMinimumWidth(actionWidth); - masternodeWidget->setMinimumWidth(actionWidth); + if (toolbar) { + // Set widths for each action dynamically + QWidget* overviewWidget = overviewAction ? toolbar->widgetForAction(overviewAction) : nullptr; + QWidget* receiveWidget = receiveCoinsAction ? toolbar->widgetForAction(receiveCoinsAction) : nullptr; + QWidget* historyWidget = historyAction ? toolbar->widgetForAction(historyAction) : nullptr; + QWidget* sendCoinsWidget = sendCoinsAction ? toolbar->widgetForAction(sendCoinsAction) : nullptr; + QWidget* masternodeWidget = masternodeAction ? toolbar->widgetForAction(masternodeAction) : nullptr; + + if (overviewWidget) overviewWidget->setMinimumWidth(actionWidth); + if (receiveWidget) receiveWidget->setMinimumWidth(actionWidth); + if (historyWidget) historyWidget->setMinimumWidth(actionWidth); + if (sendCoinsWidget) sendCoinsWidget->setMinimumWidth(actionWidth); + if (masternodeWidget) masternodeWidget->setMinimumWidth(actionWidth); + } } \ No newline at end of file diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index feed073c26..5dcf5e8363 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -6,6 +6,7 @@ #define BITCOIN_WALLET_COINCONTROL_H #include "primitives/transaction.h" +#include "base58.h" enum class CoinType { From 6da96d8a25f825f29f2812b79127dc010cffbffb Mon Sep 17 00:00:00 2001 From: levoncrypto <95240473+levoncrypto@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:59:04 +0400 Subject: [PATCH 8/8] Add memo field in UI (#1493) * Add memo field in UI * add memo warrning message and fixes * Add additional checks for memo --- src/qt/forms/sendcoinsentry.ui | 53 +++++++++++++++++++++++++++++----- src/qt/sendcoinsdialog.cpp | 13 +++++++++ src/qt/sendcoinsentry.cpp | 39 +++++++++++++++++++++++++ src/qt/sendcoinsentry.h | 1 + src/qt/transactiondesc.cpp | 42 ++++++++++++++++++++++++--- src/qt/walletmodel.cpp | 4 +-- src/spark/primitives.h | 11 +++++++ src/spark/sparkwallet.cpp | 9 +++++- src/wallet/wallet.h | 1 + 9 files changed, 159 insertions(+), 14 deletions(-) diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 361f182f0a..11a74f1155 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -221,7 +221,7 @@ - + Message: @@ -231,15 +231,54 @@ - - + + - A message that was attached to the firo: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Firo network. + Optional message for this transaction - - Qt::PlainText + + + + + + 0 - + + + + + + + + + + 5 + + + 5 + + + 10 + + + 10 + + + margin-left:-30px;margin-right:-10px;margin-top:2px; + + + + + + + + + + color: #FFA800; margin-left:-10px; + + + + diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 3bfbfbc6a7..fd3d658217 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -265,6 +265,7 @@ void SendCoinsDialog::on_sendButton_clicked() } ctx = dialog->getUnlockContext(); } + recipient.message = entry->getValue().message; recipients.append(recipient); } else @@ -532,6 +533,18 @@ void SendCoinsDialog::on_sendButton_clicked() QString questionString = tr("Are you sure you want to send?"); questionString.append(warningMessage); questionString.append("

%1"); + bool firstMessage = true; + for (const auto& rec : recipients) { + if (!rec.message.isEmpty()) { + if (firstMessage) { + questionString.append("
" + tr("Messages") + ":
"); + firstMessage = false; + } + QString sanitizedMsg = GUIUtil::HtmlEscape(rec.message, true); + questionString.append("• " + sanitizedMsg + "
"); + } + } + double txSize; if ((fAnonymousMode == false) && (recipients.size() == sparkAddressCount) && spark::IsSparkAllowed()) { diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index c250db4d21..26bd96b33c 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -11,6 +11,7 @@ #include "optionsmodel.h" #include "platformstyle.h" #include "walletmodel.h" +#include "../spark/sparkwallet.h" #include "../wallet/wallet.h" #include @@ -29,6 +30,7 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par QIcon icon_; icon_.addFile(QString::fromUtf8(":/icons/ic_warning"), QSize(), QIcon::Normal, QIcon::On); ui->iconWarning->setPixmap(icon_.pixmap(18, 18)); + ui->iconMessageWarning->setPixmap(icon_.pixmap(18, 18)); ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(":/icons/address-book")); ui->pasteButton->setIcon(platformStyle->SingleColorIcon(":/icons/editpaste")); @@ -55,6 +57,11 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par connect(ui->deleteButton, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked); connect(ui->deleteButton_is, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked); connect(ui->deleteButton_s, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked); + connect(ui->messageTextLabel, &QLineEdit::textChanged, this, &SendCoinsEntry::on_MemoTextChanged); + + ui->messageLabel->setVisible(false); + ui->messageTextLabel->setVisible(false); + ui->iconMessageWarning->setVisible(false); } SendCoinsEntry::~SendCoinsEntry() @@ -62,6 +69,31 @@ SendCoinsEntry::~SendCoinsEntry() delete ui; } +void SendCoinsEntry::on_MemoTextChanged(const QString &text) +{ + const spark::Params* params = spark::Params::get_default(); + int maxLength = params->get_memo_bytes(); + bool isOverLimit = text.length() > maxLength; + + if (isOverLimit) { + ui->messageWarning->setText(QString("Message exceeds %1 bytes limit").arg(maxLength)); + ui->messageWarning->setVisible(true); + ui->messageTextLabel->setStyleSheet("border: 1px solid red;"); + ui->iconMessageWarning->setVisible(true); + } else { + QString sanitized = text; + sanitized.remove(QRegExp("[\\x00-\\x1F\\x7F]")); + if (sanitized != text) { + ui->messageTextLabel->setText(sanitized); + return; + } + ui->messageWarning->clear(); + ui->messageWarning->setVisible(false); + ui->messageTextLabel->setStyleSheet(""); + ui->iconMessageWarning->setVisible(false); + } +} + void SendCoinsEntry::on_pasteButton_clicked() { // Paste text from clipboard into recipient field @@ -85,6 +117,13 @@ void SendCoinsEntry::on_payTo_textChanged(const QString &address) { updateLabel(address); setWarning(fAnonymousMode); + + bool isSparkAddress = false; + if (model) { + isSparkAddress = model->validateSparkAddress(address); + } + ui->messageLabel->setVisible(isSparkAddress); + ui->messageTextLabel->setVisible(isSparkAddress); } void SendCoinsEntry::setModel(WalletModel *_model) diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 2e89f0fd5e..4611bd0027 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -63,6 +63,7 @@ public Q_SLOTS: private Q_SLOTS: void deleteClicked(); void on_payTo_textChanged(const QString &address); + void on_MemoTextChanged(const QString &text); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); void updateDisplayUnit(); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index a13eb7e347..deaabaf85f 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -311,10 +311,44 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "" + tr("Transaction total size") + ": " + QString::number(wtx.tx->GetTotalSize()) + " bytes
"; strHTML += "" + tr("Output index") + ": " + QString::number(rec->getOutputIndex()) + "
"; - // Message from normal firo:URI (firo:123...?message=example) - for (const PAIRTYPE(std::string, std::string)& r : wtx.vOrderForm) - if (r.first == "Message") - strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(r.second, true) + "
"; + isminetype fAllFromMe = ISMINE_SPENDABLE; + bool foundSparkOutput = false; + + for (const CTxIn& txin : wtx.tx->vin) { + isminetype mine = wallet->IsMine(txin, *wtx.tx); + fAllFromMe = std::min(fAllFromMe, mine); + } + + bool firstMessage = true; + if (fAllFromMe) { + for (const CTxOut& txout : wtx.tx->vout) { + if (wtx.IsChange(txout)) continue; + + CSparkOutputTx sparkOutput; + if (wallet->GetSparkOutputTx(txout.scriptPubKey, sparkOutput)) { + if (!sparkOutput.memo.empty()) { + foundSparkOutput = true; + if (firstMessage) { + strHTML += "
" + tr("Messages") + ":
"; + firstMessage = false; + } + strHTML += "• " + GUIUtil::HtmlEscape(sparkOutput.memo, true) + "
"; + } + } + } + } + + if (!foundSparkOutput && wallet->sparkWallet) { + for (const auto& [id, meta] : wallet->sparkWallet->getMintMap()) { + if (meta.txid == rec->hash && !meta.memo.empty()) { + if (firstMessage) { + strHTML += "
" + tr("Messages") + ":
"; + firstMessage = false; + } + strHTML += "• " + GUIUtil::HtmlEscape(meta.memo, true) + "
"; + } + } + } if (wtx.IsCoinBase()) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 66a8538941..c625cf4b77 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1384,7 +1384,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareMintSparkTransaction(std::vecto address.decode(rcp.address.toStdString()); spark::MintedCoinData data; data.address = address; - data.memo = ""; + data.memo = rcp.message.toStdString(); data.v = rcp.amount; outputs.push_back(data); total += rcp.amount; @@ -1481,7 +1481,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareSpendSparkTransaction(WalletMod address.decode(rcp.address.toStdString()); spark::OutputCoinData data; data.address = address; - data.memo = ""; + data.memo = rcp.message.toStdString(); data.v = rcp.amount; privateRecipients.push_back(std::make_pair(data, rcp.fSubtractFeeFromAmount)); } else { diff --git a/src/spark/primitives.h b/src/spark/primitives.h index 7929e73b80..2bfa0c72bb 100644 --- a/src/spark/primitives.h +++ b/src/spark/primitives.h @@ -87,6 +87,7 @@ class CSparkOutputTx { public: std::string address; + std::string memo; int64_t amount; CSparkOutputTx() @@ -97,6 +98,7 @@ class CSparkOutputTx void SetNull() { address = ""; + memo = ""; amount = 0; } @@ -105,6 +107,15 @@ class CSparkOutputTx inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(address); READWRITE(amount); + if (ser_action.ForRead()) { + if (!s.empty()) { + READWRITE(memo); + } else { + memo = ""; + } + } else { + READWRITE(memo); + } } }; diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp index 382cb6c62d..9e5e0a49e8 100644 --- a/src/spark/sparkwallet.cpp +++ b/src/spark/sparkwallet.cpp @@ -742,7 +742,12 @@ std::vector CSparkWallet::CreateSparkMintRecipients( script.insert(script.end(), serializedCoins[i].begin(), serializedCoins[i].end()); unsigned char network = spark::GetNetworkType(); std::string addr = outputs[i].address.encode(network); - CRecipient recipient = {script, CAmount(outputs[i].v), false, addr}; + std::string memo = outputs[i].memo; + const std::size_t max_memo_size = outputs[i].address.get_params()->get_memo_bytes(); + if (memo.length() > max_memo_size) { + throw std::runtime_error(strprintf("Memo exceeds maximum length of %d bytes", max_memo_size)); + } + CRecipient recipient = {script, CAmount(outputs[i].v), false, addr, memo}; results.emplace_back(recipient); } @@ -1093,6 +1098,7 @@ bool CSparkWallet::CreateSparkMintTransactions( CSparkOutputTx output; output.address = recipient.address; output.amount = recipient.nAmount; + output.memo = recipient.memo; walletdb.WriteSparkOutputTx(recipient.scriptPubKey, output); break; } @@ -1530,6 +1536,7 @@ CWalletTx CSparkWallet::CreateSparkSpendTransaction( CSparkOutputTx output; output.address = privOutputs[i].address.encode(network); output.amount = privOutputs[i].v; + output.memo = privOutputs[i].memo; walletdb.WriteSparkOutputTx(script, output); tx.vout.push_back(CTxOut(0, script)); i++; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f9397f5150..f1f1d0c6a9 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -190,6 +190,7 @@ struct CRecipient CAmount nAmount; bool fSubtractFeeFromAmount; std::string address; + std::string memo; }; typedef std::map mapValue_t;