Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/rawtransaction.cpp
Show First 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
#include <validationinterface.h> | #include <validationinterface.h> | ||||
#include <cstdint> | #include <cstdint> | ||||
#include <numeric> | #include <numeric> | ||||
#include <univalue.h> | #include <univalue.h> | ||||
static void TxToJSON(const CTransaction &tx, const BlockHash &hashBlock, | static void TxToJSON(const CTransaction &tx, const BlockHash &hashBlock, | ||||
UniValue &entry) { | UniValue &entry, CChainState &active_chainstate) { | ||||
// Call into TxToUniv() in bitcoin-common to decode the transaction hex. | // Call into TxToUniv() in bitcoin-common to decode the transaction hex. | ||||
// | // | ||||
// Blockchain contextual information (confirmations and blocktime) is not | // Blockchain contextual information (confirmations and blocktime) is not | ||||
// available to code in bitcoin-common, so we query them here and push the | // available to code in bitcoin-common, so we query them here and push the | ||||
// data into the returned UniValue. | // data into the returned UniValue. | ||||
TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags()); | TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags()); | ||||
if (!hashBlock.IsNull()) { | if (!hashBlock.IsNull()) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
entry.pushKV("blockhash", hashBlock.GetHex()); | entry.pushKV("blockhash", hashBlock.GetHex()); | ||||
CBlockIndex *pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); | CBlockIndex *pindex = | ||||
active_chainstate.m_blockman.LookupBlockIndex(hashBlock); | |||||
if (pindex) { | if (pindex) { | ||||
if (::ChainActive().Contains(pindex)) { | if (active_chainstate.m_chain.Contains(pindex)) { | ||||
entry.pushKV("confirmations", | entry.pushKV("confirmations", | ||||
1 + ::ChainActive().Height() - pindex->nHeight); | 1 + active_chainstate.m_chain.Height() - | ||||
pindex->nHeight); | |||||
entry.pushKV("time", pindex->GetBlockTime()); | entry.pushKV("time", pindex->GetBlockTime()); | ||||
entry.pushKV("blocktime", pindex->GetBlockTime()); | entry.pushKV("blocktime", pindex->GetBlockTime()); | ||||
} else { | } else { | ||||
entry.pushKV("confirmations", 0); | entry.pushKV("confirmations", 0); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
HelpExampleRpc("getrawtransaction", "\"mytxid\", true") + | HelpExampleRpc("getrawtransaction", "\"mytxid\", true") + | ||||
HelpExampleCli("getrawtransaction", | HelpExampleCli("getrawtransaction", | ||||
"\"mytxid\" false \"myblockhash\"") + | "\"mytxid\" false \"myblockhash\"") + | ||||
HelpExampleCli("getrawtransaction", | HelpExampleCli("getrawtransaction", | ||||
"\"mytxid\" true \"myblockhash\"")}, | "\"mytxid\" true \"myblockhash\"")}, | ||||
[&](const RPCHelpMan &self, const Config &config, | [&](const RPCHelpMan &self, const Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | const JSONRPCRequest &request) -> UniValue { | ||||
const NodeContext &node = EnsureNodeContext(request.context); | const NodeContext &node = EnsureNodeContext(request.context); | ||||
ChainstateManager &chainman = EnsureChainman(request.context); | |||||
bool in_active_chain = true; | bool in_active_chain = true; | ||||
TxId txid = TxId(ParseHashV(request.params[0], "parameter 1")); | TxId txid = TxId(ParseHashV(request.params[0], "parameter 1")); | ||||
CBlockIndex *blockindex = nullptr; | CBlockIndex *blockindex = nullptr; | ||||
const CChainParams ¶ms = config.GetChainParams(); | const CChainParams ¶ms = config.GetChainParams(); | ||||
if (txid == params.GenesisBlock().hashMerkleRoot) { | if (txid == params.GenesisBlock().hashMerkleRoot) { | ||||
// Special exception for the genesis block coinbase transaction | // Special exception for the genesis block coinbase transaction | ||||
Show All 12 Lines | return RPCHelpMan{ | ||||
: request.params[1].get_bool(); | : request.params[1].get_bool(); | ||||
} | } | ||||
if (!request.params[2].isNull()) { | if (!request.params[2].isNull()) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
BlockHash blockhash( | BlockHash blockhash( | ||||
ParseHashV(request.params[2], "parameter 3")); | ParseHashV(request.params[2], "parameter 3")); | ||||
blockindex = g_chainman.m_blockman.LookupBlockIndex(blockhash); | blockindex = chainman.m_blockman.LookupBlockIndex(blockhash); | ||||
if (!blockindex) { | if (!blockindex) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Block hash not found"); | "Block hash not found"); | ||||
} | } | ||||
in_active_chain = ::ChainActive().Contains(blockindex); | in_active_chain = chainman.ActiveChain().Contains(blockindex); | ||||
} | } | ||||
bool f_txindex_ready = false; | bool f_txindex_ready = false; | ||||
if (g_txindex && !blockindex) { | if (g_txindex && !blockindex) { | ||||
f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain(); | f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain(); | ||||
} | } | ||||
BlockHash hash_block; | BlockHash hash_block; | ||||
Show All 27 Lines | return RPCHelpMan{ | ||||
if (!fVerbose) { | if (!fVerbose) { | ||||
return EncodeHexTx(*tx, RPCSerializationFlags()); | return EncodeHexTx(*tx, RPCSerializationFlags()); | ||||
} | } | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
if (blockindex) { | if (blockindex) { | ||||
result.pushKV("in_active_chain", in_active_chain); | result.pushKV("in_active_chain", in_active_chain); | ||||
} | } | ||||
TxToJSON(*tx, hash_block, result); | TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate()); | ||||
return result; | return result; | ||||
}, | }, | ||||
}; | }; | ||||
} | } | ||||
static RPCHelpMan gettxoutproof() { | static RPCHelpMan gettxoutproof() { | ||||
return RPCHelpMan{ | return RPCHelpMan{ | ||||
"gettxoutproof", | "gettxoutproof", | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
setTxIds.insert(txid); | setTxIds.insert(txid); | ||||
oneTxId = txid; | oneTxId = txid; | ||||
} | } | ||||
CBlockIndex *pblockindex = nullptr; | CBlockIndex *pblockindex = nullptr; | ||||
BlockHash hashBlock; | BlockHash hashBlock; | ||||
ChainstateManager &chainman = EnsureChainman(request.context); | |||||
if (!request.params[1].isNull()) { | if (!request.params[1].isNull()) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
hashBlock = | hashBlock = | ||||
BlockHash(ParseHashV(request.params[1], "blockhash")); | BlockHash(ParseHashV(request.params[1], "blockhash")); | ||||
pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); | pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock); | ||||
if (!pblockindex) { | if (!pblockindex) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Block not found"); | "Block not found"); | ||||
} | } | ||||
} else { | } else { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// Loop through txids and try to find which block they're in. | // Loop through txids and try to find which block they're in. | ||||
// Exit loop once a block is found. | // Exit loop once a block is found. | ||||
for (const auto &txid : setTxIds) { | for (const auto &txid : setTxIds) { | ||||
const Coin &coin = | const Coin &coin = AccessByTxid( | ||||
AccessByTxid(::ChainstateActive().CoinsTip(), txid); | chainman.ActiveChainstate().CoinsTip(), txid); | ||||
if (!coin.IsSpent()) { | if (!coin.IsSpent()) { | ||||
pblockindex = ::ChainActive()[coin.GetHeight()]; | pblockindex = chainman.ActiveChain()[coin.GetHeight()]; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Allow txindex to catch up if we need to query it and before we | // Allow txindex to catch up if we need to query it and before we | ||||
// acquire cs_main. | // acquire cs_main. | ||||
if (g_txindex && !pblockindex) { | if (g_txindex && !pblockindex) { | ||||
Show All 10 Lines | return RPCHelpMan{ | ||||
/* block_index */ nullptr, | /* block_index */ nullptr, | ||||
/* mempool */ nullptr, oneTxId, Params().GetConsensus(), | /* mempool */ nullptr, oneTxId, Params().GetConsensus(), | ||||
hashBlock); | hashBlock); | ||||
if (!tx || hashBlock.IsNull()) { | if (!tx || hashBlock.IsNull()) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Transaction not yet in block"); | "Transaction not yet in block"); | ||||
} | } | ||||
pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); | pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock); | ||||
if (!pblockindex) { | if (!pblockindex) { | ||||
throw JSONRPCError(RPC_INTERNAL_ERROR, | throw JSONRPCError(RPC_INTERNAL_ERROR, | ||||
"Transaction index corrupt"); | "Transaction index corrupt"); | ||||
} | } | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
if (!ReadBlockFromDisk(block, pblockindex, params)) { | if (!ReadBlockFromDisk(block, pblockindex, params)) { | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
std::vector<size_t> vIndex; | std::vector<size_t> vIndex; | ||||
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != | if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != | ||||
merkleBlock.header.hashMerkleRoot) { | merkleBlock.header.hashMerkleRoot) { | ||||
return res; | return res; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CBlockIndex *pindex = g_chainman.m_blockman.LookupBlockIndex( | ChainstateManager &chainman = EnsureChainman(request.context); | ||||
const CBlockIndex *pindex = chainman.m_blockman.LookupBlockIndex( | |||||
merkleBlock.header.GetHash()); | merkleBlock.header.GetHash()); | ||||
if (!pindex || !::ChainActive().Contains(pindex) || | if (!pindex || !chainman.ActiveChain().Contains(pindex) || | ||||
pindex->nTx == 0) { | pindex->nTx == 0) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Block not found in chain"); | "Block not found in chain"); | ||||
} | } | ||||
// Check if proof is valid, only add results if so | // Check if proof is valid, only add results if so | ||||
if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) { | if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) { | ||||
for (const uint256 &hash : vMatch) { | for (const uint256 &hash : vMatch) { | ||||
▲ Show 20 Lines • Show All 317 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
// Fetch previous transactions (inputs): | // Fetch previous transactions (inputs): | ||||
CCoinsView viewDummy; | CCoinsView viewDummy; | ||||
CCoinsViewCache view(&viewDummy); | CCoinsViewCache view(&viewDummy); | ||||
{ | { | ||||
const CTxMemPool &mempool = EnsureMemPool(request.context); | const CTxMemPool &mempool = EnsureMemPool(request.context); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
LOCK(mempool.cs); | LOCK(mempool.cs); | ||||
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); | CCoinsViewCache &viewChain = EnsureChainman(request.context) | ||||
.ActiveChainstate() | |||||
.CoinsTip(); | |||||
CCoinsViewMemPool viewMempool(&viewChain, mempool); | CCoinsViewMemPool viewMempool(&viewChain, mempool); | ||||
// temporarily switch cache backend to db+mempool view | // temporarily switch cache backend to db+mempool view | ||||
view.SetBackend(viewMempool); | view.SetBackend(viewMempool); | ||||
for (const CTxIn &txin : mergedTx.vin) { | for (const CTxIn &txin : mergedTx.vin) { | ||||
// Load entries from viewChain into view; can fail. | // Load entries from viewChain into view; can fail. | ||||
view.AccessCoin(txin.prevout); | view.AccessCoin(txin.prevout); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 354 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
result_0.pushKV("txid", txid.GetHex()); | result_0.pushKV("txid", txid.GetHex()); | ||||
TxValidationState state; | TxValidationState state; | ||||
bool test_accept_res; | bool test_accept_res; | ||||
Amount fee = Amount::zero(); | Amount fee = Amount::zero(); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
test_accept_res = AcceptToMemoryPool( | test_accept_res = AcceptToMemoryPool( | ||||
::ChainstateActive(), config, mempool, state, std::move(tx), | EnsureChainman(request.context).ActiveChainstate(), config, | ||||
false /* bypass_limits */, true /* test_accept */, &fee); | mempool, state, std::move(tx), false /* bypass_limits */, | ||||
true /* test_accept */, &fee); | |||||
} | } | ||||
// Check that fee does not exceed maximum fee | // Check that fee does not exceed maximum fee | ||||
if (test_accept_res && max_raw_tx_fee != Amount::zero() && | if (test_accept_res && max_raw_tx_fee != Amount::zero() && | ||||
fee > max_raw_tx_fee) { | fee > max_raw_tx_fee) { | ||||
result_0.pushKV("allowed", false); | result_0.pushKV("allowed", false); | ||||
result_0.pushKV("reject-reason", "max-fee-exceeded"); | result_0.pushKV("reject-reason", "max-fee-exceeded"); | ||||
result.push_back(std::move(result_0)); | result.push_back(std::move(result_0)); | ||||
▲ Show 20 Lines • Show All 738 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
/* nobip32derivs */ false); | /* nobip32derivs */ false); | ||||
// Fetch previous transactions (inputs): | // Fetch previous transactions (inputs): | ||||
CCoinsView viewDummy; | CCoinsView viewDummy; | ||||
CCoinsViewCache view(&viewDummy); | CCoinsViewCache view(&viewDummy); | ||||
{ | { | ||||
const CTxMemPool &mempool = EnsureMemPool(request.context); | const CTxMemPool &mempool = EnsureMemPool(request.context); | ||||
LOCK2(cs_main, mempool.cs); | LOCK2(cs_main, mempool.cs); | ||||
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); | CCoinsViewCache &viewChain = EnsureChainman(request.context) | ||||
.ActiveChainstate() | |||||
.CoinsTip(); | |||||
CCoinsViewMemPool viewMempool(&viewChain, mempool); | CCoinsViewMemPool viewMempool(&viewChain, mempool); | ||||
// temporarily switch cache backend to db+mempool view | // temporarily switch cache backend to db+mempool view | ||||
view.SetBackend(viewMempool); | view.SetBackend(viewMempool); | ||||
for (const CTxIn &txin : psbtx.tx->vin) { | for (const CTxIn &txin : psbtx.tx->vin) { | ||||
// Load entries from viewChain into view; can fail. | // Load entries from viewChain into view; can fail. | ||||
view.AccessCoin(txin.prevout); | view.AccessCoin(txin.prevout); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 320 Lines • Show Last 20 Lines |