在比特币探究之交易创建这篇文章里,可以看到在交易创建的最后,需要进行一个交易签名操作。它其实就是交易发起方要提供一个证据,证明自己可以花费这项交易的每一笔输入,也就是说提供的签名scriptSig,能够跟prevOut的scriptPubKey运算,只要最终返回结果是TRUE,就能证明交易的合法性。比特币网络上的其他节点,也是通过这样一个过程来进行交易验证,确认OK之后,交易才会放入交易池,等待打包。
比特币交易创建函数CreateTransaction的最后,调用了如下代码来对交易进行签名:
const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
if (!ProduceSignature(*this,
MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL),
scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed");
return false;
} else {
UpdateInput(txNew.vin.at(nIn), sigdata);
}
这里的ProduceSignature函数,内部流程也比较复杂。当然它的复杂,来源于签名机制的复杂。为了帮助理解,我画了一个简要流程图。
交易签名流程图
先从末端起,自底向上,理解下面几个函数。
首先是MutableTransactionSignatureCreator.CreateSig函数,顾名思义就是对CMutableTransaction进行签名。它定义在src/script/sign.cpp中。源码如下:
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider,
std::vector<unsigned char>& vchSig, const CKeyID& address,
const CScript& scriptCode, SigVersion sigversion) const
{
CKey key;
if (!provider.GetKey(address, key))
return false;
//见证脚本必须是压缩版
if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
//生成交易哈希,用于签名
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
if (!key.Sign(hash, vchSig)) //使用ECDSA椭圆曲线加密算法进行签名
return false;
vchSig.push_back((unsigned char)nHashType);
return true;
}
CKey.Sign函数逻辑很简单,这里就不贴了。SignatureHash函数是根据交易信息生成哈希值,主要源码如下(涉及隔离见证部分可参见比特币探究之隔离见证那篇文章):
template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType,
const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
if (sigversion == SigVersion::WITNESS_V0) { //如果是隔离见证,根据BIP-143的规定简化签名内容
uint256 hashPrevouts, hashSequence, hashOutputs;
const bool cacheready = cache && cache->ready;
//非任何人可付,为所有PrevOut的哈希,否则全0
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
}
//非任何人可付,不是SINGLE或NONE,为所有序列号哈希,否则全0
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE
&& (nHashType & 0x1f) != SIGHASH_NONE) {
hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
}
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
//非SINGLE和NONE,为所有输出的哈希
hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
//如果是SINGLE,为同一序列号输出的哈希。其他情况全0
CHashWriter ss(SER_GETHASH, 0);
ss << txTo.vout[nIn];
hashOutputs = ss.GetHash();
}
CHashWriter ss(SER_GETHASH, 0);
ss << txTo.nVersion;
ss << hashPrevouts;
ss << hashSequence;
ss << txTo.vin[nIn].prevout;
ss << scriptCode;
ss << amount;
ss << txTo.vin[nIn].nSequence;
ss << hashOutputs;
ss << txTo.nLockTime;
ss << nHashType;
return ss.GetHash();
}
//如果不是隔离见证,调用Serializer输出哈希,根据所有的输入输出计算得出,其复杂度高于隔离见证版
CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType);
CHashWriter ss(SER_GETHASH, 0);
ss << txTmp << nHashType;
return ss.GetHash();
}
接着看Solver函数。它根据传入的scriptPubKey,判断交易输出类型(P2PKH、P2SH、P2WPKH或P2WSH),并返回相应的数据(参见交易签名流程图)。该函数定义在src/script/standard.cpp中。源码如下:
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet,
std::vector<std::vector<unsigned char> >& vSolutionsRet)
{
vSolutionsRet.clear();
//P2SH类型,格式为 OP_HASH160 20 [20 byte hash] OP_EQUAL
if (scriptPubKey.IsPayToScriptHash())
{
typeRet = TX_SCRIPTHASH;
std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
//返回20字节的Redeem Script(赎回脚本)的Hash
vSolutionsRet.push_back(hashBytes);
return true;
}
int witnessversion;
std::vector<unsigned char> witnessprogram;
//如果采用了隔离见证,那应该是[见证版本] [见证程序]的格式
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
//长度20,说明是P2WPKH
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
typeRet = TX_WITNESS_V0_KEYHASH;
vSolutionsRet.push_back(witnessprogram); //返回20字节PubKey Hash
return true;
}
//长度32,说明是P2WSH
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
typeRet = TX_WITNESS_V0_SCRIPTHASH;
vSolutionsRet.push_back(witnessprogram); //返回见证脚本
return true;
}
if (witnessversion != 0) { //向前兼容,当前隔离见证版本号只有0
typeRet = TX_WITNESS_UNKNOWN;
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
return true;
}
typeRet = TX_NONSTANDARD; //其他情况就是非标准交易了
return false;
}
//用OP_RETURN带的一堆直推数据
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN
&& scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
typeRet = TX_NULL_DATA;
return true;
}
std::vector<unsigned char> data;
//65/33 [65/33字节公钥] OP_CHECKSIG,33为压缩版
if (MatchPayToPubkey(scriptPubKey, data)) {
typeRet = TX_PUBKEY;
vSolutionsRet.push_back(std::move(data));
return true;
}
//OP_DUP OP_HASH160 20 [20字节公钥哈希] OP_EQUALVERIFY OP_CHECKSIG
if (MatchPayToPubkeyHash(scriptPubKey, data)) {
typeRet = TX_PUBKEYHASH;
vSolutionsRet.push_back(std::move(data));
return true;
}
unsigned int required;
std::vector<std::vector<unsigned char>> keys;
//多重签名:<required> <A pubkey> [B pubkey] [C pubkey...] <keys.size()> OP_CHECKMULTISIG
if (MatchMultisig(scriptPubKey, required, keys)) {
typeRet = TX_MULTISIG;
vSolutionsRet.push_back({static_cast<unsigned char>(required)});
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())});
return true;
}
vSolutionsRet.clear();
typeRet = TX_NONSTANDARD; //以上都不是,那就是非标准交易了
return false;
}
Solver函数是被SignStep函数调用的。函数名意思是分步签名,如果是P2SH,或者是隔离见证的P2WPHK/P2WSH,那么SignStep函数会调用两次,第一次返回赎回脚本或见证脚本,第二次才能签名。它定义在src/script/sigh.cpp里。源码如下:
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator,
const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet,
SigVersion sigversion, SignatureData& sigdata)
{
CScript scriptRet;
uint160 h160;
ret.clear();
std::vector<unsigned char> sig;
std::vector<valtype> vSolutions;
if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) //判断scriptPubKey类型
return false;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
case TX_WITNESS_UNKNOWN:
return false;
case TX_PUBKEY: //公钥,调用CreateSig生成签名
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion))
return false;
ret.push_back(std::move(sig));
return true;
case TX_PUBKEYHASH: { //公钥哈希,调用CreateSig生成签名,连同公钥一并返回
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig));
CPubKey pubkey;
GetPubKey(provider, sigdata, keyID, pubkey);
ret.push_back(ToByteVector(pubkey));
return true;
}
case TX_SCRIPTHASH: //脚本哈希,取出赎回脚本redeem script
if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
return false;
case TX_MULTISIG: { //多重签名
size_t required = vSolutions.front()[0];
ret.push_back(valtype());
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
CPubKey pubkey = CPubKey(vSolutions[i]);
if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig,
pubkey.GetID(), scriptPubKey, sigversion)) {
ret.push_back(std::move(sig));
}
}
bool ok = ret.size() == required + 1; //签名数量够不够?
for (size_t i = 0; i + ret.size() < required + 1; ++i) {
ret.push_back(valtype());
}
return ok;
}
case TX_WITNESS_V0_KEYHASH: //P2WPKH,直接返回20字节Key Hash
ret.push_back(vSolutions[0]);
return true;
case TX_WITNESS_V0_SCRIPTHASH: //P2WSH,返回见证脚本(m keys n)
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
if (GetCScript(provider, sigdata, h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
return false;
default:
return false;
}
}
现在可以看ProduceSignature函数了,它的源代码如下:
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator,
const CScript& fromPubKey, SignatureData& sigdata)
{
if (sigdata.complete) return true;
std::vector<valtype> result;
txnouttype whichType;
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
if (solved && whichType == TX_SCRIPTHASH)
{ //P2SH,对子脚本(赎回脚本)二次签名
subscript = CScript(result[0].begin(), result[0].end());
sigdata.redeem_script = subscript;
solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata)
&& whichType != TX_SCRIPTHASH;
P2SH = true;
}
if (solved && whichType == TX_WITNESS_V0_KEYHASH)
{ //P2WPKH,先组建P2PKH,二次签名
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
txnouttype subType;
solved = solved && SignStep(provider, creator, witnessscript, result, subType,
SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
}
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
{ //P2WSH,m keys n再调SignStep完成多重签名
CScript witnessscript(result[0].begin(), result[0].end());
sigdata.witness_script = witnessscript;
txnouttype subType;
solved = solved
&& SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata)
&& subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH
&& subType != TX_WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
} else if (solved && whichType == TX_WITNESS_UNKNOWN) {
sigdata.witness = true;
}
if (P2SH) { //子脚本加上去
result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end()));
}
sigdata.scriptSig = PushAll(result); //填入scriptSig,注意如果是隔离见证,此前已经clear
//最后还要验证一下
sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness,
STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
return sigdata.complete;
}
关于交易验证,还是待下次开发间隙再去探究吧。
你的支持,我的动力!
网友评论