代碼分析
在script.h中,存放著類CScript的成員函式,其中最關鍵的是函式GetOp
bool GetOp(const_iterator& pc, opcodetype& opcodeRet, vector<unsigned char>& vchRet) const
GetOp的引數包括程式計數器PC、操作碼OPCode及立即數回傳值,該函式的作用是從腳本PC處讀出操作碼,調整PC指向下一個操作碼;若當前讀取的操作碼為立即數操作碼,則也一并讀出立即數,
在script.cpp中,存放著與腳本密切相關的函式,其中最基礎、最關鍵的是函式EvalScript
bool EvalScript(const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType,
vector<vector<unsigned char> >* pvStackRet)
EvalScript的引數包括待執行的程式(待執行的腳本),運行的環境(待填充的交易,交易輸入的索引等)以及運行的結果
EvalScript主要區域變數及決議如下:
CScript::const_iterator pc = script.begin(); //程式計數器,用于指向下一個執行的指令
CScript::const_iterator pend = script.end(); //用于判斷程式結束
CScript::const_iterator pbegincodehash = script.begin(); //與操作碼OP_CODESEPARATOR一起用于輔助定位待簽名腳本
vector<bool> vfExec; //條件判斷結果
vector<valtype> stack; //主運行堆疊,用于運行時存放中間數值
vector<valtype> altstack; //次堆疊,未找到具體用法
EvalScript主要邏輯是從待執行腳本中取出操作碼并執行,直至取完、執行程序中遇到OP_RETURN、執行程序中VERIFY類驗證失敗、執行程序中遇到錯誤(例如運算元不足等)才會結束執行,
各類操作碼大致行為如下表:
| 型別 | 操作 |
|---|---|
| 立即數 | 往主堆疊壓入立即數 |
| 流程控制 | 對運算元進行條件判斷;根據條件判斷結果控制指令決議執行;對條件判斷結果進行操作 |
| 堆疊操作 | 對堆疊頂某些元素進行復制、反轉、交換等操作 |
| 切片運算 | 對堆疊頂某些元素進行拼接、位移等操作 |
| 位運算 | 對堆疊頂某些元素進行位運算 |
| 數值運算 | 對堆疊頂某些元素進行數值運算 |
| 加密運算 | 對堆疊頂某些元素進行計算哈希值、驗簽等密碼學運算 |
| 邊界符 | 無 |
| 模板 | 無 |
| 非法位元組碼 | 無 |
其中,邊界符、模板、非法位元組碼三類操作碼并無實際的執行代碼(即這三類操作碼在輸入到腳本執行前已被預處理),
所有指令中,較難理解的是OP_CHECKSIGVERIFY(OP_CHECKMULTISIGVERIFY可看作升級版OP_CHECKSIGVERIFY)
case OP_CHECKSIGVERIFY:
{
// (sig pubkey -- bool)
// OP_CHECKSIGVERIFY輸入引數為sig及pubkey,輸出結果bool
if (stack.size() < 2)
return false;
valtype& vchSig = stacktop(-2);
valtype& vchPubKey = stacktop(-1);
// debug print
//PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n");
//PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");
// Subset of script starting at the most recent codeseparator
// 從最近的分隔符OP_CODESEPARATOR一直到結束的腳本需要進行驗簽
CScript scriptCode(pbegincodehash, pend);
// Drop the signature, since there's no way for a signature to sign itself
// 去掉簽名
scriptCode.FindAndDelete(CScript(vchSig));
// 跟進 CheckSig -> SignatureHash,最終在SignatureHash中,將scriptCode填充到txTo的vin[nIn]中的scriptSig,
// 序列化后在進行計算哈希值,并對該哈希值進行驗簽
bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
stack.pop_back();
stack.pop_back();
stack.push_back(fSuccess ? vchTrue : vchFalse);
if (opcode == OP_CHECKSIGVERIFY)
{
if (fSuccess)
stack.pop_back();
else
// 驗簽失敗直接回傳
pc = pend;
}
}
break;
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/306305.html
標籤:區塊鏈
上一篇:位元幣白皮書精讀翻譯
