代码分析
在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();
vector<bool> vfExec;
vector<valtype> stack;
vector<valtype> altstack;
EvalScript主要逻辑是从待执行脚本中取出操作码并执行,直至取完、执行过程中遇到OP_RETURN、执行过程中VERIFY类验证失败、执行过程中遇到错误(例如操作数不足等)才会结束执行。 各类操作码大致行为如下表:
类型 | 操作 |
---|
立即数 | 往主栈压入立即数 | 流程控制 | 对操作数进行条件判断;根据条件判断结果控制指令解析执行;对条件判断结果进行操作 | 栈操作 | 对栈顶某些元素进行复制、反转、交换等操作 | 切片运算 | 对栈顶某些元素进行拼接、位移等操作 | 位运算 | 对栈顶某些元素进行位运算 | 数值运算 | 对栈顶某些元素进行数值运算 | 加密运算 | 对栈顶某些元素进行计算哈希值、验签等密码学运算 | 边界符 | 无 | 模板 | 无 | 非法字节码 | 无 |
其中,边界符、模板、非法字节码三类操作码并无实际的执行代码(即这三类操作码在输入到脚本执行前已被预处理)。
所有指令中,较难理解的是OP_CHECKSIGVERIFY(OP_CHECKMULTISIGVERIFY可看作升级版OP_CHECKSIGVERIFY)
case OP_CHECKSIGVERIFY:
{
if (stack.size() < 2)
return false;
valtype& vchSig = stacktop(-2);
valtype& vchPubKey = stacktop(-1);
CScript scriptCode(pbegincodehash, pend);
scriptCode.FindAndDelete(CScript(vchSig));
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;
|