前一段时间看一下最近特别火的区块链,对于它的脚本系统最感兴趣,这两天特意研究了一下,下面的代码分析是基于bitcoin 0.1的版本
Script是BitCoin中的一个特色,感觉它也借鉴了汇编中的操作符与栈的概念。但是Script中用于存放操作符也对象的不是一个”栈”,看它的定义
class CScript : public vector<unsigned char>
{
protected:
CScript& push_int64(int64 n)
{
if (n == -1 || (n >= 1 && n <= 16))
{
push_back(n + (OP_1 - 1));
}
else
{
CBigNum bn(n);
*this << bn.getvch();
}
return (*this);
}
}
CScript& operator<<(char b) { return (push_int64(b)); }
其中操作符<<是我们在后面的代码中常见的一个符号。引用这段代码的目的是想说明, 在执行 << 的时候,其实是执行vector的push_back, 查看一下push_back的说明:
push_back是算法语言里面的一个函数名。如c++中的vector头文件里面就有这个push_back函数,在vector类中作用为在vector尾部加入一个数据。
即它是将数据放在vector的队尾。
对于Bitcoin而言,它是通过创建一个对象CTransaction,将输入,输出(找零,与花费)放在其中,其CTransaction的定义:
class CTransaction
{
public:
int nVersion;
vector vin;
vector vout;
int nLockTime;
}
其中的vin与vout是比较重要的两个地方,其中的Scrip信息都是存放在vout与vin中。
// Parse bitcoin address
uint160 hash160;
bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
if (fBitcoinAddress)
{
// Send to bitcoin address
CScript scriptPubKey;
scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
if (!SendMoney(scriptPubKey, nValue, wtx))
return;
}
以知道此时scriptPubKey中的信息如下
接着开始会调用main.cpp中的SendMoney()函数
class CTransaction
{
public:
int nVersion;
vector vin;
vector vout;
int nLockTime;
}
对于vout的script信息比较容易找,它是在createTransaction()的时候,直接设置进去,其代码如下:
// Fill vout[0] to the payee wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
而vin的script就比较麻烦。在另外的文档中进去解析。只需要了解到的是vin中的script信息是:
scriptSigRet << vchSig << vchPubKey;
对应的信息:
在Bitcoin 0.1.5中的验证过程,代码逻辑不是太清晰,相对于0.15的代码,这一块就很清楚。可以直接查看对应的api就可以知道:https://bitcore.io/api/lib/script#Interpreter,这里还是以0.1.5为分析模板进行分析。
系统在校验的时候,是将vin与vout的脚本合起来进行校验的。其:
VS = IS + OS
其中VS(validation script),就是组合后的脚本, 其组合后的信息如下,其校验过程如下,其校验代码在script.cpp中的EvalScript()
1. 在校验之前, 会有一个空的stack,这个stack是实际的运行结果保存位置
执行队列执行OP_DUP了,此时执OP_DUP,是一个操作符,此时它的执行过程是将获取出stack最后一个值(不是出队列),然后将这个值赋给一个新的变量,再将这个变量push_back进去。
经过OP_DUP之后,指针指向了OP_HASH160了,它会取出stack最后一个值(出队列),利用这个值计算hash160的值,计算结果然后写回到stack最后一个
此时就到最后一个项OP_CHECKSIG了,此时获取sign的值,与PK值,并通过PK值vin, vout的值,计算签名,如果计算出来的值与stack中的值相同,则会压入true,否则就会压栈false
系统就检查stack最后一个值,如果为true,表明正确,如果为false就不正确
在以前的项目中,也会碰到类似的需要进行校验的,一般的想法就是在各个节点放一个脚本或者一个jar包,但是BitCoin给了另外一种思路,让校验过程镶嵌到消息中,很是比较牛B
https://www.liaoxuefeng.com/article/001482718603696a6b6eb2bebc74211ab967146a952ae0c000
http://davidederosa.com/basic-blockchain-programming/bitcoin-script-language-part-one/