danicoin/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp

338 lines
12 KiB
C++
Raw Normal View History

// Copyright (c) 2011-2016 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2015-07-15 12:23:00 +00:00
#include "BlockchainExplorerDataBuilder.h"
#include <boost/utility/value_init.hpp>
#include <boost/range/combine.hpp>
#include "Common/StringTools.h"
2015-07-30 15:22:07 +00:00
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "CryptoNoteCore/CryptoNoteTools.h"
#include "CryptoNoteCore/TransactionExtra.h"
#include "CryptoNoteConfig.h"
2015-07-15 12:23:00 +00:00
namespace CryptoNote {
2015-07-30 15:22:07 +00:00
BlockchainExplorerDataBuilder::BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol) :
core(core),
protocol(protocol) {
2015-07-15 12:23:00 +00:00
}
bool BlockchainExplorerDataBuilder::getMixin(const Transaction& transaction, uint64_t& mixin) {
mixin = 0;
2015-07-30 15:22:07 +00:00
for (const TransactionInput& txin : transaction.inputs) {
if (txin.type() != typeid(KeyInput)) {
2015-07-15 12:23:00 +00:00
continue;
}
2015-07-30 15:22:07 +00:00
uint64_t currentMixin = boost::get<KeyInput>(txin).outputIndexes.size();
2015-07-15 12:23:00 +00:00
if (currentMixin > mixin) {
mixin = currentMixin;
}
}
return true;
}
2015-07-30 15:22:07 +00:00
bool BlockchainExplorerDataBuilder::getPaymentId(const Transaction& transaction, Crypto::Hash& paymentId) {
std::vector<TransactionExtraField> txExtraFields;
parseTransactionExtra(transaction.extra, txExtraFields);
TransactionExtraNonce extraNonce;
if (!findTransactionExtraFieldByType(txExtraFields, extraNonce)) {
2015-07-15 12:23:00 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
return getPaymentIdFromTransactionExtraNonce(extraNonce.nonce, paymentId);
2015-07-15 12:23:00 +00:00
}
bool BlockchainExplorerDataBuilder::fillTxExtra(const std::vector<uint8_t>& rawExtra, TransactionExtraDetails& extraDetails) {
extraDetails.raw = rawExtra;
2015-07-30 15:22:07 +00:00
std::vector<TransactionExtraField> txExtraFields;
parseTransactionExtra(rawExtra, txExtraFields);
for (const TransactionExtraField& field : txExtraFields) {
if (typeid(TransactionExtraPadding) == field.type()) {
extraDetails.padding.push_back(std::move(boost::get<TransactionExtraPadding>(field).size));
} else if (typeid(TransactionExtraPublicKey) == field.type()) {
extraDetails.publicKey.push_back(std::move(boost::get<TransactionExtraPublicKey>(field).publicKey));
} else if (typeid(TransactionExtraNonce) == field.type()) {
extraDetails.nonce.push_back(Common::toHex(boost::get<TransactionExtraNonce>(field).nonce.data(), boost::get<TransactionExtraNonce>(field).nonce.size()));
2015-07-15 12:23:00 +00:00
}
}
return true;
}
size_t BlockchainExplorerDataBuilder::median(std::vector<size_t>& v) {
2015-07-30 15:22:07 +00:00
if (v.empty())
2015-07-15 12:23:00 +00:00
return boost::value_initialized<size_t>();
2015-07-30 15:22:07 +00:00
if (v.size() == 1)
2015-07-15 12:23:00 +00:00
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());
2015-07-30 15:22:07 +00:00
if (v.size() % 2) {//1, 3, 5...
2015-07-15 12:23:00 +00:00
return v[n];
2015-07-30 15:22:07 +00:00
} else {//2, 4, 6...
return (v[n - 1] + v[n]) / 2;
2015-07-15 12:23:00 +00:00
}
}
bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDetails& blockDetails) {
2015-07-30 15:22:07 +00:00
Crypto::Hash hash = get_block_hash(block);
2015-07-15 12:23:00 +00:00
blockDetails.majorVersion = block.majorVersion;
blockDetails.minorVersion = block.minorVersion;
blockDetails.timestamp = block.timestamp;
2015-07-30 15:22:07 +00:00
blockDetails.prevBlockHash = block.previousBlockHash;
2015-07-15 12:23:00 +00:00
blockDetails.nonce = block.nonce;
2015-07-30 15:22:07 +00:00
blockDetails.hash = hash;
2015-07-15 12:23:00 +00:00
blockDetails.reward = 0;
2015-07-30 15:22:07 +00:00
for (const TransactionOutput& out : block.baseTransaction.outputs) {
2015-07-15 12:23:00 +00:00
blockDetails.reward += out.amount;
}
2015-07-30 15:22:07 +00:00
if (block.baseTransaction.inputs.front().type() != typeid(BaseInput))
2015-07-15 12:23:00 +00:00
return false;
2015-07-30 15:22:07 +00:00
blockDetails.height = boost::get<BaseInput>(block.baseTransaction.inputs.front()).blockIndex;
Crypto::Hash tmpHash = core.getBlockIdByHeight(blockDetails.height);
2015-07-15 12:23:00 +00:00
blockDetails.isOrphaned = hash != tmpHash;
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
if (!core.getBlockDifficulty(blockDetails.height, blockDetails.difficulty)) {
return false;
}
std::vector<size_t> 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;
2015-07-30 15:22:07 +00:00
size_t blokBlobSize = getObjectBinarySize(block);
size_t minerTxBlobSize = getObjectBinarySize(block.baseTransaction);
2015-07-15 12:23:00 +00:00
blockDetails.blockSize = blokBlobSize + blockDetails.transactionsCumulativeSize - minerTxBlobSize;
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
if (!core.getAlreadyGeneratedCoins(hash, blockDetails.alreadyGeneratedCoins)) {
return false;
}
2015-07-30 15:22:07 +00:00
if (!core.getGeneratedTransactionsNumber(blockDetails.height, blockDetails.alreadyGeneratedTransactions)) {
return false;
}
2015-07-15 12:23:00 +00:00
uint64_t prevBlockGeneratedCoins = 0;
2015-07-30 15:22:07 +00:00
if (blockDetails.height > 0) {
if (!core.getAlreadyGeneratedCoins(block.previousBlockHash, prevBlockGeneratedCoins)) {
2015-07-15 12:23:00 +00:00
return false;
}
}
uint64_t maxReward = 0;
uint64_t currentReward = 0;
int64_t emissionChange = 0;
if (!core.getBlockReward(blockDetails.sizeMedian, 0, prevBlockGeneratedCoins, 0, maxReward, emissionChange)) {
2015-07-15 12:23:00 +00:00
return false;
}
if (!core.getBlockReward(blockDetails.sizeMedian, blockDetails.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, currentReward, emissionChange)) {
2015-07-15 12:23:00 +00:00
return false;
}
blockDetails.baseReward = maxReward;
2015-07-30 15:22:07 +00:00
if (maxReward == 0 && currentReward == 0) {
2015-07-15 12:23:00 +00:00
blockDetails.penalty = static_cast<double>(0);
2015-07-30 15:22:07 +00:00
} else {
2015-07-15 12:23:00 +00:00
if (maxReward < currentReward) {
return false;
}
blockDetails.penalty = static_cast<double>(maxReward - currentReward) / static_cast<double>(maxReward);
}
2015-07-30 15:22:07 +00:00
blockDetails.transactions.reserve(block.transactionHashes.size() + 1);
2015-07-15 12:23:00 +00:00
TransactionDetails transactionDetails;
2015-07-30 15:22:07 +00:00
if (!fillTransactionDetails(block.baseTransaction, transactionDetails, block.timestamp)) {
2015-07-15 12:23:00 +00:00
return false;
}
blockDetails.transactions.push_back(std::move(transactionDetails));
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
std::list<Transaction> found;
2015-07-30 15:22:07 +00:00
std::list<Crypto::Hash> missed;
core.getTransactions(block.transactionHashes, found, missed, blockDetails.isOrphaned);
if (found.size() != block.transactionHashes.size()) {
2015-07-15 12:23:00 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
blockDetails.totalFeeAmount = 0;
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
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) {
2015-07-30 15:22:07 +00:00
Crypto::Hash hash = getObjectHash(transaction);
transactionDetails.hash = hash;
2015-07-15 12:23:00 +00:00
transactionDetails.timestamp = timestamp;
2015-07-30 15:22:07 +00:00
Crypto::Hash blockHash;
uint32_t blockHeight;
2015-07-15 12:23:00 +00:00
if (!core.getBlockContainingTx(hash, blockHash, blockHeight)) {
transactionDetails.inBlockchain = false;
2015-07-30 15:22:07 +00:00
transactionDetails.blockHeight = boost::value_initialized<uint32_t>();
transactionDetails.blockHash = boost::value_initialized<Crypto::Hash>();
2015-07-15 12:23:00 +00:00
} else {
transactionDetails.inBlockchain = true;
transactionDetails.blockHeight = blockHeight;
2015-07-30 15:22:07 +00:00
transactionDetails.blockHash = blockHash;
2015-07-15 12:23:00 +00:00
if (timestamp == 0) {
Block block;
if (!core.getBlockByHash(blockHash, block)) {
return false;
}
transactionDetails.timestamp = block.timestamp;
}
}
2015-07-30 15:22:07 +00:00
transactionDetails.size = getObjectBinarySize(transaction);
2015-07-15 12:23:00 +00:00
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;
2015-07-30 15:22:07 +00:00
if (transaction.inputs.size() > 0 && transaction.inputs.front().type() == typeid(BaseInput)) {
2015-07-15 12:23:00 +00:00
//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;
}
2015-07-30 15:22:07 +00:00
Crypto::Hash paymentId;
2015-07-15 12:23:00 +00:00
if (getPaymentId(transaction, paymentId)) {
2015-07-30 15:22:07 +00:00
transactionDetails.paymentId = paymentId;
} else {
transactionDetails.paymentId = boost::value_initialized<Crypto::Hash>();
2015-07-15 12:23:00 +00:00
}
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
fillTxExtra(transaction.extra, transactionDetails.extra);
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
transactionDetails.signatures.reserve(transaction.signatures.size());
2015-07-30 15:22:07 +00:00
for (const std::vector<Crypto::Signature>& signatures : transaction.signatures) {
std::vector<Crypto::Signature> signaturesDetails;
2015-07-15 12:23:00 +00:00
signaturesDetails.reserve(signatures.size());
2015-07-30 15:22:07 +00:00
for (const Crypto::Signature& signature : signatures) {
signaturesDetails.push_back(std::move(signature));
2015-07-15 12:23:00 +00:00
}
transactionDetails.signatures.push_back(std::move(signaturesDetails));
}
2015-07-30 15:22:07 +00:00
transactionDetails.inputs.reserve(transaction.inputs.size());
for (const TransactionInput& txIn : transaction.inputs) {
2015-07-15 12:23:00 +00:00
TransactionInputDetails txInDetails;
2015-07-30 15:22:07 +00:00
if (txIn.type() == typeid(BaseInput)) {
2015-07-15 12:23:00 +00:00
TransactionInputGenerateDetails txInGenDetails;
2015-07-30 15:22:07 +00:00
txInGenDetails.height = boost::get<BaseInput>(txIn).blockIndex;
2015-07-15 12:23:00 +00:00
txInDetails.amount = 0;
2015-07-30 15:22:07 +00:00
for (const TransactionOutput& out : transaction.outputs) {
2015-07-15 12:23:00 +00:00
txInDetails.amount += out.amount;
}
txInDetails.input = txInGenDetails;
2015-07-30 15:22:07 +00:00
} else if (txIn.type() == typeid(KeyInput)) {
2015-07-15 12:23:00 +00:00
TransactionInputToKeyDetails txInToKeyDetails;
2015-07-30 15:22:07 +00:00
const KeyInput& txInToKey = boost::get<KeyInput>(txIn);
std::list<std::pair<Crypto::Hash, size_t>> outputReferences;
2015-07-15 12:23:00 +00:00
if (!core.scanOutputkeysForIndices(txInToKey, outputReferences)) {
return false;
}
txInDetails.amount = txInToKey.amount;
2015-07-30 15:22:07 +00:00
txInToKeyDetails.outputIndexes = txInToKey.outputIndexes;
txInToKeyDetails.keyImage = txInToKey.keyImage;
txInToKeyDetails.mixin = txInToKey.outputIndexes.size();
2015-07-15 12:23:00 +00:00
txInToKeyDetails.output.number = outputReferences.back().second;
2015-07-30 15:22:07 +00:00
txInToKeyDetails.output.transactionHash = outputReferences.back().first;
2015-07-15 12:23:00 +00:00
txInDetails.input = txInToKeyDetails;
2015-07-30 15:22:07 +00:00
} else if (txIn.type() == typeid(MultisignatureInput)) {
2015-07-15 12:23:00 +00:00
TransactionInputMultisignatureDetails txInMultisigDetails;
2015-07-30 15:22:07 +00:00
const MultisignatureInput& txInMultisig = boost::get<MultisignatureInput>(txIn);
2015-07-15 12:23:00 +00:00
txInDetails.amount = txInMultisig.amount;
2015-07-30 15:22:07 +00:00
txInMultisigDetails.signatures = txInMultisig.signatureCount;
std::pair<Crypto::Hash, size_t> outputReference;
2015-07-15 12:23:00 +00:00
if (!core.getMultisigOutputReference(txInMultisig, outputReference)) {
return false;
}
txInMultisigDetails.output.number = outputReference.second;
2015-07-30 15:22:07 +00:00
txInMultisigDetails.output.transactionHash = outputReference.first;
2015-07-15 12:23:00 +00:00
txInDetails.input = txInMultisigDetails;
} else {
return false;
}
transactionDetails.inputs.push_back(std::move(txInDetails));
}
2015-07-30 15:22:07 +00:00
transactionDetails.outputs.reserve(transaction.outputs.size());
std::vector<uint32_t> globalIndices;
globalIndices.reserve(transaction.outputs.size());
if (!transactionDetails.inBlockchain || !core.get_tx_outputs_gindexs(hash, globalIndices)) {
for (size_t i = 0; i < transaction.outputs.size(); ++i) {
2015-07-15 12:23:00 +00:00
globalIndices.push_back(0);
}
}
2015-07-30 15:22:07 +00:00
typedef boost::tuple<TransactionOutput, uint32_t> outputWithIndex;
auto range = boost::combine(transaction.outputs, globalIndices);
2015-07-15 12:23:00 +00:00
for (const outputWithIndex& txOutput : range) {
TransactionOutputDetails txOutDetails;
txOutDetails.amount = txOutput.get<0>().amount;
txOutDetails.globalIndex = txOutput.get<1>();
2015-07-30 15:22:07 +00:00
if (txOutput.get<0>().target.type() == typeid(KeyOutput)) {
2015-07-15 12:23:00 +00:00
TransactionOutputToKeyDetails txOutToKeyDetails;
2015-07-30 15:22:07 +00:00
txOutToKeyDetails.txOutKey = boost::get<KeyOutput>(txOutput.get<0>().target).key;
2015-07-15 12:23:00 +00:00
txOutDetails.output = txOutToKeyDetails;
2015-07-30 15:22:07 +00:00
} else if (txOutput.get<0>().target.type() == typeid(MultisignatureOutput)) {
2015-07-15 12:23:00 +00:00
TransactionOutputMultisignatureDetails txOutMultisigDetails;
2015-07-30 15:22:07 +00:00
MultisignatureOutput txOutMultisig = boost::get<MultisignatureOutput>(txOutput.get<0>().target);
2015-07-15 12:23:00 +00:00
txOutMultisigDetails.keys.reserve(txOutMultisig.keys.size());
2015-07-30 15:22:07 +00:00
for (const Crypto::PublicKey& key : txOutMultisig.keys) {
txOutMultisigDetails.keys.push_back(std::move(key));
2015-07-15 12:23:00 +00:00
}
2015-07-30 15:22:07 +00:00
txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatureCount;
2015-07-15 12:23:00 +00:00
txOutDetails.output = txOutMultisigDetails;
} else {
return false;
}
transactionDetails.outputs.push_back(std::move(txOutDetails));
}
2015-07-30 15:22:07 +00:00
2015-07-15 12:23:00 +00:00
return true;
}
}