diff --git a/src/names/applications.cpp b/src/names/applications.cpp index ad4c82ced9..7edcad445a 100644 --- a/src/names/applications.cpp +++ b/src/names/applications.cpp @@ -12,6 +12,8 @@ #include +const std::string NAMECOIN_DOMAIN_SUFFIX = ".bit"; + namespace { @@ -171,7 +173,7 @@ DescFromName (const valtype& name, NameNamespace ns) const std::string nameStr = EncodeName (name, NameEncoding::ASCII); const std::string label = nameStr.substr(nsLen); - return label + ".bit"; + return label + NAMECOIN_DOMAIN_SUFFIX; } default: { @@ -185,21 +187,20 @@ IsValidJSONOrEmptyString (const std::string& text){ UniValue v; return text.empty() || v.read(text); -} +} bool IsMinimalJSONOrEmptyString (const std::string& text){ UniValue v; if(text.empty()){ return true; - } + } - if(!v.read(text)){ + if(!v.read(text)){ return false; - } + } const std::string minimalJSON = GetMinimalJSON(text); - const bool isMinimal = (text == minimalJSON); if(!isMinimal){ @@ -217,3 +218,13 @@ GetMinimalJSON (const std::string& text){ return v.write(0,0); } + +bool +IsPurportedNamecoinDomain (const std::string& domain) { + return domain.ends_with(NAMECOIN_DOMAIN_SUFFIX); +} + +std::string +ASCIIFromDomain(const std::string& domain) { + return "d/" + domain.substr(0, domain.size()-(NAMECOIN_DOMAIN_SUFFIX.length())); +} diff --git a/src/names/applications.h b/src/names/applications.h index 525d90f182..23e119a183 100644 --- a/src/names/applications.h +++ b/src/names/applications.h @@ -29,4 +29,9 @@ bool IsMinimalJSONOrEmptyString (const std::string& text); std::string GetMinimalJSON (const std::string& text); +bool IsPurportedNamecoinDomain (const std::string& domain); + +std::string ASCIIFromDomain (const std::string& domain); + #endif // H_BITCOIN_NAMES_APPLICATIONS + diff --git a/src/qt/buynamespage.cpp b/src/qt/buynamespage.cpp index e1cff8fb41..d4a39e6d6a 100644 --- a/src/qt/buynamespage.cpp +++ b/src/qt/buynamespage.cpp @@ -9,7 +9,7 @@ #include #include #include - +#include #include #include @@ -25,10 +25,14 @@ BuyNamesPage::BuyNamesPage(const PlatformStyle *platformStyle, QWidget *parent) ui->registerNameButton->hide(); - connect(ui->registerName, &QLineEdit::textEdited, this, &BuyNamesPage::onNameEdited); + connect(ui->registerNameDomain, &QLineEdit::textEdited, this, &BuyNamesPage::onDomainNameEdited); + connect(ui->registerNameAscii, &QLineEdit::textEdited, this, &BuyNamesPage::onAsciiNameEdited); + connect(ui->registerNameHex, &QLineEdit::textEdited, this, &BuyNamesPage::onHexNameEdited); connect(ui->registerNameButton, &QPushButton::clicked, this, &BuyNamesPage::onRegisterNameAction); - ui->registerName->installEventFilter(this); + ui->registerNameDomain->installEventFilter(this); + ui->registerNameAscii->installEventFilter(this); + ui->registerNameHex->installEventFilter(this); } BuyNamesPage::~BuyNamesPage() @@ -45,22 +49,59 @@ bool BuyNamesPage::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::FocusIn) { - if (object == ui->registerName) + if (object == ui->registerNameAscii) + { + ui->registerNameButton->setDefault(true); + } + + if (object == ui->registerNameHex) + { + ui->registerNameButton->setDefault(true); + } + + if (object == ui->registerNameDomain) { ui->registerNameButton->setDefault(true); } + } return QWidget::eventFilter(object, event); } -void BuyNamesPage::onNameEdited(const QString &name) -{ - if (!walletModel) - return; - const QString availableError = name_available(name); +QString BuyNamesPage::DomainToASCII(const QString &name) { + if(IsPurportedNamecoinDomain(name.toStdString())) + { + return QString::fromStdString(ASCIIFromDomain(name.toStdString())); + } + return QString(""); +} + + +QString BuyNamesPage::ASCIIToDomain(const QString &name) { + if(NamespaceFromName(name.toStdString()) == NameNamespace::Domain) + { + return QString::fromStdString(DescFromName(DecodeName(name.toStdString(), NameEncoding::ASCII), NameNamespace::Domain)); + } + return QString(""); +} + + +QString BuyNamesPage::HexToASCII(const QString &name) { + return NameTableModel::hexToAscii(name); +} + + +QString BuyNamesPage::ASCIIToHex(const QString &name) { + return NameTableModel::asciiToHex(name); +} - if (availableError == "") +void BuyNamesPage::RefreshAvailableError() +{ + QString name = ui->registerNameAscii->text(); + QString availableError = name_available(name); + + if (availableError.isEmpty()) { ui->statusLabel->setText(tr("%1 is available to register!").arg(name)); ui->registerNameButton->show(); @@ -72,12 +113,83 @@ void BuyNamesPage::onNameEdited(const QString &name) } } +void BuyNamesPage::onAsciiNameEdited(const QString &name) { + if (!walletModel) + return; + + try + { + const QString hexName = ASCIIToHex(name); + const QString domainName = ASCIIToDomain(name); + + ui->registerNameHex->setText(hexName); + ui->registerNameDomain->setText(domainName); + + RefreshAvailableError(); + } + catch(InvalidNameString &e) + { + ui->statusLabel->setText(tr("Not a valid ASCII entry!")); + } + +} + +void BuyNamesPage::onHexNameEdited(const QString &name) { + if (!walletModel) + return; + + try { + const QString asciiName = HexToASCII(name); + const QString domainName = ASCIIToDomain(asciiName); + + ui->registerNameAscii->setText(asciiName); + ui->registerNameDomain->setText(domainName); + + RefreshAvailableError(); + } + catch(InvalidNameString &e) + { + ui->statusLabel->setText(tr("Not a valid hex entry!")); + } + +} + +void BuyNamesPage::onDomainNameEdited(const QString &name) { + if (!walletModel) + return; + + try + { + const QString asciiName = DomainToASCII(name); + const QString hexName = ASCIIToHex(asciiName); + + ui->registerNameAscii->setText(asciiName); + ui->registerNameHex->setText(hexName); + + if(IsPurportedNamecoinDomain(name.toStdString())) + { + RefreshAvailableError(); + } + else + { + ui->statusLabel->setText(tr("\"%1\" requires .bit at the end to be a valid Namecoin domain!").arg(name)); + ui->registerNameButton->hide(); + } + } + catch(InvalidNameString &e) + { + ui->statusLabel->setText(tr("Not a valid entry!")); + } + + +} + void BuyNamesPage::onRegisterNameAction() { if (!walletModel) return; - QString name = ui->registerName->text(); + QString name = ui->registerNameAscii->text(); WalletModel::UnlockContext ctx(walletModel->requestUnlock()); if (!ctx.isValid()) @@ -99,8 +211,9 @@ void BuyNamesPage::onRegisterNameAction() return; } - // reset UI text - ui->registerName->setText("d/"); + ui->registerNameDomain->setText(""); + ui->registerNameAscii->setText("d/"); + ui->registerNameHex->setText(HexToASCII(QString("d/"))); ui->registerNameButton->setDefault(true); } diff --git a/src/qt/buynamespage.h b/src/qt/buynamespage.h index 46f37658b0..2f2b79dc8e 100644 --- a/src/qt/buynamespage.h +++ b/src/qt/buynamespage.h @@ -31,13 +31,22 @@ class BuyNamesPage : public QWidget Ui::BuyNamesPage *ui; WalletModel *walletModel; + QString DomainToASCII(const QString &name); + QString ASCIIToDomain(const QString &name); + QString HexToASCII(const QString &name); + QString ASCIIToHex(const QString &name); + void RefreshAvailableError(); + QString name_available(const QString &name) const; QString firstupdate(const QString &name, const std::optional &value, const std::optional &transferTo) const; private Q_SLOTS: bool eventFilter(QObject *object, QEvent *event); - void onNameEdited(const QString &name); + void onDomainNameEdited(const QString &name); + void onHexNameEdited(const QString &name); + void onAsciiNameEdited(const QString &name); + void onRegisterNameAction(); }; diff --git a/src/qt/forms/buynamespage.ui b/src/qt/forms/buynamespage.ui index 82d6a82802..1d6f74895f 100644 --- a/src/qt/forms/buynamespage.ui +++ b/src/qt/forms/buynamespage.ui @@ -6,10 +6,11 @@ 0 0 - 776 + 1023 364 + + + + + + + 0 + 140 + + + + + 16777213 + 150 + + + + 0 + + + + Domain + + + + + 0 + 0 + 211 + 16 + + + + + 0 + 0 + + + + New domain name: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + registerName - - - - + + + + + 0 + 50 + 1005 + 41 + + + + + 0 + 0 + + + + <html><head/><body><p>E.g. <span style=" font-weight:600;">mysite.bit</span></p></body></html> + + + Qt::RichText + + + true + + + true + + + + + + 0 + 20 + 621 + 31 + + + + + + + + + + + Enter a name to register it. - - d/ - - - - - - - - 0 - 0 - - - - Use <strong>d/</strong> prefix for domain names. E.g. <strong>d/mysite</strong> will register <strong>mysite.bit</strong><br>See <a href="https://github.com/namecoin/proposals/blob/master/ifa-0001.md#keys">Domain Names: Keys</a> for reference. Other prefixes can be used for miscellaneous purposes (not domain names).</p> - - - Qt::RichText - - - true - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::PlainText - - - true - - - - - - - - 0 - 0 - + + + + + + + + ASCII + + + + + 0 + 0 + 211 + 16 + + + + + 0 + 0 + + + + New ASCII name: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - 150 - 0 - + + registerAsciiName + + + + + 0 + 50 + 621 + 61 + + + + + 0 + 0 + + + + Use <strong>d/</strong> prefix for domain names. E.g. <strong>d/mysite</strong> will register <strong>mysite.bit</strong><br>See the <a href="https://github.com/namecoin/proposals/blob/master/ifa-0001.md#keys">Namecoin Domain Names specification</a> for reference. Other prefixes can be used for miscellaneous purposes (not domain names).</p> + + + Qt::RichText + + + true + + + true + + + + + + 0 + 20 + 621 + 31 + + + + + + + d/ + + + + - Register the name. You must have already pre-registered it. - - - &Register Name… - - - - :/icons/send:/icons/send + Enter a name to register it. - - true + + + + + + + + Hex + + + + + 0 + 0 + 211 + 16 + + + + + 0 + 0 + + + + New hex name: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - Qt::Vertical + + registerHexName - - - 20 - 40 - + + + + + 0 + 50 + 1005 + 41 + + + + + 0 + 0 + + + + Hex names are only recommended for power users and developers. + + + Qt::PlainText + + + true + + + + + + 0 + 20 + 621 + 31 + + + + + + + 642f + + + + + + Enter a name to register it. - - - + + + + + + + + + + + + 0 + 0 + + + + + + + Qt::PlainText + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::PlainText + + + true + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + Register the name. You must have already pre-registered it. + + + Register Name… + + + + :/icons/send:/icons/send + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + diff --git a/src/test/name_applications_tests.cpp b/src/test/name_applications_tests.cpp index 72cb7bc7d7..7fd7825d47 100644 --- a/src/test/name_applications_tests.cpp +++ b/src/test/name_applications_tests.cpp @@ -141,4 +141,16 @@ BOOST_AUTO_TEST_CASE( minimal_json ) BOOST_CHECK_EQUAL(IsMinimalJSONOrEmptyString("{\"bar\":[1, 2, 3]}"), false); } +BOOST_AUTO_TEST_CASE( domain_detection ) +{ + BOOST_CHECK_EQUAL(IsPurportedNamecoinDomain("test.bit"), true); + + BOOST_CHECK_EQUAL(IsPurportedNamecoinDomain("test"), false); + + BOOST_CHECK_EQUAL(IsPurportedNamecoinDomain("test.com"), false); + + const std::string testName = ASCIIFromDomain("test.bit"); + BOOST_CHECK(testName == "d/test"); +} + BOOST_AUTO_TEST_SUITE_END()