@ -554,6 +554,93 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet . push_back ( entry ) ;
vErrorsRet . push_back ( entry ) ;
}
}
UniValue combinerawtransaction ( const JSONRPCRequest & request )
{
if ( request . fHelp | | request . params . size ( ) ! = 1 )
throw std : : runtime_error (
" combinerawtransaction [ \" hexstring \" ,...] \n "
" \n Combine multiple partially signed transactions into one transaction. \n "
" The combined transaction may be another partially signed transaction or a \n "
" fully signed transaction. "
" \n Arguments: \n "
" 1. \" txs \" (string) A json array of hex strings of partially signed transactions \n "
" [ \n "
" \" hexstring \" (string) A transaction hash \n "
" ,... \n "
" ] \n "
" \n Result: \n "
" \" hex \" : \" value \" , (string) The hex-encoded raw transaction with signature(s) \n "
" \n Examples: \n "
+ HelpExampleCli ( " combinerawtransaction " , " [ \" myhex1 \" , \" myhex2 \" , \" myhex3 \" ] " )
) ;
UniValue txs = request . params [ 0 ] . get_array ( ) ;
std : : vector < CMutableTransaction > txVariants ( txs . size ( ) ) ;
for ( unsigned int idx = 0 ; idx < txs . size ( ) ; idx + + ) {
if ( ! DecodeHexTx ( txVariants [ idx ] , txs [ idx ] . get_str ( ) , true ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , strprintf ( " TX decode failed for tx %d " , idx ) ) ;
}
}
if ( txVariants . empty ( ) ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " Missing transactions " ) ;
}
// mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx:
CMutableTransaction mergedTx ( txVariants [ 0 ] ) ;
// Fetch previous transactions (inputs):
CCoinsView viewDummy ;
CCoinsViewCache view ( & viewDummy ) ;
{
LOCK ( cs_main ) ;
LOCK ( mempool . cs ) ;
CCoinsViewCache & viewChain = * pcoinsTip ;
CCoinsViewMemPool viewMempool ( & viewChain , mempool ) ;
view . SetBackend ( viewMempool ) ; // temporarily switch cache backend to db+mempool view
for ( const CTxIn & txin : mergedTx . vin ) {
view . AccessCoin ( txin . prevout ) ; // Load entries from viewChain into view; can fail.
}
view . SetBackend ( viewDummy ) ; // switch back to avoid locking mempool for too long
}
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst ( mergedTx ) ;
// Sign what we can:
for ( unsigned int i = 0 ; i < mergedTx . vin . size ( ) ; i + + ) {
CTxIn & txin = mergedTx . vin [ i ] ;
const Coin & coin = view . AccessCoin ( txin . prevout ) ;
if ( coin . IsSpent ( ) ) {
throw JSONRPCError ( RPC_VERIFY_ERROR , " Input not found or already spent " ) ;
}
const CScript & prevPubKey = coin . out . scriptPubKey ;
const CAmount & amount = coin . out . nValue ;
SignatureData sigdata ;
// ... and merge in other signatures:
for ( const CMutableTransaction & txv : txVariants ) {
if ( txv . vin . size ( ) > i ) {
sigdata = CombineSignatures ( prevPubKey , TransactionSignatureChecker ( & txConst , i , amount ) , sigdata , DataFromTransaction ( txv , i ) ) ;
}
}
UpdateTransaction ( mergedTx , i , sigdata ) ;
}
return EncodeHexTx ( mergedTx ) ;
}
UniValue signrawtransaction ( const JSONRPCRequest & request )
UniValue signrawtransaction ( const JSONRPCRequest & request )
{
{
# ifdef ENABLE_WALLET
# ifdef ENABLE_WALLET
@ -626,26 +713,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
# endif
# endif
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VARR , UniValue : : VARR , UniValue : : VSTR } , true ) ;
RPCTypeCheck ( request . params , { UniValue : : VSTR , UniValue : : VARR , UniValue : : VARR , UniValue : : VSTR } , true ) ;
std : : vector < unsigned char > txData ( ParseHexV ( request . params [ 0 ] , " argument 1 " ) ) ;
CMutableTransaction mtx ;
CDataStream ssData ( txData , SER_NETWORK , PROTOCOL_VERSION ) ;
if ( ! DecodeHexTx ( mtx , request . params [ 0 ] . get_str ( ) , true ) )
std : : vector < CMutableTransaction > txVariants ;
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
while ( ! ssData . empty ( ) ) {
try {
CMutableTransaction tx ;
ssData > > tx ;
txVariants . push_back ( tx ) ;
}
catch ( const std : : exception & ) {
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " TX decode failed " ) ;
}
}
if ( txVariants . empty ( ) )
throw JSONRPCError ( RPC_DESERIALIZATION_ERROR , " Missing transaction " ) ;
// mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx:
CMutableTransaction mergedTx ( txVariants [ 0 ] ) ;
// Fetch previous transactions (inputs):
// Fetch previous transactions (inputs):
CCoinsView viewDummy ;
CCoinsView viewDummy ;
@ -656,7 +726,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
CCoinsViewMemPool viewMempool ( & viewChain , mempool ) ;
CCoinsViewMemPool viewMempool ( & viewChain , mempool ) ;
view . SetBackend ( viewMempool ) ; // temporarily switch cache backend to db+mempool view
view . SetBackend ( viewMempool ) ; // temporarily switch cache backend to db+mempool view
for ( const CTxIn & txin : mergedT x . vin ) {
for ( const CTxIn & txin : mt x . vin ) {
view . AccessCoin ( txin . prevout ) ; // Load entries from viewChain into view; can fail.
view . AccessCoin ( txin . prevout ) ; // Load entries from viewChain into view; can fail.
}
}
@ -781,10 +851,10 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
// Use CTransaction for the constant parts of the
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
// transaction to avoid rehashing.
const CTransaction txConst ( mergedT x ) ;
const CTransaction txConst ( mt x ) ;
// Sign what we can:
// Sign what we can:
for ( unsigned int i = 0 ; i < mergedT x . vin . size ( ) ; i + + ) {
for ( unsigned int i = 0 ; i < mt x . vin . size ( ) ; i + + ) {
CTxIn & txin = mergedT x . vin [ i ] ;
CTxIn & txin = mt x . vin [ i ] ;
const Coin & coin = view . AccessCoin ( txin . prevout ) ;
const Coin & coin = view . AccessCoin ( txin . prevout ) ;
if ( coin . IsSpent ( ) ) {
if ( coin . IsSpent ( ) ) {
TxInErrorToJSON ( txin , vErrors , " Input not found or already spent " ) ;
TxInErrorToJSON ( txin , vErrors , " Input not found or already spent " ) ;
@ -795,17 +865,11 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
SignatureData sigdata ;
SignatureData sigdata ;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if ( ! fHashSingle | | ( i < mergedTx . vout . size ( ) ) )
if ( ! fHashSingle | | ( i < mtx . vout . size ( ) ) )
ProduceSignature ( MutableTransactionSignatureCreator ( & keystore , & mergedTx , i , amount , nHashType ) , prevPubKey , sigdata ) ;
ProduceSignature ( MutableTransactionSignatureCreator ( & keystore , & mtx , i , amount , nHashType ) , prevPubKey , sigdata ) ;
sigdata = CombineSignatures ( prevPubKey , TransactionSignatureChecker ( & txConst , i , amount ) , sigdata , DataFromTransaction ( mtx , i ) ) ;
// ... and merge in other signatures:
UpdateTransaction ( mtx , i , sigdata ) ;
for ( const CMutableTransaction & txv : txVariants ) {
if ( txv . vin . size ( ) > i ) {
sigdata = CombineSignatures ( prevPubKey , TransactionSignatureChecker ( & txConst , i , amount ) , sigdata , DataFromTransaction ( txv , i ) ) ;
}
}
UpdateTransaction ( mergedTx , i , sigdata ) ;
ScriptError serror = SCRIPT_ERR_OK ;
ScriptError serror = SCRIPT_ERR_OK ;
if ( ! VerifyScript ( txin . scriptSig , prevPubKey , & txin . scriptWitness , STANDARD_SCRIPT_VERIFY_FLAGS , TransactionSignatureChecker ( & txConst , i , amount ) , & serror ) ) {
if ( ! VerifyScript ( txin . scriptSig , prevPubKey , & txin . scriptWitness , STANDARD_SCRIPT_VERIFY_FLAGS , TransactionSignatureChecker ( & txConst , i , amount ) , & serror ) ) {
@ -815,7 +879,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
bool fComplete = vErrors . empty ( ) ;
bool fComplete = vErrors . empty ( ) ;
UniValue result ( UniValue : : VOBJ ) ;
UniValue result ( UniValue : : VOBJ ) ;
result . push_back ( Pair ( " hex " , EncodeHexTx ( mergedT x ) ) ) ;
result . push_back ( Pair ( " hex " , EncodeHexTx ( mt x ) ) ) ;
result . push_back ( Pair ( " complete " , fComplete ) ) ;
result . push_back ( Pair ( " complete " , fComplete ) ) ;
if ( ! vErrors . empty ( ) ) {
if ( ! vErrors . empty ( ) ) {
result . push_back ( Pair ( " errors " , vErrors ) ) ;
result . push_back ( Pair ( " errors " , vErrors ) ) ;
@ -905,6 +969,7 @@ static const CRPCCommand commands[] =
{ " rawtransactions " , " decoderawtransaction " , & decoderawtransaction , true , { " hexstring " } } ,
{ " rawtransactions " , " decoderawtransaction " , & decoderawtransaction , true , { " hexstring " } } ,
{ " rawtransactions " , " decodescript " , & decodescript , true , { " hexstring " } } ,
{ " rawtransactions " , " decodescript " , & decodescript , true , { " hexstring " } } ,
{ " rawtransactions " , " sendrawtransaction " , & sendrawtransaction , false , { " hexstring " , " allowhighfees " } } ,
{ " rawtransactions " , " sendrawtransaction " , & sendrawtransaction , false , { " hexstring " , " allowhighfees " } } ,
{ " rawtransactions " , " combinerawtransaction " , & combinerawtransaction , true , { " txs " } } ,
{ " rawtransactions " , " signrawtransaction " , & signrawtransaction , false , { " hexstring " , " prevtxs " , " privkeys " , " sighashtype " } } , /* uses wallet if enabled */
{ " rawtransactions " , " signrawtransaction " , & signrawtransaction , false , { " hexstring " , " prevtxs " , " privkeys " , " sighashtype " } } , /* uses wallet if enabled */
{ " blockchain " , " gettxoutproof " , & gettxoutproof , true , { " txids " , " blockhash " } } ,
{ " blockchain " , " gettxoutproof " , & gettxoutproof , true , { " txids " , " blockhash " } } ,