Files
thehub/libs/api/APIRPCBinding.cpp
T

932 lines
39 KiB
C++
Raw Permalink Normal View History

2016-08-16 16:51:22 +02:00
/*
2017-11-09 19:34:51 +01:00
* This file is part of the Flowee project
2020-07-27 12:33:28 +02:00
* Copyright (C) 2016-2017,2019-2020 Tom Zander <tomz@freedommail.ch>
2016-08-16 16:51:22 +02:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2018-02-15 19:50:12 +01:00
#include "APIRPCBinding.h"
2016-08-16 16:51:22 +02:00
2019-04-11 11:33:27 +02:00
#include <APIProtocol.h>
#include <streaming/MessageBuilder.h>
#include <BlocksDB.h>
#include <main.h>
#include <rpcserver.h>
#include <encodings_legacy.h>
2016-08-16 16:51:22 +02:00
#include <univalue.h>
#include <boost/algorithm/hex.hpp>
#include <streaming/MessageParser.h>
2019-06-10 16:44:17 +02:00
#include <UnspentOutputData.h>
2016-08-16 16:51:22 +02:00
#include <list>
2019-03-08 17:58:19 +01:00
#include <primitives/FastBlock.h>
#include <primitives/FastTransaction.h>
2019-06-10 16:44:17 +02:00
#include <utxo/UnspentOutputDatabase.h>
2019-03-08 17:58:19 +01:00
2016-08-16 16:51:22 +02:00
namespace {
2019-05-17 20:42:47 +02:00
void addHash256ToBuilder(Streaming::MessageBuilder &builder, uint32_t tag, const UniValue &univalue) {
2019-03-25 12:42:20 +01:00
assert(univalue.isStr());
2019-05-17 20:42:47 +02:00
assert(univalue.getValStr().size() == 64);
uint8_t buf[32];
2019-03-25 12:42:20 +01:00
const char *input = univalue.getValStr().c_str();
2019-05-17 20:42:47 +02:00
for (int i = 0; i < 32; ++i) {
2019-03-25 12:42:20 +01:00
signed char c = HexDigit(*input++);
assert(c != -1);
2019-05-17 20:42:47 +02:00
unsigned char n = c << 4;
2019-03-25 12:42:20 +01:00
c = HexDigit(*input++);
assert(c != -1);
n |= c;
2019-05-17 20:42:47 +02:00
buf[31 - i] = n;
2019-03-25 12:42:20 +01:00
}
2019-05-17 20:42:47 +02:00
builder.addByteArray(tag, buf, 32);
2019-03-25 12:42:20 +01:00
}
2016-08-16 16:51:22 +02:00
// blockchain
2019-03-07 18:38:03 +01:00
class GetBlockChainInfo : public Api::RpcParser
2016-08-16 16:51:22 +02:00
{
public:
2018-02-15 19:50:12 +01:00
GetBlockChainInfo() : RpcParser("getblockchaininfo", Api::BlockChain::GetBlockChainInfoReply, 500) {}
2017-10-06 19:12:30 +02:00
virtual void buildReply(Streaming::MessageBuilder &builder, const UniValue &result) {
2016-08-16 16:51:22 +02:00
const UniValue &chain = find_value(result, "chain");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Chain, chain.get_str());
2016-08-16 16:51:22 +02:00
const UniValue &blocks = find_value(result, "blocks");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Blocks, blocks.get_int());
2016-08-16 16:51:22 +02:00
const UniValue &headers = find_value(result, "headers");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Headers, headers.get_int());
2019-05-17 20:42:47 +02:00
addHash256ToBuilder(builder, Api::BlockChain::BestBlockHash, find_value(result, "bestblockhash"));
2016-08-16 16:51:22 +02:00
const UniValue &difficulty = find_value(result, "difficulty");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Difficulty, difficulty.get_real());
2016-08-16 16:51:22 +02:00
const UniValue &time = find_value(result, "mediantime");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::MedianTime, (uint64_t) time.get_int64());
2016-08-16 16:51:22 +02:00
const UniValue &progress = find_value(result, "verificationprogress");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::VerificationProgress, progress.get_real());
2019-05-17 20:42:47 +02:00
addHash256ToBuilder(builder, Api::BlockChain::ChainWork, find_value(result, "chainwork"));
2016-08-16 16:51:22 +02:00
}
};
2019-03-07 18:38:03 +01:00
class GetBestBlockHash : public Api::RpcParser
2016-08-16 16:51:22 +02:00
{
public:
2018-02-15 19:50:12 +01:00
GetBestBlockHash() : RpcParser("getbestblockhash", Api::BlockChain::GetBestBlockHashReply, 50) {}
2016-08-16 16:51:22 +02:00
};
2019-03-08 17:58:19 +01:00
class GetBlockLegacy : public Api::RpcParser
2016-08-16 16:51:22 +02:00
{
public:
2019-03-08 17:58:19 +01:00
GetBlockLegacy() : RpcParser("getblock", Api::BlockChain::GetBlockVerboseReply), m_verbose(true) {}
2016-08-16 16:51:22 +02:00
virtual void createRequest(const Message &message, UniValue &output) {
2019-03-08 17:58:19 +01:00
std::string blockId;
2016-08-16 16:51:22 +02:00
Streaming::MessageParser parser(message.body());
while (parser.next() == Streaming::FoundTag) {
2018-02-15 19:50:12 +01:00
if (parser.tag() == Api::BlockChain::BlockHash
2019-05-06 14:50:19 +02:00
|| parser.tag() == Api::LiveTransactions::GenericByteData) {
2019-05-08 12:49:33 +02:00
blockId = parser.uint256Data().ToString();
2019-03-08 17:58:19 +01:00
} else if (parser.tag() == Api::BlockChain::Verbose) {
2016-08-16 16:51:22 +02:00
m_verbose = parser.boolData();
2019-03-27 22:35:21 +01:00
} else if (parser.tag() == Api::BlockChain::BlockHeight) {
2019-06-11 21:15:23 +02:00
auto index = chainActive[parser.intData()];
2019-05-08 12:49:33 +02:00
if (index)
blockId = index->GetBlockHash().ToString();
2019-03-08 17:58:19 +01:00
}
2016-08-16 16:51:22 +02:00
}
2019-03-08 17:58:19 +01:00
output.push_back(std::make_pair("block", UniValue(UniValue::VSTR, blockId)));
2016-08-16 16:51:22 +02:00
output.push_back(std::make_pair("verbose", UniValue(UniValue::VBOOL, m_verbose ? "1": "0")));
}
2017-10-06 19:12:30 +02:00
virtual int calculateMessageSize(const UniValue &result) const {
2016-08-16 16:51:22 +02:00
if (m_verbose) {
const UniValue &tx = find_value(result, "tx");
return tx.size() * 70 + 200;
}
return result.get_str().size() / 2 + 20;
}
2017-10-06 19:12:30 +02:00
virtual void buildReply(Streaming::MessageBuilder &builder, const UniValue &result) {
2016-08-16 16:51:22 +02:00
if (!m_verbose) {
std::vector<char> answer;
boost::algorithm::unhex(result.get_str(), back_inserter(answer));
builder.add(1, answer);
return;
}
2019-05-17 20:42:47 +02:00
addHash256ToBuilder(builder, Api::BlockChain::BlockHash, find_value(result, "hash"));
2016-08-16 16:51:22 +02:00
const UniValue &confirmations = find_value(result, "confirmations");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Confirmations, confirmations.get_int());
2016-08-16 16:51:22 +02:00
const UniValue &size = find_value(result, "size");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Size, size.get_int());
2016-08-16 16:51:22 +02:00
const UniValue &height = find_value(result, "height");
2019-03-27 22:35:21 +01:00
builder.add(Api::BlockChain::BlockHeight, height.get_int());
2016-08-16 16:51:22 +02:00
const UniValue &version = find_value(result, "version");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Version, version.get_int());
2019-05-17 20:42:47 +02:00
addHash256ToBuilder(builder, Api::BlockChain::MerkleRoot, find_value(result, "merkleroot"));
2016-08-16 16:51:22 +02:00
const UniValue &tx = find_value(result, "tx");
bool first = true;
for (const UniValue &transaction: tx.getValues()) {
if (first) first = false;
2019-03-27 18:44:26 +01:00
else builder.add(Api::Separator, true);
2019-05-17 20:42:47 +02:00
addHash256ToBuilder(builder, Api::BlockChain::TxId, transaction);
2016-08-16 16:51:22 +02:00
}
const UniValue &time = find_value(result, "time");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Time, (uint64_t) time.get_int64());
2016-08-16 16:51:22 +02:00
const UniValue &mediantime = find_value(result, "mediantime");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::MedianTime, (uint64_t) mediantime.get_int64());
2016-08-16 16:51:22 +02:00
const UniValue &nonce = find_value(result, "nonce");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Nonce, (uint64_t) nonce.get_int64());
2019-05-17 20:42:47 +02:00
// 'bits' is an 4 bytes hex-encoded string.
const std::string &input = find_value(result, "bits").getValStr();
assert(input.size() == 8);
uint32_t bits = 0;
for (int i = 0; i < 8; ++i) {
signed char c = HexDigit(input.at(i));
assert(c != -1);
bits = (bits << 4) + c;
}
builder.add(Api::BlockChain::Bits, (uint64_t) bits);
2016-08-16 16:51:22 +02:00
const UniValue &difficulty = find_value(result, "difficulty");
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Difficulty, difficulty.get_real());
2019-05-17 20:42:47 +02:00
addHash256ToBuilder(builder, Api::BlockChain::ChainWork, find_value(result, "chainwork"));
addHash256ToBuilder(builder, Api::BlockChain::PrevBlockHash, find_value(result, "previousblockhash"));
2016-08-16 16:51:22 +02:00
const UniValue &nextblock = find_value(result, "nextblockhash");
2019-05-17 20:42:47 +02:00
if (nextblock.isStr())
addHash256ToBuilder(builder, Api::BlockChain::NextBlockHash, nextblock);
2016-08-16 16:51:22 +02:00
}
private:
bool m_verbose;
2017-10-06 21:47:22 +02:00
};
2016-08-16 16:51:22 +02:00
2019-03-07 18:38:03 +01:00
class GetBlockHeader : public Api::DirectParser
2017-10-06 21:47:22 +02:00
{
public:
2018-02-15 19:50:12 +01:00
GetBlockHeader() : DirectParser(Api::BlockChain::GetBlockHeaderReply, 190) {}
2017-10-06 21:47:22 +02:00
void buildReply(Streaming::MessageBuilder &builder, CBlockIndex *index) {
assert(index);
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::BlockHash, index->GetBlockHash());
2019-05-17 20:42:47 +02:00
builder.add(Api::BlockChain::Confirmations, chainActive.Contains(index) ? chainActive.Height() - index->nHeight + 1: -1);
2019-03-27 22:35:21 +01:00
builder.add(Api::BlockChain::BlockHeight, index->nHeight);
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::Version, index->nVersion);
builder.add(Api::BlockChain::MerkleRoot, index->hashMerkleRoot);
builder.add(Api::BlockChain::Time, (uint64_t) index->nTime);
builder.add(Api::BlockChain::MedianTime, (uint64_t) index->GetMedianTimePast());
builder.add(Api::BlockChain::Nonce, (uint64_t) index->nNonce);
builder.add(Api::BlockChain::Bits, (uint64_t) index->nBits);
builder.add(Api::BlockChain::Difficulty, GetDifficulty(index));
2019-05-17 20:42:47 +02:00
if (index->pprev)
builder.add(Api::BlockChain::PrevBlockHash, index->pprev->GetBlockHash());
2017-10-06 21:47:22 +02:00
auto next = chainActive.Next(index);
if (next)
2018-02-15 19:50:12 +01:00
builder.add(Api::BlockChain::NextBlockHash, next->GetBlockHash());
2017-10-06 21:47:22 +02:00
}
void buildReply(const Message &request, Streaming::MessageBuilder &builder) {
Streaming::MessageParser parser(request.body());
2019-05-17 20:42:47 +02:00
bool first = true;
while (parser.next() == Streaming::FoundTag) {
2018-02-15 19:50:12 +01:00
if (parser.tag() == Api::BlockChain::BlockHash) {
2019-05-17 20:42:47 +02:00
if (!first) builder.add(Api::Separator, true);
2017-10-06 21:47:22 +02:00
uint256 hash = parser.uint256Data();
2018-01-15 15:26:12 +00:00
CBlockIndex *bi = Blocks::Index::get(hash);
if (bi)
return buildReply(builder, bi);
2019-05-17 20:42:47 +02:00
first = false;
2017-10-06 21:47:22 +02:00
}
2019-05-17 20:42:47 +02:00
else if (parser.tag() == Api::BlockChain::BlockHeight) {
if (!first) builder.add(Api::Separator, true);
2017-10-06 21:47:22 +02:00
int height = parser.intData();
auto index = chainActive[height];
if (index)
return buildReply(builder, index);
2019-05-17 20:42:47 +02:00
first = false;
2017-10-06 21:47:22 +02:00
}
}
}
2016-08-16 16:51:22 +02:00
};
struct TransactionSerializationOptions
{
void serialize(Streaming::MessageBuilder &builder, Tx::Iterator &iter)
{
int outIndex = 0;
auto type = iter.next();
while (type != Tx::End) {
if (returnInputs && type == Tx::PrevTxHash) {
builder.add(Api::BlockChain::Tx_IN_TxId, iter.uint256Data());
}
else if (returnInputs && type == Tx::TxInScript) {
builder.add(Api::BlockChain::Tx_InputScript, iter.byteData());
}
else if (returnInputs && type == Tx::PrevTxIndex) {
builder.add(Api::BlockChain::Tx_IN_OutIndex, iter.intData());
}
2019-06-16 00:23:09 +02:00
else if (type == Tx::OutputValue
&& (returnOutputs || returnOutputAmounts) &&
(filterOutputs.empty() || filterOutputs.find(outIndex) != filterOutputs.end())) {
2019-06-16 00:23:09 +02:00
builder.add(Api::BlockChain::Tx_Out_Index, outIndex);
builder.add(Api::BlockChain::Tx_Out_Amount, iter.longData());
}
2019-06-16 00:23:09 +02:00
else if (type == Tx::OutputScript) {
if ((returnOutputs || returnOutputScripts || returnOutputAddresses || returnOutputScriptHashed)
2019-06-17 20:50:17 +02:00
&& (filterOutputs.empty() || filterOutputs.find(outIndex) != filterOutputs.end())) {
if (!returnOutputs && !returnOutputAmounts) // if not added before in OutputValue
builder.add(Api::BlockChain::Tx_Out_Index, outIndex);
if (returnOutputs || returnOutputScripts)
builder.add(Api::BlockChain::Tx_OutputScript, iter.byteData());
if (returnOutputAddresses) {
CScript scriptPubKey(iter.byteData());
std::vector<std::vector<unsigned char> > vSolutions;
Script::TxnOutType whichType;
bool recognizedTx = Script::solver(scriptPubKey, whichType, vSolutions);
if (recognizedTx && (whichType == Script::TX_PUBKEY || whichType == Script::TX_PUBKEYHASH)) {
if (whichType == Script::TX_PUBKEYHASH) {
assert(vSolutions[0].size() == 20);
builder.addByteArray(Api::BlockChain::Tx_Out_Address, vSolutions[0].data(), 20);
} else if (whichType == Script::TX_PUBKEY) {
CPubKey pubKey(vSolutions[0]);
assert (pubKey.IsValid());
CKeyID address = pubKey.GetID();
builder.addByteArray(Api::BlockChain::Tx_Out_Address, address.begin(), 20);
}
}
}
if (returnOutputScriptHashed) {
CSHA256 sha;
sha.Write(iter.byteData().begin(), iter.dataLength());
char buf[32];
sha.Finalize(buf);
builder.addByteArray(Api::BlockChain::Tx_Out_ScriptHash, buf, 32);
}
}
outIndex++;
}
type = iter.next();
}
}
// return true only if serialize would actually export anything
bool shouldRun() const {
const bool partialTxData = returnInputs || returnOutputs || returnOutputAmounts
|| returnOutputScripts || returnOutputAddresses || returnOutputScriptHashed;
return partialTxData;
}
bool returnInputs = false;
bool returnOutputs = false;
bool returnOutputAmounts = false;
bool returnOutputScripts = false;
bool returnOutputAddresses = false;
bool returnOutputScriptHashed = false;
std::set<int> filterOutputs;
};
2019-03-08 17:58:19 +01:00
class GetBlock : public Api::DirectParser
{
public:
2019-03-09 16:36:13 +01:00
class BlockSessionData : public Api::SessionData
{
public:
std::set<uint256> hashes; // script-hashes to filter on
2019-03-09 16:36:13 +01:00
};
2019-03-08 17:58:19 +01:00
GetBlock() : DirectParser(Api::BlockChain::GetBlockReply) {}
int calculateMessageSize(const Message &request) {
CBlockIndex *index = nullptr;
Streaming::MessageParser parser(request.body());
2019-03-09 16:36:13 +01:00
BlockSessionData *session = dynamic_cast<BlockSessionData*>(*data);
if (session == nullptr) {
session = new BlockSessionData();
*data = session;
}
2019-03-17 22:44:21 +01:00
bool filterOnScriptHashes = false;
bool requestOk = false;
2019-03-17 22:44:21 +01:00
bool fullTxData = false;
2019-03-08 17:58:19 +01:00
while (parser.next() == Streaming::FoundTag) {
if (parser.tag() == Api::BlockChain::BlockHash
2019-05-06 14:50:19 +02:00
|| parser.tag() == Api::LiveTransactions::GenericByteData) {
if (parser.dataLength() != 32)
throw Api::ParserException("BlockHash should be a 32 byte-bytearray");
index = Blocks::Index::get(uint256(&parser.bytesData()[0]));
requestOk = true;
2019-03-27 22:35:21 +01:00
} else if (parser.tag() == Api::BlockChain::BlockHeight) {
2019-06-11 21:15:23 +02:00
index = chainActive[parser.intData()];
requestOk = true;
2019-03-09 16:36:13 +01:00
} else if (parser.tag() == Api::BlockChain::ReuseAddressFilter) {
filterOnScriptHashes = parser.boolData();
} else if (parser.tag() == Api::BlockChain::SetFilterScriptHash
|| parser.tag() == Api::BlockChain::AddFilterScriptHash) {
if (parser.dataLength() != 32)
throw Api::ParserException("GetBlock: filter-script-hash should be a 32-bytes bytearray");
if (parser.tag() == Api::BlockChain::SetFilterScriptHash)
session->hashes.clear();
session->hashes.insert(parser.uint256Data());
filterOnScriptHashes = true;
2019-03-17 22:44:21 +01:00
} else if (parser.tag() == Api::BlockChain::FullTransactionData) {
fullTxData = parser.boolData();
2019-03-31 15:13:42 +02:00
if (!fullTxData)
m_fullTxData = false;
} else if (parser.tag() == Api::BlockChain::Include_TxId) {
2019-03-17 22:44:21 +01:00
m_returnTxId = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OffsetInBlock) {
2019-03-17 22:44:21 +01:00
m_returnOffsetInBlock = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_Inputs) {
opt.returnInputs = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_Outputs) {
opt.returnOutputs = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputAmounts) {
opt.returnOutputAmounts = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputScripts) {
opt.returnOutputScripts = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputAddresses) {
opt.returnOutputAddresses = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputScriptHash) {
opt.returnOutputScriptHashed = parser.boolData();
2019-03-08 17:58:19 +01:00
}
}
2019-03-17 22:44:21 +01:00
if (fullTxData) // if explicitly asked.
m_fullTxData = true;
2019-05-17 20:42:47 +02:00
else if (m_returnTxId || opt.shouldRun()) // we imply false if they want a subset.
2019-03-17 22:44:21 +01:00
m_fullTxData = false;
2019-03-09 16:36:13 +01:00
2019-03-08 17:58:19 +01:00
if (index == nullptr)
throw Api::ParserException(requestOk ? "Requested block not found" :
"Request needs to contain either height or blockhash");
m_height = index->nHeight;
2019-03-08 17:58:19 +01:00
try {
m_block = Blocks::DB::instance()->loadBlock(index->GetBlockPos());
assert(m_block.isFullBlock());
2019-03-08 17:58:19 +01:00
} catch (...) {
throw Api::ParserException("Blockdata not present on this Hub");
2019-03-08 17:58:19 +01:00
}
Tx::Iterator iter(m_block);
auto type = iter.next();
bool oneEnd = false, txMatched = !filterOnScriptHashes;
2019-03-17 22:44:21 +01:00
int size = 0, matchedOutputs = 0, matchedInputsSize = 0;
int txOutputCount = 0, txInputSize = 0, txOutputScriptSizes = 0;
int matchedOutputScriptSizes = 0;
while (true) {
if (type == Tx::End) {
2019-03-31 15:13:42 +02:00
if (oneEnd) // then the second end means end of block
break;
if (txMatched) {
Tx prevTx = iter.prevTx();
size += prevTx.size();
2019-03-17 22:44:21 +01:00
matchedInputsSize += txInputSize;
matchedOutputs += txOutputCount;
matchedOutputScriptSizes += txOutputScriptSizes;
m_transactions.push_back(std::make_pair(prevTx.offsetInBlock(m_block), prevTx.size()));
txMatched = !filterOnScriptHashes;
}
oneEnd = true;
2019-03-17 22:44:21 +01:00
txInputSize = 0;
txOutputCount = 0;
txOutputScriptSizes = 0;
} else {
oneEnd = false;
}
2019-06-13 12:30:20 +02:00
if (opt.returnInputs && type == Tx::PrevTxHash) {
2019-03-17 22:44:21 +01:00
txInputSize += 42; // prevhash: 32 + 3 + prevIndex; 6 + 1
}
else if (opt.returnInputs && type == Tx::TxInScript) {
2019-03-17 22:44:21 +01:00
txInputSize += iter.dataLength() + 3;
}
else if (type == Tx::OutputValue) {
++txOutputCount;
}
else if (type == Tx::OutputScript) {
txOutputScriptSizes += iter.dataLength() + 4;
if (!txMatched && session->hashes.find(iter.hashedByteData()) != session->hashes.end())
txMatched = true;
}
type = iter.next();
}
2019-03-17 22:44:21 +01:00
int bytesPerTx = 1;
if (m_returnTxId) bytesPerTx += 35;
if (m_returnOffsetInBlock) bytesPerTx += 6;
if (m_fullTxData) bytesPerTx += 5; // actual tx-data is in 'size'
int bytesPerOutput = 5;
if (opt.returnOutputAmounts || opt.returnOutputs) bytesPerOutput += 10;
if (opt.returnOutputAddresses) bytesPerOutput += 23;
if (opt.returnOutputScriptHashed) bytesPerOutput += 35;
2019-03-17 22:44:21 +01:00
int total = 45 + int(m_transactions.size()) * bytesPerTx;
2019-03-17 22:44:21 +01:00
if (m_fullTxData) total += size;
if (opt.returnOutputs || opt.returnOutputScripts)
2019-03-17 22:44:21 +01:00
total += matchedOutputScriptSizes;
if (opt.returnInputs)
2019-03-17 22:44:21 +01:00
total += matchedInputsSize;
if (opt.returnOutputs || opt.returnOutputAddresses || opt.returnOutputAmounts | opt.returnOutputScriptHashed)
2019-03-17 22:44:21 +01:00
total += matchedOutputs * bytesPerOutput;
logDebug(Log::ApiServer) << "GetBlock calculated to need at most" << total << "bytes";
logDebug(Log::ApiServer) << " tx" << bytesPerTx << "*" << m_transactions.size() << "(=num tx). Plus"
<< bytesPerOutput << "bytes per output (" << matchedOutputs << ")";
logDebug(Log::ApiServer) << " matched Script Output sizes:" << matchedOutputScriptSizes;
return total;
2019-03-08 17:58:19 +01:00
}
void buildReply(const Message&, Streaming::MessageBuilder &builder) {
assert(m_height >= 0);
2019-03-27 22:35:21 +01:00
builder.add(Api::BlockChain::BlockHeight, m_height);
builder.add(Api::BlockChain::BlockHash, m_block.createHash());
2019-03-17 22:44:21 +01:00
for (auto posAndSize : m_transactions) {
if (m_returnOffsetInBlock)
builder.add(Api::BlockChain::Tx_OffsetInBlock, posAndSize.first);
if (m_returnTxId) {
Tx tx(m_block.data().mid(posAndSize.first, posAndSize.second));
builder.add(Api::BlockChain::TxId, tx.createHash());
}
if (opt.shouldRun()) {
2019-03-17 22:44:21 +01:00
Tx::Iterator iter(m_block, posAndSize.first);
2019-06-19 22:44:48 +02:00
if (posAndSize.first < 91) {
iter.next(Tx::PrevTxIndex); // skip version, prevTxId and prevTxIndex for coinbase
assert(iter.tag() == Tx::PrevTxIndex);
}
opt.serialize(builder, iter);
2019-03-17 22:44:21 +01:00
}
if (m_fullTxData)
2019-03-09 16:36:13 +01:00
builder.add(Api::BlockChain::GenericByteData,
m_block.data().mid(posAndSize.first, posAndSize.second));
2019-03-17 22:44:21 +01:00
builder.add(Api::BlockChain::Separator, true);
2019-03-09 16:36:13 +01:00
}
2019-03-08 17:58:19 +01:00
}
FastBlock m_block;
2019-03-09 16:36:13 +01:00
std::vector<std::pair<int, int>> m_transactions; // list of offset-in-block and length of tx to include
2019-03-17 22:44:21 +01:00
bool m_fullTxData = true;
bool m_returnTxId = false;
bool m_returnOffsetInBlock = true;
int m_height = -1;
TransactionSerializationOptions opt;
2019-03-08 17:58:19 +01:00
};
2019-03-07 18:38:03 +01:00
class GetBlockCount : public Api::DirectParser
2017-10-07 09:27:27 +02:00
{
public:
2018-02-15 19:50:12 +01:00
GetBlockCount() : DirectParser(Api::BlockChain::GetBlockCountReply, 20) {}
2017-10-07 09:27:27 +02:00
void buildReply(const Message&, Streaming::MessageBuilder &builder) {
2019-03-27 22:35:21 +01:00
builder.add(Api::BlockChain::BlockHeight, chainActive.Height());
2017-10-07 09:27:27 +02:00
}
};
2019-05-06 14:50:19 +02:00
// Live transactions
2016-08-16 16:51:22 +02:00
2019-05-06 14:50:19 +02:00
class GetLiveTransaction : public Api::RpcParser
2016-08-16 16:51:22 +02:00
{
public:
2019-05-06 14:50:19 +02:00
GetLiveTransaction() : RpcParser("getrawtransaction", Api::LiveTransactions::GetTransactionReply) {}
2016-08-16 16:51:22 +02:00
virtual void createRequest(const Message &message, UniValue &output) {
std::string txid;
Streaming::MessageParser parser(message.body());
while (parser.next() == Streaming::FoundTag) {
2019-05-06 14:50:19 +02:00
if (parser.tag() == Api::LiveTransactions::TxId
|| parser.tag() == Api::LiveTransactions::GenericByteData)
2019-05-08 12:49:33 +02:00
txid = parser.uint256Data().ToString();
2016-08-16 16:51:22 +02:00
}
2020-07-27 12:33:28 +02:00
if (txid.empty())
throw std::runtime_error("Missing or invalid parameter: TXID should be a sha256 (bytearray)");
2016-08-16 16:51:22 +02:00
output.push_back(std::make_pair("parameter 1", UniValue(UniValue::VSTR, txid)));
}
2017-10-06 19:12:30 +02:00
virtual int calculateMessageSize(const UniValue &result) const {
2016-08-16 16:51:22 +02:00
return result.get_str().size() / 2 + 20;
}
};
2019-05-06 14:50:19 +02:00
class SendLiveTransaction : public Api::RpcParser
2016-08-16 16:51:22 +02:00
{
public:
2019-05-06 14:50:19 +02:00
SendLiveTransaction() : RpcParser("sendrawtransaction", Api::LiveTransactions::SendTransactionReply, 34) {}
2016-08-16 16:51:22 +02:00
virtual void createRequest(const Message &message, UniValue &output) {
std::string tx;
Streaming::MessageParser parser(message.body());
while (parser.next() == Streaming::FoundTag) {
2019-05-06 14:50:19 +02:00
if (parser.tag() == Api::LiveTransactions::Transaction
|| parser.tag() == Api::LiveTransactions::GenericByteData)
2016-08-16 16:51:22 +02:00
boost::algorithm::hex(parser.bytesData(), back_inserter(tx));
}
output.push_back(std::make_pair("", UniValue(UniValue::VSTR, tx)));
}
};
// Util
class CreateAddress : public Api::DirectParser
2016-08-16 16:51:22 +02:00
{
public:
CreateAddress() : DirectParser(Api::Util::CreateAddressReply, 150) {}
2016-08-16 16:51:22 +02:00
2020-07-31 15:58:15 +02:00
virtual void buildReply(const Message &, Streaming::MessageBuilder &builder) {
CKey key;
key.MakeNewKey();
assert(key.IsCompressed());
const CKeyID pkh = key.GetPubKey().GetID();
builder.addByteArray(Api::Util::BitcoinP2PKHAddress, pkh.begin(), pkh.size());
2019-03-27 22:35:21 +01:00
builder.addByteArray(Api::Util::PrivateKey, key.begin(), key.size());
2016-08-16 16:51:22 +02:00
}
};
2019-03-07 18:38:03 +01:00
class ValidateAddress : public Api::RpcParser {
2016-08-16 16:51:22 +02:00
public:
2018-02-15 19:50:12 +01:00
ValidateAddress() : RpcParser("validateaddress", Api::Util::ValidateAddressReply, 300) {}
2016-08-16 16:51:22 +02:00
2017-10-06 19:12:30 +02:00
virtual void buildReply(Streaming::MessageBuilder &builder, const UniValue &result) {
2016-08-16 16:51:22 +02:00
const UniValue &isValid = find_value(result, "isvalid");
2018-02-15 19:50:12 +01:00
builder.add(Api::Util::IsValid, isValid.getBool());
2016-08-16 16:51:22 +02:00
const UniValue &address = find_value(result, "address");
builder.add(Api::Util::BitcoinP2PKHAddress, address.get_str()); // FIXME this is wrong, we should return a ripe160 address instead.
2016-08-16 16:51:22 +02:00
const UniValue &scriptPubKey = find_value(result, "scriptPubKey");
std::vector<char> bytearray;
boost::algorithm::unhex(scriptPubKey.get_str(), back_inserter(bytearray));
2018-02-15 19:50:12 +01:00
builder.add(Api::Util::ScriptPubKey, bytearray);
2016-08-16 16:51:22 +02:00
bytearray.clear();
}
virtual void createRequest(const Message &message, UniValue &output) {
Streaming::MessageParser parser(message.body());
while (parser.next() == Streaming::FoundTag) {
if (parser.tag() == Api::Util::BitcoinP2PKHAddress) {
// FIXME bitcoin address is always ripe160, so this fails.
2016-08-16 16:51:22 +02:00
output.push_back(std::make_pair("param 1", UniValue(UniValue::VSTR, parser.stringData())));
return;
}
}
}
};
2019-03-07 18:38:03 +01:00
2019-03-09 22:00:02 +01:00
class RegTestGenerateBlock : public Api::RpcParser {
public:
RegTestGenerateBlock() : RpcParser("generate", Api::RegTest::GenerateBlockReply) {}
void createRequest(const Message &message, UniValue &output) {
Streaming::MessageParser parser(message.body());
int amount = 1;
std::vector<uint8_t> outAddress;
while (parser.next() == Streaming::FoundTag) {
if (parser.tag() == Api::RegTest::Amount)
amount = parser.intData();
else if (parser.tag() == Api::RegTest::BitcoinP2PKHAddress)
2019-03-09 22:00:02 +01:00
outAddress = parser.unsignedBytesData();
}
if (amount <= 0 || amount > 150)
throw Api::ParserException("Invalid Amount argument");
if (outAddress.size() != 20)
throw Api::ParserException("Invalid BitcoinAddress (need 20 byte array)");
std::string hex;
boost::algorithm::hex(outAddress, back_inserter(hex));
output.push_back(std::make_pair("item0", UniValue(amount)));
output.push_back(std::make_pair("item1", UniValue(UniValue::VSTR, hex)));
m_messageSize = amount * 35;
}
void buildReply(Streaming::MessageBuilder &builder, const UniValue &result) {
assert(result.getType() == UniValue::VARR);
2020-07-31 15:58:15 +02:00
for (size_t i = 0; i < result.size(); ++i) {
2019-03-09 22:00:02 +01:00
assert(result[i].get_str().size() == 64);
2019-05-17 20:42:47 +02:00
addHash256ToBuilder(builder, Api::RegTest::BlockHash, result[i]);
2019-03-09 22:00:02 +01:00
}
}
};
2019-04-10 16:16:05 +02:00
class GetTransaction : public Api::DirectParser {
public:
GetTransaction() : DirectParser(Api::BlockChain::GetTransactionReply) {}
int calculateMessageSize(const Message &request) override {
Streaming::MessageParser parser(request);
CBlockIndex *index = nullptr;
bool fullTxData = false;
while (parser.next() == Streaming::FoundTag) {
if (parser.tag() == Api::BlockChain::BlockHeight) {
2019-06-11 21:15:23 +02:00
index = chainActive[parser.intData()];
if (!index)
throw Api::ParserException("Unknown blockheight");
} else if (parser.tag() == Api::BlockChain::BlockHash) {
index = Blocks::Index::get(parser.uint256Data());
if (!index)
throw Api::ParserException("Unknown block hash");
} else if (parser.tag() == Api::BlockChain::Tx_OffsetInBlock) {
m_offsetInBlock = parser.intData();
2019-06-17 20:50:17 +02:00
if (m_offsetInBlock < 81)
throw Api::ParserException("OffsetInBlock should be a positive number");
} else if (parser.tag() == Api::BlockChain::FullTransactionData) {
fullTxData = parser.boolData();
if (!fullTxData)
m_fullTxData = false;
} else if (parser.tag() == Api::BlockChain::Include_TxId) {
m_returnTxId = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OffsetInBlock) {
m_returnOffsetInBlock = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_Inputs) {
opt.returnInputs = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_Outputs) {
opt.returnOutputs = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputAmounts) {
opt.returnOutputAmounts = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputScripts) {
opt.returnOutputScripts = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputScriptHash) {
opt.returnOutputScriptHashed = parser.boolData();
} else if (parser.tag() == Api::BlockChain::Include_OutputAddresses) {
opt.returnOutputAddresses = parser.boolData();
} else if (parser.tag() == Api::BlockChain::FilterOutputIndex) {
if (!parser.isInt() || parser.intData() < 0)
throw Api::ParserException("FilterOutputIndex should be a positive number");
opt.filterOutputs.insert(parser.intData());
}
}
if (fullTxData) // if explicitly asked.
m_fullTxData = true;
else if (m_returnTxId || opt.shouldRun()) // we imply false if they want a subset.
m_fullTxData = false;
2019-04-10 16:16:05 +02:00
2019-06-17 20:50:17 +02:00
if (!index || m_offsetInBlock < 81)
throw Api::ParserException("Incomplete request.");
if (index->nDataPos < 4 || (index->nStatus & BLOCK_HAVE_DATA) == 0)
throw Api::ParserException("Block known but data not available");
FastBlock block;
try {
block = Blocks::DB::instance()->loadBlock(index->GetBlockPos());
assert(block.isFullBlock());
} catch (...) {
throw Api::ParserException("Blockdata not present on this Hub");
}
if (m_offsetInBlock > block.size())
throw Api::ParserException("OffsetInBlock larger than block");
Tx::Iterator iter(block, m_offsetInBlock);
if (iter.next(Tx::End) == Tx::End)
m_tx = iter.prevTx();
int amount = m_fullTxData ? m_tx.size() + 10 : 0;
if (m_returnTxId) amount += 40;
if (m_returnOffsetInBlock) amount += 10;
if (opt.shouldRun()) amount += m_tx.size();
return amount;
2019-04-10 16:16:05 +02:00
}
void buildReply(const Message &, Streaming::MessageBuilder &builder) override {
if (m_returnTxId)
builder.add(Api::BlockChain::TxId, m_tx.createHash());
if (m_returnOffsetInBlock)
builder.add(Api::BlockChain::Tx_OffsetInBlock, m_offsetInBlock);
if (opt.shouldRun()) {
Tx::Iterator iter(m_tx);
opt.serialize(builder, iter);
}
if (m_fullTxData)
builder.add(Api::BlockChain::GenericByteData, m_tx.data());
}
private:
bool m_fullTxData = true;
bool m_returnTxId = false;
bool m_returnOffsetInBlock = false;
int m_offsetInBlock = 0;
Tx m_tx;
TransactionSerializationOptions opt;
2019-04-10 16:16:05 +02:00
};
2019-06-10 16:44:17 +02:00
class UtxoFetcher: public Api::DirectParser
{
public:
UtxoFetcher(int replyId)
: DirectParser(replyId)
{
}
2019-09-13 16:01:15 +02:00
uint256 lookup(int blockHeight, int offsetInBlock) const
{
if (blockHeight == -1 || offsetInBlock == 0)
throw Api::ParserException("Invalid or missing txid / blockheight+offsetInBlock");
// try to find txid
auto index = chainActive[blockHeight];
if (!index)
throw Api::ParserException("Unknown blockheight");
FastBlock block;
try {
block = Blocks::DB::instance()->loadBlock(index->GetBlockPos());
} catch (...) {
throw Api::ParserException("Blockdata not present on this Hub");
}
if (offsetInBlock > block.size())
throw Api::ParserException("OffsetInBlock larger than block");
Tx::Iterator iter(block, offsetInBlock);
2020-07-27 21:26:21 +02:00
try {
if (iter.next(Tx::End) == Tx::End)
return iter.prevTx().createHash();
} catch (const std::runtime_error &error) {
logDebug() << error;
}
throw Api::ParserException("Invalid data, is your offsetInBlock correct?");
2019-09-13 16:01:15 +02:00
}
2019-06-10 16:44:17 +02:00
int calculateMessageSize(const Message &request) override {
int validCount = 0;
Streaming::MessageParser parser(request.body());
uint256 txid;
2019-09-13 16:01:15 +02:00
int blockHeight = -1;
int offsetInBlock = 0;
2019-06-10 16:44:17 +02:00
int output = 0;
while (parser.next() == Streaming::FoundTag) {
if (parser.tag() == Api::LiveTransactions::TxId)
txid = parser.uint256Data();
2019-09-13 16:01:15 +02:00
else if (parser.tag() == Api::LiveTransactions::BlockHeight) {
blockHeight = parser.intData();
}
else if (parser.tag() == Api::LiveTransactions::OffsetInBlock) {
offsetInBlock = parser.intData();
}
2019-06-10 16:44:17 +02:00
else if (parser.tag() == Api::LiveTransactions::OutIndex) {
if (!parser.isInt())
throw Api::ParserException("index wasn't number");
output = parser.intData();
}
else if (parser.tag() == Api::Separator) {
if (txid.IsNull())
2019-09-13 16:01:15 +02:00
txid = lookup(blockHeight, offsetInBlock);
assert(!txid.IsNull());
blockHeight = -1;
offsetInBlock = 0;
2019-06-10 16:44:17 +02:00
auto out = g_utxo->find(txid, output);
m_utxos.push_back(out);
if (out.isValid())
validCount++;
output = 0;
2019-09-13 16:01:15 +02:00
txid.SetNull();
2019-06-10 16:44:17 +02:00
}
}
if (txid.IsNull())
2019-09-13 16:01:15 +02:00
txid = lookup(blockHeight, offsetInBlock);
assert(!txid.IsNull());
2019-06-10 16:44:17 +02:00
auto out = g_utxo->find(txid, output);
m_utxos.push_back(out);
if (out.isValid())
validCount++;
const bool isVerbose = replyMessageId() == Api::LiveTransactions::GetUnspentOutputReply;
2019-09-13 16:01:15 +02:00
int size = (m_utxos.size() - validCount) + validCount * 20;
2019-06-10 16:44:17 +02:00
if (isVerbose) {
// since I can't assume the max-size of the output-script, I need to actually fetch them here.
for (auto unspent : m_utxos) {
if (unspent.isValid()) {
size += 10; // for amount
UnspentOutputData uod(unspent);
size += uod.outputScript().size() + 3;
}
}
}
return size;
}
void buildReply(const Message&, Streaming::MessageBuilder &builder) override {
const bool verbose = replyMessageId() == Api::LiveTransactions::GetUnspentOutputReply;
bool first = true;
for (auto unspent : m_utxos) {
if (first)
first = false;
else
builder.add(Api::LiveTransactions::Separator, true);
const bool isValid = unspent.isValid();
builder.add(Api::LiveTransactions::UnspentState, isValid);
if (isValid) {
builder.add(Api::LiveTransactions::BlockHeight, unspent.blockHeight());
builder.add(Api::LiveTransactions::OffsetInBlock, unspent.offsetInBlock());
2019-11-30 18:56:25 +01:00
builder.add(Api::LiveTransactions::OutIndex, unspent.outIndex());
2019-06-10 16:44:17 +02:00
if (verbose) {
UnspentOutputData uod(unspent);
builder.add(Api::LiveTransactions::Amount, (uint64_t) uod.outputValue());
builder.add(Api::LiveTransactions::OutputScript, uod.outputScript());
}
}
}
}
private:
std::vector<UnspentOutput> m_utxos;
};
2016-08-16 16:51:22 +02:00
}
2019-03-07 18:38:03 +01:00
Api::Parser *Api::createParser(const Message &message)
2016-08-16 16:51:22 +02:00
{
switch (message.serviceId()) {
2018-02-15 19:50:12 +01:00
case Api::BlockChainService:
2016-08-16 16:51:22 +02:00
switch (message.messageId()) {
2018-02-15 19:50:12 +01:00
case Api::BlockChain::GetBlockChainInfo:
2016-08-16 16:51:22 +02:00
return new GetBlockChainInfo();
2018-02-15 19:50:12 +01:00
case Api::BlockChain::GetBestBlockHash:
2016-08-16 16:51:22 +02:00
return new GetBestBlockHash();
2018-02-15 19:50:12 +01:00
case Api::BlockChain::GetBlock:
2016-08-16 16:51:22 +02:00
return new GetBlock();
2019-03-08 17:58:19 +01:00
case Api::BlockChain::GetBlockVerbose:
return new GetBlockLegacy();
2018-02-15 19:50:12 +01:00
case Api::BlockChain::GetBlockHeader:
2017-10-06 21:47:22 +02:00
return new GetBlockHeader();
2018-02-15 19:50:12 +01:00
case Api::BlockChain::GetBlockCount:
2017-10-07 09:27:27 +02:00
return new GetBlockCount();
case Api::BlockChain::GetTransaction:
return new GetTransaction();
2016-08-16 16:51:22 +02:00
}
break;
2019-05-06 14:50:19 +02:00
case Api::LiveTransactionService:
2016-08-16 16:51:22 +02:00
switch (message.messageId()) {
2019-05-06 14:50:19 +02:00
case Api::LiveTransactions::GetTransaction:
return new GetLiveTransaction();
case Api::LiveTransactions::SendTransaction:
return new SendLiveTransaction();
2019-06-10 16:44:17 +02:00
case Api::LiveTransactions::IsUnspent:
return new UtxoFetcher(Api::LiveTransactions::IsUnspentReply);
case Api::LiveTransactions::GetUnspentOutput:
return new UtxoFetcher(Api::LiveTransactions::GetUnspentOutputReply);
2016-08-16 16:51:22 +02:00
}
break;
2018-02-15 19:50:12 +01:00
case Api::UtilService:
2016-08-16 16:51:22 +02:00
switch (message.messageId()) {
2018-02-15 19:50:12 +01:00
case Api::Util::CreateAddress:
2016-08-16 16:51:22 +02:00
return new CreateAddress();
2018-02-15 19:50:12 +01:00
case Api::Util::ValidateAddress:
2016-08-16 16:51:22 +02:00
return new ValidateAddress();
}
break;
2019-03-09 22:00:02 +01:00
case Api::RegTestService:
switch (message.messageId()) {
case Api::RegTest::GenerateBlock:
return new RegTestGenerateBlock();
}
break;
2016-08-16 16:51:22 +02:00
}
throw std::runtime_error("Unsupported command");
}
2019-03-07 18:38:03 +01:00
Api::Parser::Parser(ParserType type, int answerMessageId, int messageSize)
2017-10-06 19:12:30 +02:00
: m_messageSize(messageSize),
m_replyMessageId(answerMessageId),
2017-10-06 20:18:09 +02:00
m_type(type)
{
}
2019-03-09 16:36:13 +01:00
void Api::Parser::setSessionData(Api::SessionData **value)
{
data = value;
}
2019-03-07 18:38:03 +01:00
Api::RpcParser::RpcParser(const std::string &method, int replyMessageId, int messageSize)
2017-10-06 20:18:09 +02:00
: Parser(WrapsRPCCall, replyMessageId, messageSize),
2016-08-16 16:51:22 +02:00
m_method(method)
{
}
2019-03-07 18:38:03 +01:00
void Api::RpcParser::buildReply(Streaming::MessageBuilder &builder, const UniValue &result)
2016-08-16 16:51:22 +02:00
{
assert(result.isStr());
2019-06-15 19:57:35 +02:00
if (result.get_str().size() == 64) { // assume sha256 which for some reason gets reversed in text
addHash256ToBuilder(builder, 1, result);
2019-06-15 19:57:35 +02:00
} else {
std::vector<char> answer;
boost::algorithm::unhex(result.get_str(), back_inserter(answer));
builder.add(1, answer);
}
2016-08-16 16:51:22 +02:00
}
2019-03-07 18:38:03 +01:00
void Api::RpcParser::createRequest(const Message &, UniValue &)
2016-08-16 16:51:22 +02:00
{
}
2019-03-07 18:38:03 +01:00
int Api::RpcParser::calculateMessageSize(const UniValue &result) const
2016-08-16 16:51:22 +02:00
{
return result.get_str().size() + 20;
}
2017-10-06 20:18:09 +02:00
2019-03-07 18:38:03 +01:00
Api::DirectParser::DirectParser(int replyMessageId, int messageSize)
2017-10-06 20:18:09 +02:00
: Parser(IncludesHandler, replyMessageId, messageSize)
{
}