交易
區塊內各筆交易的宏觀圖
複習一下:
Timestamp:
The time when the block was found.
Reference to Parent (Prev_Hash):
This is a hash of the previous block header which ties each block to its parent, and therefore by induction to all previous blocks. This chain of references is the eponymic concept for the blockchain.
Merkle Root (Tx_Root):
The Merkle Root is a reduced representation of the set of transactions that is confirmed with this block. The transactions themselves are provided independently forming the body of the block. There must be at least one transaction: The Coinbase. The Coinbase is a special transaction that may create new bitcoins and collects the transactions fees. Other transactions are optional.
Target (Difficulty):
The target corresponds to the difficulty of finding a new block. It is updated every 2016 blocks when the difficulty reset occurs.
The block's own hash:
All of the above header items (i.e. all except the transaction data) get hashed into the block hash, which for one is proof that the other parts of the header have not been changed, and then is used as a reference by the succeeding block.
Coinbase
Thecoinbaseis the content of the 'input' of a generationtransaction. While regular transactions use the 'inputs' section to refer to their parent transaction outputs, a generation transaction has no parent, and creates new coins from nothing.
The coinbase can contain any arbitrary data. Thegenesis blockfamously contains the dated title of an FT article:
The Times 03/Jan/2009 Chancellor on brink of second bailout for banks
五種交易類型
https://medium.com/@wilsonhuang/mastering-bitcoin-筆記-standard-transactions-undone-bfb9b4ed0ed8
介紹五種類型的好文
- Pay-to-Public-Key-Hash (P2PKH,最常見的交易)
- Pay-to-Public-Key (P2PK)
- Multi-Signature (MultiSig,多重簽章交易,最多 15 個 keys)
- Data Output (OP_RETURN,可以填客製化的資料上 Bitcoin Blockchain)
- Pay-to-Script-Hash (P2SH)
認證
P2PKH
首先複習一下我們在TxIn裡面會有SignatureScript (即unlocking script),而在TxOut裡會有PublicKeyScript(即locking script)
某甲付給某乙的上一次交易,其轉出TxOut的PublicKeyScript (locking script)會是這樣:
<pre><code>
OP\_DUP OP\_HASH160 <Cafe Public Key Hash> OP\_EQUAL OP\_CHECKSIG
</code></pre>
其中OP開頭的Bitcoin的script language,是以stack(堆疊,FILO)作操作的。
而這一次交易的TxIn會有一個SignatureScript (unlocking script)會是這樣:
<pre><code>
<Cafe Signature> <Cafe Public Key>
</code></pre>
當某乙開始想花錢了。上一個交易的TxOUT成為這一個交易的TxIn,兩個script交疊會成為這樣:
<pre><code>
<Cafe Signature> <Cafe Public Key> OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUAL OP_CHECKSIG
</code></pre>
解鎖過程就開始:
OP_DUP指令造成了一個結果,就是把stack最上方的元素複製一份。
接下來OP_HASH160就是把PubK作一個RIPEMD160(SHA256(PubK)),效果就是變成一個 PubKHash:
OP_EQUAL在判斷最上方兩個元素是否相等,剛好相等!
最後用OP_CHECKSIG面對 sig,剛好就是解鎖! 結果是TRUE,成功!
檢查點 (checkpoints)
一個保護機制,hard code在每個標準的客戶端軟體裡。所有的客戶端都接受在檢查點之前的交易是合法跟不可逆的,也就是檢查點以前的交易紀錄不會再被更改。如果有人試圖去分岔(fork)產生檢查點的區塊鏈,任何的客戶端軟體都不會接受這樣的分岔。
btcd/chaincfg/params.go
<pre><code>
Checkpoints: \[\]Checkpoint{
{11111, newHashFromStr\("0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"\)},
{33333, newHashFromStr\("000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"\)},
{74000, newHashFromStr\("0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"\)},
{105000, newHashFromStr\("00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"\)},
{134444, newHashFromStr\("00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"\)},
{168000, newHashFromStr\("000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"\)},
{193000, newHashFromStr\("000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"\)},
{210000, newHashFromStr\("000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"\)},
{216116, newHashFromStr\("00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"\)},
{225430, newHashFromStr\("00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"\)},
{250000, newHashFromStr\("000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"\)},
{267300, newHashFromStr\("000000000000000a83fbd660e918f218bf37edd92b748ad940483c7c116179ac"\)},
{279000, newHashFromStr\("0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"\)},
{300255, newHashFromStr\("0000000000000000162804527c6e9b9f0563a280525f9d08c12041def0a0f3b2"\)},
{319400, newHashFromStr\("000000000000000021c6052e9becade189495d1c539aa37c58917305fd15f13b"\)},
{343185, newHashFromStr\("0000000000000000072b8bf361d01a6ba7d445dd024203fafc78768ed4368554"\)},
{352940, newHashFromStr\("000000000000000010755df42dba556bb72be6a32f3ce0b6941ce4430152c9ff"\)},
{382320, newHashFromStr\("00000000000000000a8dc6ed5b133d0eb2fd6af56203e4159789b092defd8ab2"\)},
},
</code></pre>
取得Balance (結餘)
<pre><code>
取得多帳戶結餘
btcsuite/btcwallet/wallet/wallet.go
// CalculateBalance sums the amounts of all unspent transaction
// outputs to addresses of a wallet and returns the balance.
// If confirmations is 0, all UTXOs, even those not present in a
// block \(height -1\), will be used to get the balance. Otherwise,
// a UTXO must be in a block. If confirmations is 1 or greater,
// the balance will be calculated based on how many how many blocks
// include a UTXO.
func \(w \*Wallet\) CalculateBalance\(confirms int32\) \(btcutil.Amount, error\) {
blk := w.Manager.SyncedTo\(\)
return w.TxStore.Balance\(confirms, blk.Height\)
}
</code></pre>
單一帳號(地址)結餘計算
<pre><code>
資料結構
// Balances records total, spendable \(by policy\), and immature coinbase
// reward balance amounts.
type Balances struct {
Total btcutil.Amount
Spendable btcutil.Amount
ImmatureReward btcutil.Amount
}
如何運作:
// CalculateAccountBalances sums the amounts of all unspent transaction
// outputs to the given account of a wallet and returns the balance.
// This function is much slower than it needs to be since transactions outputs
// are not indexed by the accounts they credit to, and all unspent transaction
// outputs must be iterated.
func \(w \*Wallet\) CalculateAccountBalances\(account uint32, confirms int32\) \(Balances, error\) {
var bals Balances
// Get current block. The block height used for calculating
// the number of tx confirmations.
syncBlock := w.Manager.SyncedTo\(\)
unspent, err := w.TxStore.UnspentOutputs\(\)
if err != nil {
return bals, err
}
for i := range unspent {
output := &unspent\[i\]
var outputAcct uint32
\_, addrs, \_, err := txscript.ExtractPkScriptAddrs\(
output.PkScript, w.chainParams\)
if err == nil && len\(addrs\) > 0 {
outputAcct, err = w.Manager.AddrAccount\(addrs\[0\]\)
}
if err != nil \|\| outputAcct != account {
continue
}
bals.Total += output.Amount
if output.FromCoinBase && !confirmed\(int32\(w.chainParams.CoinbaseMaturity\),
output.Height, syncBlock.Height\) {
bals.ImmatureReward += output.Amount
} else if confirmed\(confirms, output.Height, syncBlock.Height\) {
bals.Spendable += output.Amount
}
}
return bals, nil
}
</code></pre>