servicenodes: Added service node list

This commit is contained in:
jcktm 2018-06-29 14:47:00 +10:00 committed by GitHub
parent 16ac6e77cc
commit 8a92407f5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1076 additions and 157 deletions

View File

@ -253,12 +253,22 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("No inputs given");
return 1;
}
std::vector<std::unique_ptr<Blockchain>> core_storage(inputs.size());
Blockchain *blockchain = NULL;
tx_memory_pool m_mempool(*blockchain);
std::vector<Blockchain*> core_storage(inputs.size());
for (size_t n = 0; n < inputs.size(); ++n)
{
core_storage[n].reset(new Blockchain(m_mempool));
// This is done this way because of the circular constructors.
struct BlockchainObjects
{
Blockchain m_blockchain;
tx_memory_pool m_mempool;
service_nodes::service_node_list m_service_node_list;
BlockchainObjects() :
m_blockchain(m_mempool, m_service_node_list),
m_service_node_list(m_blockchain),
m_mempool(m_blockchain) { }
};
BlockchainObjects* blockchain_objects = new BlockchainObjects();
core_storage[n] = &(blockchain_objects->m_blockchain);
BlockchainDB* db = new_db(db_type);
if (db == NULL)

View File

@ -150,9 +150,21 @@ int main(int argc, char* argv[])
// because unlike blockchain_storage constructor, which takes a pointer to
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
Blockchain* core_storage = NULL;
tx_memory_pool m_mempool(*core_storage);
core_storage = new Blockchain(m_mempool);
// This is done this way because of the circular constructors.
struct BlockchainObjects
{
Blockchain m_blockchain;
tx_memory_pool m_mempool;
service_nodes::service_node_list m_service_node_list;
BlockchainObjects() :
m_blockchain(m_mempool, m_service_node_list),
m_service_node_list(m_blockchain),
m_mempool(m_blockchain) { }
};
BlockchainObjects* blockchain_objects = new BlockchainObjects();
Blockchain* core_storage = &(blockchain_objects->m_blockchain);
tx_memory_pool& m_mempool = blockchain_objects->m_mempool;
BlockchainDB* db = new_db(db_type);
if (db == NULL)

View File

@ -168,9 +168,22 @@ int main(int argc, char* argv[])
// tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
const std::string input = command_line::get_arg(vm, arg_input);
std::unique_ptr<Blockchain> core_storage;
tx_memory_pool m_mempool(*core_storage);
core_storage.reset(new Blockchain(m_mempool));
// This is done this way because of the circular constructors.
struct BlockchainObjects
{
Blockchain m_blockchain;
tx_memory_pool m_mempool;
service_nodes::service_node_list m_service_node_list;
BlockchainObjects() :
m_blockchain(m_mempool, m_service_node_list),
m_service_node_list(m_blockchain),
m_mempool(m_blockchain) { }
};
BlockchainObjects* blockchain_objects = new BlockchainObjects();
Blockchain* core_storage;
tx_memory_pool& m_mempool = blockchain_objects->m_mempool;
core_storage = &(blockchain_objects->m_blockchain);
BlockchainDB* db = new_db(db_type);
if (db == NULL)
{

View File

@ -467,6 +467,26 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
bool add_account_public_address_to_tx_extra(std::vector<uint8_t>& tx_extra, const cryptonote::account_public_address& address)
{
tx_extra.resize(tx_extra.size() + 1 + sizeof(cryptonote::account_public_address));
tx_extra[tx_extra.size() - 1 - sizeof(cryptonote::account_public_address)] = TX_EXTRA_TAG_ACCOUNT_PUBLIC_ADDRESS;
*reinterpret_cast<cryptonote::account_public_address*>(&tx_extra[tx_extra.size() - sizeof(cryptonote::account_public_address)]) = address;
return true;
}
//---------------------------------------------------------------
cryptonote::account_public_address get_account_public_address_from_tx_extra(const std::vector<uint8_t>& tx_extra)
{
// parse
std::vector<tx_extra_field> tx_extra_fields;
parse_tx_extra(tx_extra, tx_extra_fields);
// find corresponding field
tx_extra_account_public_address address;
if (!find_tx_extra_field_by_type(tx_extra_fields, address))
return cryptonote::account_public_address{ crypto::null_pkey, crypto::null_pkey };
return cryptonote::account_public_address{ address.m_spend_public_key, address.m_view_public_key };
}
//---------------------------------------------------------------
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type)
{
if (tx_extra.empty())

View File

@ -70,10 +70,14 @@ namespace cryptonote
bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key);
bool add_tx_pub_key_to_extra(transaction_prefix& tx, const crypto::public_key& tx_pub_key);
bool add_tx_pub_key_to_extra(std::vector<uint8_t>& tx_extra, const crypto::public_key& tx_pub_key);
bool add_account_public_address_to_tx_extra(std::vector<uint8_t>& tx_extra, const cryptonote::account_public_address& address);
cryptonote::account_public_address get_account_public_address_from_tx_extra(const std::vector<uint8_t>& tx_extra);
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const std::vector<uint8_t>& tx_extra);
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx);
bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys);
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
bool add_viewkey_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::secret_key& viewkey);
crypto::secret_key get_viewkey_from_tx_extra(const std::vector<uint8_t>& tx_extra);
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type);
void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id);
void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id);

View File

@ -30,7 +30,6 @@
#pragma once
#define TX_EXTRA_PADDING_MAX_COUNT 255
#define TX_EXTRA_NONCE_MAX_COUNT 255
@ -39,6 +38,7 @@
#define TX_EXTRA_NONCE 0x02
#define TX_EXTRA_MERGE_MINING_TAG 0x03
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
#define TX_EXTRA_TAG_ACCOUNT_PUBLIC_ADDRESS 0x70
#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
@ -170,6 +170,17 @@ namespace cryptonote
END_SERIALIZE()
};
struct tx_extra_account_public_address
{
crypto::public_key m_spend_public_key;
crypto::public_key m_view_public_key;
BEGIN_SERIALIZE()
FIELD(m_spend_public_key)
FIELD(m_view_public_key)
END_SERIALIZE()
};
struct tx_extra_mysterious_minergate
{
std::string data;
@ -183,7 +194,13 @@ namespace cryptonote
// varint tag;
// varint size;
// varint data[];
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate> tx_extra_field;
typedef boost::variant<tx_extra_padding,
tx_extra_pub_key,
tx_extra_nonce,
tx_extra_merge_mining_tag,
tx_extra_additional_pub_keys,
tx_extra_account_public_address,
tx_extra_mysterious_minergate> tx_extra_field;
}
VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING);
@ -191,4 +208,5 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_account_public_address, TX_EXTRA_TAG_ACCOUNT_PUBLIC_ADDRESS);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);

View File

@ -49,6 +49,9 @@
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 60*10
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10
#define STAKING_REQUIREMENT_LOCK_BLOCKS (30*24*31)
#define STAKING_RELOCK_WINDOW_BLOCKS (30*6)
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 11
// MONEY_SUPPLY - total number coins to be generated

View File

@ -30,6 +30,7 @@
set(cryptonote_core_sources
blockchain.cpp
cryptonote_core.cpp
service_node_list.cpp
tx_pool.cpp
cryptonote_tx_utils.cpp)
@ -38,6 +39,7 @@ set(cryptonote_core_headers)
set(cryptonote_core_private_headers
blockchain_storage_boost_serialization.h
blockchain.h
service_node_list.h
cryptonote_core.h
tx_pool.h
cryptonote_tx_utils.h)

View File

@ -56,6 +56,7 @@
#if defined(PER_BLOCK_CHECKPOINT)
#include "blocks/blocks.h"
#endif
#include "service_node_list.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "blockchain"
@ -101,8 +102,9 @@ static const struct {
} testnet_hard_forks[] = {
// version 7 from the start of the blockchain, inhereted from Monero testnet
{ 7, 1, 0, 1512211236 },
{ 8, 50, 0, 1528768370 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
static const uint64_t testnet_hard_fork_version_1_till = 0;
static const struct {
uint8_t version;
@ -115,11 +117,12 @@ static const struct {
};
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
Blockchain::Blockchain(tx_memory_pool& tx_pool, service_nodes::service_node_list& service_node_list) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0),
m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false),
m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1)
m_difficulty_for_next_block(1),
m_service_node_list(service_node_list)
{
LOG_PRINT_L3("Blockchain::" << __func__);
}
@ -452,6 +455,10 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
}
update_next_cumulative_size_limit();
for (InitHook* hook : m_init_hooks)
hook->init();
return true;
}
//------------------------------------------------------------------
@ -785,7 +792,6 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
auto height = m_db->height();
uint8_t version = get_current_hard_fork_version();
assert(version == 7);
size_t difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2;
// ND: Speedup
@ -835,6 +841,11 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
return diff;
}
//------------------------------------------------------------------
uint64_t Blockchain::get_staking_requirement(uint64_t height) const
{
return UINT64_C(1000000000000);
}
//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the
// position where the blockchain switch started and then re-adds the blocks
// that had been removed.
@ -908,6 +919,9 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
auto split_height = m_db->height();
for (BlockchainDetachedHook* hook : m_blockchain_detached_hooks)
hook->blockchain_detached(split_height);
//connecting new alternative chain
for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
{
@ -982,7 +996,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
std::vector<difficulty_type> cumulative_difficulties;
uint8_t version = get_current_hard_fork_version();
size_t difficulty_blocks_count;
assert(version==7);
difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2;
// if the alt chain isn't long enough to calculate the difficulty target
@ -1101,6 +1115,10 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
return false;
}
for (ValidateMinerTxHook* hook : m_validate_miner_tx_hooks)
if (!hook->validate_miner_tx(b.prev_id, b.miner_tx, m_db->height(), get_current_hard_fork_version(), base_reward))
return false;
if (already_generated_coins != 0)
{
uint64_t governance_reward = get_governance_reward(m_db->height(), base_reward);
@ -1302,7 +1320,10 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
uint8_t hf_version = m_hardfork->get_current_version();
size_t max_outs = hf_version >= 4 ? 1 : 11;
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version, m_nettype);
account_public_address service_node_address = m_service_node_list.select_winner(b.prev_id);
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version, m_nettype, service_node_address);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
@ -1311,7 +1332,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
#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, max_outs, hf_version, m_nettype);
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version, m_nettype, service_node_address);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
@ -3615,6 +3636,9 @@ leave:
LOG_ERROR("Blocks that failed verification should not reach here");
}
for (BlockAddedHook* hook : m_block_added_hooks)
hook->block_added(bl, txs);
TIME_MEASURE_FINISH(addblock);
// do this after updating the hard fork state since the size limit may change due to fork
@ -4563,6 +4587,26 @@ bool Blockchain::for_all_outputs(uint64_t amount, std::function<bool(uint64_t he
return m_db->for_all_outputs(amount, f);;
}
void Blockchain::hook_init(Blockchain::InitHook& init_hook)
{
m_init_hooks.push_back(&init_hook);
}
void Blockchain::hook_block_added(Blockchain::BlockAddedHook& block_added_hook)
{
m_block_added_hooks.push_back(&block_added_hook);
}
void Blockchain::hook_blockchain_detached(Blockchain::BlockchainDetachedHook& blockchain_detached_hook)
{
m_blockchain_detached_hooks.push_back(&blockchain_detached_hook);
}
void Blockchain::hook_validate_miner_tx(Blockchain::ValidateMinerTxHook& validate_miner_tx_hook)
{
m_validate_miner_tx_hooks.push_back(&validate_miner_tx_hook);
}
namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::list<transaction>&, std::list<crypto::hash>&) const;
template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::list<cryptonote::blobdata>&, std::list<crypto::hash>&, bool) const;

View File

@ -55,6 +55,11 @@
#include "cryptonote_basic/hardfork.h"
#include "blockchain_db/blockchain_db.h"
namespace service_nodes
{
class service_node_list;
};
namespace cryptonote
{
class tx_memory_pool;
@ -100,12 +105,36 @@ namespace cryptonote
uint64_t already_generated_coins; //!< the total coins minted after that block
};
class BlockAddedHook
{
public:
virtual void block_added(const block& block, const std::vector<transaction>& txs) = 0;
};
class BlockchainDetachedHook
{
public:
virtual void blockchain_detached(uint64_t height) = 0;
};
class InitHook
{
public:
virtual void init() = 0;
};
class ValidateMinerTxHook
{
public:
virtual bool validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, uint64_t base_reward) = 0;
};
/**
* @brief Blockchain constructor
*
* @param tx_pool a reference to the transaction pool to be kept by the Blockchain
*/
Blockchain(tx_memory_pool& tx_pool);
Blockchain(tx_memory_pool& tx_pool, service_nodes::service_node_list& service_node_list);
/**
* @brief Initialize the Blockchain state
@ -286,6 +315,13 @@ namespace cryptonote
*/
difficulty_type get_difficulty_for_next_block();
/**
* @brief returns the staking requirement for the block at height
*
* @return the target
*/
uint64_t get_staking_requirement(uint64_t height) const;
/**
* @brief adds a block to the blockchain
*
@ -959,6 +995,14 @@ namespace cryptonote
*/
void on_new_tx_from_block(const cryptonote::transaction &tx);
/**
* @brief add a hook for processing new blocks and rollbacks for reorgs
*/
void hook_block_added(BlockAddedHook& block_added_hook);
void hook_blockchain_detached(BlockchainDetachedHook& blockchain_detached_hook);
void hook_init(InitHook& init_hook);
void hook_validate_miner_tx(ValidateMinerTxHook& validate_miner_tx_hook);
private:
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
@ -981,6 +1025,8 @@ namespace cryptonote
tx_memory_pool& m_tx_pool;
service_nodes::service_node_list& m_service_node_list;
mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
// main chain
@ -1025,6 +1071,10 @@ namespace cryptonote
// some invalid blocks
blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info
std::vector<BlockAddedHook*> m_block_added_hooks;
std::vector<BlockchainDetachedHook*> m_blockchain_detached_hooks;
std::vector<InitHook*> m_init_hooks;
std::vector<ValidateMinerTxHook*> m_validate_miner_tx_hooks;
checkpoints m_checkpoints;
bool m_enforce_dns_checkpoints;

View File

@ -162,7 +162,8 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
core::core(i_cryptonote_protocol* pprotocol):
m_mempool(m_blockchain_storage),
m_blockchain_storage(m_mempool),
m_service_node_list(m_blockchain_storage),
m_blockchain_storage(m_mempool, m_service_node_list),
m_miner(this),
m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false),

View File

@ -43,6 +43,7 @@
#include "common/command_line.h"
#include "tx_pool.h"
#include "blockchain.h"
#include "service_node_list.h"
#include "cryptonote_basic/miner.h"
#include "cryptonote_basic/connection_context.h"
#include "cryptonote_basic/cryptonote_stat_info.h"
@ -945,6 +946,7 @@ namespace cryptonote
tx_memory_pool m_mempool; //!< transaction pool instance
Blockchain m_blockchain_storage; //!< Blockchain instance
service_nodes::service_node_list m_service_node_list;
i_cryptonote_protocol* m_pprotocol; //!< cryptonote protocol instance

View File

@ -101,6 +101,11 @@ namespace cryptonote
return base_reward / 20;
}
uint64_t get_service_node_reward(uint64_t height, uint64_t base_reward, int hard_fork_version)
{
return hard_fork_version >= 8 ? base_reward / 2 : 0;
}
bool get_deterministic_output_key(const account_public_address& address, const keypair& tx_key, size_t output_index, crypto::public_key& output_key)
{
@ -146,7 +151,7 @@ namespace cryptonote
}
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version, network_type nettype) {
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs /* unused */, uint8_t hard_fork_version, network_type nettype, const account_public_address service_node_address) {
tx.vin.clear();
tx.vout.clear();
tx.extra.clear();
@ -180,64 +185,51 @@ namespace cryptonote
//TODO: declining governance reward schedule
uint64_t governance_reward = 0;
uint64_t service_node_reward = 0;
if (already_generated_coins != 0)
{
governance_reward = get_governance_reward(height, block_reward);
service_node_reward = get_service_node_reward(height, block_reward, hard_fork_version);
block_reward -= governance_reward;
block_reward -= service_node_reward;
}
block_reward += fee;
// from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
// keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
// emission schedule
// from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
// and avoids the quantization. These outputs will be added as rct outputs with identity
// masks, to they can be used as rct inputs.
if (hard_fork_version >= 2 && hard_fork_version < 4) {
block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
}
std::vector<uint64_t> out_amounts;
decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
if (height == 0 || hard_fork_version >= 4)
{
// the genesis block was not decomposed, for unknown reasons
while (max_outs < out_amounts.size())
{
//out_amounts[out_amounts.size() - 2] += out_amounts.back();
//out_amounts.resize(out_amounts.size() - 1);
out_amounts[1] += out_amounts[0];
for (size_t n = 1; n < out_amounts.size(); ++n)
out_amounts[n - 1] = out_amounts[n];
out_amounts.resize(out_amounts.size() - 1);
}
}
else
{
CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded");
}
uint64_t summary_amounts = 0;
for (size_t no = 0; no < out_amounts.size(); no++)
{
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")");
r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")");
r = crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 0 << ", "<< miner_address.m_spend_public_key << ")");
txout_to_key tk;
tk.key = out_eph_public_key;
tx_out out;
summary_amounts += out.amount = out_amounts[no];
summary_amounts += out.amount = block_reward;
out.target = tk;
tx.vout.push_back(out);
}
if (hard_fork_version >= 8)
{
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
bool r = crypto::generate_key_derivation(service_node_address.m_view_public_key, gov_key.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << service_node_address.m_view_public_key << ", " << gov_key.sec << ")");
r = crypto::derive_public_key(derivation, 1, service_node_address.m_spend_public_key, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 1 << ", "<< service_node_address.m_spend_public_key << ")");
txout_to_key tk;
tk.key = out_eph_public_key;
tx_out out;
summary_amounts += out.amount = service_node_reward;
out.target = tk;
tx.vout.push_back(out);
}
@ -266,7 +258,7 @@ namespace cryptonote
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
if (!get_deterministic_output_key(governance_wallet_address.address, gov_key, out_amounts.size(), out_eph_public_key))
if (!get_deterministic_output_key(governance_wallet_address.address, gov_key, tx.vout.size(), out_eph_public_key))
{
MERROR("Failed to generate deterministic output key for governance wallet output creation");
return false;
@ -281,12 +273,9 @@ namespace cryptonote
tx.vout.push_back(out);
}
CHECK_AND_ASSERT_MES(summary_amounts == (block_reward + governance_reward), false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal total block_reward = " << (block_reward + governance_reward));
CHECK_AND_ASSERT_MES(summary_amounts == (block_reward + governance_reward + service_node_reward), false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal total block_reward = " << (block_reward + governance_reward + service_node_reward));
if (hard_fork_version >= 4)
tx.version = 2;
else
tx.version = 1;
tx.version = 2;
//lock
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
@ -727,11 +716,17 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout, bool is_staking_tx)
{
hw::device &hwdev = sender_account_keys.get_device();
hwdev.open_tx(tx_key);
if (is_staking_tx)
{
// quietly override the tx key.
tx_key = get_deterministic_keypair_from_height(1).sec;
}
// figure out if we need to make additional tx pubkeys
size_t num_stdaddresses = 0;
size_t num_subaddresses = 0;

View File

@ -37,11 +37,12 @@
namespace cryptonote
{
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1, network_type nettype = MAINNET);
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1, network_type nettype = MAINNET, const account_public_address service_node_address=account_public_address());
keypair get_deterministic_keypair_from_height(uint64_t height);
uint64_t get_governance_reward(uint64_t height, uint64_t base_reward);
uint64_t get_service_node_reward(uint64_t height, uint64_t base_reward, int hard_fork_version);
bool get_deterministic_output_key(const account_public_address& address, const keypair& tx_key, size_t output_index, crypto::public_key& output_key);
@ -99,7 +100,7 @@ namespace cryptonote
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL, bool is_staking_tx = false);
bool generate_genesis_block(
block& bl

View File

@ -0,0 +1,455 @@
// Copyright (c) 2018, The Loki Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <functional>
#include "ringct/rctSigs.h"
#include "wallet/wallet2.h"
#include "cryptonote_tx_utils.h"
#include "service_node_list.h"
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "service_nodes"
namespace service_nodes
{
service_node_list::service_node_list(cryptonote::Blockchain& blockchain)
: m_blockchain(blockchain)
{
blockchain.hook_block_added(*this);
blockchain.hook_blockchain_detached(*this);
blockchain.hook_init(*this);
blockchain.hook_validate_miner_tx(*this);
}
void service_node_list::init()
{
// TODO: Save this calculation, only do it if it's not here.
LOG_PRINT_L0("Recalculating service nodes list, scanning last 30 days");
m_service_nodes_last_reward.clear();
while (!m_rollback_events.empty())
{
m_rollback_events.pop_front();
}
uint64_t current_height = m_blockchain.get_current_blockchain_height();
uint64_t start_height = 0;
if (current_height >= STAKING_REQUIREMENT_LOCK_BLOCKS + STAKING_RELOCK_WINDOW_BLOCKS)
{
start_height = current_height - STAKING_REQUIREMENT_LOCK_BLOCKS - STAKING_RELOCK_WINDOW_BLOCKS;
}
for (uint64_t height = start_height; height <= current_height; height += 1000)
{
std::list<std::pair<cryptonote::blobdata, cryptonote::block>> blocks;
if (!m_blockchain.get_blocks(height, 1000, blocks))
{
LOG_ERROR("Unable to initialize service nodes list");
return;
}
for (const auto& block_pair : blocks)
{
const cryptonote::block& block = block_pair.second;
std::list<cryptonote::transaction> txs;
std::list<crypto::hash> missed_txs;
if (!m_blockchain.get_transactions(block.tx_hashes, txs, missed_txs))
{
LOG_ERROR("Unable to get transactions for block " << block.hash);
return;
}
block_added_generic(block, txs);
}
}
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new prevent_rollback(current_height)));
}
std::vector<cryptonote::account_public_address> service_node_list::get_service_nodes_pubkeys()
{
std::vector<cryptonote::account_public_address> ret;
for (const auto& pubkey_last_reward_pair : m_service_nodes_last_reward)
{
ret.push_back(pubkey_last_reward_pair.first);
}
return ret;
}
bool service_node_list::reg_tx_has_correct_unlock_time(const cryptonote::transaction& tx, uint64_t block_height) const
{
return tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && tx.unlock_time >= block_height + STAKING_REQUIREMENT_LOCK_BLOCKS;
}
bool service_node_list::reg_tx_extract_fields(const cryptonote::transaction& tx, cryptonote::account_public_address& address, crypto::public_key& tx_pub_key) const
{
address = cryptonote::get_account_public_address_from_tx_extra(tx.extra);
tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx.extra);
return address.m_spend_public_key != crypto::null_pkey &&
address.m_view_public_key != crypto::null_pkey &&
tx_pub_key != crypto::null_pkey;
}
bool service_node_list::is_reg_tx_staking_output(const cryptonote::transaction& tx, int i, uint64_t block_height, crypto::key_derivation derivation, hw::device& hwdev) const
{
if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
{
return false;
}
rct::key mask;
uint64_t money_transferred = 0;
crypto::secret_key scalar1;
hwdev.derivation_to_scalar(derivation, i, scalar1);
try
{
switch (tx.rct_signatures.type)
{
case rct::RCTTypeSimple:
case rct::RCTTypeSimpleBulletproof:
money_transferred = rct::decodeRctSimple(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev);
break;
case rct::RCTTypeFull:
case rct::RCTTypeFullBulletproof:
money_transferred = rct::decodeRct(tx.rct_signatures, rct::sk2rct(scalar1), i, mask, hwdev);
break;
default:
LOG_ERROR("Unsupported rct type: " << tx.rct_signatures.type);
return false;
}
}
catch (const std::exception &e)
{
LOG_ERROR("Failed to decode input " << i);
return false;
}
return money_transferred >= m_blockchain.get_staking_requirement(block_height);
}
// This function takes a tx and returns true if it is a staking transaction.
// It also sets the address argument to the public spendkey and pub viewkey of
// the transaction.
//
bool service_node_list::process_registration_tx(const cryptonote::transaction& tx, uint64_t block_height, cryptonote::account_public_address& address)
{
if (!reg_tx_has_correct_unlock_time(tx, block_height))
{
return false;
}
crypto::public_key tx_pub_key;
cryptonote::account_public_address service_node_address;
if (!reg_tx_extract_fields(tx, service_node_address, tx_pub_key))
{
return false;
}
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(1);
crypto::key_derivation derivation;
crypto::generate_key_derivation(service_node_address.m_view_public_key, gov_key.sec, derivation);
hw::device& hwdev = hw::get_device("default");
for (size_t i = 0; i < tx.vout.size(); ++i)
{
if (is_reg_tx_staking_output(tx, i, block_height, derivation, hwdev))
{
address = service_node_address;
return true;
}
}
return false;
}
void service_node_list::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
{
block_added_generic(block, txs);
}
cryptonote::account_public_address service_node_list::find_service_node_from_miner_tx(const cryptonote::transaction& miner_tx, uint64_t height) const
{
if (miner_tx.vout.size() != 3)
{
MERROR("Miner tx should have 3 outputs");
return null_address;
}
if (miner_tx.vout[1].target.type() != typeid(cryptonote::txout_to_key))
{
MERROR("Service node output target type should be txout_to_key");
return null_address;
}
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(height);
for (const auto& address_blockheight : m_service_nodes_last_reward)
{
const cryptonote::account_public_address address = address_blockheight.first;
const crypto::public_key& pub_spendkey = address.m_spend_public_key;
const crypto::public_key& pub_viewkey = address.m_view_public_key;
crypto::key_derivation derivation;
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
bool r = crypto::generate_key_derivation(pub_viewkey, gov_key.sec, derivation);
CHECK_AND_ASSERT_MES(r, null_address, "while creating outs: failed to generate_key_derivation(" << pub_viewkey << ", " << gov_key.sec << ")");
r = crypto::derive_public_key(derivation, 1, pub_spendkey, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, null_address, "while creating outs: failed to derive_public_key(" << derivation << ", " << 1 << ", "<< pub_spendkey << ")");
if (boost::get<cryptonote::txout_to_key>(miner_tx.vout[1].target).key == out_eph_public_key)
{
return address;
}
}
return null_address;
}
template<typename T>
void service_node_list::block_added_generic(const cryptonote::block& block, const T& txs)
{
uint64_t block_height = cryptonote::get_block_height(block);
int hard_fork_version = m_blockchain.get_hard_fork_version(block_height);
if (hard_fork_version < 8)
return;
assert(block.miner_tx.vout.size() == 3);
while (!m_rollback_events.empty() && m_rollback_events.front()->m_block_height < block_height - ROLLBACK_EVENT_EXPIRATION_BLOCKS)
{
m_rollback_events.pop_front();
}
cryptonote::account_public_address winner_address = find_service_node_from_miner_tx(block.miner_tx, block_height);
if (m_service_nodes_last_reward.count(winner_address) == 1)
{
m_rollback_events.push_back(
std::unique_ptr<rollback_event>(
new rollback_change(block_height, winner_address, m_service_nodes_last_reward[winner_address])
)
);
m_service_nodes_last_reward[winner_address] = std::pair<uint64_t, size_t>(block_height, 0);
}
for (const cryptonote::account_public_address address : get_expired_nodes(block_height))
{
auto i = m_service_nodes_last_reward.find(address);
if (i != m_service_nodes_last_reward.end())
{
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, address, i->second)));
m_service_nodes_last_reward.erase(i);
}
}
size_t index = 1;
for (const cryptonote::transaction& tx : txs)
{
cryptonote::account_public_address address;
if (process_registration_tx(tx, block_height, address))
{
auto iter = m_service_nodes_last_reward.find(address);
if (iter == m_service_nodes_last_reward.end())
{
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_new(block_height, address)));
m_service_nodes_last_reward[address] = std::pair<uint64_t, size_t>(block_height, index);
}
else
{
m_rollback_events.push_back(std::unique_ptr<rollback_event>(new rollback_change(block_height, address, iter->second)));
iter->second = std::pair<uint64_t, size_t>(block_height, index);
}
}
index++;
}
}
void service_node_list::blockchain_detached(uint64_t height)
{
while (!m_rollback_events.empty() && m_rollback_events.back()->m_block_height >= height)
{
if (!m_rollback_events.back()->apply(m_service_nodes_last_reward))
{
init();
break;
}
m_rollback_events.pop_back();
}
}
std::vector<cryptonote::account_public_address> service_node_list::get_expired_nodes(uint64_t block_height)
{
std::vector<cryptonote::account_public_address> expired_nodes;
if (block_height < STAKING_REQUIREMENT_LOCK_BLOCKS + STAKING_RELOCK_WINDOW_BLOCKS)
return expired_nodes;
const uint64_t expired_nodes_block_height = block_height - STAKING_REQUIREMENT_LOCK_BLOCKS - STAKING_RELOCK_WINDOW_BLOCKS;
std::list<std::pair<cryptonote::blobdata, cryptonote::block>> blocks;
if (!m_blockchain.get_blocks(expired_nodes_block_height, 1, blocks))
{
LOG_ERROR("Unable to get historical blocks");
return expired_nodes;
}
const cryptonote::block& block = blocks.begin()->second;
std::list<cryptonote::transaction> txs;
std::list<crypto::hash> missed_txs;
if (!m_blockchain.get_transactions(block.tx_hashes, txs, missed_txs))
{
LOG_ERROR("Unable to get transactions for block " << block.hash);
return expired_nodes;
}
for (const cryptonote::transaction& tx : txs)
{
cryptonote::account_public_address address;
if (process_registration_tx(tx, expired_nodes_block_height, address))
{
expired_nodes.push_back(address);
}
}
return expired_nodes;
}
cryptonote::account_public_address service_node_list::select_winner(const crypto::hash& prev_id)
{
auto lowest_height = std::pair<uint64_t, size_t>(std::numeric_limits<uint64_t>::max(), std::numeric_limits<size_t>::max());
cryptonote::account_public_address address = null_address;
for (const auto& spendkey_blockheight : m_service_nodes_last_reward)
{
if (spendkey_blockheight.second < lowest_height)
{
lowest_height = spendkey_blockheight.second;
address = spendkey_blockheight.first;
}
}
return address;
}
/// validates the miner TX for the next block
//
bool service_node_list::validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, uint64_t base_reward)
{
if (hard_fork_version < 8)
return true;
uint64_t service_node_reward = cryptonote::get_service_node_reward(height, base_reward, hard_fork_version);
if (miner_tx.vout.size() != 3)
{
MERROR("Miner TX should have exactly 3 outputs");
return false;
}
if (miner_tx.vout[1].amount != service_node_reward)
{
MERROR("Service node reward amount incorrect. Should be " << cryptonote::print_money(service_node_reward) << ", is: " << cryptonote::print_money(miner_tx.vout[1].amount));
return false;
}
if (miner_tx.vout[1].target.type() != typeid(cryptonote::txout_to_key))
{
MERROR("Service node output target type should be txout_to_key");
return false;
}
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
cryptonote::account_public_address address = select_winner(prev_id);
cryptonote::keypair gov_key = cryptonote::get_deterministic_keypair_from_height(height);
bool r = crypto::generate_key_derivation(address.m_view_public_key, gov_key.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << address.m_view_public_key << ", " << gov_key.sec << ")");
r = crypto::derive_public_key(derivation, 1, address.m_spend_public_key, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << 1 << ", "<< address.m_spend_public_key << ")");
if (boost::get<cryptonote::txout_to_key>(miner_tx.vout[1].target).key != out_eph_public_key)
{
MERROR("Invalid service node reward output");
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
service_node_list::rollback_event::rollback_event(uint64_t block_height) : m_block_height(block_height)
{
}
service_node_list::rollback_change::rollback_change(uint64_t block_height, cryptonote::account_public_address address, std::pair<uint64_t, size_t> height_index)
: service_node_list::rollback_event(block_height), m_address(address), m_height_index(height_index)
{
}
bool service_node_list::rollback_change::apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward) const
{
auto iter = service_nodes_last_reward.find(m_address);
if (iter == service_nodes_last_reward.end())
{
MERROR("Could not find service node address in rollback change");
return false;
}
service_nodes_last_reward[m_address] = m_height_index;
return true;
}
service_node_list::rollback_new::rollback_new(uint64_t block_height, cryptonote::account_public_address address)
: service_node_list::rollback_event(block_height), m_address(address)
{
}
bool service_node_list::rollback_new::apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward) const
{
auto iter = service_nodes_last_reward.find(m_address);
if (iter == service_nodes_last_reward.end())
{
MERROR("Could not find service node address in rollback new");
return false;
}
service_nodes_last_reward.erase(iter);
return true;
}
service_node_list::prevent_rollback::prevent_rollback(uint64_t block_height) : service_node_list::rollback_event(block_height)
{
}
bool service_node_list::prevent_rollback::apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward) const
{
MERROR("Unable to rollback any further!");
return false;
}
}

View File

@ -0,0 +1,112 @@
// Copyright (c) 2018, The Loki Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "blockchain.h"
#define ROLLBACK_EVENT_EXPIRATION_BLOCKS 30
namespace service_nodes
{
class service_node_list
: public cryptonote::Blockchain::BlockAddedHook,
public cryptonote::Blockchain::BlockchainDetachedHook,
public cryptonote::Blockchain::InitHook,
public cryptonote::Blockchain::ValidateMinerTxHook
{
public:
service_node_list(cryptonote::Blockchain& blockchain);
void block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs);
void blockchain_detached(uint64_t height);
void init();
bool validate_miner_tx(const crypto::hash& prev_id, const cryptonote::transaction& miner_tx, uint64_t height, int hard_fork_version, uint64_t base_reward);
std::vector<cryptonote::account_public_address> get_expired_nodes(uint64_t block_height);
cryptonote::account_public_address select_winner(const crypto::hash& prev_id);
std::vector<cryptonote::account_public_address> get_service_nodes_pubkeys();
private:
bool process_registration_tx(const cryptonote::transaction& tx, uint64_t block_height, cryptonote::account_public_address& address);
template<typename T>
void block_added_generic(const cryptonote::block& block, const T& txs);
bool reg_tx_has_correct_unlock_time(const cryptonote::transaction& tx, uint64_t block_height) const;
bool reg_tx_extract_fields(const cryptonote::transaction& tx, cryptonote::account_public_address& address, crypto::public_key& tx_pub_key) const;
bool is_reg_tx_staking_output(const cryptonote::transaction& tx, int i, uint64_t block_height, crypto::key_derivation derivation, hw::device& hwdev) const;
cryptonote::account_public_address find_service_node_from_miner_tx(const cryptonote::transaction& miner_tx, uint64_t block_height) const;
class rollback_event
{
public:
rollback_event(uint64_t block_height);
virtual ~rollback_event() { }
virtual bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward) const = 0;
uint64_t m_block_height;
};
class rollback_change : public rollback_event
{
public:
rollback_change(uint64_t block_height, cryptonote::account_public_address address, std::pair<uint64_t, size_t> height_index);
bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward) const;
private:
cryptonote::account_public_address m_address;
std::pair<uint64_t, size_t> m_height_index;
};
class rollback_new : public rollback_event
{
public:
rollback_new(uint64_t block_height, cryptonote::account_public_address address);
bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward) const;
private:
cryptonote::account_public_address m_address;
};
class prevent_rollback : public rollback_event
{
public:
prevent_rollback(uint64_t block_height);
bool apply(std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>>& service_nodes_last_reward) const;
};
// Service nodes are organized by time since last reward or registration
// This value is given by block height, and differentiated by transaction index for
// registrations that occured in the same block. index = 0 for block reward, 1 for first transaction, etc.
// hence a std::pair<uint64_t, size_t> is used here for this value.
std::unordered_map<cryptonote::account_public_address, std::pair<uint64_t, size_t>> m_service_nodes_last_reward;
std::list<std::unique_ptr<rollback_event>> m_rollback_events;
cryptonote::Blockchain& m_blockchain;
};
const static cryptonote::account_public_address null_address{ crypto::null_pkey, crypto::null_pkey };
}

View File

@ -2019,6 +2019,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::locked_sweep_all, this, _1),
tr("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] <address> <lockblocks> [<payment_id>]"),
tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used."));
m_cmd_binder.set_handler("stake_all",
boost::bind(&simple_wallet::stake_all, this, _1),
tr("stake_all [index=<N1>[,<N2>,...]] [<priority>] [lockblocks]"),
tr("Send all unlocked balance to the same address. Lock it for [lockblocks] (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet stakes outputs received by those address indices. <priority> is the priority of the stake. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used."));
m_cmd_binder.set_handler("sweep_unmixable",
boost::bind(&simple_wallet::sweep_unmixable, this, _1),
tr("Deprecated"));
@ -4275,6 +4279,28 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
}
return true;
}
//----------------------------------------------------------------------------------------------------
static bool locked_blocks_arg_valid(const std::string& arg, uint64_t& duration)
{
try
{
duration = boost::lexical_cast<uint64_t>(arg);
}
catch (const std::exception &e)
{
return false;
}
if (duration > 1000000)
{
fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)");
return false;
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_)
{
@ -4355,18 +4381,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
uint64_t locked_blocks = 0;
if (transfer_type == TransferLocked)
{
try
if (!locked_blocks_arg_valid(local_args.back(), locked_blocks))
{
locked_blocks = boost::lexical_cast<uint64_t>(local_args.back());
}
catch (const std::exception &e)
{
fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back();
return true;
}
if (locked_blocks > 1000000)
{
fail_msg_writer() << tr("Locked blocks too high, max 1000000 (??4 yrs)");
return true;
}
local_args.pop_back();
@ -4661,6 +4677,157 @@ bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
return sweep_main(0, true, args_);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::stake_all(const std::vector<std::string> &args_)
{
// stake_all [index=<N1>[,<N2>,...]] [priority]
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
if (!try_connect_to_daemon())
return true;
std::vector<std::string> local_args = args_;
std::set<uint32_t> subaddr_indices;
if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
{
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
return true;
local_args.erase(local_args.begin());
}
uint32_t priority = 0;
if (local_args.size() > 0 && parse_priority(local_args[0], priority))
local_args.erase(local_args.begin());
priority = m_wallet->adjust_priority(priority);
size_t mixins = DEFAULT_MIX;
uint64_t unlock_block = 0;
uint64_t locked_blocks = STAKING_REQUIREMENT_LOCK_BLOCKS;
std::string err;
uint64_t bc_height = get_daemon_blockchain_height(err);
if (!err.empty())
{
fail_msg_writer() << tr("failed to get blockchain height: ") << err;
return true;
}
unlock_block = bc_height + locked_blocks;
cryptonote::account_public_address address = m_wallet->get_address();
std::vector<uint8_t> extra;
if (!add_account_public_address_to_tx_extra(extra, address))
{
fail_msg_writer() << tr("failed to add account public address to tx extra");
return true;
}
LOCK_IDLE_SCOPE();
try
{
// figure out what tx will be necessary
auto ptx_vector = m_wallet->create_transactions_all(0, address, false, mixins, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices, is_daemon_trusted(), true);
if (ptx_vector.empty())
{
fail_msg_writer() << tr("No outputs found, or daemon is not ready");
return true;
}
// give user total and fee, and prompt to confirm
uint64_t total_fee = 0, total_sent = 0;
for (size_t n = 0; n < ptx_vector.size(); ++n)
{
total_fee += ptx_vector[n].fee;
for (auto i: ptx_vector[n].selected_transfers)
total_sent += m_wallet->get_transfer_details(i).amount();
}
total_sent -= total_fee;
std::ostringstream prompt;
for (size_t n = 0; n < ptx_vector.size(); ++n)
{
prompt << tr("\nTransaction ") << (n + 1) << "/" << ptx_vector.size() << ":\n";
subaddr_indices.clear();
for (uint32_t i : ptx_vector[n].construction_data.subaddr_indices)
subaddr_indices.insert(i);
for (uint32_t i : subaddr_indices)
prompt << boost::format(tr("Spending from address index %d\n")) % i;
if (subaddr_indices.size() > 1)
prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
}
if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
return true;
if (ptx_vector.size() > 1) {
prompt << boost::format(tr("Staking %s for %u blocks in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): ")) %
locked_blocks %
print_money(total_sent) %
((unsigned long long)ptx_vector.size()) %
print_money(total_fee);
}
else {
prompt << boost::format(tr("Staking %s for %u blocks a total fee of %s. Is this okay? (Y/Yes/N/No): ")) %
locked_blocks %
print_money(total_sent) %
print_money(total_fee);
}
std::string accepted = input_line(prompt.str());
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
{
fail_msg_writer() << tr("transaction cancelled.");
return true;
}
// actually commit the transactions
if (m_wallet->multisig())
{
bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_loki_tx");
if (!r)
{
fail_msg_writer() << tr("Failed to write transaction(s) to file");
}
else
{
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_loki_tx";
}
}
else if (m_wallet->watch_only())
{
bool r = m_wallet->save_tx(ptx_vector, "unsigned_loki_tx");
if (!r)
{
fail_msg_writer() << tr("Failed to write transaction(s) to file");
}
else
{
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_loki_tx";
}
}
else
{
commit_or_save(ptx_vector, m_do_not_relay);
}
}
catch (const std::exception& e)
{
handle_transfer_exception(std::current_exception(), is_daemon_trusted());
}
catch (...)
{
LOG_ERROR("unknown error");
fail_msg_writer() << tr("unknown error");
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
{
@ -7330,6 +7497,23 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
success_msg_writer() << "Change: " << print_money(change);
success_msg_writer() << "Fee: " << print_money(fee);
success_msg_writer() << "Destinations: " << dests;
if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
{
uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
if (bh >= last_block_height)
success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock";
else
success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations";
}
else
{
uint64_t current_time = static_cast<uint64_t>(time(NULL));
uint64_t threshold = current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2;
if (threshold >= pd.m_unlock_time)
success_msg_writer() << "unlocked for " << get_human_readable_timespan(std::chrono::seconds(threshold - pd.m_unlock_time));
else
success_msg_writer() << "locked for " << get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
}
success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
return true;
}
@ -7540,3 +7724,4 @@ int main(int argc, char* argv[])
return 0;
//CATCH_ENTRY_L0("main", 1);
}

View File

@ -154,6 +154,7 @@ namespace cryptonote
bool transfer_new(const std::vector<std::string> &args);
bool locked_transfer(const std::vector<std::string> &args);
bool locked_sweep_all(const std::vector<std::string> &args);
bool stake_all(const std::vector<std::string> &args);
bool sweep_main(uint64_t below, bool locked, const std::vector<std::string> &args);
bool sweep_all(const std::vector<std::string> &args);
bool sweep_below(const std::vector<std::string> &args);

View File

@ -104,9 +104,6 @@ using namespace cryptonote;
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
#define SUBADDRESS_LOOKAHEAD_MAJOR 50
#define SUBADDRESS_LOOKAHEAD_MINOR 200
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Loki key image export\002"
#define MULTISIG_EXPORT_FILE_MAGIC "Loki multisig export\001"
@ -1123,7 +1120,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
break;
LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
if(0 != m_callback)
m_callback->on_skip_transaction(height, txid, tx);
m_callback->on_skip_transaction(height, txid, tx);
break;
}
@ -1163,34 +1160,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, tx_scan_info[0]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
// this assumes that the miner tx pays a single address
if (tx_scan_info[0].received)
for (size_t i = 0; i < tx.vout.size(); ++i)
{
// process the other outs from that tx
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::ref(tx_scan_info[i])));
}
waiter.wait();
// then scan all outputs from 0
hwdev_lock.lock();
hwdev.set_mode(hw::device::NONE);
for (size_t i = 0; i < tx.vout.size(); ++i)
{
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
hwdev_lock.unlock();
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::ref(tx_scan_info[i])));
}
waiter.wait();
// then scan all outputs from 0
hwdev_lock.lock();
hwdev.set_mode(hw::device::NONE);
for (size_t i = 0; i < tx.vout.size(); ++i)
{
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received)
{
hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys, derivation, additional_derivations);
scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
hwdev_lock.unlock();
}
else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1)
{
@ -1244,11 +1232,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
for(size_t o: outs)
{
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
error::wallet_internal_error, std::string("Unexpected transfer index from public key: ")
+ "got " + (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(kit->second))
+ ", m_transfers.size() is " + boost::lexical_cast<std::string>(m_transfers.size()));
@ -1256,13 +1244,13 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
if (!pool)
{
m_transfers.push_back(boost::value_initialized<transfer_details>());
transfer_details& td = m_transfers.back();
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
m_transfers.push_back(boost::value_initialized<transfer_details>());
transfer_details& td = m_transfers.back();
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
td.m_key_image = tx_scan_info[o].ki;
td.m_key_image_known = !m_watch_only && !m_multisig;
td.m_key_image_partial = m_multisig;
@ -1286,10 +1274,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_mask = rct::identity();
td.m_rct = false;
}
set_unspent(m_transfers.size()-1);
set_unspent(m_transfers.size()-1);
if (!m_multisig && !m_watch_only)
m_key_images[td.m_key_image] = m_transfers.size()-1;
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
m_key_images[td.m_key_image] = m_transfers.size()-1;
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
if (m_multisig)
{
THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info,
@ -1297,21 +1285,21 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
if (m_multisig_rescan_info && m_multisig_rescan_info->front().size() >= m_transfers.size())
update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1);
}
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
}
}
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
{
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
<< " from received " << print_money(tx.vout[o].amount) << " output already exists with "
<< (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
<< print_money(m_transfers[kit->second].amount()) << ", received output ignored");
}
else
{
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
<< " from received " << print_money(tx.vout[o].amount) << " output already exists with "
<< print_money(m_transfers[kit->second].amount()) << ", replacing with new output");
// The new larger output replaced a previous smaller one
@ -1320,11 +1308,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
if (!pool)
{
transfer_details &td = m_transfers[kit->second];
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
td.m_block_height = height;
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
td.m_amount = tx.vout[o].amount;
td.m_pk_index = pk_index - 1;
td.m_subaddr_index = tx_scan_info[o].received->index;
@ -1353,11 +1341,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1);
}
THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub, error::wallet_internal_error, "Inconsistent public keys");
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
}
}
}
@ -5463,15 +5451,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
cryptonote::transaction tx;
pending_tx ptx;
// loop until fee is met without increasing tx size to next KB boundary.
const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false);
uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do
{
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx, trusted_daemon);
auto txBlob = t_serializable_object_to_blob(ptx.tx);
// loop until fee is met without increasing tx size to next KB boundary.
const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false);
uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do
{
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx, trusted_daemon);
auto txBlob = t_serializable_object_to_blob(ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
} while (ptx.fee < needed_fee);
} while (ptx.fee < needed_fee);
ptx_vector.push_back(ptx);
@ -6481,7 +6469,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof)
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof, bool is_staking_tx)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
@ -6637,7 +6625,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
rct::multisig_out msout;
LOG_PRINT_L2("constructing tx");
auto sources_copy = sources;
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, bulletproof, m_multisig ? &msout : NULL);
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, bulletproof, m_multisig ? &msout : NULL, is_staking_tx);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
@ -7839,7 +7827,7 @@ skip_tx:
return ptx_vector;
}
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon)
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon, bool is_staking_tx)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@ -7890,7 +7878,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
}
}
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon, is_staking_tx);
}
std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon)
@ -7914,7 +7902,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
}
std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon)
std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon, bool is_staking_tx)
{
//ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device();
@ -7992,7 +7980,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.selected_transfers.size() << " outputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, bulletproof);
test_tx, test_ptx, bulletproof, is_staking_tx);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
@ -8009,7 +7997,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.dsts[0].amount = available_for_fee - needed_fee;
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, bulletproof);
test_tx, test_ptx, bulletproof, is_staking_tx);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
@ -8047,7 +8035,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
pending_tx test_ptx;
if (use_rct) {
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, bulletproof);
test_tx, test_ptx, bulletproof, is_staking_tx);
} else {
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);

View File

@ -63,6 +63,9 @@
#undef LOKI_DEFAULT_LOG_CATEGORY
#define LOKI_DEFAULT_LOG_CATEGORY "wallet.wallet2"
#define SUBADDRESS_LOOKAHEAD_MAJOR 50
#define SUBADDRESS_LOOKAHEAD_MINOR 200
class Serialization_portability_wallet_Test;
namespace tools
@ -681,7 +684,7 @@ namespace tools
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, pending_tx &ptx);
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof);
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof, bool is_staking_tx=false);
void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector);
@ -700,9 +703,9 @@ namespace tools
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, bool trusted_daemon, bool is_staking_tx=false);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, bool trusted_daemon, bool is_staking_tx=false);
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func);