diff options
38 files changed, 849 insertions, 232 deletions
@@ -29,4 +29,4 @@ cd build cmake -G "Visual Studio 11 Win64" .. And then do Build. -Good luck! +Good luck!
\ No newline at end of file diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 7d6f152e2..3aa32a238 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,6 +1,12 @@ -Release notes -> BMR +Bitmonero -- forked for Bitmonero project +Release notes 0.8.6 + +- Simplwallet can set extra for transfers +- Improvements in JSON RPC for wallet +- UX improvements in simplewallet +- Win32 compilation +- Mac OSX compilation Release notes 0.8.5 @@ -28,4 +34,4 @@ Release notes 0.8.2 Release notes 0.8.1 -Bytecoin project is moved to GitHub +Bytecoin project is moved to GitHub
\ No newline at end of file diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index 7e6b52d9c..5897a017e 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -856,7 +856,7 @@ using namespace std; http::url_content u_c; bool res = parse_url(url, u_c); - if(!tr.is_connected()) + if(!tr.is_connected() && !u_c.host.empty()) { CHECK_AND_ASSERT_MES(res, false, "failed to parse url: " << url); diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 74016ae75..0bf924802 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -14,15 +14,55 @@ namespace tools bool serialize_obj_to_file(t_object& obj, const std::string& file_path) { TRY_ENTRY(); +#if defined(_MSC_VER) + // Need to know HANDLE of file to call FlushFileBuffers + HANDLE data_file_handle = ::CreateFile(file_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == data_file_handle) + return false; + + int data_file_descriptor = _open_osfhandle((intptr_t)data_file_handle, 0); + if (-1 == data_file_descriptor) + { + ::CloseHandle(data_file_handle); + return false; + } + + FILE* data_file_file = _fdopen(data_file_descriptor, "wb"); + if (0 == data_file_file) + { + // Call CloseHandle is not necessary + _close(data_file_descriptor); + return false; + } + + // HACK: undocumented constructor, this code may not compile + std::ofstream data_file(data_file_file); + if (data_file.fail()) + { + // Call CloseHandle and _close are not necessary + fclose(data_file_file); + return false; + } +#else std::ofstream data_file; - data_file.open( file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); - if(data_file.fail()) + data_file.open(file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); + if (data_file.fail()) return false; +#endif boost::archive::binary_oarchive a(data_file); a << obj; + if (data_file.fail()) + return false; - return !data_file.fail(); + data_file.flush(); +#if defined(_MSC_VER) + // To make sure the file is fully stored on disk + ::FlushFileBuffers(data_file_handle); + fclose(data_file_file); +#endif + + return true; CATCH_ENTRY_L0("serialize_obj_to_file", false); } diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index a29896ee7..84fa73b92 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -2,7 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#pragma once +#pragma once #include <boost/serialization/split_free.hpp> #include <unordered_map> @@ -41,6 +41,35 @@ namespace boost } + template <class Archive, class h_key, class hval> + inline void save(Archive &a, const std::unordered_multimap<h_key, hval> &x, const boost::serialization::version_type ver) + { + size_t s = x.size(); + a << s; + BOOST_FOREACH(auto& v, x) + { + a << v.first; + a << v.second; + } + } + + template <class Archive, class h_key, class hval> + inline void load(Archive &a, std::unordered_multimap<h_key, hval> &x, const boost::serialization::version_type ver) + { + x.clear(); + size_t s = 0; + a >> s; + for(size_t i = 0; i != s; i++) + { + h_key k; + hval v; + a >> k; + a >> v; + x.emplace(k, v); + } + } + + template <class Archive, class hval> inline void save(Archive &a, const std::unordered_set<hval> &x, const boost::serialization::version_type ver) { @@ -73,6 +102,12 @@ namespace boost split_free(a, x, ver); } + template <class Archive, class h_key, class hval> + inline void serialize(Archive &a, std::unordered_multimap<h_key, hval> &x, const boost::serialization::version_type ver) + { + split_free(a, x, ver); + } + template <class Archive, class hval> inline void serialize(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver) { diff --git a/src/common/util.h b/src/common/util.h index af92adf94..8a1f4b041 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -52,7 +52,7 @@ namespace tools private: #if defined(WIN32) - static BOOL win_handler(DWORD type) + static BOOL WINAPI win_handler(DWORD type) { if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type) { diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index c1b9619f2..0e20b454b 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -582,6 +582,42 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) { return false; } +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + size_t real_txs_size = 0; + uint64_t real_fee = 0; + CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); + BOOST_FOREACH(crypto::hash &cur_hash, b.tx_hashes) { + auto cur_res = m_tx_pool.m_transactions.find(cur_hash); + if (cur_res == m_tx_pool.m_transactions.end()) { + LOG_ERROR("Creating block template: error: transaction not found"); + continue; + } + tx_memory_pool::tx_details &cur_tx = cur_res->second; + real_txs_size += cur_tx.blob_size; + real_fee += cur_tx.fee; + if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) { + LOG_ERROR("Creating block template: error: invalid transaction size"); + } + uint64_t inputs_amount; + if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) { + LOG_ERROR("Creating block template: error: cannot get inputs amount"); + } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) { + LOG_ERROR("Creating block template: error: invalid fee"); + } + } + if (txs_size != real_txs_size) { + LOG_ERROR("Creating block template: error: wrongly calculated transaction size"); + } + if (fee != real_fee) { + LOG_ERROR("Creating block template: error: wrongly calculated fee"); + } + CRITICAL_REGION_END(); + LOG_PRINT_L1("Creating block template: height " << height << + ", median size " << median_size << + ", already generated coins " << already_generated_coins << + ", transaction size " << txs_size << + ", fee " << fee); +#endif /* two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know @@ -590,27 +626,32 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); -#ifdef _DEBUG - std::list<size_t> try_val; - try_val.push_back(get_object_blobsize(b.miner_tx)); -#endif - size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) << + ", cumulative size " << cumulative_size); +#endif for (size_t try_count = 0; try_count != 10; ++try_count) { r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11); -#ifdef _DEBUG - try_val.push_back(get_object_blobsize(b.miner_tx)); -#endif CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); if (coinbase_blob_size > cumulative_size - txs_size) { cumulative_size = txs_size + coinbase_blob_size; +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << + ", cumulative size " << cumulative_size << " is greater then before"); +#endif continue; } if (coinbase_blob_size < cumulative_size - txs_size) { size_t delta = cumulative_size - txs_size - coinbase_blob_size; +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << + ", cumulative size " << txs_size + coinbase_blob_size << + " is less then before, adding " << delta << " zero bytes"); +#endif b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) { @@ -626,6 +667,10 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad } } CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << + ", cumulative size " << cumulative_size << " is now good"); +#endif return true; } LOG_ERROR("Failed to create_block_template with " << 10 << " tries"); @@ -1603,4 +1648,4 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont } return handle_block_to_main_chain(bl, id, bvc); -} +}
\ No newline at end of file diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index 8dff85d84..304096ef2 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -11,7 +11,8 @@ namespace cryptonote { inline bool create_checkpoints(cryptonote::checkpoints& checkpoints) - { + { + ADD_CHECKPOINT(22231, "d69526de16c58b07058bd80a9a8c99389814d17b1935623223b0d6e4a311b80f"); return true; } } diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index a231f9c75..aa2c82b2a 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -61,8 +61,8 @@ namespace cryptonote keypair txkey = keypair::generate(); add_tx_pub_key_to_extra(tx, txkey.pub); - if(extra_nonce.size()) - if(!add_tx_extra_nonce(tx, extra_nonce)) + if(!extra_nonce.empty()) + if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) return false; txin_gen in; @@ -74,6 +74,10 @@ namespace cryptonote LOG_PRINT_L0("Block is too big"); return false; } +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: reward " << block_reward << + ", fee " << fee) +#endif block_reward += fee; std::vector<size_t> out_amounts; @@ -204,53 +208,52 @@ namespace cryptonote return r; } //--------------------------------------------------------------- - crypto::public_key get_tx_pub_key_from_extra(const transaction& tx) + bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields) { - crypto::public_key pk = null_pkey; - parse_and_validate_tx_extra(tx, pk); - return pk; - } - //--------------------------------------------------------------- - bool parse_and_validate_tx_extra(const transaction& tx, crypto::public_key& tx_pub_key) - { - tx_pub_key = null_pkey; - bool padding_started = false; //let the padding goes only at the end - bool tx_extra_tag_pubkey_found = false; - bool tx_extra_extra_nonce_found = false; - for(size_t i = 0; i != tx.extra.size();) + tx_extra_fields.clear(); + + if(tx_extra.empty()) + return true; + + std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()); + std::istringstream iss(extra_str); + binary_archive<false> ar(iss); + + bool eof = false; + while (!eof) { - if(padding_started) - { - CHECK_AND_ASSERT_MES(!tx.extra[i], false, "Failed to parse transaction extra (not 0 after padding) in tx " << get_transaction_hash(tx)); - } - else if(tx.extra[i] == TX_EXTRA_TAG_PUBKEY) - { - CHECK_AND_ASSERT_MES(sizeof(crypto::public_key) <= tx.extra.size()-1-i, false, "Failed to parse transaction extra (TX_EXTRA_TAG_PUBKEY have not enough bytes) in tx " << get_transaction_hash(tx)); - CHECK_AND_ASSERT_MES(!tx_extra_tag_pubkey_found, false, "Failed to parse transaction extra (duplicate TX_EXTRA_TAG_PUBKEY entry) in tx " << get_transaction_hash(tx)); - tx_pub_key = *reinterpret_cast<const crypto::public_key*>(&tx.extra[i+1]); - i += 1 + sizeof(crypto::public_key); - tx_extra_tag_pubkey_found = true; - continue; - }else if(tx.extra[i] == TX_EXTRA_NONCE) - { - //CHECK_AND_ASSERT_MES(is_coinbase(tx), false, "Failed to parse transaction extra (TX_EXTRA_NONCE can be only in coinbase) in tx " << get_transaction_hash(tx)); - CHECK_AND_ASSERT_MES(!tx_extra_extra_nonce_found, false, "Failed to parse transaction extra (duplicate TX_EXTRA_NONCE entry) in tx " << get_transaction_hash(tx)); - CHECK_AND_ASSERT_MES(tx.extra.size()-1-i >= 1, false, "Failed to parse transaction extra (TX_EXTRA_NONCE have not enough bytes) in tx " << get_transaction_hash(tx)); - ++i; - CHECK_AND_ASSERT_MES(tx.extra.size()-1-i >= tx.extra[i], false, "Failed to parse transaction extra (TX_EXTRA_NONCE have wrong bytes counter) in tx " << get_transaction_hash(tx)); - tx_extra_extra_nonce_found = true; - i += tx.extra[i];//actually don't need to extract it now, just skip - } - else if(!tx.extra[i]) - { - padding_started = true; - continue; - } - ++i; + tx_extra_field field; + bool r = ::do_serialize(ar, field); + CHECK_AND_NO_ASSERT_MES(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()))); + tx_extra_fields.push_back(field); + + std::ios_base::iostate state = iss.rdstate(); + eof = (EOF == iss.peek()); + iss.clear(state); } + CHECK_AND_NO_ASSERT_MES(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size()))); + return true; } //--------------------------------------------------------------- + crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra) + { + std::vector<tx_extra_field> tx_extra_fields; + if (!parse_tx_extra(tx_extra, tx_extra_fields)) + return null_pkey; + + tx_extra_pub_key pub_key_field; + if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) + return null_pkey; + + return pub_key_field.pub_key; + } + //--------------------------------------------------------------- + crypto::public_key get_tx_pub_key_from_extra(const transaction& tx) + { + return get_tx_pub_key_from_extra(tx.extra); + } + //--------------------------------------------------------------- bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key) { tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); @@ -259,32 +262,50 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool add_tx_extra_nonce(transaction& tx, const blobdata& extra_nonce) + bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce) { - CHECK_AND_ASSERT_MES(extra_nonce.size() <=255, false, "extra nonce could be 255 bytes max"); - size_t start_pos = tx.extra.size(); - tx.extra.resize(tx.extra.size() + 2 + extra_nonce.size()); + CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max"); + size_t start_pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); //write tag - tx.extra[start_pos] = TX_EXTRA_NONCE; + tx_extra[start_pos] = TX_EXTRA_NONCE; //write len ++start_pos; - tx.extra[start_pos] = static_cast<uint8_t>(extra_nonce.size()); + tx_extra[start_pos] = static_cast<uint8_t>(extra_nonce.size()); //write data ++start_pos; - memcpy(&tx.extra[start_pos], extra_nonce.data(), extra_nonce.size()); + memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); + return true; + } + //--------------------------------------------------------------- + void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) + { + extra_nonce.clear(); + extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); + const uint8_t* payment_id_ptr = reinterpret_cast<const uint8_t*>(&payment_id); + std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); + } + //--------------------------------------------------------------- + bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id) + { + if(sizeof(crypto::hash) + 1 != extra_nonce.size()) + return false; + if(TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) + return false; + payment_id = *reinterpret_cast<const crypto::hash*>(extra_nonce.data() + 1); return true; } //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, transaction& tx, uint64_t unlock_time) + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) { tx.vin.clear(); tx.vout.clear(); tx.signatures.clear(); - tx.extra.clear(); tx.version = CURRENT_TRANSACTION_VERSION; tx.unlock_time = unlock_time; + tx.extra = extra; keypair txkey = keypair::generate(); add_tx_pub_key_to_extra(tx, txkey.pub); @@ -506,7 +527,10 @@ namespace cryptonote //--------------------------------------------------------------- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered) { - return lookup_acc_outs(acc, tx, get_tx_pub_key_from_extra(tx), outs, money_transfered); + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); + if(null_pkey == tx_pub_key) + return false; + return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); } //--------------------------------------------------------------- bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered) @@ -594,6 +618,7 @@ namespace cryptonote //genesis block bl = boost::value_initialized<block>(); + account_public_address ac = boost::value_initialized<account_public_address>(); std::vector<size_t> sz; construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis @@ -601,16 +626,16 @@ namespace cryptonote std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same - std::string genesis_coinbase_tx_hex = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1"; + std::string genesis_coinbase_tx_hex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; blobdata tx_bl; string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); - bl.major_version = 1; - bl.minor_version = 0; + bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; + bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; bl.timestamp = 0; - bl.nonce = 10000; + bl.nonce = 70; miner::find_nonce_for_given_block(bl, 1, 0); return true; } diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 5873d2943..138fb5224 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -41,11 +41,26 @@ namespace cryptonote }; //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, transaction& tx, uint64_t unlock_time); - bool parse_and_validate_tx_extra(const transaction& tx, crypto::public_key& tx_pub_key); + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); + + template<typename T> + bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field) + { + auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), [](const tx_extra_field& f) { return typeid(T) == f.type(); }); + if(tx_extra_fields.end() == it) + return false; + + field = boost::get<T>(*it); + return true; + } + + bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields); + crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra); crypto::public_key get_tx_pub_key_from_extra(const transaction& tx); bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key); - bool add_tx_extra_nonce(transaction& tx, const blobdata& extra_nonce); + bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce); + void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); + bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered); bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered); diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp index 052f46662..3dde6ad6c 100644 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/cryptonote_core/difficulty.cpp @@ -24,7 +24,7 @@ namespace cryptonote { #include <winnt.h> static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { - low = UnsignedMultiply128(a, b, &high); + low = mul128(a, b, &high); } #else diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h index 7e27cbc7f..8cff085dc 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_core/tx_extra.h @@ -2,10 +2,93 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#pragma once +#pragma once +#define TX_EXTRA_PADDING_MAX_COUNT 255 +#define TX_EXTRA_NONCE_MAX_COUNT 255 -#define TX_EXTRA_PADDING_MAX_COUNT 40 +#define TX_EXTRA_TAG_PADDING 0x00 #define TX_EXTRA_TAG_PUBKEY 0x01 #define TX_EXTRA_NONCE 0x02 + +#define TX_EXTRA_NONCE_PAYMENT_ID 0x00 + +namespace cryptonote +{ + struct tx_extra_padding + { + size_t size; + + // load + template <template <bool> class Archive> + bool do_serialize(Archive<false>& ar) + { + // size - 1 - because of variant tag + for (size = 1; size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) + { + std::ios_base::iostate state = ar.stream().rdstate(); + bool eof = EOF == ar.stream().peek(); + ar.stream().clear(state); + + if (eof) + break; + + uint8_t zero; + if (!::do_serialize(ar, zero)) + return false; + + if (0 != zero) + return false; + } + + return size <= TX_EXTRA_PADDING_MAX_COUNT; + } + + // store + template <template <bool> class Archive> + bool do_serialize(Archive<true>& ar) + { + if(TX_EXTRA_PADDING_MAX_COUNT < size) + return false; + + // i = 1 - because of variant tag + for (size_t i = 1; i < size; ++i) + { + uint8_t zero = 0; + if (!::do_serialize(ar, zero)) + return false; + } + return true; + } + }; + + struct tx_extra_pub_key + { + crypto::public_key pub_key; + + BEGIN_SERIALIZE() + FIELD(pub_key) + END_SERIALIZE() + }; + + struct tx_extra_nonce + { + std::string nonce; + + BEGIN_SERIALIZE() + FIELD(nonce) + if(TX_EXTRA_NONCE_MAX_COUNT < nonce.size()) return false; + END_SERIALIZE() + }; + + // tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: + // varint tag; + // varint size; + // varint data[]; + typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce> tx_extra_field; +} + +VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY); +VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 3ac1331bd..3978dfb96 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -141,6 +141,9 @@ namespace cryptonote uint64_t operator()(const txin_to_scripthash& tx) const {return 0;} }; +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + friend class blockchain_storage; +#endif }; } diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp index a9f0ceab0..3af04954f 100644 --- a/src/miner/simpleminer.cpp +++ b/src/miner/simpleminer.cpp @@ -120,6 +120,7 @@ namespace mining COMMAND_RPC_LOGIN::request req = AUTO_VAL_INIT(req); req.login = m_login; req.pass = m_pass; + req.agent = "simpleminer/0.1"; COMMAND_RPC_LOGIN::response resp = AUTO_VAL_INIT(resp); if(!epee::net_utils::invoke_http_json_rpc<mining::COMMAND_RPC_LOGIN>("/", req, resp, m_http_client)) { @@ -137,7 +138,12 @@ namespace mining } pool_session_id = resp.id; //78 - if(!text_job_details_to_native_job_details(resp.job, job)) + if (resp.job.blob.empty() && resp.job.target.empty() && resp.job.job_id.empty()) + { + LOG_PRINT_L0("Job didn't change"); + continue; + } + else if(!text_job_details_to_native_job_details(resp.job, job)) { LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); m_http_client.disconnect(); @@ -161,7 +167,8 @@ namespace mining COMMAND_RPC_SUBMITSHARE::response submit_response = AUTO_VAL_INIT(submit_response); submit_request.id = pool_session_id; submit_request.job_id = job.job_id; - submit_request.nonce = epee::string_tools::pod_to_hex((*((uint32_t*)&job.blob.data()[78]))); + submit_request.nonce = epee::string_tools::pod_to_hex((*((uint32_t*)&job.blob.data()[39]))); + submit_request.result = epee::string_tools::pod_to_hex(h); LOG_PRINT_L0("Share found: nonce=" << submit_request.nonce << " for job=" << job.job_id << ", submitting..."); if(!epee::net_utils::invoke_http_json_rpc<mining::COMMAND_RPC_SUBMITSHARE>("/", submit_request, submit_response, m_http_client)) { @@ -193,7 +200,12 @@ namespace mining epee::misc_utils::sleep_no_w(1000); break; } - if(!text_job_details_to_native_job_details(getjob_response, job)) + if (getjob_response.blob.empty() && getjob_response.target.empty() && getjob_response.job_id.empty()) + { + LOG_PRINT_L0("Job didn't change"); + continue; + } + else if(!text_job_details_to_native_job_details(getjob_response, job)) { LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); m_http_client.disconnect(); diff --git a/src/miner/simpleminer_protocol_defs.h b/src/miner/simpleminer_protocol_defs.h index 9b70f8cbf..06b6a9053 100644 --- a/src/miner/simpleminer_protocol_defs.h +++ b/src/miner/simpleminer_protocol_defs.h @@ -36,10 +36,12 @@ namespace mining { std::string login; std::string pass; + std::string agent; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(login) KV_SERIALIZE(pass) + KV_SERIALIZE(agent) END_KV_SERIALIZE_MAP() }; @@ -82,11 +84,13 @@ namespace mining { std::string id; std::string nonce; + std::string result; std::string job_id; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(id) KV_SERIALIZE(nonce) + KV_SERIALIZE(result) KV_SERIALIZE(job_id) END_KV_SERIALIZE_MAP() }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 4d78367b6..631fb5cf8 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2,7 +2,6 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - #include <boost/foreach.hpp> #include "include_base_utils.h" using namespace epee; @@ -359,8 +358,7 @@ namespace cryptonote return false; } blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = null_pkey; - cryptonote::parse_and_validate_tx_extra(b.miner_tx, tx_pub_key); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); if(tx_pub_key == null_pkey) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index d1c6f7f63..2d78baf3a 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -122,7 +122,7 @@ namespace serialization { { std::ios_base::iostate state = s.rdstate(); result = EOF == s.peek(); - s.setstate(state); + s.clear(state); } return result; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a9c171f6b..919782ab6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -64,6 +64,19 @@ namespace return err; } + bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) + { + blobdata payment_id_data; + if(!string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) + return false; + + if(sizeof(crypto::hash) != payment_id_data.size()) + return false; + + payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_data.data()); + return true; + } + class message_writer { public: @@ -83,7 +96,7 @@ namespace , m_oss(std::move(rhs.m_oss)) #else // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 - , m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate) + , m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate) #endif , m_color(std::move(rhs.m_color)) , m_log_level(std::move(rhs.m_log_level)) @@ -171,8 +184,9 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); + m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments <payment_id_1> [<payment_id_2> ... <payment_id_N>] - Show payments <payment_id_1>, ... <payment_id_N>"); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer <mixin_count> <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer <mixin_count> <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log <level> - Change current log detalization level, <level> is a number 0-4"); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); @@ -181,7 +195,7 @@ simple_wallet::simple_wallet() //---------------------------------------------------------------------------------------------------- bool simple_wallet::set_log(const std::vector<std::string> &args) { - if(args.size() != 1) + if(args.size() != 1) { fail_msg_writer() << "use: set_log <log_level_number_0-4>"; return true; @@ -202,6 +216,43 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::ask_wallet_create_if_needed() +{ + std::string wallet_path; + + std::cout << "Specify wallet file name (e.g., wallet.bin). If the wallet doesn't exist, it will be created.\n"; + std::cout << "Wallet file name: "; + + std::getline(std::cin, wallet_path); + + wallet_path = string_tools::trim(wallet_path); + + bool keys_file_exists; + bool wallet_file_exitst; + tools::wallet2::wallet_exists(wallet_path, keys_file_exists, wallet_file_exitst); + + bool r; + if(keys_file_exists) + { + m_wallet_file = wallet_path; + r = true; + }else + { + if(!wallet_file_exitst) + { + std::cout << "The wallet doesn't exist, generating new one" << std::endl; + m_generate_new = wallet_path; + r = true; + }else + { + fail_msg_writer() << "failed to open wallet \"" << wallet_path << "\". Keys file wasn't found"; + r = false; + } + } + + return r; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { handle_command_line(vm); @@ -212,13 +263,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } - size_t c = 0; + size_t c = 0; if(!m_generate_new.empty()) ++c; if(!m_wallet_file.empty()) ++c; if (1 != c) { - fail_msg_writer() << "you must specify --wallet-file or --generate-new-wallet params"; - return false; + if(!ask_wallet_create_if_needed()) + return false; } if (m_daemon_host.empty()) @@ -463,6 +514,15 @@ void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transactio m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- +void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) +{ + message_writer(epee::log_space::console_color_red, true) << + "Height " << height << + ", transaction " << get_transaction_hash(tx) << + ", unsupported transaction format"; + m_refresh_progress_reporter.update(height, true); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::refresh(const std::vector<std::string>& args) { if (!try_connect_to_daemon()) @@ -587,6 +647,55 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::show_payments(const std::vector<std::string> &args) +{ + if(args.empty()) + { + fail_msg_writer() << "expected at least one payment_id"; + return true; + } + + message_writer() << " payment \t" << + " transaction \t" << + " height\t amount \tunlock time"; + + bool payments_found = false; + for(std::string arg : args) + { + crypto::hash payment_id; + if(parse_payment_id(arg, payment_id)) + { + std::list<tools::wallet2::payment_details> payments; + m_wallet->get_payments(payment_id, payments); + if(payments.empty()) + { + success_msg_writer() << "No payments with id " << payment_id; + continue; + } + + for (const tools::wallet2::payment_details& pd : payments) + { + if(!payments_found) + { + payments_found = true; + } + success_msg_writer(true) << + payment_id << '\t' << + pd.m_tx_hash << '\t' << + std::setw(8) << pd.m_block_height << '\t' << + std::setw(21) << print_money(pd.m_amount) << '\t' << + pd.m_unlock_time; + } + } + else + { + fail_msg_writer() << "payment id has invalid format: \"" << arg << "\", expected 64-character string"; + } + } + + return true; +} +//---------------------------------------------------------------------------------------------------- uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err) { COMMAND_RPC_GET_HEIGHT::request req; @@ -628,20 +737,37 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_) fail_msg_writer() << "mixin_count should be non-negative integer, got " << local_args[0]; return true; } + local_args.erase(local_args.begin()); - vector<cryptonote::tx_destination_entry> dsts; - for (size_t i = 1; i < local_args.size(); i += 2) + std::vector<uint8_t> extra; + if (1 == local_args.size() % 2) { - cryptonote::tx_destination_entry de; - if(!get_account_address_from_str(de.addr, local_args[i])) + std::string payment_id_str = local_args.back(); + local_args.pop_back(); + + crypto::hash payment_id; + bool r = parse_payment_id(payment_id_str, payment_id); + if(r) { - fail_msg_writer() << "wrong address: " << local_args[i]; + std::string extra_nonce; + set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + r = add_extra_nonce_to_tx_extra(extra, extra_nonce); + } + + if(!r) + { + fail_msg_writer() << "payment id has invalid format: \"" << payment_id_str << "\", expected 64-character string"; return true; } + } - if (local_args.size() <= i + 1) + vector<cryptonote::tx_destination_entry> dsts; + for (size_t i = 0; i < local_args.size(); i += 2) + { + cryptonote::tx_destination_entry de; + if(!get_account_address_from_str(de.addr, local_args[i])) { - fail_msg_writer() << "amount for the last address " << local_args[i] << " is not specified"; + fail_msg_writer() << "wrong address: " << local_args[i]; return true; } @@ -659,7 +785,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_) try { cryptonote::transaction tx; - m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, tx); + m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, extra, tx); success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); } catch (const tools::error::daemon_busy&) @@ -801,6 +927,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { + success_msg_writer() << "bytecoin wallet v" << PROJECT_VERSION_LONG; success_msg_writer() << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]"; success_msg_writer() << desc_all << '\n' << w.get_commands_str(); return false; @@ -833,11 +960,11 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Setting log level = " << command_line::get_arg(vm, arg_log_level)); log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level)); } - + if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) { log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); - //runs wallet with rpc interface + //runs wallet with rpc interface if(!command_line::has_arg(vm, arg_wallet_file) ) { LOG_ERROR("Wallet file not set."); @@ -854,7 +981,7 @@ int main(int argc, char* argv[]) return 1; } - std::string wallet_file = command_line::get_arg(vm, arg_wallet_file); + std::string wallet_file = command_line::get_arg(vm, arg_wallet_file); std::string wallet_password = command_line::get_arg(vm, arg_password); std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); std::string daemon_host = command_line::get_arg(vm, arg_daemon_host); @@ -904,7 +1031,7 @@ int main(int argc, char* argv[]) } }else { - //runs wallet with console interface + //runs wallet with console interface r = w.init(vm); CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet"); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 002490e89..a6c771804 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -49,6 +49,7 @@ namespace cryptonote bool refresh(const std::vector<std::string> &args); bool show_balance(const std::vector<std::string> &args = std::vector<std::string>()); bool show_incoming_transfers(const std::vector<std::string> &args); + bool show_payments(const std::vector<std::string> &args); bool show_blockchain_height(const std::vector<std::string> &args); bool transfer(const std::vector<std::string> &args); bool print_address(const std::vector<std::string> &args = std::vector<std::string>()); @@ -57,11 +58,13 @@ namespace cryptonote uint64_t get_daemon_blockchain_height(std::string& err); bool try_connect_to_daemon(); + bool ask_wallet_create_if_needed(); //----------------- i_wallet2_callback --------------------- virtual void on_new_block(uint64_t height, const cryptonote::block& block); virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index); virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx); + virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx); //---------------------------------------------------------- friend class refresh_progress_reporter_t; diff --git a/src/version.h.in b/src/version.h.in index 331657755..2578ea635 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.8.5" -#define PROJECT_VERSION_BUILD_NO "294" +#define PROJECT_VERSION "0.8.6" +#define PROJECT_VERSION_BUILD_NO "295" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9bd487997..e8d67eec2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -22,6 +22,23 @@ using namespace epee; using namespace cryptonote; +namespace +{ +void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) +{ + keys_file = file_path; + wallet_file = file_path; + boost::system::error_code e; + if(string_tools::get_extension(keys_file) == "keys") + {//provided keys file name + wallet_file = string_tools::cut_off_extension(wallet_file); + }else + {//provided wallet file name + keys_file += ".keys"; + } +} +} //namespace + namespace tools { //---------------------------------------------------------------------------------------------------- @@ -36,17 +53,25 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ process_unconfirmed(tx); std::vector<size_t> outs; uint64_t tx_money_got_in_outs = 0; - crypto::public_key tx_pub_key = null_pkey; - bool r = parse_and_validate_tx_extra(tx, tx_pub_key); - - // Temporarily disabled due to messed up tx_extra from someone - // screwing around with MMing. 2014-04-30 - // THROW_WALLET_EXCEPTION_IF(!r, error::tx_extra_parse_error, tx); - - // We don't know how to handle this weird tx, so return - if (!r) return; - - r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs); + + std::vector<tx_extra_field> tx_extra_fields; + if(!parse_tx_extra(tx.extra, tx_extra_fields)) + { + // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key + LOG_PRINT_L0("Transaction extra has unsupported format: " << get_transaction_hash(tx)); + } + + tx_extra_pub_key pub_key_field; + if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) + { + LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << get_transaction_hash(tx)); + if(0 != m_callback) + m_callback->on_skip_transaction(height, tx); + return; + } + + crypto::public_key tx_pub_key = pub_key_field.pub_key; + bool r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs); THROW_WALLET_EXCEPTION_IF(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); if(!outs.empty() && tx_money_got_in_outs) @@ -87,6 +112,8 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index); } } + + uint64_t tx_money_spent_in_ins = 0; // check all outputs for spending (compare key images) BOOST_FOREACH(auto& in, tx.vin) { @@ -96,12 +123,33 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ if(it != m_key_images.end()) { LOG_PRINT_L0("Spent money: " << print_money(boost::get<cryptonote::txin_to_key>(in).amount) << ", with tx: " << get_transaction_hash(tx)); + tx_money_spent_in_ins += boost::get<cryptonote::txin_to_key>(in).amount; transfer_details& td = m_transfers[it->second]; td.m_spent = true; if (0 != m_callback) m_callback->on_money_spent(height, td.m_tx, td.m_internal_output_index, tx); } } + + tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + crypto::hash payment_id; + if(get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0; + if (0 < received && null_hash != payment_id) + { + payment_details payment; + payment.m_tx_hash = cryptonote::get_transaction_hash(tx); + payment.m_amount = received; + payment.m_block_height = height; + payment.m_unlock_time = tx.unlock_time; + m_payments.emplace(payment_id, payment); + LOG_PRINT_L2("Payment found: " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount); + } + } + } } //---------------------------------------------------------------------------------------------------- void wallet2::process_unconfirmed(const cryptonote::transaction& tx) @@ -203,7 +251,7 @@ void wallet2::pull_blocks(size_t& blocks_added) { //split detected here !!! THROW_WALLET_EXCEPTION_IF(current_index == res.start_height, error::wallet_internal_error, - "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) + + "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) + " (height " + std::to_string(res.start_height) + "), local block id at this height: " + string_tools::pod_to_hex(m_blockchain[current_index])); @@ -304,6 +352,14 @@ void wallet2::detach_blockchain(uint64_t height) m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); m_local_bc_height -= blocks_detached; + for (auto it = m_payments.begin(); it != m_payments.end(); ) + { + if(height <= it->second.m_block_height) + it = m_payments.erase(it); + else + ++it; + } + LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); } //---------------------------------------------------------------------------------------------------- @@ -399,18 +455,19 @@ void wallet2::generate(const std::string& wallet_, const std::string& password) store(); } //---------------------------------------------------------------------------------------------------- +void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exitst) +{ + std::string keys_file, wallet_file; + do_prepare_file_names(file_path, keys_file, wallet_file); + + boost::system::error_code ignore; + keys_file_exists = boost::filesystem::exists(keys_file, ignore); + wallet_file_exitst = boost::filesystem::exists(wallet_file, ignore); +} +//---------------------------------------------------------------------------------------------------- bool wallet2::prepare_file_names(const std::string& file_path) { - m_keys_file = file_path; - m_wallet_file = file_path; - boost::system::error_code e; - if(string_tools::get_extension(m_keys_file) == "keys") - {//provided keys file name - m_wallet_file = string_tools::cut_off_extension(m_wallet_file); - }else - {//provided wallet file name - m_keys_file += ".keys"; - } + do_prepare_file_names(file_path, m_keys_file, m_wallet_file); return true; } //---------------------------------------------------------------------------------------------------- @@ -422,7 +479,7 @@ bool wallet2::check_connection() net_utils::http::url_content u; net_utils::parse_url(m_daemon_address, u); if(!u.port) - u.port = RPC_DEFAULT_PORT; + u.port = RPC_DEFAULT_PORT; return m_http_client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT); } //---------------------------------------------------------------------------------------------------- @@ -497,6 +554,14 @@ void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) con incoming_transfers = m_transfers; } //---------------------------------------------------------------------------------------------------- +void wallet2::get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments) const +{ + auto range = m_payments.equal_range(payment_id); + std::for_each(range.first, range.second, [&payments](const payment_container::value_type& x) { + payments.push_back(x.second); + }); +} +//---------------------------------------------------------------------------------------------------- bool wallet2::is_transfer_unlocked(const transfer_details& td) const { if(!is_tx_spendtime_unlocked(td.m_tx.unlock_time)) @@ -596,16 +661,16 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t cha } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx) + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx) { - transfer(dsts, fake_outputs_count, unlock_time, fee, detail::digit_split_strategy, tx_dust_policy(fee), tx); + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(fee), tx); } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee) + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra) { cryptonote::transaction tx; - transfer(dsts, fake_outputs_count, unlock_time, fee, tx); + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx); } //---------------------------------------------------------------------------------------------------- } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 221f3d25c..edbf31f2d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -34,6 +34,7 @@ namespace tools virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index) {} virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx) {} + virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) {} }; struct tx_dust_policy @@ -67,14 +68,23 @@ namespace tools uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; } }; + struct payment_details + { + crypto::hash m_tx_hash; + uint64_t m_amount; + uint64_t m_block_height; + uint64_t m_unlock_time; + }; + struct unconfirmed_transfer_details { cryptonote::transaction m_tx; uint64_t m_change; - time_t m_sent_time; + time_t m_sent_time; }; typedef std::vector<transfer_details> transfer_container; + typedef std::unordered_multimap<crypto::hash, payment_details> payment_container; struct keys_file_data { @@ -108,13 +118,14 @@ namespace tools uint64_t balance(); uint64_t unlocked_balance(); template<typename T> - void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy); + void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy); template<typename T> - void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx); - void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee); - void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx); + void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx); + void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra); + void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx); bool check_connection(); void get_transfers(wallet2::transfer_container& incoming_transfers) const; + void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments) const; uint64_t get_blockchain_current_height() const { return m_local_bc_height; } template <class t_archive> inline void serialize(t_archive &a, const unsigned int ver) @@ -128,8 +139,13 @@ namespace tools if(ver < 6) return; a & m_unconfirmed_txs; + if(ver < 7) + return; + a & m_payments; } + static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exitst); + private: bool store_keys(const std::string& keys_file_name, const std::string& password); void load_keys(const std::string& keys_file_name, const std::string& password); @@ -152,10 +168,11 @@ namespace tools std::string m_keys_file; epee::net_utils::http::http_simple_client m_http_client; std::vector<crypto::hash> m_blockchain; - std::atomic<uint64_t> m_local_bc_height; //temporary workaround + std::atomic<uint64_t> m_local_bc_height; //temporary workaround std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs; transfer_container m_transfers; + payment_container m_payments; std::unordered_map<crypto::key_image, size_t> m_key_images; cryptonote::account_public_address m_account_public_address; uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value @@ -165,7 +182,7 @@ namespace tools i_wallet2_callback* m_callback; }; } -BOOST_CLASS_VERSION(tools::wallet2, 6) +BOOST_CLASS_VERSION(tools::wallet2, 7) namespace boost { @@ -190,7 +207,14 @@ namespace boost a & x.m_tx; } - + template <class Archive> + inline void serialize(Archive& a, tools::wallet2::payment_details& x, const boost::serialization::version_type ver) + { + a & x.m_tx_hash; + a & x.m_amount; + a & x.m_block_height; + a & x.m_unlock_time; + } } } @@ -261,15 +285,15 @@ namespace tools //---------------------------------------------------------------------------------------------------- template<typename T> void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy) + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy) { cryptonote::transaction tx; - transfer(dsts, fake_outputs_count, unlock_time, fee, destination_split_strategy, dust_policy, tx); + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx); } template<typename T> void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx) + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx) { using namespace cryptonote; THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); @@ -320,7 +344,7 @@ namespace tools } THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count); } - + //prepare inputs size_t i = 0; std::vector<cryptonote::tx_source_entry> sources; @@ -381,7 +405,7 @@ namespace tools splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust)); } - bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, tx, unlock_time); + bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time); THROW_WALLET_EXCEPTION_IF(m_upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, m_upper_transaction_size_limit); @@ -409,7 +433,7 @@ namespace tools BOOST_FOREACH(transfer_container::iterator it, selected_transfers) it->m_spent = true; - LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL + LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL << "Commission: " << print_money(fee+dust) << " (dust: " << print_money(dust) << ")" << ENDL << "Balance: " << print_money(balance()) << ENDL << "Unlocked: " << print_money(unlocked_balance()) << ENDL diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index b2a863436..0d42dbaf4 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -34,7 +34,6 @@ namespace tools // block_parse_error // get_blocks_error // get_out_indexes_error - // tx_extra_parse_error // tx_parse_error // transfer_error * // get_random_outs_general_error @@ -248,28 +247,6 @@ namespace tools //---------------------------------------------------------------------------------------------------- typedef failed_rpc_request<refresh_error, get_out_indices_error_message_index> get_out_indices_error; //---------------------------------------------------------------------------------------------------- - struct tx_extra_parse_error : public refresh_error - { - explicit tx_extra_parse_error(std::string&& loc, const cryptonote::transaction& tx) - : refresh_error(std::move(loc), "transaction extra parse error") - , m_tx(tx) - { - } - - const cryptonote::transaction& tx() const { return m_tx; } - - std::string to_string() const - { - std::ostringstream ss; - cryptonote::transaction tx = m_tx; - ss << refresh_error::to_string() << ", tx: " << cryptonote::obj_to_json_str(tx); - return ss.str(); - } - - private: - const cryptonote::transaction m_tx; - }; - //---------------------------------------------------------------------------------------------------- struct tx_parse_error : public refresh_error { explicit tx_parse_error(std::string&& loc, const cryptonote::blobdata& tx_blob) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a89cc4e72..f1766c3b4 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -11,6 +11,7 @@ using namespace epee; #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/account.h" #include "misc_language.h" +#include "string_tools.h" #include "crypto/hash.h" namespace tools @@ -74,7 +75,7 @@ namespace tools { std::vector<cryptonote::tx_destination_entry> dsts; - for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) + for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { cryptonote::tx_destination_entry de; if(!get_account_address_from_str(de.addr, it->address)) @@ -89,7 +90,7 @@ namespace tools try { cryptonote::transaction tx; - m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, tx); + m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, std::vector<uint8_t>(), tx); res.tx_hash = boost::lexical_cast<std::string>(cryptonote::get_transaction_hash(tx)); return true; } @@ -97,19 +98,19 @@ namespace tools { er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; er.message = e.what(); - return false; + return false; } catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; er.message = e.what(); - return false; + return false; } catch (...) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; - return false; + return false; } return true; } @@ -129,4 +130,40 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ -}
\ No newline at end of file + bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + crypto::hash payment_id; + cryptonote::blobdata payment_id_blob; + if(!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id_blob)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Payment ID has invald format"; + return false; + } + + if(sizeof(payment_id) != payment_id_blob.size()) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Payment ID has invalid size"; + return false; + } + + payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_blob.data()); + + res.payments.clear(); + std::list<wallet2::payment_details> payment_list; + m_wallet.get_payments(payment_id, payment_list); + for (auto payment : payment_list) + { + wallet_rpc::payment_details rpc_payment; + rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash); + rpc_payment.amount = payment.m_amount; + rpc_payment.block_height = payment.m_block_height; + rpc_payment.unlock_time = payment.m_unlock_time; + res.payments.push_back(rpc_payment); + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ +} diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 418f055d9..db49df574 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -2,7 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#pragma once +#pragma once #include <boost/program_options/options_description.hpp> #include <boost/program_options/variables_map.hpp> @@ -35,9 +35,10 @@ namespace tools BEGIN_URI_MAP2() BEGIN_JSON_RPC_MAP("/json_rpc") - MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) - MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) - MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) + MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) + MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) + MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) + MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) END_JSON_RPC_MAP() END_URI_MAP2() @@ -45,6 +46,7 @@ namespace tools bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool handle_command_line(const boost::program_options::variables_map& vm); diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index a94ce36b0..b99d92ca2 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -86,6 +86,41 @@ namespace wallet_rpc }; }; + struct payment_details + { + std::string tx_hash; + uint64_t amount; + uint64_t block_height; + uint64_t unlock_time; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(amount) + KV_SERIALIZE(block_height) + KV_SERIALIZE(unlock_time) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_PAYMENTS + { + struct request + { + std::string payment_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list<payment_details> payments; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payments) + END_KV_SERIALIZE_MAP() + }; + }; } } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 415abf406..7fa536dac 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -2,10 +2,11 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#pragma once +#pragma once #define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR -1 #define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS -2 #define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY -3 #define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR -4 +#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID -5 diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 36e769c65..57e147133 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -323,7 +323,7 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events destinations.push_back(de); transaction tmp_tx; - if (!construct_tx(miner_account.get_keys(), sources, destinations, tmp_tx, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tmp_tx, 0)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); @@ -365,7 +365,7 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry> destinations.push_back(de); transaction tmp_tx; - if (!construct_tx(miner_account.get_keys(), sources, destinations, tmp_tx, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tmp_tx, 0)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_1); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index f00323518..124800db7 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -425,9 +425,12 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te ts.amount = oi.amount; ts.real_output_in_tx_index = oi.out_no; ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // incoming tx public key - if (!fill_output_entries(outs[o.first], sender_out, nmix, ts.real_output, ts.outputs)) + size_t realOutput; + if (!fill_output_entries(outs[o.first], sender_out, nmix, realOutput, ts.outputs)) continue; + ts.real_output = realOutput; + sources.push_back(ts); sources_amount += ts.amount; @@ -528,7 +531,7 @@ bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote vector<tx_destination_entry> destinations; fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations); - return construct_tx(from.get_keys(), sources, destinations, tx, 0); + return construct_tx(from.get_keys(), sources, destinations, std::vector<uint8_t>(), tx, 0); } transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head, diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 38873a8b4..2d75d994a 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -1,4 +1,4 @@ -#pragma once +#pragma once //====================================================================================================================== @@ -113,7 +113,7 @@ bool gen_double_spend_in_tx<txs_keeped_by_block>::generate(std::vector<test_even destinations.push_back(de); cryptonote::transaction tx_1; - if (!construct_tx(bob_account.get_keys(), sources, destinations, tx_1, 0)) + if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_1, 0)) return false; SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 82bfb2150..5c619e97c 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -72,7 +72,7 @@ bool gen_uint_overflow_base::mark_last_valid_block(cryptonote::core& c, size_t e bool gen_uint_overflow_1::generate(std::vector<test_event_entry>& events) const { - uint64_t ts_start = 1338224400; + uint64_t ts_start = 1338224400; GENERATE_ACCOUNT(miner_account); MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); @@ -116,7 +116,7 @@ bool gen_uint_overflow_1::generate(std::vector<test_event_entry>& events) const bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const { - uint64_t ts_start = 1338224400; + uint64_t ts_start = 1338224400; GENERATE_ACCOUNT(miner_account); MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); @@ -148,7 +148,7 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const destinations.push_back(tx_destination_entry(sources.front().amount - MONEY_SUPPLY - MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr)); cryptonote::transaction tx_1; - if (!construct_tx(miner_account.get_keys(), sources, destinations, tx_1, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_1, 0)) return false; events.push_back(tx_1); @@ -174,7 +174,7 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const destinations.push_back(de); cryptonote::transaction tx_2; - if (!construct_tx(bob_account.get_keys(), sources, destinations, tx_2, 0)) + if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_2, 0)) return false; events.push_back(tx_2); diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 07e6661d8..635c41503 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -81,9 +81,7 @@ bool test_transaction_generation_and_ring_signature() oe.second = boost::get<txout_to_key>(tx_mine_6.vout[0].target).key; src.outputs.push_back(oe); - crypto::public_key tx_pub_key = null_pkey; - cryptonote::parse_and_validate_tx_extra(tx_mine_2, tx_pub_key); - src.real_out_tx_key = tx_pub_key; + src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(tx_mine_2); src.real_output = 1; src.real_output_in_tx_index = 0; } @@ -95,7 +93,7 @@ bool test_transaction_generation_and_ring_signature() destinations.push_back(td); transaction tx_rc1; - bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, tx_rc1, 0); + bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, std::vector<uint8_t>(), tx_rc1, 0); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); crypto::hash pref_hash = get_transaction_prefix_hash(tx_rc1); diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index ce65c8f20..4c6d5b07f 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -52,7 +52,7 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, try { - w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_FEE), tx); + w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, std::vector<uint8_t>(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_FEE), tx); return true; } catch (const std::exception&) @@ -144,7 +144,7 @@ bool transactions_flow_test(std::string& working_folder, } //lets make a lot of small outs to ourselves - //since it is not possible to start from transaction that bigger than 20Kb, we gonna make transactions + //since it is not possible to start from transaction that bigger than 20Kb, we gonna make transactions //with 500 outs (about 18kb), and we have to wait appropriate count blocks, mined for test wallet while(true) { diff --git a/tests/gtest/include/gtest/internal/gtest-port.h b/tests/gtest/include/gtest/internal/gtest-port.h index fbf28cd4e..157b47f86 100644 --- a/tests/gtest/include/gtest/internal/gtest-port.h +++ b/tests/gtest/include/gtest/internal/gtest-port.h @@ -204,8 +204,6 @@ #define GTEST_NAME_ "Google Test" #define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/" -#define GTEST_HAS_TR1_TUPLE 0 - // Determines the version of gcc that is used to compile this. #ifdef __GNUC__ // 40302 means version 4.3.2. diff --git a/tests/performance_tests/check_ring_signature.h b/tests/performance_tests/check_ring_signature.h index fc7574f92..dafa172e3 100644 --- a/tests/performance_tests/check_ring_signature.h +++ b/tests/performance_tests/check_ring_signature.h @@ -36,7 +36,7 @@ public: std::vector<tx_destination_entry> destinations; destinations.push_back(tx_destination_entry(this->m_source_amount, m_alice.get_keys().m_account_address)); - if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, m_tx, 0)) + if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector<uint8_t>(), m_tx, 0)) return false; get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 36507d6f3..1e7005941 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -42,7 +42,7 @@ public: bool test() { - return cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, m_destinations, m_tx, 0); + return cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, m_destinations, std::vector<uint8_t>(), m_tx, 0); } private: diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index fe1e1c7cd..45ca5b27b 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -14,7 +14,7 @@ void set_process_affinity(int core) { -#if defined(__APPLE__) +#if defined (__APPLE__) return; #elif defined(BOOST_WINDOWS) DWORD_PTR mask = 1; diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index dfc05f55a..4dd877591 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -4,52 +4,132 @@ #include "gtest/gtest.h" +#include <vector> + #include "common/util.h" #include "cryptonote_core/cryptonote_format_utils.h" -TEST(parse_and_validate_tx_extra, is_correct_parse_and_validate_tx_extra) + +TEST(parse_tx_extra, handles_empty_extra) +{ + std::vector<uint8_t> extra;; + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + ASSERT_TRUE(tx_extra_fields.empty()); +} + +TEST(parse_tx_extra, handles_padding_only_size_1) +{ + const uint8_t extra_arr[] = {0}; + std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + ASSERT_EQ(1, tx_extra_fields.size()); + ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[0].type()); + ASSERT_EQ(1, boost::get<cryptonote::tx_extra_padding>(tx_extra_fields[0]).size); +} + +TEST(parse_tx_extra, handles_padding_only_size_2) +{ + const uint8_t extra_arr[] = {0, 0}; + std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + ASSERT_EQ(1, tx_extra_fields.size()); + ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[0].type()); + ASSERT_EQ(2, boost::get<cryptonote::tx_extra_padding>(tx_extra_fields[0]).size); +} + +TEST(parse_tx_extra, handles_padding_only_max_size) +{ + std::vector<uint8_t> extra(TX_EXTRA_NONCE_MAX_COUNT, 0); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + ASSERT_EQ(1, tx_extra_fields.size()); + ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[0].type()); + ASSERT_EQ(TX_EXTRA_NONCE_MAX_COUNT, boost::get<cryptonote::tx_extra_padding>(tx_extra_fields[0]).size); +} + +TEST(parse_tx_extra, handles_padding_only_exceed_max_size) +{ + std::vector<uint8_t> extra(TX_EXTRA_NONCE_MAX_COUNT + 1, 0); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_FALSE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); +} + +TEST(parse_tx_extra, handles_invalid_padding_only) +{ + std::vector<uint8_t> extra(2, 0); + extra[1] = 42; + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_FALSE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); +} + +TEST(parse_tx_extra, handles_pub_key_only) +{ + const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230}; + std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + ASSERT_EQ(1, tx_extra_fields.size()); + ASSERT_EQ(typeid(cryptonote::tx_extra_pub_key), tx_extra_fields[0].type()); +} + +TEST(parse_tx_extra, handles_extra_nonce_only) +{ + const uint8_t extra_arr[] = {2, 1, 42}; + std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + ASSERT_EQ(1, tx_extra_fields.size()); + ASSERT_EQ(typeid(cryptonote::tx_extra_nonce), tx_extra_fields[0].type()); + cryptonote::tx_extra_nonce extra_nonce = boost::get<cryptonote::tx_extra_nonce>(tx_extra_fields[0]); + ASSERT_EQ(1, extra_nonce.nonce.size()); + ASSERT_EQ(42, extra_nonce.nonce[0]); +} + +TEST(parse_tx_extra, handles_pub_key_and_padding) +{ + const uint8_t extra_arr[] = {1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, + 80, 63, 198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + std::vector<uint8_t> extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + ASSERT_EQ(2, tx_extra_fields.size()); + ASSERT_EQ(typeid(cryptonote::tx_extra_pub_key), tx_extra_fields[0].type()); + ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[1].type()); +} + +TEST(parse_and_validate_tx_extra, is_valid_tx_extra_parsed) { cryptonote::transaction tx = AUTO_VAL_INIT(tx); cryptonote::account_base acc; acc.generate(); cryptonote::blobdata b = "dsdsdfsdfsf"; - bool r = cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1); - ASSERT_TRUE(r); - crypto::public_key tx_pub_key; - r = cryptonote::parse_and_validate_tx_extra(tx, tx_pub_key); - ASSERT_TRUE(r); + ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1)); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx); + ASSERT_NE(tx_pub_key, cryptonote::null_pkey); } -TEST(parse_and_validate_tx_extra, is_correct_extranonce_too_big) +TEST(parse_and_validate_tx_extra, fails_on_big_extra_nonce) { cryptonote::transaction tx = AUTO_VAL_INIT(tx); cryptonote::account_base acc; acc.generate(); - cryptonote::blobdata b(260, 0); - bool r = cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1); - ASSERT_FALSE(r); + cryptonote::blobdata b(TX_EXTRA_NONCE_MAX_COUNT + 1, 0); + ASSERT_FALSE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1)); } -TEST(parse_and_validate_tx_extra, is_correct_wrong_extra_couner_too_big) +TEST(parse_and_validate_tx_extra, fails_on_wrong_size_in_extra_nonce) { cryptonote::transaction tx = AUTO_VAL_INIT(tx); tx.extra.resize(20, 0); tx.extra[0] = TX_EXTRA_NONCE; tx.extra[1] = 255; - crypto::public_key tx_pub_key; - bool r = parse_and_validate_tx_extra(tx, tx_pub_key); - ASSERT_FALSE(r); + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + ASSERT_FALSE(cryptonote::parse_tx_extra(tx.extra, tx_extra_fields)); } -TEST(parse_and_validate_tx_extra, is_correct_wrong_extra_nonce_double_entry) -{ - cryptonote::transaction tx = AUTO_VAL_INIT(tx); - tx.extra.resize(20, 0); - cryptonote::blobdata v = "asasdasd"; - cryptonote::add_tx_extra_nonce(tx, v); - cryptonote::add_tx_extra_nonce(tx, v); - crypto::public_key tx_pub_key; - bool r = parse_and_validate_tx_extra(tx, tx_pub_key); - ASSERT_FALSE(r); -} - TEST(validate_parse_amount_case, validate_parse_amount) { uint64_t res = 0; |