// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see .
#include "BlockchainExplorerDataBuilder.h"
#include
#include
#include "Common/StringTools.h"
#include "cryptonote_core/cryptonote_format_utils.h"
namespace CryptoNote {
BlockchainExplorerDataBuilder::BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) :
core(core),
protocol(protocol)
{
}
bool BlockchainExplorerDataBuilder::getMixin(const Transaction& transaction, uint64_t& mixin) {
mixin = 0;
for (const TransactionInput& txin : transaction.vin) {
if (txin.type() != typeid(TransactionInputToKey)) {
continue;
}
uint64_t currentMixin = boost::get(txin).keyOffsets.size();
if (currentMixin > mixin) {
mixin = currentMixin;
}
}
return true;
}
bool BlockchainExplorerDataBuilder::getPaymentId(const Transaction& transaction, crypto::hash& paymentId) {
std::vector txExtraFields;
parse_tx_extra(transaction.extra, txExtraFields);
tx_extra_nonce extraNonce;
if (!find_tx_extra_field_by_type(txExtraFields, extraNonce)) {
return false;
}
return get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId);
}
bool BlockchainExplorerDataBuilder::fillTxExtra(const std::vector& rawExtra, TransactionExtraDetails& extraDetails) {
extraDetails.raw = rawExtra;
std::vector txExtraFields;
parse_tx_extra(rawExtra, txExtraFields);
for (const tx_extra_field& field : txExtraFields) {
if (typeid(tx_extra_padding) == field.type()) {
extraDetails.padding.push_back(std::move(boost::get(field).size));
}
else if (typeid(tx_extra_pub_key) == field.type()) {
extraDetails.publicKey.push_back(std::move(reinterpret_cast&>(boost::get(field).pub_key)));
}
else if (typeid(tx_extra_nonce) == field.type()) {
extraDetails.nonce.push_back(std::move(Common::toHex(boost::get(field).nonce.data(), boost::get(field).nonce.size())));
}
}
return true;
}
size_t BlockchainExplorerDataBuilder::median(std::vector& v) {
if(v.empty())
return boost::value_initialized();
if(v.size() == 1)
return v[0];
size_t n = (v.size()) / 2;
std::sort(v.begin(), v.end());
//nth_element(v.begin(), v.begin()+n-1, v.end());
if(v.size()%2)
{//1, 3, 5...
return v[n];
}else
{//2, 4, 6...
return (v[n-1] + v[n])/2;
}
}
bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDetails& blockDetails) {
crypto::hash hash = get_block_hash(block);
blockDetails.majorVersion = block.majorVersion;
blockDetails.minorVersion = block.minorVersion;
blockDetails.timestamp = block.timestamp;
blockDetails.prevBlockHash = reinterpret_cast&>(block.prevId);
blockDetails.nonce = block.nonce;
blockDetails.hash = reinterpret_cast&>(hash);
blockDetails.reward = 0;
for (const TransactionOutput& out : block.minerTx.vout) {
blockDetails.reward += out.amount;
}
if (block.minerTx.vin.front().type() != typeid(TransactionInputGenerate))
return false;
blockDetails.height = boost::get(block.minerTx.vin.front()).height;
crypto::hash tmpHash = core.getBlockIdByHeight(blockDetails.height);
blockDetails.isOrphaned = hash != tmpHash;
if (!core.getBlockDifficulty(blockDetails.height, blockDetails.difficulty)) {
return false;
}
std::vector blocksSizes;
if (!core.getBackwardBlocksSizes(blockDetails.height, blocksSizes, parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW)) {
return false;
}
blockDetails.sizeMedian = median(blocksSizes);
size_t blockSize = 0;
if (!core.getBlockSize(hash, blockSize)) {
return false;
}
blockDetails.transactionsCumulativeSize = blockSize;
size_t blokBlobSize = get_object_blobsize(block);
size_t minerTxBlobSize = get_object_blobsize(block.minerTx);
blockDetails.blockSize = blokBlobSize + blockDetails.transactionsCumulativeSize - minerTxBlobSize;
if (!core.getAlreadyGeneratedCoins(hash, blockDetails.alreadyGeneratedCoins)) {
return false;
}
blockDetails.alreadyGeneratedTransactions = 0; //TODO
uint64_t prevBlockGeneratedCoins = 0;
if (blockDetails.height > 0)
{
if (!core.getAlreadyGeneratedCoins(block.prevId, prevBlockGeneratedCoins)) {
return false;
}
}
uint64_t maxReward = 0;
uint64_t currentReward = 0;
int64_t emissionChange = 0;
bool penalizeFee = block.majorVersion >= 2;
if(!core.getBlockReward(blockDetails.sizeMedian, 0, prevBlockGeneratedCoins, 0, penalizeFee, maxReward, emissionChange))
{
return false;
}
if(!core.getBlockReward(blockDetails.sizeMedian, blockDetails.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, penalizeFee, currentReward, emissionChange))
{
return false;
}
blockDetails.baseReward = maxReward;
if (maxReward == 0 && currentReward == 0)
{
blockDetails.penalty = static_cast(0);
}
else
{
if (maxReward < currentReward) {
return false;
}
blockDetails.penalty = static_cast(maxReward - currentReward) / static_cast(maxReward);
}
blockDetails.transactions.reserve(block.txHashes.size() + 1);
TransactionDetails transactionDetails;
if (!fillTransactionDetails(block.minerTx, transactionDetails, block.timestamp)) {
return false;
}
blockDetails.transactions.push_back(std::move(transactionDetails));
std::list found;
std::list missed;
core.getTransactions(block.txHashes, found, missed);
if (found.size() != block.txHashes.size()) {
return false;
}
blockDetails.totalFeeAmount = 0;
for (const Transaction& tx : found) {
TransactionDetails transactionDetails;
if (!fillTransactionDetails(tx, transactionDetails, block.timestamp)) {
return false;
}
blockDetails.transactions.push_back(std::move(transactionDetails));
blockDetails.totalFeeAmount += transactionDetails.fee;
}
return true;
}
bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& transaction, TransactionDetails& transactionDetails, uint64_t timestamp) {
crypto::hash hash = get_transaction_hash(transaction);
transactionDetails.hash = reinterpret_cast&>(hash);
transactionDetails.timestamp = timestamp;
crypto::hash blockHash;
uint64_t blockHeight;
if (!core.getBlockContainingTx(hash, blockHash, blockHeight)) {
transactionDetails.inBlockchain = false;
transactionDetails.blockHeight = boost::value_initialized();
transactionDetails.blockHash = boost::value_initialized>();
} else {
transactionDetails.inBlockchain = true;
transactionDetails.blockHeight = blockHeight;
transactionDetails.blockHash = reinterpret_cast&>(blockHash);
if (timestamp == 0) {
Block block;
if (!core.getBlockByHash(blockHash, block)) {
return false;
}
transactionDetails.timestamp = block.timestamp;
}
}
transactionDetails.size = get_object_blobsize(transaction);
transactionDetails.unlockTime = transaction.unlockTime;
transactionDetails.totalOutputsAmount = get_outs_money_amount(transaction);
uint64_t inputsAmount;
if (!get_inputs_money_amount(transaction, inputsAmount)) {
return false;
}
transactionDetails.totalInputsAmount = inputsAmount;
if (transaction.vin.size() > 0 && transaction.vin.front().type() == typeid(TransactionInputGenerate)) {
//It's gen transaction
transactionDetails.fee = 0;
transactionDetails.mixin = 0;
} else {
uint64_t fee;
if (!get_tx_fee(transaction, fee)) {
return false;
}
transactionDetails.fee = fee;
uint64_t mixin;
if (!getMixin(transaction, mixin)) {
return false;
}
transactionDetails.mixin = mixin;
}
crypto::hash paymentId;
if (getPaymentId(transaction, paymentId)) {
transactionDetails.paymentId = reinterpret_cast&>(paymentId);
}
else {
transactionDetails.paymentId = boost::value_initialized>();
}
fillTxExtra(transaction.extra, transactionDetails.extra);
transactionDetails.signatures.reserve(transaction.signatures.size());
for (const std::vector& signatures : transaction.signatures) {
std::vector> signaturesDetails;
signaturesDetails.reserve(signatures.size());
for (const crypto::signature& signature : signatures) {
signaturesDetails.push_back(std::move(reinterpret_cast&>(signature)));
}
transactionDetails.signatures.push_back(std::move(signaturesDetails));
}
transactionDetails.inputs.reserve(transaction.vin.size());
for (const TransactionInput& txIn : transaction.vin) {
TransactionInputDetails txInDetails;
if (txIn.type() == typeid(TransactionInputGenerate)) {
TransactionInputGenerateDetails txInGenDetails;
txInGenDetails.height = boost::get(txIn).height;
txInDetails.amount = 0;
for (const TransactionOutput& out : transaction.vout) {
txInDetails.amount += out.amount;
}
txInDetails.input = txInGenDetails;
} else if (txIn.type() == typeid(TransactionInputToKey)) {
TransactionInputToKeyDetails txInToKeyDetails;
const TransactionInputToKey& txInToKey = boost::get(txIn);
std::list> outputReferences;
if (!core.scanOutputkeysForIndices(txInToKey, outputReferences)) {
return false;
}
txInDetails.amount = txInToKey.amount;
txInToKeyDetails.keyOffsets = txInToKey.keyOffsets;
txInToKeyDetails.keyImage = reinterpret_cast&>(txInToKey.keyImage);
txInToKeyDetails.mixin = txInToKey.keyOffsets.size();
txInToKeyDetails.output.number = outputReferences.back().second;
txInToKeyDetails.output.transactionHash = reinterpret_cast&>(outputReferences.back().first);
txInDetails.input = txInToKeyDetails;
} else if (txIn.type() == typeid(TransactionInputMultisignature)) {
TransactionInputMultisignatureDetails txInMultisigDetails;
const TransactionInputMultisignature& txInMultisig = boost::get(txIn);
txInDetails.amount = txInMultisig.amount;
txInMultisigDetails.signatures = txInMultisig.signatures;
std::pair outputReference;
if (!core.getMultisigOutputReference(txInMultisig, outputReference)) {
return false;
}
txInMultisigDetails.output.number = outputReference.second;
txInMultisigDetails.output.transactionHash = reinterpret_cast&>(outputReference.first);
txInDetails.input = txInMultisigDetails;
} else {
return false;
}
transactionDetails.inputs.push_back(std::move(txInDetails));
}
transactionDetails.outputs.reserve(transaction.vout.size());
std::vector globalIndices;
globalIndices.reserve(transaction.vout.size());
if (!core.get_tx_outputs_gindexs(hash, globalIndices)) {
for (size_t i = 0; i < transaction.vout.size(); ++i) {
globalIndices.push_back(0);
}
}
typedef boost::tuple outputWithIndex;
auto range = boost::combine(transaction.vout, globalIndices);
for (const outputWithIndex& txOutput : range) {
TransactionOutputDetails txOutDetails;
txOutDetails.amount = txOutput.get<0>().amount;
txOutDetails.globalIndex = txOutput.get<1>();
if (txOutput.get<0>().target.type() == typeid(TransactionOutputToKey)) {
TransactionOutputToKeyDetails txOutToKeyDetails;
txOutToKeyDetails.txOutKey = reinterpret_cast&>(boost::get(txOutput.get<0>().target).key);
txOutDetails.output = txOutToKeyDetails;
} else if (txOutput.get<0>().target.type() == typeid(TransactionOutputMultisignature)) {
TransactionOutputMultisignatureDetails txOutMultisigDetails;
TransactionOutputMultisignature txOutMultisig = boost::get(txOutput.get<0>().target);
txOutMultisigDetails.keys.reserve(txOutMultisig.keys.size());
for (const crypto::public_key& key : txOutMultisig.keys) {
txOutMultisigDetails.keys.push_back(std::move(reinterpret_cast&>(key)));
}
txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatures;
txOutDetails.output = txOutMultisigDetails;
} else {
return false;
}
transactionDetails.outputs.push_back(std::move(txOutDetails));
}
return true;
}
}