Transaction history and 'reset' command for simplewallet

This commit is contained in:
jezal 2014-08-25 15:35:07 +01:00
parent c3b1a00085
commit 9df3a81801
17 changed files with 376 additions and 98 deletions

View file

@ -1,3 +1,9 @@
Release notes 1.0.2
- Transaction history for simplewallet
- Reset command for simplewallet
- Various simplewallet improvements
Release notes 1.0.1
- Fix transfers in simplewallet

View file

@ -63,6 +63,7 @@ const uint64_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS = DIFFICULTY_TARGET
const uint64_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = 60 * 60 * 24; //seconds, one day
const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week
const uint64_t UPGRADE_HEIGHT = 546602;
const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent
const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks
@ -104,7 +105,7 @@ const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; //
const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds
const uint64_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes
const size_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds
const char P2P_STAT_TRUSTED_PUB_KEY[] = "8f80f9a5a434a9f1510d13336228debfee9c918ce505efe225d8c94d045fa115";
const char P2P_STAT_TRUSTED_PUB_KEY[] = "93467628927eaa0b13a4e52e61864a75aa475e67f6b5748eb3fc1d2fe468aed4";
const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70;
const unsigned THREAD_STACK_SIZE = 5 * 1024 * 1024;
@ -143,7 +144,8 @@ const CheckpointData CHECKPOINTS[] = {
{480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"},
{484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"},
{506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"},
{544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"}
{544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"},
{553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"}
};
} // cryptonote

View file

@ -40,6 +40,7 @@ namespace cryptonote {
CHECK_AND_ASSERT_MES(r, false, "Failed to get genesis block hash");
if (isTestnet()) {
m_upgradeHeight = 0;
m_blocksFileName = "testnet_" + m_blocksFileName;
m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName;
m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName;
@ -380,7 +381,7 @@ namespace cryptonote {
mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME);
mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME);
upgradeHeight(UpgradeDetectorBase::UNDEF_HEIGHT);
upgradeHeight(parameters::UPGRADE_HEIGHT);
upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD);
upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW);
upgradeWindow(parameters::UPGRADE_WINDOW);

View file

@ -231,8 +231,9 @@ namespace cryptonote
if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER)
return;
std::string operation;
if (Archive::is_loading::value) {
operation = "- loading ";
crypto::hash blockHash;
ar & blockHash;
@ -241,13 +242,23 @@ namespace cryptonote
}
} else {
operation = "- saving ";
ar & m_lastBlockHash;
}
LOG_PRINT_L0(operation << "block index...");
ar & m_bs.m_blockIndex;
LOG_PRINT_L0(operation << "transaction map...");
ar & m_bs.m_transactionMap;
LOG_PRINT_L0(operation << "spend keys...");
ar & m_bs.m_spent_keys;
LOG_PRINT_L0(operation << "outputs...");
ar & m_bs.m_outputs;
LOG_PRINT_L0(operation << "multi-signature outputs...");
ar & m_bs.m_multisignatureOutputs;
m_loaded = true;
@ -377,6 +388,9 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi
m_outputs.clear();
m_multisignatureOutputs.clear();
for (uint32_t b = 0; b < m_blocks.size(); ++b) {
if (b % 1000 == 0) {
std::cout << "Height " << b << " of " << m_blocks.size() << '\r';
}
const BlockEntry& block = m_blocks[b];
crypto::hash blockHash = get_block_hash(block.bl);
m_blockIndex.push(blockHash);
@ -435,6 +449,7 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi
bool blockchain_storage::storeCache() {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
LOG_PRINT_L0("Saving blockchain...");
BlockCacheSerializer ser(*this, get_tail_id());
if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) {
LOG_ERROR("Failed to save blockchain cache");
@ -900,9 +915,9 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto::
}
if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) {
LOG_PRINT_RED_L0("Block with id: " << id
<< ENDL << " can't be accepted for alternative chain, block height: " << block_height
<< ENDL << " blockchain height: " << get_current_blockchain_height());
LOG_PRINT_L2("Block with id: " << id << std::endl <<
" can't be accepted for alternative chain, block height: " << block_height << std::endl <<
" blockchain height: " << get_current_blockchain_height());
bvc.m_verifivation_failed = true;
return false;
}
@ -1525,7 +1540,7 @@ bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& b
uint64_t height = get_block_height(b);
const uint8_t expectedBlockVersion = get_block_major_version_for_height(height);
if (b.majorVersion != expectedBlockVersion) {
LOG_PRINT_L0("Block " << blockHash << " has wrong major version: " << static_cast<int>(b.majorVersion) <<
LOG_PRINT_L2("Block " << blockHash << " has wrong major version: " << static_cast<int>(b.majorVersion) <<
", at height " << height << " expected version is " << static_cast<int>(expectedBlockVersion));
return false;
}

View file

@ -257,7 +257,7 @@ namespace cryptonote
if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize())
{
LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " <<
LOG_PRINT_RED_L0("transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " <<
(m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()));
return false;
}

View file

@ -123,9 +123,9 @@ namespace cryptonote
int64_t diff = static_cast<int64_t>(hshd.current_height) - static_cast<int64_t>(m_core.get_current_blockchain_height());
LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height
<< " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) "
<< (0 <= diff ? std::string("behind") : std::string("ahead"))
<< "] " << ENDL << "SYNCHRONIZATION started", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1));
<< " [" << std::abs(diff) << " blocks (" << std::abs(diff) / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) "
<< (diff >= 0 ? std::string("behind") : std::string("ahead")) << "] " << std::endl <<
"SYNCHRONIZATION started", (diff >= 0 ? (is_inital ? LOG_LEVEL_0 : LOG_LEVEL_1) : LOG_LEVEL_2));
LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id);
context.m_state = cryptonote_connection_context::state_synchronizing;
context.m_remote_blockchain_height = hshd.current_height;
@ -173,7 +173,7 @@ namespace cryptonote
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false);
if (bvc.m_verifivation_failed) {
LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection");
LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection");
m_p2p->drop_connection(context);
return 1;
}
@ -332,7 +332,7 @@ namespace cryptonote
m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false);
if (bvc.m_verifivation_failed) {
LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection");
LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection");
m_p2p->drop_connection(context);
return 1;
} else if (bvc.m_marked_as_orphaned) {

View file

@ -230,7 +230,7 @@ namespace
}
if (!r) {
fail_msg_writer() << "payment id has invalid format: \"" << value << "\", expected 64-character string";
fail_msg_writer() << "payment ID has invalid format: \"" << value << "\", expected 64-character string";
return false;
}
} else if (arg == "-f") {
@ -249,7 +249,13 @@ namespace
cryptonote::tx_destination_entry de;
if (!m_currency.parseAccountAddressString(arg, de.addr)) {
fail_msg_writer() << "wrong address: " << arg;
crypto::hash paymentId;
if (tools::wallet2::parse_payment_id(arg, paymentId)) {
fail_msg_writer() << "Invalid payment ID usage. Please, use -p <payment_id>. See help for details.";
} else {
fail_msg_writer() << "Wrong address: " << arg;
}
return false;
}
@ -307,6 +313,7 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency)
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("list_transfers", boost::bind(&simple_wallet::listTransfers, this, _1), "Show all known transfers");
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),
@ -316,6 +323,7 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency)
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");
m_cmd_binder.set_handler("reset", boost::bind(&simple_wallet::reset, this, _1), "Discard cache data and start synchronizing from the start");
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help");
}
//----------------------------------------------------------------------------------------------------
@ -560,7 +568,14 @@ bool simple_wallet::save(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::reset(const std::vector<std::string> &args) {
m_wallet->reset();
success_msg_writer(true) << "Reset is complete successfully";
refresh();
return true;
}
bool simple_wallet::start_mining(const std::vector<std::string>& args)
{
if (!try_connect_to_daemon())
@ -652,7 +667,7 @@ void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::Trans
m_refresh_progress_reporter.update(height, true);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::refresh(const std::vector<std::string>& args)
bool simple_wallet::refresh(const std::vector<std::string>& args/* = std::vector<std::string>()*/)
{
if (!try_connect_to_daemon())
return true;
@ -776,12 +791,35 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::listTransfers(const std::vector<std::string>& args) {
const std::vector<tools::wallet2::Transfer>& transfers = m_wallet->getTransfers();
for (const tools::wallet2::Transfer& transfer : transfers) {
std::string address = "UNKNOWN";
if (transfer.hasAddress) {
address = getAccountAddressAsStr(m_currency.publicAddressBase58Prefix(), transfer.address);
}
message_writer(transfer.output ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false)
<< transfer.time
<< ", " << (transfer.output ? "OUTPUT" : "INPUT")
<< ", " << transfer.transactionHash
<< ", " << m_currency.formatAmount(transfer.amount)
<< ", " << m_currency.formatAmount(transfer.fee)
<< ", " << transfer.paymentId
<< ", " << address
<< ", " << transfer.blockIndex
<< ", " << transfer.unlockTime;
}
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";
fail_msg_writer() << "expected at least one payment ID";
return true;
}
@ -819,7 +857,7 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
}
else
{
fail_msg_writer() << "payment id has invalid format: \"" << arg << "\", expected 64-character string";
fail_msg_writer() << "payment ID has invalid format: \"" << arg << "\", expected 64-character string";
}
}
@ -864,6 +902,13 @@ bool simple_wallet::transfer(const std::vector<std::string> &args)
cryptonote::Transaction tx;
m_wallet->transfer(cmd.dsts, cmd.fake_outs_count, 0, cmd.fee, cmd.extra, tx);
success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx);
try {
m_wallet->store();
} catch (const std::exception& e) {
fail_msg_writer() << e.what();
return false;
}
}
catch (const tools::error::daemon_busy&)
{

View file

@ -62,14 +62,16 @@ namespace cryptonote
bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool start_mining(const std::vector<std::string> &args);
bool stop_mining(const std::vector<std::string> &args);
bool refresh(const std::vector<std::string> &args);
bool refresh(const std::vector<std::string> &args = std::vector<std::string>());
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 listTransfers(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>());
bool save(const std::vector<std::string> &args);
bool reset(const std::vector<std::string> &args);
bool set_log(const std::vector<std::string> &args);
uint64_t get_daemon_blockchain_height(std::string& err);

View file

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0.1"
#define PROJECT_VERSION_BUILD_NO "316"
#define PROJECT_VERSION "1.0.2"
#define PROJECT_VERSION_BUILD_NO "336"
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"

View file

@ -74,7 +74,7 @@ WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& cur
m_unconfirmedTransactions(unconfirmedTransactions),
m_isInitialized(false),
m_isStoping(false) {
m_upperTransactionSizeLimit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize();
m_upperTransactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize();
}
void WalletTransactionSender::init(cryptonote::account_keys keys) {

View file

@ -65,7 +65,7 @@ void wallet2::init(const std::string& daemon_address)
m_daemon_address = daemon_address;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id) {
bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id) {
process_unconfirmed(tx);
std::vector<tx_extra_field> tx_extra_fields;
@ -84,7 +84,7 @@ bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transactio
return false;
}
TxItem txItem = { tx, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) };
TxItem txItem = { tx, time, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) };
queue.push(std::unique_ptr<TxItem>(new TxItem(std::move(txItem))));
return true;
}
@ -159,26 +159,59 @@ void wallet2::processCheckedTransaction(const TxItem& item) {
}
}
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.unlockTime;
m_payments.emplace(payment_id, payment);
LOG_PRINT_L2("Payment found: " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount);
}
crypto::hash transactionHash = get_transaction_hash(tx);
bool ownTransfer = false;
for (Transfer& transfer : transfers) {
if (transfer.transactionHash == transactionHash) {
transfer.blockIndex = height;
ownTransfer = true;
}
}
if (!ownTransfer) {
crypto::hash paymentId = null_hash;
tx_extra_nonce extraNonce;
if (find_tx_extra_field_by_type(tx_extra_fields, extraNonce)) {
get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId);
}
if (tx_money_spent_in_ins < tx_money_got_in_outs) {
if (paymentId != null_hash) {
payment_details payment;
payment.m_tx_hash = transactionHash;
payment.m_amount = tx_money_got_in_outs - tx_money_spent_in_ins;
payment.m_block_height = height;
payment.m_unlock_time = tx.unlockTime;
m_payments.emplace(paymentId, payment);
LOG_PRINT_L2("Payment found: " << paymentId << " / " << payment.m_tx_hash << " / " << payment.m_amount);
}
Transfer transfer;
transfer.time = item.time;
transfer.output = false;
transfer.transactionHash = transactionHash;
transfer.amount = tx_money_got_in_outs - tx_money_spent_in_ins;
transfer.fee = 0;
transfer.paymentId = paymentId;
transfer.hasAddress = false;
transfer.blockIndex = height;
transfer.unlockTime = tx.unlockTime;
transfers.push_back(transfer);
} else if (tx_money_got_in_outs < tx_money_spent_in_ins) {
Transfer transfer;
transfer.time = item.time;
transfer.output = true;
transfer.transactionHash = transactionHash;
transfer.amount = tx_money_spent_in_ins - tx_money_got_in_outs;
transfer.fee = 0;
transfer.paymentId = paymentId;
transfer.hasAddress = false;
transfer.blockIndex = height;
transfer.unlockTime = tx.unlockTime;
transfers.push_back(transfer);
}
}
}
//----------------------------------------------------------------------------------------------------
@ -211,7 +244,6 @@ bool wallet2::addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_he
"current_index=" + std::to_string(current_index) + ", m_blockchain.size()=" + std::to_string(m_blockchain.size()));
m_blockchain.push_back(bl_id);
++m_local_bc_height;
if (0 != m_callback) {
m_callback->on_new_block(current_index);
@ -234,7 +266,7 @@ size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::bloc
if (b.timestamp + 60 * 60 * 24 > m_account.get_createtime())
{
TIME_MEASURE_START(miner_tx_handle_time);
if(processNewTransaction(queue, b.minerTx, height, bl_id))
if(processNewTransaction(queue, b.minerTx, height, b.timestamp, bl_id))
++processedTransactions;
TIME_MEASURE_FINISH(miner_tx_handle_time);
@ -244,7 +276,7 @@ size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::bloc
cryptonote::Transaction tx;
bool r = parse_and_validate_tx_from_blob(txblob, tx);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
if(processNewTransaction(queue, tx, height, bl_id))
if(processNewTransaction(queue, tx, height, b.timestamp, bl_id))
++processedTransactions;
}
TIME_MEASURE_FINISH(txs_handle_time);
@ -512,7 +544,6 @@ size_t wallet2::detach_blockchain(uint64_t height)
size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+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(); )
{
@ -522,6 +553,14 @@ size_t wallet2::detach_blockchain(uint64_t height)
++it;
}
for (std::size_t transferIndex = 0; transferIndex < transfers.size();) {
if (transfers[transferIndex].blockIndex != 0 && transfers[transferIndex].blockIndex >= height) {
transfers.erase(transfers.begin() + transferIndex);
} else {
++transferIndex;
}
}
LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached);
return blocks_detached;
}
@ -536,7 +575,6 @@ bool wallet2::clear()
m_blockchain.clear();
m_transfers.clear();
m_blockchain.push_back(m_currency.genesisBlockHash());
m_local_bc_height = 1;
return true;
}
//----------------------------------------------------------------------------------------------------
@ -702,8 +740,6 @@ void wallet2::load(const std::string& wallet_, const std::string& password)
THROW_WALLET_EXCEPTION_IF(m_blockchain[0] != m_currency.genesisBlockHash(), error::wallet_internal_error,
"Genesis block missmatch. You probably use wallet without testnet flag with blockchain from test network or vice versa");
}
m_local_bc_height = m_blockchain.size();
}
//----------------------------------------------------------------------------------------------------
void wallet2::store()
@ -852,4 +888,23 @@ void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx);
}
//----------------------------------------------------------------------------------------------------
const std::vector<wallet2::Transfer>& wallet2::getTransfers() {
return transfers;
}
void wallet2::reset() {
clear();
m_unconfirmed_txs.clear();
m_payments.clear();
m_key_images.clear();
for (std::size_t transferIndex = 0; transferIndex < transfers.size();) {
if (transfers[transferIndex].hasAddress) {
transfers[transferIndex].blockIndex = 0;
++transferIndex;
} else {
transfers.erase(transfers.begin() + transferIndex);
}
}
}
}

View file

@ -70,14 +70,12 @@ namespace tools
wallet2(const wallet2& rhs) :
m_currency(rhs.m_currency),
m_run(true),
m_callback(0),
m_upper_transaction_size_limit(rhs.m_upper_transaction_size_limit) {
m_callback(0) {
};
public:
wallet2(const cryptonote::Currency& currency) :
m_currency(currency), m_run(true), m_callback(0) {
m_upper_transaction_size_limit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize();
}
struct transfer_details
@ -121,6 +119,37 @@ namespace tools
END_SERIALIZE()
};
struct Transfer {
uint64_t time;
bool output;
crypto::hash transactionHash;
uint64_t amount;
uint64_t fee;
crypto::hash paymentId;
bool hasAddress;
cryptonote::AccountPublicAddress address;
uint64_t blockIndex;
uint64_t unlockTime;
template <class Archive> void serialize(Archive& archive, unsigned int version) {
archive & time;
archive & output;
archive & transactionHash;
archive & amount;
archive & fee;
archive & paymentId;
archive & hasAddress;
if (hasAddress) {
archive & address;
}
archive & blockIndex;
archive & unlockTime;
}
};
typedef std::vector<Transfer> Transfers;
void generate(const std::string& wallet, const std::string& password);
void load(const std::string& wallet, const std::string& password);
void store();
@ -153,7 +182,10 @@ namespace tools
bool connectClient(epee::net_utils::http::http_simple_client& client);
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; }
uint64_t get_blockchain_current_height() const { return m_blockchain.size(); }
const std::vector<Transfer>& getTransfers();
void reset();
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
@ -169,6 +201,9 @@ namespace tools
if(ver < 7)
return;
a & m_payments;
if (ver >= 8) {
a & transfers;
}
}
static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists);
@ -180,6 +215,7 @@ namespace tools
struct TxItem {
cryptonote::Transaction tx;
uint64_t time;
uint64_t height;
crypto::hash blockId;
crypto::public_key txPubKey;
@ -196,7 +232,7 @@ namespace tools
bool addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_height, uint64_t height);
size_t processNewBlockchainEntry(TxQueue& queue, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height);
bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id);
bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id);
void processCheckedTransaction(const TxItem& item);
// returns number of blocks added
@ -234,21 +270,21 @@ 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::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::AccountPublicAddress 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
std::atomic<bool> m_run;
i_wallet2_callback* m_callback;
Transfers transfers;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 7)
BOOST_CLASS_VERSION(tools::wallet2, 8)
namespace boost
{
@ -466,7 +502,8 @@ namespace tools
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, m_currency.publicAddressBase58Prefix(), 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);
uint64_t transactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize();
THROW_WALLET_EXCEPTION_IF(get_object_blobsize(tx) > transactionSizeLimit, error::tx_too_big, tx, transactionSizeLimit);
std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const TransactionInput& s_e) -> bool
@ -487,13 +524,38 @@ namespace tools
add_unconfirmed_tx(tx, change_dts.amount);
LOG_PRINT_L2("transaction " << get_transaction_hash(tx) << " generated ok and sent to daemon, key_images: [" << key_images << "]");
crypto::hash transactionHash = get_transaction_hash(tx);
LOG_PRINT_L2("transaction " << transactionHash << " generated ok and sent to daemon, key_images: [" << key_images << "]");
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
it->m_spent = true;
LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL
<< "Commission: " << m_currency.formatAmount(fee + dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL
crypto::hash paymentId = null_hash;
std::vector<tx_extra_field> transactionExtras;
if (parse_tx_extra(tx.extra, transactionExtras)) {
tx_extra_nonce extraNonce;
if (find_tx_extra_field_by_type(transactionExtras, extraNonce)) {
get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId);
}
}
for (auto& destination : dsts) {
Transfer transfer;
transfer.time = static_cast<uint64_t>(time(NULL));
transfer.output = true;
transfer.transactionHash = transactionHash;
transfer.amount = destination.amount;
transfer.fee = fee;
transfer.paymentId = paymentId;
transfer.hasAddress = true;
transfer.address = destination.addr;
transfer.blockIndex = 0;
transfer.unlockTime = unlock_time;
transfers.push_back(transfer);
}
LOG_PRINT_L0("Transaction successfully sent. <" << transactionHash << ">" << ENDL
<< "Commission: " << m_currency.formatAmount(fee+dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL
<< "Balance: " << m_currency.formatAmount(balance()) << ENDL
<< "Unlocked: " << m_currency.formatAmount(unlocked_balance()) << ENDL
<< "Please, wait for confirmation for your balance to be unlocked.");

View file

@ -46,9 +46,16 @@ namespace tools
m_net_server.add_idle_handler([this](){
try {
m_wallet.refresh();
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
} catch (const std::exception& e) {
LOG_ERROR("Exception while refreshing, what=" << e.what());
}
try {
m_wallet.store();
} catch (const std::exception& e) {
LOG_ERROR("Exception while storing, what=" << e.what());
}
return true;
}, 20000);
@ -192,5 +199,34 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx) {
res.transfers.clear();
const std::vector<wallet2::Transfer>& transfers = m_wallet.getTransfers();
for (const tools::wallet2::Transfer& transfer : transfers) {
wallet_rpc::Transfer transfer2;
transfer2.time = transfer.time;
transfer2.output = transfer.output;
transfer2.transactionHash = epee::string_tools::pod_to_hex(transfer.transactionHash);
transfer2.amount = transfer.amount;
transfer2.fee = transfer.fee;
transfer2.paymentId = transfer.paymentId == cryptonote::null_hash ? "" : epee::string_tools::pod_to_hex(transfer.paymentId);
transfer2.address = transfer.hasAddress ? getAccountAddressAsStr(m_wallet.currency().publicAddressBase58Prefix(), transfer.address) : "";
transfer2.blockIndex = transfer.blockIndex;
transfer2.unlockTime = transfer.unlockTime;
res.transfers.push_back(transfer2);
}
return true;
}
bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx) {
res.height = m_wallet.get_blockchain_current_height();
return true;
}
bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx) {
m_wallet.reset();
return true;
}
}

View file

@ -48,10 +48,13 @@ 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("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
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)
MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS)
MAP_JON_RPC_WE("get_height", on_get_height, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
MAP_JON_RPC_WE("reset", on_reset, wallet_rpc::COMMAND_RPC_RESET)
END_JSON_RPC_MAP()
END_URI_MAP2()
@ -60,6 +63,9 @@ namespace tools
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 on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool handle_command_line(const boost::program_options::variables_map& vm);

View file

@ -136,5 +136,71 @@ namespace wallet_rpc
END_KV_SERIALIZE_MAP()
};
};
struct Transfer {
uint64_t time;
bool output;
std::string transactionHash;
uint64_t amount;
uint64_t fee;
std::string paymentId;
std::string address;
uint64_t blockIndex;
uint64_t unlockTime;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(time)
KV_SERIALIZE(output)
KV_SERIALIZE(transactionHash)
KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(paymentId)
KV_SERIALIZE(address)
KV_SERIALIZE(blockIndex)
KV_SERIALIZE(unlockTime)
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_GET_TRANSFERS {
struct request {
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
struct response {
std::list<Transfer> transfers;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfers)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_GET_HEIGHT {
struct request {
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
struct response {
uint64_t height;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(height)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_RESET {
struct request {
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
struct response {
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
};
}
}

View file

@ -591,37 +591,14 @@ bool gen_block_has_invalid_tx::generate(std::vector<test_event_entry>& events) c
bool gen_block_is_too_big::generate(std::vector<test_event_entry>& events) const
{
BLOCK_VALIDATION_INIT_GENERATE();
generator.defaultMajorVersion = m_blockMajorVersion;
// Creating a huge miner_tx, it will have a lot of outs
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
static const size_t tx_out_count = m_currency.blockGrantedFullRewardZone() / 2;
uint64_t amount = get_outs_money_amount(miner_tx);
uint64_t portion = amount / tx_out_count;
uint64_t remainder = amount % tx_out_count;
TransactionOutputTarget target = miner_tx.vout[0].target;
miner_tx.vout.clear();
for (size_t i = 0; i < tx_out_count; ++i)
{
TransactionOutput o;
o.amount = portion;
o.target = target;
miner_tx.vout.push_back(o);
}
if (0 < remainder)
{
TransactionOutput o;
o.amount = remainder;
o.target = target;
miner_tx.vout.push_back(o);
}
// Block reward will be incorrect, as it must be reduced if cumulative block size is very big,
// but in this test it doesn't matter
Block blk_1;
if (!generator.constructBlockManually(blk_1, blk_0, miner_account,
test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx))
if (!generator.constructMaxSizeBlock(blk_1, blk_0, miner_account)) {
return false;
}
blk_1.minerTx.extra.resize(blk_1.minerTx.extra.size() + 1);
events.push_back(blk_1);
DO_CALLBACK(events, "check_block_purged");

View file

@ -297,7 +297,12 @@ struct gen_block_has_invalid_tx : public CheckBlockPurged
struct gen_block_is_too_big : public CheckBlockPurged
{
gen_block_is_too_big(uint8_t blockMajorVersion)
: CheckBlockPurged(1, blockMajorVersion) {}
: CheckBlockPurged(1, blockMajorVersion) {
cryptonote::CurrencyBuilder currencyBuilder;
currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0));
currencyBuilder.maxBlockSizeInitial(std::numeric_limits<size_t>::max() / 2);
m_currency = currencyBuilder.currency();
}
bool generate(std::vector<test_event_entry>& events) const;
};