diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 2150e9cd6..ad640545c 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -847,7 +847,7 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector> outputs; for (auto& o : b.protocol_tx.vout) { @@ -1514,6 +1505,9 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, return false; } } + + // Maintain a count of outputs that we have verified + std::vector outputs_verified; size_t tx_index = 0; // Iterate over the block's transaction hashes, grabbing each @@ -1530,69 +1524,119 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, return false; } - // Check to see if the TX is a conversion or not - if (tx->type != cryptonote::transaction_type::CONVERT) { - // Only conversion (and failed conversion, aka refund) TXs need to be verified - skip this TX - continue; - } - /* - // Verify that the TX has an output in the protocol_tx to verify - if (outputs.count(tx->return_address) != 1) { - LOG_ERROR("Failed to locate output for conversion TX id " << tx->hash << " - rejecting block"); - return false; - } + if (hf_version >= HF_VERSION_ENABLE_CONVERT) { + + // Check to see if the TX is a conversion or not + if (tx->type != cryptonote::transaction_type::CONVERT) { + // Only conversion (and failed conversion, aka refund) TXs need to be verified - skip this TX + continue; + } - // Get the output information - std::string output_asset_type; - uint64_t output_amount; - uint64_t output_unlock_time; - std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[tx->return_address]; - - // Verify the asset_type - if (tx->source_asset_type == output_asset_type) { - // Check the amount for REFUND - if (tx->amount_burnt != output_amount) { - LOG_ERROR("Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block"); + // Verify that the TX has an output in the protocol_tx to verify + if (outputs.count(tx->return_address) != 1) { + LOG_ERROR("Block at height: " << height << " - Failed to locate output for conversion TX id " << tx->hash << " - rejecting block"); return false; } - } else if (tx->destination_asset_type == output_asset_type) { - // Check the amount for CONVERT - // Verify the amount of the conversion - uint64_t amount_minted_check = 0, amount_slippage_check = 0; - bool ok = cryptonote::calculate_conversion(tx->source_asset_type, tx->destination_asset_type, tx->amount_burnt, tx->amount_slippage_limit, amount_minted_check, amount_slippage_check, circ_supply, b.pricing_record, hf_version); - if (!ok) { - LOG_ERROR("Failed to calculate conversion for TX id " << tx->hash << " - rejecting block"); + // Get the output information + std::string output_asset_type; + uint64_t output_amount; + uint64_t output_unlock_time; + std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[tx->return_address]; + + // Verify the asset_type + if (tx->source_asset_type == output_asset_type) { + // Check the amount for REFUND + if (tx->amount_burnt != output_amount) { + LOG_ERROR("Block at height: " << height << " - Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block"); + return false; + } + + // Verified the refund successfully + outputs_verified.push_back(tx->return_address); + + } else if (tx->destination_asset_type == output_asset_type) { + // Check the amount for CONVERT + + // Verify the amount of the conversion + uint64_t amount_minted_check = 0, amount_slippage_check = 0; + bool ok = cryptonote::calculate_conversion(tx->source_asset_type, tx->destination_asset_type, tx->amount_burnt, tx->amount_slippage_limit, amount_minted_check, amount_slippage_check, circ_supply, b.pricing_record, hf_version); + if (!ok) { + LOG_ERROR("Block at height: " << height << " - Failed to calculate conversion for TX id " << tx->hash << " - rejecting block"); + return false; + } + if (amount_minted_check != output_amount) { + LOG_ERROR("Block at height: " << height << " - Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block"); + return false; + } + + // Verified the conversion successfully + outputs_verified.push_back(tx->return_address); + + } else { + LOG_ERROR("Block at height: " << height << " - Output asset type incorrect: source " << tx->source_asset_type << ", dest " << tx->destination_asset_type << ", got " << output_asset_type << " - rejecting block"); return false; } - if (amount_minted_check != output_amount) { - LOG_ERROR("Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block"); - return false; - } - } else { - LOG_ERROR("Output asset type incorrect: source " << tx->source_asset_type << ", dest " << tx->destination_asset_type << ", got " << output_asset_type << " - rejecting block"); - return false; } - */ } - // Now consider the payouts from matured YIELD transactions - - // Get the data for the block that matured this time - cryptonote::yield_block_info ybi_matured; + // Can we have matured STAKE transactions yet? uint64_t lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD; - uint64_t start_height = (height > lock_period) ? height - lock_period - 1 : 0; - bool ok = get_ybi_entry(start_height, ybi_matured); - if (ok && ybi_matured.locked_coins_this_block > 0) { - - // Iterate over the cached data for block yield, calculating the yield payouts due - std::vector> yield_payouts; - if (!calculate_yield_payouts(start_height, yield_payouts)) { - LOG_ERROR("Failed to obtain yield payout information - aborting"); - return false; + if (height > lock_period) { + + // Yes - Get the staking data for the block that matured this time + cryptonote::yield_block_info ybi_matured; + uint64_t matured_height = height - lock_period - 1; + bool ok = get_ybi_entry(matured_height, ybi_matured); + if (ok && ybi_matured.locked_coins_this_block > 0) { + + // Iterate over the cached data for block yield, calculating the yield payouts due + std::vector> yield_payouts; + if (!calculate_yield_payouts(matured_height, yield_payouts)) { + LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting"); + return false; + } + + // Iterate the yield payouts, verifying as we go + for (const auto& payout: yield_payouts) { + + // Do we have a singular matching output in tx.vout? + if (outputs.count(payout.first.return_address) != 1) { + LOG_ERROR("Block at height: " << height << " - Failed to locate output for matured TX id " << payout.first.tx_hash << " - rejecting block"); + return false; + } + + // Get the output information + std::string output_asset_type; + uint64_t output_amount; + uint64_t output_unlock_time; + std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[payout.first.return_address]; + + // Verify the asset type - must be SAL + if (output_asset_type != "SAL") { + LOG_ERROR("Block at height: " << height << " - Incorrect output asset type for matured TX id " << payout.first.tx_hash << " - rejecting block"); + return false; + } + + // Verify the amount + if (output_amount != payout.second) { + LOG_ERROR("Block at height: " << height << " - Incorrect output amount for matured TX id " << payout.first.tx_hash << " - rejecting block"); + return false; + } + + // Amount and return_address match our expectation + outputs_verified.push_back(payout.first.return_address); + } } } - + + // All candidates have been evaluated - make sure there are no other outputs that have not been catered for + if (outputs.size() != outputs_verified.size()) { + LOG_ERROR("Block at height: " << height << " - Incorrect number of outputs - expected " << outputs_verified.size() << " but received " << outputs.size() << " - rejecting block"); + return false; + } + + // Everything checks out return true; } //------------------------------------------------------------------ diff --git a/src/version.cpp.in b/src/version.cpp.in index 0d6faa520..e85e69973 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_SALVIUM_VERSION_TAG "7f6b8da" -#define DEF_SALVIUM_VERSION "0.2.1" +#define DEF_SALVIUM_VERSION "0.2.3" #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" #define DEF_MONERO_VERSION "0.18.3.3" #define DEF_MONERO_RELEASE_NAME "Zero" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bd77b0402..04153f675 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2179,9 +2179,15 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons // Calculate the subaddress public_key (P_change) crypto::public_key pk_change = crypto::null_pkey; - bool ok = m_account.get_device().derive_subaddress_public_key(output_public_key, tx_scan_info.received->derivation, i, pk_change); - THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to derive subaddress public key for CONVERT/YIELD/RETURN TX"); - + if (tx.type == cryptonote::transaction_type::PROTOCOL) { + // Force the output index to be 0 in all cases, despite where it appears in this TX + bool ok = m_account.get_device().derive_subaddress_public_key(output_public_key, tx_scan_info.received->derivation, 0, pk_change); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to derive subaddress public key for CONVERT/YIELD TX"); + } else { + bool ok = m_account.get_device().derive_subaddress_public_key(output_public_key, tx_scan_info.received->derivation, i, pk_change); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to derive subaddress public key for RETURN TX"); + } + // Flag to indicate this is a TX that uses a return_address bool use_od = false; cryptonote::origin_data od = AUTO_VAL_INIT(od); @@ -2194,6 +2200,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons use_od = true; od.tx_pub_key = get_tx_pub_key_from_extra(td_origin.m_tx); od.output_index = td_origin.m_internal_output_index; + od.tx_type = td_origin.m_tx.type; } if (m_multisig) @@ -2215,12 +2222,14 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); } + /* if (tx_scan_info.money_transfered == 0) { MERROR("Invalid output amount, skipping"); tx_scan_info.error = true; return; } + */ outs.push_back(i); THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index][tx_scan_info.asset_type] >= std::numeric_limits::max() - tx_scan_info.money_transfered, error::wallet_internal_error, "Overflow in received amounts"); @@ -2993,7 +3002,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (tx.type == cryptonote::transaction_type::PROTOCOL) { if (td_origin_idx != ((uint64_t)-1)) { // Get the origin TD information - payment.m_amount = payment.m_amounts[0]; + payment.m_amount = payment.m_amounts.empty() ? 0 : payment.m_amounts[0]; payment.m_tx_type = m_transfers[td_origin_idx].m_tx.type; payment.m_fee = m_transfers[td_origin_idx].m_tx.amount_burnt; } else {