mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
Merge pull request #932 from Doy-lee/ClassifyStakes
Classify stakes in show/export_transfers
This commit is contained in:
commit
9510e2b5ea
11 changed files with 333 additions and 243 deletions
|
@ -412,7 +412,7 @@ uint8_t HardFork::get(uint64_t height) const
|
|||
CRITICAL_REGION_LOCAL(lock);
|
||||
if (height > db.height()) {
|
||||
assert(false);
|
||||
return INVALID_HF_VERSION_FOR_HEIGHT;
|
||||
return INVALID_HF_VERSION;
|
||||
}
|
||||
if (height == db.height()) {
|
||||
return get_current_version();
|
||||
|
|
|
@ -46,8 +46,8 @@ namespace cryptonote
|
|||
time_t time;
|
||||
};
|
||||
|
||||
constexpr static uint8_t INVALID_HF_VERSION_FOR_HEIGHT = 255;
|
||||
constexpr static uint64_t INVALID_HF_VERSION_HEIGHT = static_cast<uint64_t>(-1);
|
||||
constexpr static uint8_t INVALID_HF_VERSION = 255;
|
||||
constexpr static uint64_t INVALID_HF_VERSION_HEIGHT = static_cast<uint64_t>(-1);
|
||||
typedef enum {
|
||||
LikelyForked,
|
||||
UpdateNeeded,
|
||||
|
|
|
@ -41,6 +41,7 @@ extern "C" {
|
|||
#include "wallet/wallet2.h"
|
||||
#include "cryptonote_tx_utils.h"
|
||||
#include "cryptonote_basic/tx_extra.h"
|
||||
#include "cryptonote_basic/hardfork.h"
|
||||
#include "int-util.h"
|
||||
#include "common/scoped_message_writer.h"
|
||||
#include "common/i18n.h"
|
||||
|
@ -326,15 +327,7 @@ namespace service_nodes
|
|||
return result;
|
||||
}
|
||||
|
||||
struct parsed_tx_contribution
|
||||
{
|
||||
cryptonote::account_public_address address;
|
||||
uint64_t transferred;
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<service_node_info::contribution_t> locked_contributions;
|
||||
};
|
||||
|
||||
static uint64_t get_tx_output_amount(const cryptonote::transaction& tx, int i, crypto::key_derivation const &derivation, hw::device& hwdev)
|
||||
static uint64_t get_staking_output_contribution(const cryptonote::transaction& tx, int i, crypto::key_derivation const &derivation, hw::device& hwdev)
|
||||
{
|
||||
if (tx.vout[i].target.type() != typeid(cryptonote::txout_to_key))
|
||||
{
|
||||
|
@ -372,6 +365,177 @@ namespace service_nodes
|
|||
return money_transferred;
|
||||
}
|
||||
|
||||
bool tx_get_staking_components(cryptonote::transaction_prefix const &tx, staking_components *contribution, crypto::hash const &txid)
|
||||
{
|
||||
staking_components contribution_unused_ = {};
|
||||
if (!contribution) contribution = &contribution_unused_;
|
||||
if (!cryptonote::get_service_node_pubkey_from_tx_extra(tx.extra, contribution->service_node_pubkey))
|
||||
return false; // Is not a contribution TX don't need to check it.
|
||||
|
||||
if (!cryptonote::get_service_node_contributor_from_tx_extra(tx.extra, contribution->address))
|
||||
return false;
|
||||
|
||||
if (!cryptonote::get_tx_secret_key_from_tx_extra(tx.extra, contribution->tx_key))
|
||||
{
|
||||
LOG_PRINT_L1("TX: There was a service node contributor but no secret key in the tx extra for tx: " << txid);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tx_get_staking_components(cryptonote::transaction const &tx, staking_components *contribution)
|
||||
{
|
||||
bool result = tx_get_staking_components(tx, contribution, cryptonote::get_transaction_hash(tx));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool tx_get_staking_components_and_amounts(cryptonote::network_type nettype,
|
||||
uint8_t hf_version,
|
||||
cryptonote::transaction const &tx,
|
||||
uint64_t block_height,
|
||||
staking_components *contribution)
|
||||
{
|
||||
staking_components contribution_unused_ = {};
|
||||
if (!contribution) contribution = &contribution_unused_;
|
||||
|
||||
if (!tx_get_staking_components(tx, contribution))
|
||||
return false;
|
||||
|
||||
// A cryptonote transaction is constructed as follows
|
||||
// P = Hs(aR)G + B
|
||||
|
||||
// P := Stealth Address
|
||||
// a := Receiver's secret view key
|
||||
// B := Receiver's public spend key
|
||||
// R := TX Public Key
|
||||
// G := Elliptic Curve
|
||||
|
||||
// In Loki we pack into the tx extra information to reveal information about the TX
|
||||
// A := Public View Key (we pack contributor into tx extra, 'parsed_contribution.address')
|
||||
// r := TX Secret Key (we pack secret key into tx extra, 'parsed_contribution.tx_key`)
|
||||
|
||||
// Calulate 'Derivation := Hs(Ar)G'
|
||||
crypto::key_derivation derivation;
|
||||
if (!crypto::generate_key_derivation(contribution->address.m_view_public_key, contribution->tx_key, derivation))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Failed to generate key derivation on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
hw::device &hwdev = hw::get_device("default");
|
||||
contribution->transferred = 0;
|
||||
bool stake_decoded = true;
|
||||
if (hf_version >= cryptonote::network_version_11_infinite_staking || hf_version == cryptonote::HardFork::INVALID_HF_VERSION)
|
||||
{
|
||||
// In Infinite Staking, we lock the key image that would be generated if
|
||||
// you tried to send your stake and prevent it from being transacted on
|
||||
// the network whilst you are a Service Node. To do this, we calculate
|
||||
// the future key image that would be generated when they user tries to
|
||||
// spend the staked funds. A key image is derived from the ephemeral, one
|
||||
// time transaction private key, 'x' in the Cryptonote Whitepaper.
|
||||
|
||||
// This is only possible to generate if they are the staking to themselves
|
||||
// as you need the recipients private keys to generate the key image that
|
||||
// would be generated, when they want to spend it in the future.
|
||||
|
||||
cryptonote::tx_extra_tx_key_image_proofs key_image_proofs;
|
||||
if (!get_tx_key_image_proofs_from_tx_extra(tx.extra, key_image_proofs))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Didn't have key image proofs in the tx_extra, rejected on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
stake_decoded = false;
|
||||
}
|
||||
|
||||
for (size_t output_index = 0; stake_decoded && output_index < tx.vout.size(); ++output_index)
|
||||
{
|
||||
uint64_t transferred = get_staking_output_contribution(tx, output_index, derivation, hwdev);
|
||||
if (transferred == 0)
|
||||
continue;
|
||||
|
||||
// So prove that the destination stealth address can be decoded using the
|
||||
// staker's packed address, which means that the recipient of the
|
||||
// contribution is themselves (and hence they have the necessary secrets
|
||||
// to generate the future key image).
|
||||
|
||||
// i.e Verify the packed information is valid by computing the stealth
|
||||
// address P' (which should equal P if matching) using
|
||||
|
||||
// 'Derivation := Hs(Ar)G' (we calculated earlier) instead of 'Hs(aR)G'
|
||||
// P' = Hs(Ar)G + B
|
||||
// = Hs(aR)G + B
|
||||
// = Derivation + B
|
||||
// = P
|
||||
|
||||
crypto::public_key ephemeral_pub_key;
|
||||
{
|
||||
// P' := Derivation + B
|
||||
if (!hwdev.derive_public_key(derivation, output_index, contribution->address.m_spend_public_key, ephemeral_pub_key))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Could not derive TX ephemeral key on height: " << block_height << " for tx: " << get_transaction_hash(tx) << " for output: " << output_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Stealth address public key should match the public key referenced in the TX only if valid information is given.
|
||||
const auto& out_to_key = boost::get<cryptonote::txout_to_key>(tx.vout[output_index].target);
|
||||
if (out_to_key.key != ephemeral_pub_key)
|
||||
{
|
||||
LOG_PRINT_L1("TX: Derived TX ephemeral key did not match tx stored key on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx) << " for output: " << output_index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// To prevent the staker locking any arbitrary key image, the provided
|
||||
// key image is included and verified in a ring signature which
|
||||
// guarantees that 'the staker proves that he knows such 'x' (one time
|
||||
// ephemeral secret key) and that (the future key image) P = xG'.
|
||||
// Consequently the key image is not falsified and actually the future
|
||||
// key image.
|
||||
|
||||
// The signer can try falsify the key image, but the equation used to
|
||||
// construct the key image is re-derived by the verifier, false key
|
||||
// images will not match the re-derived key image.
|
||||
crypto::public_key const *ephemeral_pub_key_ptr = &ephemeral_pub_key;
|
||||
for (auto proof = key_image_proofs.proofs.begin(); proof != key_image_proofs.proofs.end(); proof++)
|
||||
{
|
||||
if (!crypto::check_ring_signature((const crypto::hash &)(proof->key_image), proof->key_image, &ephemeral_pub_key_ptr, 1, &proof->signature))
|
||||
continue;
|
||||
|
||||
contribution->locked_contributions.emplace_back(service_node_info::contribution_t::version_t::v0, ephemeral_pub_key, proof->key_image, transferred);
|
||||
contribution->transferred += transferred;
|
||||
key_image_proofs.proofs.erase(proof);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hf_version < cryptonote::network_version_11_infinite_staking || (hf_version == cryptonote::HardFork::INVALID_HF_VERSION && !stake_decoded))
|
||||
{
|
||||
// Pre Infinite Staking, we only need to prove the amount sent is
|
||||
// sufficient to become a contributor to the Service Node and that there
|
||||
// is sufficient lock time on the staking output.
|
||||
for (size_t i = 0; i < tx.vout.size(); i++)
|
||||
{
|
||||
bool has_correct_unlock_time = false;
|
||||
{
|
||||
uint64_t unlock_time = tx.unlock_time;
|
||||
if (tx.version >= cryptonote::txversion::v3_per_output_unlock_times)
|
||||
unlock_time = tx.output_unlock_times[i];
|
||||
|
||||
uint64_t min_height = block_height + staking_num_lock_blocks(nettype);
|
||||
has_correct_unlock_time = unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && unlock_time >= min_height;
|
||||
}
|
||||
|
||||
if (has_correct_unlock_time)
|
||||
{
|
||||
contribution->transferred += get_staking_output_contribution(tx, i, derivation, hwdev);
|
||||
stake_decoded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stake_decoded;
|
||||
}
|
||||
|
||||
/// Makes a copy of the given service_node_info and replaces the shared_ptr with a pointer to the copy.
|
||||
/// Returns the non-const service_node_info (which is now held by the passed-in shared_ptr lvalue ref).
|
||||
static service_node_info &duplicate_info(std::shared_ptr<const service_node_info> &info_ptr) {
|
||||
|
@ -639,153 +803,6 @@ namespace service_nodes
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool get_contribution(cryptonote::network_type nettype, int hf_version, const cryptonote::transaction& tx, uint64_t block_height, parsed_tx_contribution &parsed_contribution)
|
||||
{
|
||||
if (!cryptonote::get_service_node_contributor_from_tx_extra(tx.extra, parsed_contribution.address))
|
||||
return false;
|
||||
|
||||
if (!cryptonote::get_tx_secret_key_from_tx_extra(tx.extra, parsed_contribution.tx_key))
|
||||
{
|
||||
LOG_PRINT_L1("TX: There was a service node contributor but no secret key in the tx extra on height: " << block_height << " for tx: " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
// A cryptonote transaction is constructed as follows
|
||||
// P = Hs(aR)G + B
|
||||
|
||||
// P := Stealth Address
|
||||
// a := Receiver's secret view key
|
||||
// B := Receiver's public spend key
|
||||
// R := TX Public Key
|
||||
// G := Elliptic Curve
|
||||
|
||||
// In Loki we pack into the tx extra information to reveal information about the TX
|
||||
// A := Public View Key (we pack contributor into tx extra, 'parsed_contribution.address')
|
||||
// r := TX Secret Key (we pack secret key into tx extra, 'parsed_contribution.tx_key`)
|
||||
|
||||
// Calulate 'Derivation := Hs(Ar)G'
|
||||
crypto::key_derivation derivation;
|
||||
if (!crypto::generate_key_derivation(parsed_contribution.address.m_view_public_key, parsed_contribution.tx_key, derivation))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Failed to generate key derivation on height: " << block_height << " for tx: " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
hw::device& hwdev = hw::get_device("default");
|
||||
parsed_contribution.transferred = 0;
|
||||
|
||||
if (hf_version >= cryptonote::network_version_11_infinite_staking)
|
||||
{
|
||||
// In Infinite Staking, we lock the key image that would be generated if
|
||||
// you tried to send your stake and prevent it from being transacted on
|
||||
// the network whilst you are a Service Node. To do this, we calculate
|
||||
// the future key image that would be generated when they user tries to
|
||||
// spend the staked funds. A key image is derived from the ephemeral, one
|
||||
// time transaction private key, 'x' in the Cryptonote Whitepaper.
|
||||
|
||||
// This is only possible to generate if they are the staking to themselves
|
||||
// as you need the recipients private keys to generate the key image that
|
||||
// would be generated, when they want to spend it in the future.
|
||||
|
||||
cryptonote::tx_extra_tx_key_image_proofs key_image_proofs;
|
||||
if (!get_tx_key_image_proofs_from_tx_extra(tx.extra, key_image_proofs))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Didn't have key image proofs in the tx_extra, rejected on height: " << block_height << " for tx: " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t output_index = 0; output_index < tx.vout.size(); ++output_index)
|
||||
{
|
||||
uint64_t transferred = get_tx_output_amount(tx, output_index, derivation, hwdev);
|
||||
if (transferred == 0)
|
||||
continue;
|
||||
|
||||
// So prove that the destination stealth address can be decoded using the
|
||||
// staker's packed address, which means that the recipient of the
|
||||
// contribution is themselves (and hence they have the necessary secrets
|
||||
// to generate the future key image).
|
||||
|
||||
// i.e Verify the packed information is valid by computing the stealth
|
||||
// address P' (which should equal P if matching) using
|
||||
|
||||
// 'Derivation := Hs(Ar)G' (we calculated earlier) instead of 'Hs(aR)G'
|
||||
// P' = Hs(Ar)G + B
|
||||
// = Hs(aR)G + B
|
||||
// = Derivation + B
|
||||
// = P
|
||||
|
||||
crypto::public_key ephemeral_pub_key;
|
||||
{
|
||||
// P' := Derivation + B
|
||||
if (!hwdev.derive_public_key(derivation, output_index, parsed_contribution.address.m_spend_public_key, ephemeral_pub_key))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Could not derive TX ephemeral key on height: " << block_height << " for tx: " << get_transaction_hash(tx) << " for output: " << output_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Stealth address public key should match the public key referenced in the TX only if valid information is given.
|
||||
const auto& out_to_key = boost::get<cryptonote::txout_to_key>(tx.vout[output_index].target);
|
||||
if (out_to_key.key != ephemeral_pub_key)
|
||||
{
|
||||
LOG_PRINT_L1("TX: Derived TX ephemeral key did not match tx stored key on height: " << block_height << " for tx: " << get_transaction_hash(tx) << " for output: " << output_index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// To prevent the staker locking any arbitrary key image, the provided
|
||||
// key image is included and verified in a ring signature which
|
||||
// guarantees that 'the staker proves that he knows such 'x' (one time
|
||||
// ephemeral secret key) and that (the future key image) P = xG'.
|
||||
// Consequently the key image is not falsified and actually the future
|
||||
// key image.
|
||||
|
||||
// The signer can try falsify the key image, but the equation used to
|
||||
// construct the key image is re-derived by the verifier, false key
|
||||
// images will not match the re-derived key image.
|
||||
crypto::public_key const *ephemeral_pub_key_ptr = &ephemeral_pub_key;
|
||||
for (auto proof = key_image_proofs.proofs.begin(); proof != key_image_proofs.proofs.end(); proof++)
|
||||
{
|
||||
if (!crypto::check_ring_signature((const crypto::hash &)(proof->key_image), proof->key_image, &ephemeral_pub_key_ptr, 1, &proof->signature))
|
||||
continue;
|
||||
|
||||
parsed_contribution.locked_contributions.emplace_back(
|
||||
service_node_info::contribution_t::version_t::v0,
|
||||
ephemeral_pub_key,
|
||||
proof->key_image,
|
||||
transferred
|
||||
);
|
||||
|
||||
parsed_contribution.transferred += transferred;
|
||||
key_image_proofs.proofs.erase(proof);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pre Infinite Staking, we only need to prove the amount sent is
|
||||
// sufficient to become a contributor to the Service Node and that there
|
||||
// is sufficient lock time on the staking output.
|
||||
for (size_t i = 0; i < tx.vout.size(); i++)
|
||||
{
|
||||
bool has_correct_unlock_time = false;
|
||||
{
|
||||
uint64_t unlock_time = tx.unlock_time;
|
||||
if (tx.version >= cryptonote::txversion::v3_per_output_unlock_times)
|
||||
unlock_time = tx.output_unlock_times[i];
|
||||
|
||||
uint64_t min_height = block_height + staking_num_lock_blocks(nettype);
|
||||
has_correct_unlock_time = unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && unlock_time >= min_height;
|
||||
}
|
||||
|
||||
if (has_correct_unlock_time)
|
||||
parsed_contribution.transferred += get_tx_output_amount(tx, i, derivation, hwdev);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_registration_tx(cryptonote::network_type nettype, uint8_t hf_version, const cryptonote::transaction& tx, uint64_t block_timestamp, uint64_t block_height, uint32_t index, crypto::public_key& key, service_node_info& info)
|
||||
{
|
||||
crypto::public_key service_node_key;
|
||||
|
@ -847,22 +864,22 @@ namespace service_nodes
|
|||
uint64_t staking_requirement = get_staking_requirement(nettype, block_height, hf_version);
|
||||
cryptonote::account_public_address address;
|
||||
|
||||
parsed_tx_contribution parsed_contribution = {};
|
||||
if (!get_contribution(nettype, hf_version, tx, block_height, parsed_contribution))
|
||||
staking_components stake = {};
|
||||
if (!tx_get_staking_components_and_amounts(nettype, hf_version, tx, block_height, &stake))
|
||||
{
|
||||
LOG_PRINT_L1("Register TX: Had service node registration fields, but could not decode contribution on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t min_transfer = get_min_node_contribution(hf_version, staking_requirement, info.total_reserved, info.total_num_locked_contributions());
|
||||
if (parsed_contribution.transferred < min_transfer)
|
||||
if (stake.transferred < min_transfer)
|
||||
{
|
||||
LOG_PRINT_L1("Register TX: Contribution transferred: " << parsed_contribution.transferred << " didn't meet the minimum transfer requirement: " << min_transfer << " on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
LOG_PRINT_L1("Register TX: Contribution transferred: " << stake.transferred << " didn't meet the minimum transfer requirement: " << min_transfer << " on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t total_num_of_addr = service_node_addresses.size();
|
||||
if (std::find(service_node_addresses.begin(), service_node_addresses.end(), parsed_contribution.address) == service_node_addresses.end())
|
||||
if (std::find(service_node_addresses.begin(), service_node_addresses.end(), stake.address) == service_node_addresses.end())
|
||||
total_num_of_addr++;
|
||||
|
||||
if (total_num_of_addr > MAX_NUMBER_OF_CONTRIBUTORS)
|
||||
|
@ -996,38 +1013,34 @@ namespace service_nodes
|
|||
uint64_t const block_height = cryptonote::get_block_height(block);
|
||||
uint8_t const hf_version = block.major_version;
|
||||
|
||||
crypto::public_key pubkey;
|
||||
|
||||
if (!cryptonote::get_service_node_pubkey_from_tx_extra(tx.extra, pubkey))
|
||||
return false; // Is not a contribution TX don't need to check it.
|
||||
|
||||
parsed_tx_contribution parsed_contribution = {};
|
||||
if (!get_contribution(nettype, hf_version, tx, block_height, parsed_contribution))
|
||||
staking_components stake = {};
|
||||
if (!tx_get_staking_components_and_amounts(nettype, hf_version, tx, block_height, &stake))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Could not decode contribution for service node: " << pubkey << " on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
LOG_PRINT_L1("TX: Could not decode contribution for service node: " << stake.service_node_pubkey << " on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto iter = service_nodes_infos.find(pubkey);
|
||||
auto iter = service_nodes_infos.find(stake.service_node_pubkey);
|
||||
if (iter == service_nodes_infos.end())
|
||||
{
|
||||
LOG_PRINT_L1("TX: Contribution received for service node: " << pubkey <<
|
||||
", but could not be found in the service node list on height: " << block_height <<
|
||||
" for tx: " << cryptonote::get_transaction_hash(tx )<< "\n"
|
||||
"This could mean that the service node was deregistered before the contribution was processed.");
|
||||
LOG_PRINT_L1("TX: Contribution received for service node: "
|
||||
<< stake.service_node_pubkey << ", but could not be found in the service node list on height: "
|
||||
<< block_height << " for tx: " << cryptonote::get_transaction_hash(tx)
|
||||
<< "\n"
|
||||
"This could mean that the service node was deregistered before the contribution was processed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const service_node_info& curinfo = *iter->second;
|
||||
if (curinfo.is_fully_funded())
|
||||
{
|
||||
LOG_PRINT_L1("TX: Service node: " << pubkey <<
|
||||
" is already fully funded, but contribution received on height: " << block_height <<
|
||||
" for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
LOG_PRINT_L1("TX: Service node: " << stake.service_node_pubkey
|
||||
<< " is already fully funded, but contribution received on height: "
|
||||
<< block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cryptonote::get_tx_secret_key_from_tx_extra(tx.extra, parsed_contribution.tx_key))
|
||||
if (!cryptonote::get_tx_secret_key_from_tx_extra(tx.extra, stake.tx_key))
|
||||
{
|
||||
LOG_PRINT_L1("TX: Failed to get tx secret key from contribution received on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
|
@ -1037,7 +1050,7 @@ namespace service_nodes
|
|||
bool new_contributor = true;
|
||||
size_t contributor_position = 0;
|
||||
for (size_t i = 0; i < contributors.size(); i++)
|
||||
if (contributors[i].address == parsed_contribution.address){
|
||||
if (contributors[i].address == stake.address){
|
||||
contributor_position = i;
|
||||
new_contributor = false;
|
||||
break;
|
||||
|
@ -1048,17 +1061,17 @@ namespace service_nodes
|
|||
bool too_many_contributions = false;
|
||||
if (hf_version >= cryptonote::network_version_11_infinite_staking)
|
||||
// As of HF11 we allow up to 4 stakes total.
|
||||
too_many_contributions = curinfo.total_num_locked_contributions() + parsed_contribution.locked_contributions.size() > MAX_NUMBER_OF_CONTRIBUTORS;
|
||||
too_many_contributions = curinfo.total_num_locked_contributions() + stake.locked_contributions.size() > MAX_NUMBER_OF_CONTRIBUTORS;
|
||||
else
|
||||
// Before HF11 we allowed up to 4 contributors, but each can contribute multiple times
|
||||
too_many_contributions = new_contributor && contributors.size() >= MAX_NUMBER_OF_CONTRIBUTORS;
|
||||
|
||||
if (too_many_contributions)
|
||||
{
|
||||
LOG_PRINT_L1("TX: Already hit the max number of contributions: " << MAX_NUMBER_OF_CONTRIBUTORS <<
|
||||
" for contributor: " << cryptonote::get_account_address_as_str(nettype, false, parsed_contribution.address) <<
|
||||
" on height: " << block_height <<
|
||||
" for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
LOG_PRINT_L1("TX: Already hit the max number of contributions: "
|
||||
<< MAX_NUMBER_OF_CONTRIBUTORS
|
||||
<< " for contributor: " << cryptonote::get_account_address_as_str(nettype, false, stake.address)
|
||||
<< " on height: " << block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1070,13 +1083,11 @@ namespace service_nodes
|
|||
? 1 // Follow-up contributions from an existing contributor could be any size before HF11
|
||||
: get_min_node_contribution(hf_version, curinfo.staking_requirement, curinfo.total_reserved, curinfo.total_num_locked_contributions());
|
||||
|
||||
if (parsed_contribution.transferred < min_contribution)
|
||||
if (stake.transferred < min_contribution)
|
||||
{
|
||||
LOG_PRINT_L1("TX: Amount " << parsed_contribution.transferred <<
|
||||
" did not meet min " << min_contribution <<
|
||||
" for service node: " << pubkey <<
|
||||
" on height: " << block_height <<
|
||||
" for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
LOG_PRINT_L1("TX: Amount " << stake.transferred << " did not meet min " << min_contribution
|
||||
<< " for service node: " << stake.service_node_pubkey << " on height: "
|
||||
<< block_height << " for tx: " << cryptonote::get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1090,7 +1101,7 @@ namespace service_nodes
|
|||
{
|
||||
contributor_position = info.contributors.size();
|
||||
info.contributors.emplace_back();
|
||||
info.contributors.back().address = parsed_contribution.address;
|
||||
info.contributors.back().address = stake.address;
|
||||
}
|
||||
service_node_info::contributor_t& contributor = info.contributors[contributor_position];
|
||||
|
||||
|
@ -1098,10 +1109,10 @@ namespace service_nodes
|
|||
// increase total_reserved so much that it is >= staking_requirement
|
||||
uint64_t can_increase_reserved_by = info.staking_requirement - info.total_reserved;
|
||||
uint64_t max_amount = contributor.reserved + can_increase_reserved_by;
|
||||
parsed_contribution.transferred = std::min(max_amount - contributor.amount, parsed_contribution.transferred);
|
||||
stake.transferred = std::min(max_amount - contributor.amount, stake.transferred);
|
||||
|
||||
contributor.amount += parsed_contribution.transferred;
|
||||
info.total_contributed += parsed_contribution.transferred;
|
||||
contributor.amount += stake.transferred;
|
||||
info.total_contributed += stake.transferred;
|
||||
|
||||
if (contributor.amount > contributor.reserved)
|
||||
{
|
||||
|
@ -1113,10 +1124,10 @@ namespace service_nodes
|
|||
info.last_reward_transaction_index = index;
|
||||
|
||||
if (hf_version >= cryptonote::network_version_11_infinite_staking)
|
||||
for (const auto &contribution : parsed_contribution.locked_contributions)
|
||||
for (const auto &contribution : stake.locked_contributions)
|
||||
contributor.locked_contributions.push_back(contribution);
|
||||
|
||||
LOG_PRINT_L1("Contribution of " << parsed_contribution.transferred << " received for service node " << pubkey);
|
||||
LOG_PRINT_L1("Contribution of " << stake.transferred << " received for service node " << stake.service_node_pubkey);
|
||||
if (info.is_fully_funded()) {
|
||||
info.active_since_height = block_height;
|
||||
return true;
|
||||
|
|
|
@ -560,6 +560,18 @@ namespace service_nodes
|
|||
bool reg_tx_extract_fields(const cryptonote::transaction& tx, std::vector<cryptonote::account_public_address>& addresses, uint64_t& portions_for_operator, std::vector<uint64_t>& portions, uint64_t& expiration_timestamp, crypto::public_key& service_node_key, crypto::signature& signature, crypto::public_key& tx_pub_key);
|
||||
uint64_t offset_testing_quorum_height(quorum_type type, uint64_t height);
|
||||
|
||||
struct staking_components
|
||||
{
|
||||
crypto::public_key service_node_pubkey;
|
||||
cryptonote::account_public_address address;
|
||||
uint64_t transferred;
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<service_node_info::contribution_t> locked_contributions;
|
||||
};
|
||||
bool tx_get_staking_components (cryptonote::transaction_prefix const &tx_prefix, staking_components *contribution, crypto::hash const &txid);
|
||||
bool tx_get_staking_components (cryptonote::transaction const &tx, staking_components *contribution);
|
||||
bool tx_get_staking_components_and_amounts(cryptonote::network_type nettype, uint8_t hf_version, cryptonote::transaction const &tx, uint64_t block_height, staking_components *contribution);
|
||||
|
||||
struct converted_registration_args
|
||||
{
|
||||
bool success;
|
||||
|
|
|
@ -2696,7 +2696,7 @@ namespace cryptonote
|
|||
for (size_t height = start; height != end;)
|
||||
{
|
||||
uint8_t hf_version = m_core.get_hard_fork_version(height);
|
||||
if (hf_version != HardFork::INVALID_HF_VERSION_FOR_HEIGHT)
|
||||
if (hf_version != HardFork::INVALID_HF_VERSION)
|
||||
{
|
||||
auto start_quorum_iterator = static_cast<service_nodes::quorum_type>(0);
|
||||
auto end_quorum_iterator = service_nodes::max_quorum_type_for_hf(hf_version);
|
||||
|
|
|
@ -205,7 +205,7 @@ namespace
|
|||
const char* USAGE_CHECK_SPEND_PROOF("check_spend_proof <txid> <signature_file> [<message>]");
|
||||
const char* USAGE_GET_RESERVE_PROOF("get_reserve_proof (all|<amount>) [<message>]");
|
||||
const char* USAGE_CHECK_RESERVE_PROOF("check_reserve_proof <address> <signature_file> [<message>]");
|
||||
const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
|
||||
const char* USAGE_SHOW_TRANSFERS("show_transfers [in] [out] [stake] [all] [pending] [failed] [coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
|
||||
const char* USAGE_EXPORT_TRANSFERS("export_transfers [in|out|all|pending|failed] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<path>]");
|
||||
const char* USAGE_UNSPENT_OUTPUTS("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]");
|
||||
const char* USAGE_RESCAN_BC("rescan_bc [hard|soft|keep_ki] [start_height=0]");
|
||||
|
@ -2813,10 +2813,10 @@ simple_wallet::simple_wallet()
|
|||
tr(R"(Show the incoming/outgoing transfers within an optional height range.
|
||||
|
||||
Output format:
|
||||
In or Coinbase: Block Number, "block"|"in", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note
|
||||
Out: Block Number, "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, "-", Note
|
||||
Pool: "pool", "in", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note, Double Spend Note
|
||||
Pending or Failed: "failed"|"pending", "out", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, "-", Note
|
||||
In or Coinbase: Block Number, "block"|"in", Lock, Checkpointed, Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note
|
||||
Out: Block Number, "out", Lock, Checkpointed, Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, "-", Note
|
||||
Pool: "pool", "in", Lock, Checkpointed, Time, Amount, Transaction Hash, Payment ID, Subaddress Index, "-", Note, Double Spend Note
|
||||
Pending or Failed: "failed"|"pending", "out", Lock, Checkpointed, Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, "-", Note
|
||||
|
||||
* Excluding change and fee.
|
||||
** Set of address indices used as inputs in this transfer.)"));
|
||||
|
@ -8048,35 +8048,44 @@ bool simple_wallet::check_reserve_proof(const std::vector<std::string> &args)
|
|||
static bool parse_get_transfers_args(std::vector<std::string>& local_args, tools::wallet2::get_transfers_args_t& args)
|
||||
{
|
||||
// optional in/out selector
|
||||
if (local_args.size() > 0) {
|
||||
while (local_args.size() > 0)
|
||||
{
|
||||
if (local_args[0] == "in" || local_args[0] == "incoming") {
|
||||
args.out = args.pending = args.failed = false;
|
||||
args.in = args.coinbase = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else if (local_args[0] == "out" || local_args[0] == "outgoing") {
|
||||
args.in = args.pool = args.coinbase = false;
|
||||
args.out = args.stake = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else if (local_args[0] == "pending") {
|
||||
args.in = args.out = args.failed = args.coinbase = false;
|
||||
args.pending = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else if (local_args[0] == "failed") {
|
||||
args.in = args.out = args.pending = args.pool = args.coinbase = false;
|
||||
args.failed = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else if (local_args[0] == "pool") {
|
||||
args.in = args.out = args.pending = args.failed = args.coinbase = false;
|
||||
args.pool = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else if (local_args[0] == "coinbase") {
|
||||
args.in = args.out = args.pending = args.failed = args.pool = false;
|
||||
args.coinbase = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else if (local_args[0] == "all" || local_args[0] == "both") {
|
||||
else if (local_args[0] == "stake") {
|
||||
args.stake = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else if (local_args[0] == "all" || local_args[0] == "both") {
|
||||
args.in = args.out = args.stake = args.pending = args.failed = args.pool = args.coinbase = true;
|
||||
local_args.erase(local_args.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// subaddr_index
|
||||
|
@ -8120,7 +8129,7 @@ static bool parse_get_transfers_args(std::vector<std::string>& local_args, tools
|
|||
// mutates local_args as it parses and consumes arguments
|
||||
bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vector<tools::transfer_view>& transfers)
|
||||
{
|
||||
tools::wallet2::get_transfers_args_t args;
|
||||
tools::wallet2::get_transfers_args_t args = {};
|
||||
if (!parse_get_transfers_args(local_args, args))
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -51,6 +51,7 @@ using namespace epee;
|
|||
#include "rpc/core_rpc_server.h"
|
||||
#include "misc_language.h"
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_basic/hardfork.h"
|
||||
#include "multisig/multisig.h"
|
||||
#include "common/boost_serialization_helper.h"
|
||||
#include "common/command_line.h"
|
||||
|
@ -2425,7 +2426,11 @@ void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::tr
|
|||
if(unconf_it != m_unconfirmed_txs.end()) {
|
||||
if (store_tx_info()) {
|
||||
try {
|
||||
m_confirmed_txs.emplace(txid, confirmed_transfer_details(unconf_it->second, height));
|
||||
// TODO(doyle): LNS introduces tx type stake, we can use this to quickly determine if a transaction is staking
|
||||
// transaction without having to parse tx_extra.
|
||||
bool stake = service_nodes::tx_get_staking_components(tx, nullptr /*stake*/);
|
||||
tools::pay_type pay_type = stake ? tools::pay_type::stake : tools::pay_type::out;
|
||||
m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details(unconf_it->second, height)));
|
||||
}
|
||||
catch (...) {
|
||||
// can fail if the tx has unexpected input types
|
||||
|
@ -2462,6 +2467,9 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
|
|||
}
|
||||
entry.first->second.m_subaddr_account = subaddr_account;
|
||||
entry.first->second.m_subaddr_indices = subaddr_indices;
|
||||
|
||||
bool stake = service_nodes::tx_get_staking_components(tx, nullptr /*stake*/);
|
||||
entry.first->second.m_pay_type = stake ? tools::pay_type::stake : tools::pay_type::out;
|
||||
}
|
||||
|
||||
entry.first->second.m_rings.clear();
|
||||
|
@ -5973,7 +5981,7 @@ transfer_view wallet2::wallet2::make_transfer_view(const crypto::hash &txid, con
|
|||
td.address = d.original.empty() ? get_account_address_as_str(nettype(), d.is_subaddress, d.addr) : d.original;
|
||||
}
|
||||
|
||||
result.pay_type = pay_type::out;
|
||||
result.pay_type = pd.m_pay_type;
|
||||
result.subaddr_index = { pd.m_subaddr_account, 0 };
|
||||
for (uint32_t i: pd.m_subaddr_indices)
|
||||
result.subaddr_indices.push_back({pd.m_subaddr_account, i});
|
||||
|
@ -6008,7 +6016,7 @@ transfer_view wallet2::make_transfer_view(const crypto::hash &txid, const tools:
|
|||
td.address = d.original.empty() ? get_account_address_as_str(nettype(), d.is_subaddress, d.addr) : d.original;
|
||||
}
|
||||
|
||||
result.pay_type = pay_type::unspecified;
|
||||
result.pay_type = pd.m_pay_type;
|
||||
result.type = is_failed ? "failed" : "pending";
|
||||
result.subaddr_index = { pd.m_subaddr_account, 0 };
|
||||
for (uint32_t i: pd.m_subaddr_indices)
|
||||
|
@ -6057,6 +6065,9 @@ void wallet2::get_transfers(get_transfers_args_t args, std::vector<transfer_view
|
|||
args.max_height = std::min<uint64_t>(args.max_height, CRYPTONOTE_MAX_BLOCK_NUMBER);
|
||||
}
|
||||
|
||||
int args_count = args.in + args.out + args.stake + args.pending + args.failed + args.pool + args.coinbase;
|
||||
if (args_count == 0) args.in = args.out = args.stake = args.pending = args.failed = args.pool = args.coinbase = true;
|
||||
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> in;
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> out;
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> pending_or_failed;
|
||||
|
@ -6072,7 +6083,7 @@ void wallet2::get_transfers(get_transfers_args_t args, std::vector<transfer_view
|
|||
size += in.size();
|
||||
}
|
||||
|
||||
if (args.out)
|
||||
if (args.out || args.stake)
|
||||
{
|
||||
get_payments_out(out, args.min_height, args.max_height, account_index, args.subaddr_indices);
|
||||
size += out.size();
|
||||
|
@ -6095,7 +6106,14 @@ void wallet2::get_transfers(get_transfers_args_t args, std::vector<transfer_view
|
|||
for (const auto &i : in)
|
||||
transfers.push_back(make_transfer_view(i.second.m_tx_hash, i.first, i.second));
|
||||
for (const auto &o : out)
|
||||
transfers.push_back(make_transfer_view(o.first, o.second));
|
||||
{
|
||||
bool add_entry = true;
|
||||
if (args.stake && args_count == 1)
|
||||
add_entry = o.second.m_pay_type == tools::pay_type::stake;
|
||||
|
||||
if (add_entry)
|
||||
transfers.push_back(make_transfer_view(o.first, o.second));
|
||||
}
|
||||
for (const auto &pof : pending_or_failed)
|
||||
{
|
||||
bool is_failed = pof.second.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
|
||||
|
@ -6634,6 +6652,8 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amo
|
|||
utd.m_timestamp = time(NULL);
|
||||
utd.m_subaddr_account = subaddr_account;
|
||||
utd.m_subaddr_indices = subaddr_indices;
|
||||
bool stake = service_nodes::tx_get_staking_components(tx, nullptr /*stake*/);
|
||||
utd.m_pay_type = stake ? tools::pay_type::stake : tools::pay_type::out;
|
||||
for (const auto &in: tx.vin)
|
||||
{
|
||||
if (in.type() != typeid(cryptonote::txin_to_key))
|
||||
|
@ -13389,10 +13409,12 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
|
|||
{
|
||||
const transfer_details& td = m_transfers[n];
|
||||
confirmed_transfer_details pd;
|
||||
pd.m_change = (uint64_t)-1; // change is unknown
|
||||
pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
|
||||
pd.m_block_height = 0; // spent block height is unknown
|
||||
const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
|
||||
pd.m_change = (uint64_t)-1; // change is unknown
|
||||
pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
|
||||
pd.m_block_height = 0; // spent block height is unknown
|
||||
const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
|
||||
bool stake = service_nodes::tx_get_staking_components(td.m_tx, nullptr /*stake*/, td.m_txid);
|
||||
pd.m_pay_type = stake ? tools::pay_type::stake : tools::pay_type::out;
|
||||
m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
|
||||
}
|
||||
PERF_TIMER_STOP(import_key_images_G);
|
||||
|
|
|
@ -511,6 +511,7 @@ private:
|
|||
uint32_t m_subaddr_account; // subaddress account of your wallet to be used in this transfer
|
||||
std::set<uint32_t> m_subaddr_indices; // set of address indices used as inputs in this transfer
|
||||
std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> m_rings; // relative
|
||||
tools::pay_type m_pay_type = tools::pay_type::out;
|
||||
};
|
||||
|
||||
struct confirmed_transfer_details
|
||||
|
@ -527,10 +528,25 @@ private:
|
|||
uint32_t m_subaddr_account; // subaddress account of your wallet to be used in this transfer
|
||||
std::set<uint32_t> m_subaddr_indices; // set of address indices used as inputs in this transfer
|
||||
std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> m_rings; // relative
|
||||
tools::pay_type m_pay_type = tools::pay_type::out;
|
||||
|
||||
confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0), m_subaddr_account((uint32_t)-1) {}
|
||||
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
|
||||
m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time), m_unlock_times(utd.m_tx.output_unlock_times), m_subaddr_account(utd.m_subaddr_account), m_subaddr_indices(utd.m_subaddr_indices), m_rings(utd.m_rings) {}
|
||||
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height)
|
||||
: m_amount_in(utd.m_amount_in)
|
||||
, m_amount_out(utd.m_amount_out)
|
||||
, m_change(utd.m_change)
|
||||
, m_block_height(height)
|
||||
, m_dests(utd.m_dests)
|
||||
, m_payment_id(utd.m_payment_id)
|
||||
, m_timestamp(utd.m_timestamp)
|
||||
, m_unlock_time(utd.m_tx.unlock_time)
|
||||
, m_unlock_times(utd.m_tx.output_unlock_times)
|
||||
, m_subaddr_account(utd.m_subaddr_account)
|
||||
, m_subaddr_indices(utd.m_subaddr_indices)
|
||||
, m_rings(utd.m_rings)
|
||||
, m_pay_type(utd.m_pay_type)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct tx_construction_data
|
||||
|
@ -997,12 +1013,13 @@ private:
|
|||
|
||||
struct get_transfers_args_t
|
||||
{
|
||||
bool in = true;
|
||||
bool out = true;
|
||||
bool pending = true;
|
||||
bool failed = true;
|
||||
bool pool = true;
|
||||
bool coinbase = true;
|
||||
bool in = false;
|
||||
bool out = false;
|
||||
bool stake = false;
|
||||
bool pending = false;
|
||||
bool failed = false;
|
||||
bool pool = false;
|
||||
bool coinbase = false;
|
||||
bool filter_by_height = false;
|
||||
uint64_t min_height = 0;
|
||||
uint64_t max_height = CRYPTONOTE_MAX_BLOCK_NUMBER;
|
||||
|
@ -1828,8 +1845,8 @@ BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
|
|||
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 6)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 7)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 9)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 8)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0)
|
||||
|
@ -1934,6 +1951,8 @@ namespace boost
|
|||
{
|
||||
a & x.m_tx;
|
||||
}
|
||||
if (ver < 9)
|
||||
x.m_pay_type = tools::pay_type::out;
|
||||
if (ver < 1)
|
||||
return;
|
||||
a & x.m_dests;
|
||||
|
@ -1966,6 +1985,9 @@ namespace boost
|
|||
if (ver < 8)
|
||||
return;
|
||||
a & x.m_rings;
|
||||
if (ver < 9)
|
||||
return;
|
||||
a & x.m_pay_type;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
@ -1975,6 +1997,8 @@ namespace boost
|
|||
a & x.m_amount_out;
|
||||
a & x.m_change;
|
||||
a & x.m_block_height;
|
||||
if (ver < 8)
|
||||
x.m_pay_type = tools::pay_type::out;
|
||||
if (ver < 1)
|
||||
return;
|
||||
a & x.m_dests;
|
||||
|
@ -2017,6 +2041,9 @@ namespace boost
|
|||
if (ver < 7)
|
||||
return;
|
||||
a & x.m_unlock_times;
|
||||
if (ver < 8)
|
||||
return;
|
||||
a & x.m_pay_type;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
@ -2035,12 +2062,13 @@ namespace boost
|
|||
if (ver < 3)
|
||||
x.m_fee = 0;
|
||||
if (ver < 4)
|
||||
x.m_type = tools::pay_type::unspecified;
|
||||
x.m_type = tools::pay_type::in;
|
||||
if (ver < 5)
|
||||
x.m_unmined_blink = false;
|
||||
if (ver < 6)
|
||||
x.m_was_blink = false;
|
||||
|
||||
|
||||
if (ver < 1) return;
|
||||
a & x.m_timestamp;
|
||||
if (ver < 2) return;
|
||||
|
|
|
@ -83,6 +83,8 @@ namespace tools
|
|||
bool mempool; // States if the transaction is sitting in the mempool. `true if the transaction is, `false` if not.
|
||||
uint32_t mixin; // The number of other signatures (aside from yours) in the ring signature that authorises the transaction.
|
||||
|
||||
// TODO(loki): Also the pay type, is it a stake? But since this is undocumented and not used, not implemented yet
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(id)
|
||||
KV_SERIALIZE(hash)
|
||||
|
|
|
@ -2324,12 +2324,13 @@ namespace tools
|
|||
return false;
|
||||
}
|
||||
|
||||
wallet2::get_transfers_args_t args;
|
||||
wallet2::get_transfers_args_t args = {};
|
||||
args.in = req.in;
|
||||
args.out = req.out;
|
||||
args.pending = req.pending;
|
||||
args.failed = req.failed;
|
||||
args.pool = req.pool;
|
||||
args.stake = req.stake;
|
||||
args.filter_by_height = req.filter_by_height;
|
||||
args.min_height = req.min_height;
|
||||
args.max_height = req.max_height;
|
||||
|
@ -2351,7 +2352,7 @@ namespace tools
|
|||
{
|
||||
res.in.push_back(entry);
|
||||
}
|
||||
else if (entry.pay_type == tools::pay_type::out)
|
||||
else if (entry.pay_type == tools::pay_type::out || entry.pay_type == tools::pay_type::stake)
|
||||
{
|
||||
res.out.push_back(entry);
|
||||
}
|
||||
|
@ -2377,10 +2378,11 @@ namespace tools
|
|||
wallet2::get_transfers_args_t args;
|
||||
args.in = req.in;
|
||||
args.out = req.out;
|
||||
args.stake = req.stake;
|
||||
args.pending = req.pending;
|
||||
args.failed = req.failed;
|
||||
args.pool = req.pool;
|
||||
// args.coinbase = req.coinbase;
|
||||
args.coinbase = req.coinbase;
|
||||
args.filter_by_height = req.filter_by_height;
|
||||
args.min_height = req.min_height;
|
||||
args.max_height = req.max_height;
|
||||
|
|
|
@ -1561,16 +1561,18 @@ namespace wallet_rpc
|
|||
};
|
||||
|
||||
LOKI_RPC_DOC_INTROSPECT
|
||||
// Returns a list of transfers.
|
||||
// Returns a list of transfers, by default all transfer types are included. If all requested type fields are false, then all transfers will be queried.
|
||||
struct COMMAND_RPC_GET_TRANSFERS
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
bool in; // (Optional) Include incoming transfers.
|
||||
bool out; // (Optional) Include outgoing transfers.
|
||||
bool stake; // (Optional) Include outgoing stakes.
|
||||
bool pending; // (Optional) Include pending transfers.
|
||||
bool failed; // (Optional) Include failed transfers.
|
||||
bool pool; // (Optional) Include transfers from the daemon's transaction pool.
|
||||
bool coinbase; // (Optional) Include transfers from the daemon's transaction pool.
|
||||
|
||||
bool filter_by_height; // (Optional) Filter transfers by block height.
|
||||
uint64_t min_height; // (Optional) Minimum block height to scan for transfers, if filtering by height is enabled.
|
||||
|
@ -1580,11 +1582,13 @@ namespace wallet_rpc
|
|||
bool all_accounts; // If true, return transfers for all accounts, subaddr_indices and account_index are ignored
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(in);
|
||||
KV_SERIALIZE(out);
|
||||
KV_SERIALIZE(pending);
|
||||
KV_SERIALIZE(failed);
|
||||
KV_SERIALIZE(pool);
|
||||
KV_SERIALIZE_OPT(in, true);
|
||||
KV_SERIALIZE_OPT(out, true);
|
||||
KV_SERIALIZE_OPT(stake, true);
|
||||
KV_SERIALIZE_OPT(pending, true);
|
||||
KV_SERIALIZE_OPT(failed, true);
|
||||
KV_SERIALIZE_OPT(pool, true);
|
||||
KV_SERIALIZE_OPT(coinbase, true);
|
||||
KV_SERIALIZE(filter_by_height);
|
||||
KV_SERIALIZE(min_height);
|
||||
KV_SERIALIZE_OPT(max_height, (uint64_t)CRYPTONOTE_MAX_BLOCK_NUMBER);
|
||||
|
|
Loading…
Reference in a new issue