diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp index 8f93ea08..e8772d9b 100644 --- a/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp @@ -224,6 +224,23 @@ bool constructTransaction( return true; } +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) { + money = 0; + + for (const auto& in : tx.inputs) { + uint64_t amount = 0; + + if (in.type() == typeid(KeyInput)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(MultisignatureInput)) { + amount = boost::get(in).amount; + } + + money += amount; + } + return true; +} + uint32_t get_block_height(const Block& b) { if (b.baseTransaction.inputs.size() != 1) { return 0; diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.h b/src/CryptoNoteCore/CryptoNoteFormatUtils.h index 3df5a350..647e80af 100755 --- a/src/CryptoNoteCore/CryptoNoteFormatUtils.h +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.h @@ -75,6 +75,7 @@ bool get_aux_block_header_hash(const Block& b, Crypto::Hash& res); bool get_block_hash(const Block& b, Crypto::Hash& res); Crypto::Hash get_block_hash(const Block& b); bool get_block_longhash(Crypto::cn_context &context, const Block& b, Crypto::Hash& res); +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); uint64_t get_outs_money_amount(const Transaction& tx); bool check_inputs_types_supported(const TransactionPrefix& tx); bool check_outs_valid(const TransactionPrefix& tx, std::string* error = 0); diff --git a/src/CryptoNoteCore/CryptoNoteTools.cpp b/src/CryptoNoteCore/CryptoNoteTools.cpp index fa27c2b0..5fee8d9d 100755 --- a/src/CryptoNoteCore/CryptoNoteTools.cpp +++ b/src/CryptoNoteCore/CryptoNoteTools.cpp @@ -31,6 +31,19 @@ Crypto::Hash getBinaryArrayHash(const BinaryArray& binaryArray) { return hash; } +uint64_t getInputAmount(const Transaction& transaction) { + uint64_t amount = 0; + for (auto& input : transaction.inputs) { + if (input.type() == typeid(KeyInput)) { + amount += boost::get(input).amount; + } else if (input.type() == typeid(MultisignatureInput)) { + amount += boost::get(input).amount; + } + } + + return amount; +} + std::vector getInputsAmounts(const Transaction& transaction) { std::vector inputsAmounts; inputsAmounts.reserve(transaction.inputs.size()); diff --git a/src/CryptoNoteCore/CryptoNoteTools.h b/src/CryptoNoteCore/CryptoNoteTools.h index 6c3e28f1..d756bc65 100755 --- a/src/CryptoNoteCore/CryptoNoteTools.h +++ b/src/CryptoNoteCore/CryptoNoteTools.h @@ -32,7 +32,7 @@ bool toBinaryArray(const T& object, BinaryArray& binaryArray) { } template<> -bool toBinaryArray(const BinaryArray& object, BinaryArray& binaryArray); +bool toBinaryArray(const BinaryArray& object, BinaryArray& binaryArray); template BinaryArray toBinaryArray(const T& object) { @@ -107,6 +107,7 @@ Crypto::Hash getObjectHash(const T& object) { return hash; } +uint64_t getInputAmount(const Transaction& transaction); std::vector getInputsAmounts(const Transaction& transaction); uint64_t getOutputAmount(const Transaction& transaction); void decomposeAmount(uint64_t amount, uint64_t dustThreshold, std::vector& decomposedAmounts); diff --git a/src/Rpc/CoreRpcServerCommandsDefinitions.h b/src/Rpc/CoreRpcServerCommandsDefinitions.h index 40d415cb..93818e6e 100755 --- a/src/Rpc/CoreRpcServerCommandsDefinitions.h +++ b/src/Rpc/CoreRpcServerCommandsDefinitions.h @@ -47,7 +47,7 @@ struct COMMAND_RPC_GET_BLOCKS_FAST { struct request { std::vector block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ - + void serialize(ISerializer &s) { serializeAsBinary(block_ids, "block_ids", s); } @@ -85,7 +85,7 @@ struct COMMAND_RPC_GET_TRANSACTIONS { void serialize(ISerializer &s) { KV_MEMBER(txs_as_hex) KV_MEMBER(missed_tx) - KV_MEMBER(status) + KV_MEMBER(status) } }; }; @@ -144,7 +144,7 @@ struct COMMAND_RPC_GET_POOL_CHANGES_LITE { //----------------------------------------------- struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES { - + struct request { Crypto::Hash txid; @@ -403,6 +403,169 @@ struct BLOCK_HEADER_RESPONSE { }; +struct f_transaction_short_response { + std::string hash; + uint64_t fee; + uint64_t amount_out; + uint64_t size; + + void serialize(ISerializer &s) { + KV_MEMBER(hash) + KV_MEMBER(fee) + KV_MEMBER(amount_out) + KV_MEMBER(size) + } +}; + +struct f_transaction_details_response { + std::string hash; + size_t size; + std::string paymentId; + uint64_t mixin; + uint64_t fee; + uint64_t amount_out; + + void serialize(ISerializer &s) { + KV_MEMBER(hash) + KV_MEMBER(size) + KV_MEMBER(paymentId) + KV_MEMBER(mixin) + KV_MEMBER(fee) + KV_MEMBER(amount_out) + } +}; + +struct f_block_short_response { + uint64_t timestamp; + uint32_t height; + difficulty_type difficulty; + std::string hash; + uint64_t tx_count; + uint64_t cumul_size; + + void serialize(ISerializer &s) { + KV_MEMBER(timestamp) + KV_MEMBER(height) + KV_MEMBER(difficulty) + KV_MEMBER(hash) + KV_MEMBER(cumul_size) + KV_MEMBER(tx_count) + } +}; + +struct f_block_details_response { + uint8_t major_version; + uint8_t minor_version; + uint64_t timestamp; + std::string prev_hash; + uint32_t nonce; + bool orphan_status; + uint64_t height; + uint64_t depth; + std::string hash; + difficulty_type difficulty; + uint64_t reward; + uint64_t blockSize; + size_t sizeMedian; + uint64_t effectiveSizeMedian; + uint64_t transactionsCumulativeSize; + std::string alreadyGeneratedCoins; + uint64_t alreadyGeneratedTransactions; + uint64_t baseReward; + double penalty; + uint64_t totalFeeAmount; + std::vector transactions; + + void serialize(ISerializer &s) { + KV_MEMBER(major_version) + KV_MEMBER(minor_version) + KV_MEMBER(timestamp) + KV_MEMBER(prev_hash) + KV_MEMBER(nonce) + KV_MEMBER(orphan_status) + KV_MEMBER(height) + KV_MEMBER(depth) + KV_MEMBER(hash) + KV_MEMBER(difficulty) + KV_MEMBER(reward) + KV_MEMBER(blockSize) + KV_MEMBER(sizeMedian) + KV_MEMBER(effectiveSizeMedian) + KV_MEMBER(transactionsCumulativeSize) + KV_MEMBER(alreadyGeneratedCoins) + KV_MEMBER(alreadyGeneratedTransactions) + KV_MEMBER(baseReward) + KV_MEMBER(penalty) + KV_MEMBER(transactions) + KV_MEMBER(totalFeeAmount) + } +}; +struct currency_base_coin { + std::string name; + std::string git; + + void serialize(ISerializer &s) { + KV_MEMBER(name) + KV_MEMBER(git) + } +}; + +struct currency_core { + std::vector SEED_NODES; + uint64_t EMISSION_SPEED_FACTOR; + uint64_t DIFFICULTY_TARGET; + uint64_t CRYPTONOTE_DISPLAY_DECIMAL_POINT; + std::string MONEY_SUPPLY; +//uint64_t GENESIS_BLOCK_REWARD; + uint64_t DEFAULT_DUST_THRESHOLD; + uint64_t MINIMUM_FEE; + uint64_t CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + uint64_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; +// uint64_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + uint64_t P2P_DEFAULT_PORT; + uint64_t RPC_DEFAULT_PORT; + uint64_t MAX_BLOCK_SIZE_INITIAL; + uint64_t EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; + uint64_t UPGRADE_HEIGHT; + uint64_t DIFFICULTY_CUT; + uint64_t DIFFICULTY_LAG; + //std::string BYTECOIN_NETWORK; + std::string CRYPTONOTE_NAME; + std::string GENESIS_COINBASE_TX_HEX; + std::vector CHECKPOINTS; + + void serialize(ISerializer &s) { + KV_MEMBER(SEED_NODES) + KV_MEMBER(EMISSION_SPEED_FACTOR) + KV_MEMBER(DIFFICULTY_TARGET) + KV_MEMBER(CRYPTONOTE_DISPLAY_DECIMAL_POINT) + KV_MEMBER(MONEY_SUPPLY) +// KV_MEMBER(GENESIS_BLOCK_REWARD) + KV_MEMBER(DEFAULT_DUST_THRESHOLD) + KV_MEMBER(MINIMUM_FEE) + KV_MEMBER(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) + KV_MEMBER(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) +// KV_MEMBER(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1) + KV_MEMBER(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX) + KV_MEMBER(P2P_DEFAULT_PORT) + KV_MEMBER(RPC_DEFAULT_PORT) + KV_MEMBER(MAX_BLOCK_SIZE_INITIAL) + KV_MEMBER(EXPECTED_NUMBER_OF_BLOCKS_PER_DAY) + KV_MEMBER(UPGRADE_HEIGHT) + KV_MEMBER(DIFFICULTY_CUT) + KV_MEMBER(DIFFICULTY_LAG) +// KV_MEMBER(BYTECOIN_NETWORK) + KV_MEMBER(CRYPTONOTE_NAME) + KV_MEMBER(GENESIS_COINBASE_TX_HEX) + KV_MEMBER(CHECKPOINTS) + } +}; + + + + + struct COMMAND_RPC_GET_LAST_BLOCK_HEADER { typedef EMPTY_STRUCT request; typedef BLOCK_HEADER_RESPONSE response; @@ -432,6 +595,105 @@ struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT { typedef BLOCK_HEADER_RESPONSE response; }; + + +struct F_COMMAND_RPC_GET_BLOCKS_LIST { + struct request { + uint64_t height; + + void serialize(ISerializer &s) { + KV_MEMBER(height) + } + }; + + struct response { + std::vector blocks; //transactions blobs as hex + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(blocks) + KV_MEMBER(status) + } + }; +}; + +struct F_COMMAND_RPC_GET_BLOCK_DETAILS { + struct request { + std::string hash; + + void serialize(ISerializer &s) { + KV_MEMBER(hash) + } + }; + + struct response { + f_block_details_response block; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(block) + KV_MEMBER(status) + } + }; +}; + +struct F_COMMAND_RPC_GET_TRANSACTION_DETAILS { + struct request { + std::string hash; + + void serialize(ISerializer &s) { + KV_MEMBER(hash) + } + }; + + struct response { + Transaction tx; + f_transaction_details_response txDetails; + f_block_short_response block; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(tx) + KV_MEMBER(txDetails) + KV_MEMBER(block) + KV_MEMBER(status) + } + }; +}; + +struct F_COMMAND_RPC_GET_POOL { + typedef EMPTY_STRUCT request; + + struct response { + std::vector transactions; //transactions blobs as hex + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(transactions) + KV_MEMBER(status) + } + }; +}; + +struct F_COMMAND_RPC_GET_BLOCKCHAIN_SETTINGS { + typedef EMPTY_STRUCT request; + struct response { + currency_base_coin base_coin; + currency_core core; + std::vector extensions; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(base_coin) + KV_MEMBER(core) + KV_MEMBER(extensions) + KV_MEMBER(status) + } + }; +}; + + + struct COMMAND_RPC_QUERY_BLOCKS { struct request { std::vector block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ diff --git a/src/Rpc/RpcServer.cpp b/src/Rpc/RpcServer.cpp index 1bca07ae..7e4a3f48 100755 --- a/src/Rpc/RpcServer.cpp +++ b/src/Rpc/RpcServer.cpp @@ -68,9 +68,9 @@ RpcServer::HandlerFunction jsonMethod(bool (RpcServer::*handler)(typename Comman } } - + std::unordered_map> RpcServer::s_handlers = { - + // binary handlers { "/getblocks.bin", { binMethod(&RpcServer::on_get_blocks), false } }, { "/queryblocks.bin", { binMethod(&RpcServer::on_query_blocks), false } }, @@ -130,6 +130,11 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& jsonResponse.setId(jsonRequest.getId()); // copy id static std::unordered_map> jsonRpcHandlers = { + { "f_blocks_list_json", { makeMemberMethod(&RpcServer::f_on_blocks_list_json), false } }, + { "f_block_json", { makeMemberMethod(&RpcServer::f_on_block_json), false } }, + { "f_transaction_json", { makeMemberMethod(&RpcServer::f_on_transaction_json), false } }, + { "f_on_transactions_pool_json", { makeMemberMethod(&RpcServer::f_on_transactions_pool_json), false } }, + // { "f_get_blockchain_settings", { makeMemberMethod(&RpcServer::f_on_get_blockchain_settings), true } }, { "getblockcount", { makeMemberMethod(&RpcServer::on_getblockcount), true } }, { "on_getblockhash", { makeMemberMethod(&RpcServer::on_getblockhash), false } }, { "getblocktemplate", { makeMemberMethod(&RpcServer::on_getblocktemplate), false } }, @@ -322,7 +327,15 @@ bool RpcServer::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RP res.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); res.last_known_block_index = std::max(static_cast(1), m_protocolQuery.getObservedHeight()) - 1; - res.full_deposit_amount = m_core.fullDepositAmount(); + + + uint64_t height = m_core.get_current_blockchain_height() - 1; + uint64_t totalCoinsInNetwork = m_core.coinsEmittedAtHeight(height); + uint64_t totalCoinsOnDeposits = m_core.depositAmountAtHeight(height); + if(totalCoinsOnDeposits > totalCoinsInNetwork){ + totalCoinsOnDeposits = totalCoinsInNetwork - (totalCoinsOnDeposits - totalCoinsInNetwork ) - (totalCoinsInNetwork * 0.0375 ); + } + res.full_deposit_amount = totalCoinsOnDeposits; res.full_deposit_interest = m_core.fullDepositInterest(); res.status = CORE_RPC_STATUS_OK; return true; @@ -444,6 +457,297 @@ bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMM //------------------------------------------------------------------------------------------------------------------------------ // JSON RPC methods //------------------------------------------------------------------------------------------------------------------------------ +bool RpcServer::f_on_blocks_list_json(const F_COMMAND_RPC_GET_BLOCKS_LIST::request& req, F_COMMAND_RPC_GET_BLOCKS_LIST::response& res) { + if (m_core.get_current_blockchain_height() <= req.height) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; + } + + uint32_t print_blocks_count = 30; + uint32_t last_height = req.height - print_blocks_count; + if (req.height <= print_blocks_count) { + last_height = 0; + } + + for (uint32_t i = req.height; i >= last_height; i--) { + Hash block_hash = m_core.getBlockIdByHeight(static_cast(i)); + Block blk; + if (!m_core.getBlockByHash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by height. Height = " + std::to_string(i) + '.' }; + } + + size_t tx_cumulative_block_size; + m_core.getBlockSize(block_hash, tx_cumulative_block_size); + size_t blokBlobSize = getObjectBinarySize(blk); + size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); + + f_block_short_response block_short; + block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; + block_short.timestamp = blk.timestamp; + block_short.height = i; + m_core.getBlockDifficulty(static_cast(block_short.height), block_short.difficulty); + block_short.hash = Common::podToHex(block_hash); + block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; + block_short.tx_count = blk.transactionHashes.size() + 1; + + res.blocks.push_back(block_short); + + if (i == 0) + break; + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::f_on_block_json(const F_COMMAND_RPC_GET_BLOCK_DETAILS::request& req, F_COMMAND_RPC_GET_BLOCK_DETAILS::response& res) { + Hash hash; + + if (!parse_hash256(req.hash, hash)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of block hash. Hex = " + req.hash + '.' }; + } + + Block blk; + if (!m_core.getBlockByHash(hash, blk)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by hash. Hash = " + req.hash + '.' }; + } + + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: coinbase transaction in the block has the wrong type" }; + } + + block_header_response block_header; + res.block.height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; + fill_block_header_response(blk, false, res.block.height, hash, block_header); + + res.block.major_version = block_header.major_version; + res.block.minor_version = block_header.minor_version; + res.block.timestamp = block_header.timestamp; + res.block.prev_hash = block_header.prev_hash; + res.block.nonce = block_header.nonce; + res.block.hash = Common::podToHex(hash); + res.block.depth = m_core.get_current_blockchain_height() - res.block.height - 1; + m_core.getBlockDifficulty(static_cast(res.block.height), res.block.difficulty); + + res.block.reward = block_header.reward; + + std::vector blocksSizes; + if (!m_core.getBackwardBlocksSizes(res.block.height, blocksSizes, parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW)) { + return false; + } + res.block.sizeMedian = Common::medianValue(blocksSizes); + + size_t blockSize = 0; + if (!m_core.getBlockSize(hash, blockSize)) { + return false; + } + res.block.transactionsCumulativeSize = blockSize; + + size_t blokBlobSize = getObjectBinarySize(blk); + size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); + res.block.blockSize = blokBlobSize + res.block.transactionsCumulativeSize - minerTxBlobSize; + + uint64_t alreadyGeneratedCoins; + if (!m_core.getAlreadyGeneratedCoins(hash, alreadyGeneratedCoins)) { + return false; + } + res.block.alreadyGeneratedCoins = std::to_string(alreadyGeneratedCoins); + + if (!m_core.getGeneratedTransactionsNumber(res.block.height, res.block.alreadyGeneratedTransactions)) { + return false; + } + + uint64_t prevBlockGeneratedCoins = 0; + if (res.block.height > 0) { + if (!m_core.getAlreadyGeneratedCoins(blk.previousBlockHash, prevBlockGeneratedCoins)) { + return false; + } + } + uint64_t maxReward = 0; + uint64_t currentReward = 0; + int64_t emissionChange = 0; + bool penalizeFee = blk.majorVersion >= 2; + size_t blockGrantedFullRewardZone = penalizeFee ? + m_core.currency().blockGrantedFullRewardZone() : + //m_core.currency().blockGrantedFullRewardZoneV1(); + res.block.effectiveSizeMedian = std::max(res.block.sizeMedian, blockGrantedFullRewardZone); + + // virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint32_t height, + // uint64_t& reward, int64_t& emissionChange) = 0; + + if (!m_core.getBlockReward(res.block.sizeMedian, 0, prevBlockGeneratedCoins, 0, res.block.height, maxReward, emissionChange)) { + return false; + } + if (!m_core.getBlockReward(res.block.sizeMedian, res.block.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, res.block.height, currentReward, emissionChange)) { + return false; + } + + // if (!m_core.getBlockReward(res.block.sizeMedian, 0, prevBlockGeneratedCoins, 0, penalizeFee, maxReward, emissionChange)) { + // return false; + // } + // if (!m_core.getBlockReward(res.block.sizeMedian, res.block.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, penalizeFee, currentReward, emissionChange)) { + // return false; + // } + + res.block.baseReward = maxReward; + if (maxReward == 0 && currentReward == 0) { + res.block.penalty = static_cast(0); + } else { + if (maxReward < currentReward) { + return false; + } + res.block.penalty = static_cast(maxReward - currentReward) / static_cast(maxReward); + } + + // Base transaction adding + f_transaction_short_response transaction_short; + transaction_short.hash = Common::podToHex(getObjectHash(blk.baseTransaction)); + transaction_short.fee = 0; + transaction_short.amount_out = get_outs_money_amount(blk.baseTransaction); + transaction_short.size = getObjectBinarySize(blk.baseTransaction); + res.block.transactions.push_back(transaction_short); + + + std::list missed_txs; + std::list txs; + m_core.getTransactions(blk.transactionHashes, txs, missed_txs); + + res.block.totalFeeAmount = 0; + + for (const Transaction& tx : txs) { + f_transaction_short_response transaction_short; + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + transaction_short.hash = Common::podToHex(getObjectHash(tx)); + transaction_short.fee = amount_in - amount_out; + transaction_short.amount_out = amount_out; + transaction_short.size = getObjectBinarySize(tx); + res.block.transactions.push_back(transaction_short); + + res.block.totalFeeAmount += transaction_short.fee; + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::f_on_transaction_json(const F_COMMAND_RPC_GET_TRANSACTION_DETAILS::request& req, F_COMMAND_RPC_GET_TRANSACTION_DETAILS::response& res) { + Hash hash; + + if (!parse_hash256(req.hash, hash)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of transaction hash. Hex = " + req.hash + '.' }; + } + + std::vector tx_ids; + tx_ids.push_back(hash); + + std::list missed_txs; + std::list txs; + m_core.getTransactions(tx_ids, txs, missed_txs); + + if (1 == txs.size()) { + res.tx = txs.front(); + } else { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "transaction wasn't found. Hash = " + req.hash + '.' }; + } + + Crypto::Hash blockHash; + uint32_t blockHeight; + if (m_core.getBlockContainingTx(hash, blockHash, blockHeight)) { + Block blk; + if (m_core.getBlockByHash(blockHash, blk)) { + size_t tx_cumulative_block_size; + m_core.getBlockSize(blockHash, tx_cumulative_block_size); + size_t blokBlobSize = getObjectBinarySize(blk); + size_t minerTxBlobSize = getObjectBinarySize(blk.baseTransaction); + f_block_short_response block_short; + + block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; + block_short.timestamp = blk.timestamp; + block_short.height = blockHeight; + block_short.hash = Common::podToHex(blockHash); + block_short.cumul_size = blokBlobSize + tx_cumulative_block_size - minerTxBlobSize; + block_short.tx_count = blk.transactionHashes.size() + 1; + res.block = block_short; + } + } + + uint64_t amount_in = 0; + get_inputs_money_amount(res.tx, amount_in); + uint64_t amount_out = get_outs_money_amount(res.tx); + + res.txDetails.hash = Common::podToHex(getObjectHash(res.tx)); + res.txDetails.fee = amount_in - amount_out; + if (amount_in == 0) + res.txDetails.fee = 0; + res.txDetails.amount_out = amount_out; + res.txDetails.size = getObjectBinarySize(res.tx); + + uint64_t mixin; + if (!f_getMixin(res.tx, mixin)) { + return false; + } + res.txDetails.mixin = mixin; + + Crypto::Hash paymentId; + if (CryptoNote::getPaymentIdFromTxExtra(res.tx.extra, paymentId)) { + res.txDetails.paymentId = Common::podToHex(paymentId); + } else { + res.txDetails.paymentId = ""; + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::f_getMixin(const Transaction& transaction, uint64_t& mixin) { + mixin = 0; + for (const TransactionInput& txin : transaction.inputs) { + if (txin.type() != typeid(KeyInput)) { + continue; + } + uint64_t currentMixin = boost::get(txin).outputIndexes.size(); + if (currentMixin > mixin) { + mixin = currentMixin; + } + } + return true; +} + +bool RpcServer::f_on_transactions_pool_json(const F_COMMAND_RPC_GET_POOL::request& req, F_COMMAND_RPC_GET_POOL::response& res) { + auto pool = m_core.getPoolTransactions(); + for (const Transaction tx : pool) { + f_transaction_short_response transaction_short; + uint64_t amount_in = getInputAmount(tx); + uint64_t amount_out = getOutputAmount(tx); + + transaction_short.hash = Common::podToHex(getObjectHash(tx)); + transaction_short.fee = + amount_in < amount_out + parameters::MINIMUM_FEE //account for interest in output, it always has minimum fee + ? parameters::MINIMUM_FEE + : amount_in - amount_out; + transaction_short.amount_out = amount_out; + transaction_short.size = getObjectBinarySize(tx); + res.transactions.push_back(transaction_short); + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + bool RpcServer::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { res.count = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; @@ -458,7 +762,7 @@ bool RpcServer::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, CO uint32_t h = static_cast(req[0]); Crypto::Hash blockId = m_core.getBlockIdByHeight(h); if (blockId == NULL_HASH) { - throw JsonRpc::JsonRpcError{ + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; @@ -588,14 +892,14 @@ void RpcServer::fill_block_header_response(const Block& blk, bool orphan_status, bool RpcServer::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res) { uint32_t last_block_height; Hash last_block_hash; - + m_core.get_blockchain_top(last_block_height, last_block_hash); Block last_block; if (!m_core.getBlockByHash(last_block_hash, last_block)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; } - + fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header); res.status = CORE_RPC_STATUS_OK; return true; diff --git a/src/Rpc/RpcServer.h b/src/Rpc/RpcServer.h index f3c43cd7..f89cfdf1 100755 --- a/src/Rpc/RpcServer.h +++ b/src/Rpc/RpcServer.h @@ -9,6 +9,7 @@ #include #include +#include "Common/Math.h" #include "CoreRpcServerCommandsDefinitions.h" namespace CryptoNote { @@ -68,6 +69,13 @@ class RpcServer : public HttpServer { void fill_block_header_response(const Block& blk, bool orphan_status, uint64_t height, const Crypto::Hash& hash, block_header_response& responce); + bool f_on_blocks_list_json(const F_COMMAND_RPC_GET_BLOCKS_LIST::request& req, F_COMMAND_RPC_GET_BLOCKS_LIST::response& res); + bool f_on_block_json(const F_COMMAND_RPC_GET_BLOCK_DETAILS::request& req, F_COMMAND_RPC_GET_BLOCK_DETAILS::response& res); + bool f_on_transaction_json(const F_COMMAND_RPC_GET_TRANSACTION_DETAILS::request& req, F_COMMAND_RPC_GET_TRANSACTION_DETAILS::response& res); + bool f_on_transactions_pool_json(const F_COMMAND_RPC_GET_POOL::request& req, F_COMMAND_RPC_GET_POOL::response& res); + bool f_getMixin(const Transaction& transaction, uint64_t& mixin); + // bool f_on_get_blockchain_settings(const F_COMMAND_RPC_GET_BLOCKCHAIN_SETTINGS::request& req, F_COMMAND_RPC_GET_BLOCKCHAIN_SETTINGS::response& res); + Logging::LoggerRef logger; core& m_core; NodeServer& m_p2p;