Wallet: payment id and integrated address
This commit is contained in:
parent
23cbf6fd97
commit
ca61153003
4 changed files with 144 additions and 14 deletions
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
#include "mnemonics/electrum-words.h"
|
#include "mnemonics/electrum-words.h"
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -137,6 +138,13 @@ uint64_t Wallet::amountFromDouble(double amount)
|
||||||
return amountFromString(ss.str());
|
return amountFromString(ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Wallet::genPaymentId()
|
||||||
|
{
|
||||||
|
crypto::hash8 payment_id = crypto::rand<crypto::hash8>();
|
||||||
|
return epee::string_tools::pod_to_hex(payment_id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////// WalletImpl implementation ////////////////////////
|
///////////////////////// WalletImpl implementation ////////////////////////
|
||||||
|
@ -302,6 +310,15 @@ std::string WalletImpl::address() const
|
||||||
return m_wallet->get_account().get_public_address_str(m_wallet->testnet());
|
return m_wallet->get_account().get_public_address_str(m_wallet->testnet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
|
||||||
|
{
|
||||||
|
crypto::hash8 pid;
|
||||||
|
if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) {
|
||||||
|
pid = crypto::rand<crypto::hash8>();
|
||||||
|
}
|
||||||
|
return m_wallet->get_account().get_public_integrated_address_str(pid, m_wallet->testnet());
|
||||||
|
}
|
||||||
|
|
||||||
bool WalletImpl::store(const std::string &path)
|
bool WalletImpl::store(const std::string &path)
|
||||||
{
|
{
|
||||||
clearStatus();
|
clearStatus();
|
||||||
|
@ -380,31 +397,56 @@ bool WalletImpl::refresh()
|
||||||
// - payment_details;
|
// - payment_details;
|
||||||
// - unconfirmed_transfer_details;
|
// - unconfirmed_transfer_details;
|
||||||
// - confirmed_transfer_details)
|
// - confirmed_transfer_details)
|
||||||
PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, uint64_t amount, uint32_t mixin_count)
|
PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, uint64_t amount, uint32_t mixin_count)
|
||||||
{
|
{
|
||||||
clearStatus();
|
clearStatus();
|
||||||
vector<cryptonote::tx_destination_entry> dsts;
|
vector<cryptonote::tx_destination_entry> dsts;
|
||||||
cryptonote::tx_destination_entry de;
|
cryptonote::tx_destination_entry de;
|
||||||
|
// indicates if dst_addr is integrated address (address + payment_id)
|
||||||
bool has_payment_id;
|
bool has_payment_id;
|
||||||
crypto::hash8 new_payment_id;
|
crypto::hash8 payment_id_short;
|
||||||
|
|
||||||
// TODO: (https://bitcointalk.org/index.php?topic=753252.msg9985441#msg9985441)
|
// TODO: (https://bitcointalk.org/index.php?topic=753252.msg9985441#msg9985441)
|
||||||
|
|
||||||
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
|
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
|
||||||
if (fake_outs_count == 0)
|
if (fake_outs_count == 0)
|
||||||
fake_outs_count = DEFAULT_MIXIN;
|
fake_outs_count = DEFAULT_MIXIN;
|
||||||
|
|
||||||
PendingTransactionImpl * transaction = new PendingTransactionImpl(this);
|
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
if(!cryptonote::get_account_integrated_address_from_str(de.addr, has_payment_id, payment_id_short, m_wallet->testnet(), dst_addr)) {
|
||||||
if(!cryptonote::get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), dst_addr)) {
|
|
||||||
// TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
|
// TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
|
||||||
m_status = Status_Error;
|
m_status = Status_Error;
|
||||||
m_errorString = "Invalid destination address";
|
m_errorString = "Invalid destination address";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> extra;
|
||||||
|
// if dst_addr is not an integrated address, parse payment_id
|
||||||
|
if (!has_payment_id && !payment_id.empty()) {
|
||||||
|
// copy-pasted from simplewallet.cpp:2212
|
||||||
|
crypto::hash payment_id_long;
|
||||||
|
bool r = tools::wallet2::parse_long_payment_id(payment_id, payment_id_long);
|
||||||
|
if (r) {
|
||||||
|
std::string extra_nonce;
|
||||||
|
cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id_long);
|
||||||
|
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
|
||||||
|
} else {
|
||||||
|
r = tools::wallet2::parse_short_payment_id(payment_id, payment_id_short);
|
||||||
|
if (r) {
|
||||||
|
std::string extra_nonce;
|
||||||
|
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id_short);
|
||||||
|
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r) {
|
||||||
|
m_status = Status_Error;
|
||||||
|
m_errorString = tr("payment id has invalid format, expected 16 or 64 character hex string: ") + payment_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
de.amount = amount;
|
de.amount = amount;
|
||||||
if (de.amount <= 0) {
|
if (de.amount <= 0) {
|
||||||
m_status = Status_Error;
|
m_status = Status_Error;
|
||||||
|
@ -414,7 +456,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, uint64
|
||||||
|
|
||||||
dsts.push_back(de);
|
dsts.push_back(de);
|
||||||
//std::vector<tools::wallet2::pending_tx> ptx_vector;
|
//std::vector<tools::wallet2::pending_tx> ptx_vector;
|
||||||
std::vector<uint8_t> extra;
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -60,6 +60,7 @@ public:
|
||||||
std::string errorString() const;
|
std::string errorString() const;
|
||||||
bool setPassword(const std::string &password);
|
bool setPassword(const std::string &password);
|
||||||
std::string address() const;
|
std::string address() const;
|
||||||
|
std::string integratedAddress(const std::string &payment_id) const;
|
||||||
bool store(const std::string &path);
|
bool store(const std::string &path);
|
||||||
std::string filename() const;
|
std::string filename() const;
|
||||||
std::string keysFilename() const;
|
std::string keysFilename() const;
|
||||||
|
@ -70,7 +71,8 @@ public:
|
||||||
uint64_t balance() const;
|
uint64_t balance() const;
|
||||||
uint64_t unlockedBalance() const;
|
uint64_t unlockedBalance() const;
|
||||||
bool refresh();
|
bool refresh();
|
||||||
PendingTransaction * createTransaction(const std::string &dst_addr, uint64_t amount, uint32_t mixin_count);
|
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
|
||||||
|
uint64_t amount, uint32_t mixin_count);
|
||||||
virtual void disposeTransaction(PendingTransaction * t);
|
virtual void disposeTransaction(PendingTransaction * t);
|
||||||
virtual TransactionHistory * history() const;
|
virtual TransactionHistory * history() const;
|
||||||
virtual void setListener(WalletListener * l);
|
virtual void setListener(WalletListener * l);
|
||||||
|
|
|
@ -134,6 +134,16 @@ struct Wallet
|
||||||
virtual std::string errorString() const = 0;
|
virtual std::string errorString() const = 0;
|
||||||
virtual bool setPassword(const std::string &password) = 0;
|
virtual bool setPassword(const std::string &password) = 0;
|
||||||
virtual std::string address() const = 0;
|
virtual std::string address() const = 0;
|
||||||
|
/*!
|
||||||
|
* \brief integratedAddress - returns integrated address for current wallet address and given payment_id.
|
||||||
|
* if passed "payment_id" param is an empty string or not-valid payment id string
|
||||||
|
* (16 characters hexadecimal string) - random payment_id will be generated
|
||||||
|
*
|
||||||
|
* \param payment_id - 16 characters hexadecimal string or empty string if new random payment id needs to be
|
||||||
|
* generated
|
||||||
|
* \return - 106 characters string representing integrated address
|
||||||
|
*/
|
||||||
|
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
|
||||||
/*!
|
/*!
|
||||||
* \brief store - stores wallet to file.
|
* \brief store - stores wallet to file.
|
||||||
* \param path - main filename to store wallet to. additionally stores address file and keys file.
|
* \param path - main filename to store wallet to. additionally stores address file and keys file.
|
||||||
|
@ -162,19 +172,22 @@ struct Wallet
|
||||||
static std::string displayAmount(uint64_t amount);
|
static std::string displayAmount(uint64_t amount);
|
||||||
static uint64_t amountFromString(const std::string &amount);
|
static uint64_t amountFromString(const std::string &amount);
|
||||||
static uint64_t amountFromDouble(double amount);
|
static uint64_t amountFromDouble(double amount);
|
||||||
|
static std::string genPaymentId();
|
||||||
|
|
||||||
// TODO?
|
// TODO?
|
||||||
// virtual uint64_t unlockedDustBalance() const = 0;
|
// virtual uint64_t unlockedDustBalance() const = 0;
|
||||||
virtual bool refresh() = 0;
|
virtual bool refresh() = 0;
|
||||||
/*!
|
/*!
|
||||||
* \brief createTransaction creates transaction
|
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
|
||||||
* \param dst_addr destination address as string
|
* \param dst_addr destination address as string
|
||||||
|
* \param payment_id optional payment_id, can be empty string
|
||||||
* \param amount amount
|
* \param amount amount
|
||||||
* \param mixin_count mixin count. if 0 passed, wallet will use default value
|
* \param mixin_count mixin count. if 0 passed, wallet will use default value
|
||||||
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
|
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
|
||||||
* after object returned
|
* after object returned
|
||||||
*/
|
*/
|
||||||
virtual PendingTransaction * createTransaction(const std::string &dst_addr, uint64_t amount, uint32_t mixin_count) = 0;
|
virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
|
||||||
|
uint64_t amount, uint32_t mixin_count) = 0;
|
||||||
virtual void disposeTransaction(PendingTransaction * t) = 0;
|
virtual void disposeTransaction(PendingTransaction * t) = 0;
|
||||||
virtual TransactionHistory * history() const = 0;
|
virtual TransactionHistory * history() const = 0;
|
||||||
virtual void setListener(WalletListener *) = 0;
|
virtual void setListener(WalletListener *) = 0;
|
||||||
|
|
|
@ -75,6 +75,7 @@ const std::string TESTNET_WALLET4_NAME = WALLETS_ROOT_DIR + "wallet_04.bin";
|
||||||
const std::string TESTNET_WALLET5_NAME = WALLETS_ROOT_DIR + "wallet_05.bin";
|
const std::string TESTNET_WALLET5_NAME = WALLETS_ROOT_DIR + "wallet_05.bin";
|
||||||
const std::string TESTNET_WALLET6_NAME = WALLETS_ROOT_DIR + "wallet_06.bin";
|
const std::string TESTNET_WALLET6_NAME = WALLETS_ROOT_DIR + "wallet_06.bin";
|
||||||
|
|
||||||
|
|
||||||
const char * TESTNET_WALLET_PASS = "";
|
const char * TESTNET_WALLET_PASS = "";
|
||||||
|
|
||||||
const std::string CURRENT_SRC_WALLET = TESTNET_WALLET1_NAME;
|
const std::string CURRENT_SRC_WALLET = TESTNET_WALLET1_NAME;
|
||||||
|
@ -85,6 +86,8 @@ const uint64_t AMOUNT_10XMR = 10000000000000L;
|
||||||
const uint64_t AMOUNT_5XMR = 5000000000000L;
|
const uint64_t AMOUNT_5XMR = 5000000000000L;
|
||||||
const uint64_t AMOUNT_1XMR = 1000000000000L;
|
const uint64_t AMOUNT_1XMR = 1000000000000L;
|
||||||
|
|
||||||
|
const std::string PAYMENT_ID_EMPTY = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -247,7 +250,7 @@ TEST_F(WalletManagerTest, WalletManagerChangesPassword)
|
||||||
ASSERT_TRUE(wallet1->setPassword(WALLET_PASS2));
|
ASSERT_TRUE(wallet1->setPassword(WALLET_PASS2));
|
||||||
ASSERT_TRUE(wmgr->closeWallet(wallet1));
|
ASSERT_TRUE(wmgr->closeWallet(wallet1));
|
||||||
Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME, WALLET_PASS2);
|
Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME, WALLET_PASS2);
|
||||||
ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);quint64
|
ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);
|
||||||
ASSERT_TRUE(wallet2->seed() == seed1);
|
ASSERT_TRUE(wallet2->seed() == seed1);
|
||||||
ASSERT_TRUE(wmgr->closeWallet(wallet2));
|
ASSERT_TRUE(wmgr->closeWallet(wallet2));
|
||||||
Bitmonero::Wallet * wallet3 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
|
Bitmonero::Wallet * wallet3 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
|
||||||
|
@ -359,6 +362,24 @@ TEST_F(WalletManagerTest, WalletManagerFindsWallet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WalletManagerTest, WalletGeneratesPaymentId)
|
||||||
|
{
|
||||||
|
std::string payment_id = Bitmonero::Wallet::genPaymentId();
|
||||||
|
ASSERT_TRUE(payment_id.length() == 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(WalletManagerTest, WalletGeneratesIntegratedAddress)
|
||||||
|
{
|
||||||
|
std::string payment_id = Bitmonero::Wallet::genPaymentId();
|
||||||
|
|
||||||
|
Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
|
||||||
|
std::string integrated_address = wallet1->integratedAddress(payment_id);
|
||||||
|
ASSERT_TRUE(integrated_address.length() == 106);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TEST_F(WalletTest1, WalletShowsBalance)
|
TEST_F(WalletTest1, WalletShowsBalance)
|
||||||
{
|
{
|
||||||
|
@ -438,6 +459,8 @@ TEST_F(WalletTest1, WalletTransactionWithMixin)
|
||||||
mixins.push_back(7); mixins.push_back(8); mixins.push_back(9); mixins.push_back(10); mixins.push_back(15);
|
mixins.push_back(7); mixins.push_back(8); mixins.push_back(9); mixins.push_back(10); mixins.push_back(15);
|
||||||
mixins.push_back(20); mixins.push_back(25);
|
mixins.push_back(20); mixins.push_back(25);
|
||||||
|
|
||||||
|
std::string payment_id = "";
|
||||||
|
|
||||||
|
|
||||||
Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
|
Bitmonero::Wallet * wallet1 = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
|
||||||
|
|
||||||
|
@ -452,7 +475,7 @@ TEST_F(WalletTest1, WalletTransactionWithMixin)
|
||||||
for (auto mixin : mixins) {
|
for (auto mixin : mixins) {
|
||||||
std::cerr << "Transaction mixin count: " << mixin << std::endl;
|
std::cerr << "Transaction mixin count: " << mixin << std::endl;
|
||||||
Bitmonero::PendingTransaction * transaction = wallet1->createTransaction(
|
Bitmonero::PendingTransaction * transaction = wallet1->createTransaction(
|
||||||
recepient_address, AMOUNT_5XMR, mixin);
|
recepient_address, payment_id, AMOUNT_5XMR, mixin);
|
||||||
|
|
||||||
std::cerr << "Transaction status: " << transaction->status() << std::endl;
|
std::cerr << "Transaction status: " << transaction->status() << std::endl;
|
||||||
std::cerr << "Transaction fee: " << Bitmonero::Wallet::displayAmount(transaction->fee()) << std::endl;
|
std::cerr << "Transaction fee: " << Bitmonero::Wallet::displayAmount(transaction->fee()) << std::endl;
|
||||||
|
@ -504,7 +527,9 @@ TEST_F(WalletTest1, WalletTransactionAndHistory)
|
||||||
|
|
||||||
std::string wallet4_addr = Utils::get_wallet_address(CURRENT_DST_WALLET, TESTNET_WALLET_PASS);
|
std::string wallet4_addr = Utils::get_wallet_address(CURRENT_DST_WALLET, TESTNET_WALLET_PASS);
|
||||||
|
|
||||||
Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr, AMOUNT_10XMR * 5, 0);
|
Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr,
|
||||||
|
PAYMENT_ID_EMPTY,
|
||||||
|
AMOUNT_10XMR * 5, 0);
|
||||||
ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok);
|
ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok);
|
||||||
ASSERT_TRUE(tx->commit());
|
ASSERT_TRUE(tx->commit());
|
||||||
history = wallet_src->history();
|
history = wallet_src->history();
|
||||||
|
@ -518,6 +543,54 @@ TEST_F(WalletTest1, WalletTransactionAndHistory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WalletTest1, WalletTransactionWithPaymentId)
|
||||||
|
{
|
||||||
|
|
||||||
|
Bitmonero::Wallet * wallet_src = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true);
|
||||||
|
// make sure testnet daemon is running
|
||||||
|
ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0));
|
||||||
|
ASSERT_TRUE(wallet_src->refresh());
|
||||||
|
Bitmonero::TransactionHistory * history = wallet_src->history();
|
||||||
|
history->refresh();
|
||||||
|
ASSERT_TRUE(history->count() > 0);
|
||||||
|
size_t count1 = history->count();
|
||||||
|
|
||||||
|
std::cout << "**** Transactions before transfer (" << count1 << ")" << std::endl;
|
||||||
|
for (auto t: history->getAll()) {
|
||||||
|
ASSERT_TRUE(t != nullptr);
|
||||||
|
Utils::print_transaction(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string wallet4_addr = Utils::get_wallet_address(CURRENT_DST_WALLET, TESTNET_WALLET_PASS);
|
||||||
|
|
||||||
|
std::string payment_id = Bitmonero::Wallet::genPaymentId();
|
||||||
|
ASSERT_TRUE(payment_id.length() == 16);
|
||||||
|
|
||||||
|
|
||||||
|
Bitmonero::PendingTransaction * tx = wallet_src->createTransaction(wallet4_addr,
|
||||||
|
payment_id,
|
||||||
|
AMOUNT_1XMR, 1);
|
||||||
|
|
||||||
|
ASSERT_TRUE(tx->status() == Bitmonero::PendingTransaction::Status_Ok);
|
||||||
|
ASSERT_TRUE(tx->commit());
|
||||||
|
history = wallet_src->history();
|
||||||
|
history->refresh();
|
||||||
|
ASSERT_TRUE(count1 != history->count());
|
||||||
|
|
||||||
|
bool payment_id_in_history = false;
|
||||||
|
|
||||||
|
std::cout << "**** Transactions after transfer (" << history->count() << ")" << std::endl;
|
||||||
|
for (auto t: history->getAll()) {
|
||||||
|
ASSERT_TRUE(t != nullptr);
|
||||||
|
Utils::print_transaction(t);
|
||||||
|
if (t->paymentId() == payment_id) {
|
||||||
|
payment_id_in_history = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_TRUE(payment_id_in_history);
|
||||||
|
}
|
||||||
|
|
||||||
struct MyWalletListener : public Bitmonero::WalletListener
|
struct MyWalletListener : public Bitmonero::WalletListener
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue