From 4d1d8e4118882643f33086f004d75acf0f2ef2b3 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 9 Aug 2011 16:57:18 -0400 Subject: [PATCH] Implement BIP 20 URI amount parsing --- doc/bips.md | 1 + src/qt/guiutil.cpp | 39 ++++++++++++++++++++++++++++++++++++--- src/qt/test/uritests.cpp | 13 +++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/doc/bips.md b/doc/bips.md index 1d5c91b8bd..c2fafbbfee 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -5,6 +5,7 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v24.0**): * [`BIP 13`](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki): The address format for P2SH addresses has been implemented since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)). * [`BIP 14`](https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki): The subversion string is being used as User Agent since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)). * [`BIP 16`](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki): The pay-to-script-hash evaluation rules have been implemented since **v0.6.0**, and took effect on *April 1st 2012* ([PR #748](https://github.com/bitcoin/bitcoin/pull/748)). +* [`BIP 20`](https://github.com/bitcoin/bips/blob/master/bip-0020.mediawiki): The extended amount format in URIs for Bitcoin payments has been implemented since **next-test 2011-12-23** * [`BIP 21`](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki): The URI format for Bitcoin payments has been implemented since **v0.6.0** ([PR #176](https://github.com/bitcoin/bitcoin/pull/176)). * [`BIP 22`](https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki): The 'getblocktemplate' (GBT) RPC protocol for mining has been implemented since **v0.7.0** ([PR #936](https://github.com/bitcoin/bitcoin/pull/936)). * [`BIP 23`](https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki): Some extensions to GBT have been implemented since **v0.10.0rc1**, including longpolling and block proposals ([PR #1816](https://github.com/bitcoin/bitcoin/pull/1816)). diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index e98d953fd2..2ad88c9f61 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -139,6 +139,39 @@ void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut) QObject::connect(new QShortcut(shortcut, button), &QShortcut::activated, [button]() { button->animateClick(); }); } +qint64 URIParseAmount(std::string amount_str, bool * const ok) +{ + bool is_hex = false; + if (amount_str[0] == 'x' || amount_str[0] == 'X') { + is_hex = true; + amount_str = amount_str.substr(1); + } + size_t exponent_sep_pos = amount_str.find_first_of("Xx", 1); + int exponent; + if (exponent_sep_pos != std::string::npos) { + exponent = QString::fromStdString(amount_str.substr(exponent_sep_pos + 1)).toInt(ok, is_hex ? 0x10 : 10); + if (!*ok) return -1; + } else { + exponent = is_hex ? 4 : 8; + exponent_sep_pos = amount_str.size(); + } + size_t fractional_sep_pos = amount_str.find('.'); + size_t fractional_digits = 0; + if (fractional_sep_pos == std::string::npos) + fractional_sep_pos = exponent_sep_pos; + else + fractional_digits = (exponent_sep_pos - fractional_sep_pos) - 1; + exponent -= fractional_digits; + amount_str = amount_str.substr(0, fractional_sep_pos) + (fractional_digits ? amount_str.substr(fractional_sep_pos + 1, fractional_digits) : ""); + if (exponent > 0) { + amount_str.append(exponent, '0'); + } else if (exponent < 0) { + // Sub-satoshi amount? Truncate + amount_str = amount_str.substr(0, amount_str.size() + exponent); + } + return QString::fromStdString(amount_str).toLongLong(ok, is_hex ? 0x10 : 10); +} + bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { // return if URI is not valid or is no bitcoin: URI @@ -178,9 +211,9 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { if(!i->second.isEmpty()) { - if (!BitcoinUnits::parse(BitcoinUnit::BTC, i->second, &rv.amount)) { - return false; - } + bool ok; + rv.amount = URIParseAmount((i->second).toStdString(), &ok); + if (!ok) return false; } fShouldReturnFalse = false; } diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp index b87d3b21ca..65d23cd6ca 100644 --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -46,6 +46,16 @@ void URITests::uriTests() QVERIFY(rv.amount == 10000000000LL); QVERIFY(rv.label == QString("Wikipedia Example")); + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=x100x4")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.amount == 16777216LL); + + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100x2")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + QVERIFY(rv.amount == 10000LL); + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=Wikipedia Example Address")); QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); @@ -61,6 +71,9 @@ void URITests::uriTests() uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000&label=Wikipedia Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=x1,0000&label=Wikipedia Example")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000.0&label=Wikipedia Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); }