Recently in the exchange with colleagues in our palletone on the Utxo and signature processing, some experience, write down this blog post. The basic concept of Bitcoin is that bitcoin is a ECDSA digital signature to unlock the unspent balance in Utxo.
About Utxo I do not need to do too much introduction, after all, the introduction of the concept of the article has been many. I mainly talk about already have utxo, how to spend it.
Structure of the transaction
Let's take a look. What is the structure of a transaction in bitcoin?
struct { Version int32 txin []*txin txout []*txout struct { Value int64 Pkscript []bytestruct { previousoutpoint outpoint Signaturescript [] byte struct { Hash chainhash. Hash Index UInt32}
As we can see, a trade (MSGTX) consists of multiple input and multiple output, whereas in input it consists of a outpoint that points to Utxo, an unlock script signaturescript, and a sequence sequence.
Utxo we can think of it as a keyvalue large table in which the hash of the transaction and the position index of the output in the transaction make up the Utxo key, and value is the information such as Bitcoin amount, lock script, and so on in the Utxo database , we can quickly find the corresponding amount and locking scripts through outpoint.
In Bitcoin, a transaction is divided into three steps:
- Build the original trading rawtransaction, which contains the outpoint that the input points to, and also contains the full output, but without a signature, that is, the contents of the Signaturescript are not set.
- Sign the signed rawtransaction with the private key and build the signature into the full unlock script, filling in the Signaturescript field of the corresponding input.
- Sends the signed transaction to the peer network.
Building the original trading rawtransaction
Now suppose I have an address mx3krujrzzqytcsyyvwbihbnclrrtpxnkv (this is a test network address), which received two transfers, a 0.4BTC (testnet.blockchain.info/tx-index/ 239152566/1), another 1.1BTC (TESTNET.BLOCKCHAIN.INFO/TX-INDEX/239157459/1), both of which are in the second of their trading output, namely index= 1 (index starts from 0). Now we want to make a 1.2BTC transfer, then give a certain fee, change to the original address, so we will build a deal, the deal has 2Input and 2Output.
Here's the sample code I wrote with Go based on BTCD, where we built a rawtransaction.
Func BUILDRAWTX () *Wire . MSGTX {//Testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true TX:=Wire . NEWMSGTX (Wire. Txversion)//TESTNET.BLOCKCHAIN.INFO/TX-INDEX/239152566/10.4BTCUtxohash, _:= Chainhash. NEWHASHFROMSTR ("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52") point:= Wire. Outpoint{hash: *utxohash, Index:1}//build the first input, point to a 0.4BTC Utxo, the second parameter is the unlock script, now nilTX. Addtxin (Wire. Newtxin (&Point , Nil, nil))//TESTNET.BLOCKCHAIN.INFO/TX-INDEX/239157459/11.1BTCUtxoHash2, _:= Chainhash. NEWHASHFROMSTR ("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66") Point2:= Wire. Outpoint{hash: *utxohash2, Index:1}//build the second input, point to a 1.1BTC Utxo, the second parameter is the unlock script, now nilTX. Addtxin (Wire. Newtxin (&Point2, nil, nil))//Change the address (here is the 16 binary form, into the Base58 format is mx3krujrzzqytcsyyvwbihbnclrrtpxnkv)Pubkeyhash, _:= Hex. Decodestring ("B5407CEC767317D41442AAB35BAD2712626E17CA")Lock, _ :=Txscript. Newscriptbuilder (). Addop (Txscript. Op_dup). Addop (Txscript. op_hash160). AddData (Pubkeyhash). Addop (Txscript. op_equalverify). Addop (Txscript. OP_CHECKSIG). Script ()//build the first output, the change 0.2991024 BTCTX. Addtxout (Wire. Newtxout (29910240,Lock))//paid to an address, still 16 in the form of Base58, the form is: MXQNGTEKZKQNMQNFHKYI8FHV99WCVQGHFH. PubKeyHash2, _:= Hex. Decodestring ("be09abcbfda1f2c26899f062979ab0708731235a") Lock2, _:=Txscript. Newscriptbuilder (). Addop (Txscript. Op_dup). Addop (Txscript. op_hash160). AddData (PUBKEYHASH2). Addop (Txscript. op_equalverify). Addop (Txscript. OP_CHECKSIG). Script ()//build a second output, pay 1.2 BTC outTX. Addtxout (Wire. Newtxout (120000000, Lock2))returnTX}
The signing process of the transaction
Now that we know the private key, we need to sign the transaction, because there are 2 input, so we have to sign 2 times, the principle of each signature is the same, I will use the first input as an example to illustrate it.
In bitcoin, the signature process for a transaction is this:
1. Find the corresponding Utxo for the deal
2. Get the lock script corresponding to the Utxo
3. Copy the transaction object and set the value of the Unlock script field for that input to the corresponding lock script in the replica
4. Clear the Unlock script field for other input
5. Calculate the hash for the modified trading object
6. Use the private key to sign the hash.
It can be more easily expressed in tabular form:
This is the original unsigned transaction rawtransaction, mainly the second and third columns:
UTXO |
Input |
Output |
Txhash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52, Outindex:1, Amount:0.4btc,pkscript: Op_dup op_hash160 Pushdata (20) B5407cec767317d41442aab35bad2712626e17ca op_equalverify op_checksig |
PreviousOutPoint={ Txhash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52, Outindex:1} Signaturescript =null,sequence =0xffffffff |
value=29910240 pkscript= Op_dup op_hash160 Pushdata (20) B5407cec767317d41442aab35bad2712626e17ca op_equalverify op_checksig |
TXHASH:24F284AED2B9DBC19F0D435B1FE1EE3B3DDC763F28CA28BAD798D22B6BEA0C66, Outindex:1, Amount:1.1btc,pkscript: Op_dup op_hash160 pushdata () b5407cec767317d41442aab35bad2712626e17ca op_equalverify OP_CHECKSIG |
previousoutpoint={ TXHASH:24F284AED2B9DBC19F0D435B1FE1EE3B3DDC763F28CA28BAD798D22B6BEA0C66, Outindex:1} Signaturescript =null,sequence =0XFFFFFFFF |
value=120000000 pkscript= Op_dup op_hash160 pushdata () be09abcbfda1f2c26899f062979ab0708731235a op_equalverify OP_CHECKSIG |
Next we'll sign the first input, so we need to copy a copy of the transaction and replace it with:
input |
output |
previousoutpoin t={ Txhash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52, Outindex:1} Signaturescript = Op_dup op_hash160 pushdata b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_ Checksig , Sequence =0xffffffff |
value=29910240 pkscript= Op_dup op_ HASH160 pushdata () b5407cec767317d41442aab35bad2712626e17ca op_equalverify op_checksig |
previousoutpoint={ Txhash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66, Outindex:1} Signaturescript =null,sequence =0xffffffff |
value=120000000 pkscript= Op_dup op_hash160 pushdata (be09abcbfda1f2c26899f062979ab0708731235a op_equalverify OP_CHECKSIG) |
Next, hash the deal and sign it. Get signature Result: 3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5 d6d754aff14a88a6e8659b5fdad501 and we know the public key of this address is: 038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
So after signing the name, our deal turns out to be:
Input |
Output |
previousoutpoint={ Txhash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52, Outindex:1} Signaturescript = Pushdata (*) [3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2 2AE022031E47B9EBED5B90F6D51CD05E6F53BDC59F5D6D754AFF14A88A6E8659B5FDAD501] Pushdata (33) [ 038CC8C907B29A58B00F8C2590303BFC93C69D773B9DA204337678865EE0CAFADB] , Sequence =0XFFFFFFFF |
value=29910240 pkscript= Op_dup op_hash160 pushdata () b5407cec767317d41442aab35bad2712626e17ca op_equalverify OP_CHECKSIG |
previousoutpoint={ TXHASH:24F284AED2B9DBC19F0D435B1FE1EE3B3DDC763F28CA28BAD798D22B6BEA0C66, Outindex:1} Signaturescript =null,sequence =0XFFFFFFFF |
value=120000000 pkscript= Op_dup op_hash160 pushdata () be09abcbfda1f2c26899f062979ab0708731235a op_equalverify OP_CHECKSIG |
This just completes the signature of the first input, and then we sign the second input, the same reason we need to make a copy of the transaction and then empty the first input signaturescript, Then assign the value of the second input to the Signaturescript:
previousoutpoint={
Txhash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
Outindex:1}
Signaturescript =op_dup op_hash160 pushdata (B5407CEC767317D41442AAB35BAD2712626E17CA) Op_equalverify op_checksig
, Sequence =0xffffffff
input |
output |
previousoutpoin t={ Txhash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52, Outindex:1} Signaturescript =null,sequence =0xffffffff |
value=29910240 pkscript= Op_ DUP op_hash160 pushdata () b5407cec767317d41442aab35bad2712626e17ca op_equalverify op_checksig |
value=120000000 pkscript= Op_dup op_hash160 pushdata (be09abcbfda1f2c26899f062979ab0708731235a op_equalverify OP_CHECKSIG) |
Obviously, this copy is not the same as the data at the first signature, so the signature result is not the same, The final signature result is: 30440220196BCE75F0A25AC8AFA7218AEFC86CBA3924845450F3D311C89E9C2A3438A99C0220230BED598A610BE971CA49690F4B42AC2ACFA 80c09d4cbabd278b03c824af14501, of course, because we're the same address, So the public key is the same: 038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
We put the signature and the public key back into the original transaction, and it becomes the full signature transaction we need:
Input |
Output |
previousoutpoint={ Txhash : 1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52, Outindex:1} Signaturescript = Pushdata (72) [ 3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754a FF14A88A6E8659B5FDAD501] pushdata [038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb] , Sequence =0xffffffff |
value=29910240 pkscript= Op_dup op_hash160 Pushdata (20) B5407cec767317d41442aab35bad2712626e17ca op_equalverify op_checksig |
previousoutpoint={ TXHASH:24F284AED2B9DBC19F0D435B1FE1EE3B3DDC763F28CA28BAD798D22B6BEA0C66, Outindex:1} Signaturescript =pushdata (71) [ 30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4c BABD278B03C824AF14501] Pushdata [038CC8C907B29A58B00F8C2590303BFC93C69D773B9DA204337678865EE0CAFADB] , Sequence =0XFFFFFFFF |
value=120000000 pkscript= Op_dup op_hash160 pushdata () be09abcbfda1f2c26899f062979ab0708731235a op_equalverify OP_CHECKSIG |
This is a real and complete transaction, which can then be sent through the peer-to-peer network and eventually confirmed by the miners.
Summarize
In fact, in the source of Bitcoin is more complicated than what I said above, also related to this hash is the entire transaction Sighashall or Sighashsingle or sighashnone, these are very special circumstances, the general Bitcoin wallet is not supported, Specifically, you can participate in the introduction of the Bitcoin book: 6.5.3 signature Hash type (Sighash)
Generally speaking, we want to sign or check a transaction, that is, the current input in the unlock script to replace the lock script, and other input to unlock the script case, and then calculate the hash and signature!
In fact, I still do not understand, why Bitcoin does not have any unlocked script rawtransaction to sign it? But instead of locking the script to sign? I don't know if there's any deeper consideration in this.