The bitcoin-cli command can be used to create raw transaction to send to any recipient address. There is decode raw transaction command to display the raw transaction that we have created. After that, we can use send raw transaction command to actually spend the bitcoin.
For testing purpose, the regtest network is used.
Firstly, we use listunspent command to find out the UTXO (the txid) in the wallet
./src/bitcoin-cli -regtest listunspent
Then, we use getaccountaddress to find out the default address (the address)
./src/bitcoin-cli -regtest getaccountaddress ""
./src/bitcoin-cli -regtest createrawtransaction "[{\"txid\":\"2ca49406de60010f88876ac142f7846647942b33522a3a10bc493df480cffb34\",\"vout\":0}]" "{\"mjQgnYbydSCupJzkQS9HDBoP1CGcZZQMjt\":0.01}
The txid is from the listunspent command and address is from the getaccountaddress command. The vout of 0 is the vector output associated with the txid. The 0.01 is the amount to be paid to the address.
** when forming a new transaction, the txid of vin, must be a valid txid in listunspent, the vout of vin, must be a valid vout of that referenced transaction.
We can see that relationship using decoderawtransaction and listunspent, this decoderawransaction command shows the output of the transaction that was created.
{
"txid": "23eb6ae82550ce2dfcc9014c9226a755609195100139cdd57e58d2a0509ea175",
"hash": "23eb6ae82550ce2dfcc9014c9226a755609195100139cdd57e58d2a0509ea175",
"version": 2,
"size": 85,
"vsize": 85,
"locktime": 0,
"vin": [
{
"txid": "2ca49406de60010f88876ac142f7846647942b33522a3a10bc493df480cffb34",
"vout": 0,
"scriptSig": {
"asm": "",
"hex": ""
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.01000000,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 a3a350969090584be8887193c4eb96125ae06226 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914a3a350969090584be8887193c4eb96125ae0622688ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"mjQgnYbydSCupJzkQS9HDBoP1CGcZZQMjt"
]
}
}
]
}
Then, we sign the raw transaction
./src/bitcoin-cli -regtest signrawtransaction 020000000134fbcf80f43d49bc103a2a52332b94476684f742c16a87880f0160de0694a42c0000000000ffffffff0140420f00000000001976a9142ab10fb15ad20af203ee529a973cdbb1a0c2e68688ac00000000
{
"hex": "020000000134fbcf80f43d49bc103a2a52332b94476684f742c16a87880f0160de0694a42c0000000049483045022100b5928c21d2ea2436e669948e6eb4036a0d1de9b70210d3b103d475c9cf5b7b0b02200ac3a7fd690b16607ba833bef0e15dd84a21f0b609692727af7838dd7ac21f9d01ffffffff0140420f00000000001976a9142ab10fb15ad20af203ee529a973cdbb1a0c2e68688ac00000000",
"complete": true
}
Finally, we spend the bitcoins.
./src/bitcoin-cli -regtest sendrawtransaction 020000000134fbcf80f43d49bc103a2a52332b94476684f742c16a87880f0160de0694a42c0000000049483045022100b5928c21d2ea2436e669948e6eb4036a0d1de9b70210d3b103d475c9cf5b7b0b02200ac3a7fd690b16607ba833bef0e15dd84a21f0b609692727af7838dd7ac21f9d01ffffffff0140420f00000000001976a9142ab10fb15ad20af203ee529a973cdbb1a0c2e68688ac00000000
error code: -26
error message:
256: absurdly-high-fee
See here, if the ( input - output ) = transaction fee is too big, bitcoin core will reject to send the transaction.
2) In this analysis, the source code that handles raw transaction RPC will be walked through.
rpc/rawtransaction.cpp
UniValue createrawtransaction(const JSONRPCRequest& request)
This function creates an object of CMutableTransaction rawTx. It then sets the rawTx.nLockTime from parameter 2. It parses txid and vout, and push CTxIn object into rawTx. The function checks for output data or address. If address is used, it sets CScript scriptPubKey with the address. The the CTxOut is set with the amount and scriptPubKey. Lastly, it encodes rawTx with the function EncodeHexTx(rawTx) and returns the hex.
UniValue decoderawtransaction(const JSONRPCRequest& request)
The function creates an object of CMutableTransaction mtx, call DecodeHexTx() into mtx. It then calls TxToUniv() and returns the UniValue result.
UniValue sendrawtransaction(const JSONRPCRequest& request)
The function creates an object of CMutableTransaction mtx, decode the parameter with DecodeHexTx(). It creates CTransactionRef object , with the data of decoded mtx. It checks the mempool, the connection manager. If connection manager (g_connman) is available, it creates CInv object and push the inventory into CNode * pnode. It returns the transaction hash (txid).
UniValue signrawtransaction(const JSONRPCRequest& request)
The function creates std::vector<unsigned char> txData from the raw transaction hex data, and then CDataStream is made from the txData.
Next, the std::vector<CMutableTransaction> txVariants is made from the CDataStream objects. Then, a new CMutableTransaction mergedTx is created from txVariants.
Then, then CCoinsView and CCoinsViewCache are created to fetch previous inputs.
If the optional key is passed as parameters, it is stored in CKey object.
It previous txout is passed as parameters, a COutPoint object and Coin are created to add to CCoinsView object.
It also checks if redeem script is given or not in the parameters.
Then, CKeyStore object is added.
In the signing part, a SignatureData sigdata is created. ProduceSignature() is called to sign and CombineSignature() is called to merge other signatures. The UpdateTransaction() is called to update the mergedTx object. Finally, the UniValue result is filled with hex and complete field and returned.
script/sign.cpp
script/standard.cpp
Next, the std::vector<CMutableTransaction> txVariants is made from the CDataStream objects. Then, a new CMutableTransaction mergedTx is created from txVariants.
Then, then CCoinsView and CCoinsViewCache are created to fetch previous inputs.
If the optional key is passed as parameters, it is stored in CKey object.
It previous txout is passed as parameters, a COutPoint object and Coin are created to add to CCoinsView object.
It also checks if redeem script is given or not in the parameters.
Then, CKeyStore object is added.
In the signing part, a SignatureData sigdata is created. ProduceSignature() is called to sign and CombineSignature() is called to merge other signatures. The UpdateTransaction() is called to update the mergedTx object. Finally, the UniValue result is filled with hex and complete field and returned.
script/sign.cpp
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
This function takes the CScript, SignatureData, and BaseSignatureCreator, and calls SignStep() to update boolean variable named "solved". Depending the type of TX_SCRIPTHASH or TX_WITNESS_V0_KEYHASH or TX_WITNESS_V0_SCRIPTHASH, it processes and updates the sigdata. The return value is a logical "and" of VerifyScript() and the bool variable "solved".
static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion)
The function consists of a big switch-case conditions. Depending on the key type, different actions are taken to sign the public key. The key type is decided using Solver().script/standard.cpp
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
This function firstly setup a std:multimap templates of txnouttype and CScript. If transaction is pay to script hash, or witness keyhash, or witness script hash, or null data; set the transaction type and return. If not, the function enters a for loop. In the for loop, it setup two CScript iterator, compare the iterators. If there is a match, it returns the transaction type if it is not TX_MULTISIG. Otherwise, the function set type to TX_NONSTANDARD and returns false.