//--------------------------------------------------------------- bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce) { CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max"); size_t start_pos = tx_extra.size(); tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); //write tag tx_extra[start_pos] = TX_EXTRA_NONCE; //write len ++start_pos; tx_extra[start_pos] = static_cast<uint8_t>(extra_nonce.size()); //write data ++start_pos; memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); return true; }
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) { std::stringstream ss; ss << tx_blob; binary_archive<false> ba(ss); bool r = ::serialization::serialize(ba, tx); if (!r) { return false; } //TODO: validate tx crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); get_transaction_prefix_hash(tx, tx_prefix_hash); return true; }
//----------------------------------------------------------------------------------------------- bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { if (block_blob.size() > m_currency.maxBlockBlobSize()) { LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); bvc.m_verifivation_failed = true; return false; } Block b = AUTO_VAL_INIT(b); if (!parse_and_validate_block_from_blob(block_blob, b)) { LOG_PRINT_L0("Failed to parse and validate new block"); bvc.m_verifivation_failed = true; return false; } return handle_incoming_block(b, bvc, control_miner, relay_block); }
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce) { if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { return false; } size_t start_pos = tx_extra.size(); tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); //write tag tx_extra[start_pos] = TX_EXTRA_NONCE; //write len ++start_pos; tx_extra[start_pos] = static_cast<uint8_t>(extra_nonce.size()); //write data ++start_pos; memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); return true; }
//--------------------------------------------------------------- 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) { tx.vin.clear(); tx.vout.clear(); tx.extra.clear(); keypair txkey = keypair::generate(); add_tx_pub_key_to_extra(tx, txkey.pub); if(!extra_nonce.empty()) if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) return false; txin_gen in; in.height = height; uint64_t block_reward; if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward)) { LOG_PRINT_L0("Block is too big"); return false; } #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) LOG_PRINT_L1("Creating block template: reward " << block_reward << ", fee " << fee) #endif block_reward += fee; std::vector<uint64_t> out_amounts; decompose_amount_into_digits(block_reward, DEFAULT_FEE, [&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"); while (max_outs < out_amounts.size()) { out_amounts[out_amounts.size() - 2] += out_amounts.back(); out_amounts.resize(out_amounts.size() - 1); } 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 << ")"); txout_to_key tk; tk.key = out_eph_public_key; tx_out out; summary_amounts += out.amount = out_amounts[no]; out.target = tk; tx.vout.push_back(out); } CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); tx.version = CURRENT_TRANSACTION_VERSION; //lock tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; tx.vin.push_back(in); //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); return true; }
//--------------------------------------------------------------- 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) { tx.vin.clear(); tx.vout.clear(); tx.extra.clear(); keypair txkey = keypair::generate(); add_tx_pub_key_to_extra(tx, txkey.pub); if(!extra_nonce.empty()) if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) return false; txin_gen in; in.height = height; uint64_t block_reward; if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) { LOG_PRINT_L0("Block is too big"); return false; } #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) LOG_PRINT_L1("Creating block template: reward " << block_reward << ", fee " << fee); #endif block_reward += fee; // 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 << ")"); txout_to_key tk; tk.key = out_eph_public_key; tx_out out; summary_amounts += out.amount = out_amounts[no]; out.target = tk; tx.vout.push_back(out); } CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); if (hard_fork_version >= 4) tx.version = 2; else tx.version = 1; //lock tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; tx.vin.push_back(in); tx.invalidate_hashes(); //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); return true; }
bool Currency::constructMinerTx(size_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, const blobdata& extraNonce/* = blobdata()*/, size_t maxOuts/* = 1*/) const { tx.vin.clear(); tx.vout.clear(); tx.extra.clear(); KeyPair txkey = KeyPair::generate(); add_tx_pub_key_to_extra(tx, txkey.pub); if (!extraNonce.empty()) { if (!add_extra_nonce_to_tx_extra(tx.extra, extraNonce)) { return false; } } TransactionInputGenerate in; in.height = height; uint64_t blockReward; int64_t emissionChange; if (!getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, blockReward, emissionChange)) { LOG_PRINT_L0("Block is too big"); return false; } #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) LOG_PRINT_L1("Creating block template: reward " << blockReward << ", fee " << fee); #endif std::vector<uint64_t> outAmounts; decompose_amount_into_digits(blockReward, m_defaultDustThreshold, [&outAmounts](uint64_t a_chunk) { outAmounts.push_back(a_chunk); }, [&outAmounts](uint64_t a_dust) { outAmounts.push_back(a_dust); }); CHECK_AND_ASSERT_MES(1 <= maxOuts, false, "max_out must be non-zero"); while (maxOuts < outAmounts.size()) { outAmounts[outAmounts.size() - 2] += outAmounts.back(); outAmounts.resize(outAmounts.size() - 1); } uint64_t summaryAmounts = 0; for (size_t no = 0; no < outAmounts.size(); no++) { crypto::key_derivation derivation = boost::value_initialized<crypto::key_derivation>(); crypto::public_key outEphemeralPubKey = boost::value_initialized<crypto::public_key>(); bool r = crypto::generate_key_derivation(minerAddress.m_viewPublicKey, txkey.sec, derivation); CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << minerAddress.m_viewPublicKey << ", " << txkey.sec << ")"); r = crypto::derive_public_key(derivation, no, minerAddress.m_spendPublicKey, outEphemeralPubKey); CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< minerAddress.m_spendPublicKey << ")"); TransactionOutputToKey tk; tk.key = outEphemeralPubKey; TransactionOutput out; summaryAmounts += out.amount = outAmounts[no]; out.target = tk; tx.vout.push_back(out); } CHECK_AND_ASSERT_MES(summaryAmounts == blockReward, false, "Failed to construct miner tx, summaryAmounts = " << summaryAmounts << " not equal blockReward = " << blockReward); tx.version = CURRENT_TRANSACTION_VERSION; //lock tx.unlockTime = height + m_minedMoneyUnlockWindow; tx.vin.push_back(in); return true; }
//--------------------------------------------------------------- void get_blob_hash(const blobdata& blob, crypto::hash& res) { cn_fast_hash(blob.data(), blob.size(), res); }
void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) { extra_nonce.clear(); extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); const uint8_t* payment_id_ptr = reinterpret_cast<const uint8_t*>(&payment_id); std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); }