@ -32,6 +32,7 @@
# include <script/signingprovider.h>
# include <script/signingprovider.h>
# include <script/standard.h>
# include <script/standard.h>
# include <uint256.h>
# include <uint256.h>
# include <undo.h>
# include <util/bip32.h>
# include <util/bip32.h>
# include <util/check.h>
# include <util/check.h>
# include <util/strencodings.h>
# include <util/strencodings.h>
@ -50,15 +51,17 @@ using node::FindCoins;
using node : : GetTransaction ;
using node : : GetTransaction ;
using node : : NodeContext ;
using node : : NodeContext ;
using node : : PSBTAnalysis ;
using node : : PSBTAnalysis ;
using node : : ReadBlockFromDisk ;
using node : : UndoReadFromDisk ;
static void TxToJSON ( const CTransaction & tx , const uint256 hashBlock , UniValue & entry , Chainstate & active_chainstate )
static void TxToJSON ( const CTransaction & tx , const uint256 hashBlock , UniValue & entry , Chainstate & active_chainstate , const CTxUndo * txundo = nullptr , TxVerbosity verbosity = TxVerbosity : : SHOW_TXID )
{
{
// 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 , /*block_hash=*/ uint256 ( ) , entry , /*include_hex=*/ true , RPCSerializationFlags ( ) );
TxToUniv ( tx , /*block_hash=*/ uint256 ( ) , entry , /*include_hex=*/ true , RPCSerializationFlags ( ) , txundo , verbosity );
if ( ! hashBlock . IsNull ( ) ) {
if ( ! hashBlock . IsNull ( ) ) {
LOCK ( cs_main ) ;
LOCK ( cs_main ) ;
@ -166,26 +169,27 @@ static RPCHelpMan getrawtransaction()
{
{
return RPCHelpMan {
return RPCHelpMan {
" getrawtransaction " ,
" getrawtransaction " ,
" Return the raw transaction data. \n "
" \n By default, this call only returns a transaction if it is in the mempool. If -txindex is enabled \n "
" By default, this call only returns a transaction if it is in the mempool. If -txindex is enabled \n "
" and no blockhash argument is passed, it will return the transaction if it is in the mempool or any block. \n "
" and no blockhash argument is passed, it will return the transaction if it is in the mempool or any block. \n "
" If a blockhash argument is passed, it will return the transaction if \n "
" If a blockhash argument is passed, it will return the transaction if \n "
" the specified block is available and the transaction is in that block. \n "
" the specified block is available and the transaction is in that block. \n \n "
" \n Hint: Use gettransaction for wallet transactions. \n "
" Hint: Use gettransaction for wallet transactions. \n \n "
" \n If verbose is 'true', returns an Object with information about 'txid'. \n "
" If verbosity is 0 or omitted, returns the serialized transaction as a hex-encoded string. \n "
" If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'. " ,
" If verbosity is 1, returns a JSON Object with information about transaction. \n "
" If verbosity is 2, returns a JSON Object with information about transaction, including fee and prevout information. " ,
{
{
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " txid " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : NO , " The transaction id " } ,
{ " verbos e" , RPCArg : : Type : : BOOL, RPCArg : : Default { false } , " If false, return a string, otherwise return a json objec t" } ,
{ " verbos ity|verbos e" , RPCArg : : Type : : NUM, RPCArg : : Default { 0 } , " 0 for hex-encoded data, 1 for a JSON object, and 2 for JSON object with fee and prevou t" } ,
{ " blockhash " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The block in which to look for the transaction " } ,
{ " blockhash " , RPCArg : : Type : : STR_HEX , RPCArg : : Optional : : OMITTED_NAMED_ARG , " The block in which to look for the transaction " } ,
} ,
} ,
{
{
RPCResult { " if verbos e is not set or set to false " ,
RPCResult { " if verbos ity is not set or set to 0 " ,
RPCResult : : Type : : STR , " data " , " The serialized , hex-encoded data for 'txid'"
RPCResult : : Type : : STR , " data " , " The serialized transaction as a hex-encoded string for 'txid'"
} ,
} ,
RPCResult { " if verbose is set to true " ,
RPCResult { " if verbosity is set to 1 " ,
// When updating this documentation, update `decoderawtransaction` in the same way.
RPCResult : : Type : : OBJ , " " , " " ,
RPCResult : : Type : : OBJ , " " , " " ,
Cat < std : : vector < RPCResult > > (
Cat < std : : vector < RPCResult > > (
{
{
@ -198,20 +202,47 @@ static RPCHelpMan getrawtransaction()
} ,
} ,
DecodeTxDoc ( /*txid_field_doc=*/ " The transaction id (same as provided) " ) ) ,
DecodeTxDoc ( /*txid_field_doc=*/ " The transaction id (same as provided) " ) ) ,
} ,
} ,
RPCResult { " for verbosity = 2 " ,
RPCResult : : Type : : OBJ , " " , " " ,
{
{ RPCResult : : Type : : ELISION , " " , " Same output as verbosity = 1 " } ,
{ RPCResult : : Type : : NUM , " fee " , /* optional */ true , " transaction fee in " + CURRENCY_UNIT + " , omitted if block undo data is not available " } ,
{ RPCResult : : Type : : ARR , " vin " , " " ,
{
{ RPCResult : : Type : : OBJ , " " , /* optional */ true , " utxo being spent, omitted if block undo data is not available " ,
{
{ RPCResult : : Type : : ELISION , " " , " Same output as verbosity = 1 " } ,
{ RPCResult : : Type : : OBJ , " prevout " , " Only if undo information is available) " ,
{
{ RPCResult : : Type : : BOOL , " generated " , " Coinbase or not " } ,
{ RPCResult : : Type : : NUM , " height " , " The height of the prevout " } ,
{ RPCResult : : Type : : STR_AMOUNT , " value " , " The value in " + CURRENCY_UNIT } ,
{ RPCResult : : Type : : OBJ , " scriptPubKey " , " " ,
{
{ RPCResult : : Type : : STR , " asm " , " Disassembly of the public key script " } ,
{ RPCResult : : Type : : STR , " desc " , " Inferred descriptor for the output " } ,
{ RPCResult : : Type : : STR_HEX , " hex " , " The raw public key script bytes, hex-encoded " } ,
{ RPCResult : : Type : : STR , " address " , /*optional=*/ true , " The Bitcoin address (only if a well-defined address exists) " } ,
{ RPCResult : : Type : : STR , " type " , " The type (one of: " + GetAllOutputTypes ( ) + " ) " } ,
} } ,
} } ,
} } ,
} } ,
} } ,
} ,
} ,
RPCExamples {
RPCExamples {
HelpExampleCli ( " getrawtransaction " , " \" mytxid \" " )
HelpExampleCli ( " getrawtransaction " , " \" mytxid \" " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" true " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" 1 " )
+ HelpExampleRpc ( " getrawtransaction " , " \" mytxid \" , true " )
+ HelpExampleRpc ( " getrawtransaction " , " \" mytxid \" , 1 " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" false \" myblockhash \" " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" 0 \" myblockhash \" " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" true \" myblockhash \" " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" 1 \" myblockhash \" " )
+ HelpExampleCli ( " getrawtransaction " , " \" mytxid \" 2 \" myblockhash \" " )
} ,
} ,
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
[ & ] ( const RPCHelpMan & self , const JSONRPCRequest & request ) - > UniValue
{
{
const NodeContext & node = EnsureAnyNodeContext ( request . context ) ;
const NodeContext & node = EnsureAnyNodeContext ( request . context ) ;
ChainstateManager & chainman = EnsureChainman ( node ) ;
ChainstateManager & chainman = EnsureChainman ( node ) ;
bool in_active_chain = true ;
uint256 hash = ParseHashV ( request . params [ 0 ] , " parameter 1 " ) ;
uint256 hash = ParseHashV ( request . params [ 0 ] , " parameter 1 " ) ;
const CBlockIndex * blockindex = nullptr ;
const CBlockIndex * blockindex = nullptr ;
@ -220,10 +251,14 @@ static RPCHelpMan getrawtransaction()
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved " ) ;
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved " ) ;
}
}
// Accept either a bool (true) or a num (>= 1) to indicate verbose output .
// Accept either a bool (true) or a num (>= 0) to indicate verbosity .
bool fVerbose = false ;
int verbosity { 0 } ;
if ( ! request . params [ 1 ] . isNull ( ) ) {
if ( ! request . params [ 1 ] . isNull ( ) ) {
fVerbose = request . params [ 1 ] . isNum ( ) ? ( request . params [ 1 ] . getInt < int > ( ) ! = 0 ) : request . params [ 1 ] . get_bool ( ) ;
if ( request . params [ 1 ] . isBool ( ) ) {
verbosity = request . params [ 1 ] . get_bool ( ) ;
} else {
verbosity = request . params [ 1 ] . getInt < int > ( ) ;
}
}
}
if ( ! request . params [ 2 ] . isNull ( ) ) {
if ( ! request . params [ 2 ] . isNull ( ) ) {
@ -234,7 +269,6 @@ static RPCHelpMan getrawtransaction()
if ( ! blockindex ) {
if ( ! blockindex ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Block hash not found " ) ;
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Block hash not found " ) ;
}
}
in_active_chain = chainman . ActiveChain ( ) . Contains ( blockindex ) ;
}
}
bool f_txindex_ready = false ;
bool f_txindex_ready = false ;
@ -262,14 +296,44 @@ static RPCHelpMan getrawtransaction()
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , errmsg + " . Use gettransaction for wallet transactions. " ) ;
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , errmsg + " . Use gettransaction for wallet transactions. " ) ;
}
}
if ( ! fVerbose ) {
if ( verbosity < = 0 ) {
return EncodeHexTx ( * tx , RPCSerializationFlags ( ) ) ;
return EncodeHexTx ( * tx , RPCSerializationFlags ( ) ) ;
}
}
UniValue result ( UniValue : : VOBJ ) ;
UniValue result ( UniValue : : VOBJ ) ;
if ( blockindex ) result . pushKV ( " in_active_chain " , in_active_chain ) ;
if ( blockindex ) {
LOCK ( cs_main ) ;
result . pushKV ( " in_active_chain " , chainman . ActiveChain ( ) . Contains ( blockindex ) ) ;
}
// If request is verbosity >= 1 but no blockhash was given, then look up the blockindex
if ( request . params [ 2 ] . isNull ( ) ) {
LOCK ( cs_main ) ;
blockindex = chainman . m_blockman . LookupBlockIndex ( hash_block ) ;
}
if ( verbosity = = 1 ) {
TxToJSON ( * tx , hash_block , result , chainman . ActiveChainstate ( ) ) ;
TxToJSON ( * tx , hash_block , result , chainman . ActiveChainstate ( ) ) ;
return result ;
return result ;
}
CBlockUndo blockUndo ;
CBlock block ;
const bool is_block_pruned { WITH_LOCK ( cs_main , return chainman . m_blockman . IsBlockPruned ( blockindex ) ) } ;
if ( tx - > IsCoinBase ( ) | |
! blockindex | | is_block_pruned | |
! ( UndoReadFromDisk ( blockUndo , blockindex ) & & ReadBlockFromDisk ( block , blockindex , Params ( ) . GetConsensus ( ) ) ) ) {
TxToJSON ( * tx , hash_block , result , chainman . ActiveChainstate ( ) ) ;
return result ;
}
CTxUndo * undoTX { nullptr } ;
auto it = std : : find_if ( block . vtx . begin ( ) , block . vtx . end ( ) , [ tx ] ( CTransactionRef t ) { return * t = = * tx ; } ) ;
if ( it ! = block . vtx . end ( ) ) {
// -1 as blockundo does not have coinbase tx
undoTX = & blockUndo . vtxundo . at ( it - block . vtx . begin ( ) - 1 ) ;
}
TxToJSON ( * tx , hash_block , result , chainman . ActiveChainstate ( ) , undoTX , TxVerbosity : : SHOW_DETAILS_AND_PREVOUT ) ;
return result ;
} ,
} ,
} ;
} ;
}
}