交易的产生(一)–生成地址
交易的产生(二)–创建交易
交易的产生(三)–提交交易
交易的产生(四)–脚本和签名
在sendmany里为每个接受者地址构建脚本是
CScript scriptPubKey = GetScriptForDestination(address.Get());
这里的address是CBitcoinAddress address(name_);,调用了CTxDestination CBitcoinAddress::Get()
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
介绍一下variant,variant相当于增强型的union,也就是CTxDestination可以是CNoDestination, CKeyID, CScriptID中的任意一个类型
vchVersion是CBitcoinAddress的父类CBase58Data的保护变量
CTxDestination CBitcoinAddress::Get() const
{
if (!IsValid())
return CNoDestination();
uint160 id;
memcpy(&id, &vchData[0], 20);
if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
return CKeyID(id);
else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
return CScriptID(id);
else
return CNoDestination();
}
这段代码就是做逻辑判断,如果是公钥地址则调用CKeyID(id),如果是脚本地址则调用CScriptID(id)。
其中的CChainParams::PUBKEY_ADDRESS参数是
class CChainParams
{
public:
enum Base58Type {
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
MAX_BASE58_TYPES
};
这个CChainParams下还有三个子类,分别对应不同网络的可调整参数,我们先来看测试网的,其中一段
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >();
接下来看CKeyID
CKeyID(const uint160& in) : uint160(in) {}
就是对输入做了hash160的处理,那么来看CScriptID
CScriptID(const uint160& in) : uint160(in) {}
也是一样的处理
接着来看GetScriptForDestination函数
CScript GetScriptForDestination(const CTxDestination& dest)
{
CScript script;
boost::apply_visitor(CScriptVisitor(&script), dest);
return script;
}
因为CTxDestination是boost::variant类型的,根据CScriptVisitor的内部结构来看是用了访问者模式
CScriptVisitor继承类boost::static_visitor,在类里面需要重载()操作符,通过boost::apply_visitor来访问原始类型的值
namespace
{
class CScriptVisitor : public boost::static_visitor<bool>
{
private:
CScript *script;
public:
CScriptVisitor(CScript *scriptin) { script = scriptin; }
bool operator()(const CNoDestination &dest) const {
script->clear();
return false;
}
//这里是比特币地址的处理,script用于保存组装好的脚本
bool operator()(const CKeyID &keyID) const {
script->clear();
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return true;
}
//这里是支付到脚本地址的处理
bool operator()(const CScriptID &scriptID) const {
script->clear();
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true;
}
};
}
回顾一下锁定脚本,给出的例子就是
1)P2PKH: OP_DUP OP_HSAH160 OP_EQUALVERIFY OP_CHECKSIG
2)P2SH: OP_HASH160 <20-byte hash of redeem script> OP_EQUAL
和上面的代码是符合的,另外脚本操作都定义在script.h中
在standard.cpp目录下还有多重签名脚本和隔离见证的脚本构造也一并介绍了
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
{
CScript script;
script << CScript::EncodeOP_N(nRequired);
BOOST_FOREACH(const CPubKey& key, keys)
script << ToByteVector(key);
script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
return script;
}
多重签名的锁定脚本例子
M
其中M 是花费输出所需的签名的数量,N 是列出的公钥的总数
CScript GetScriptForWitness(const CScript& redeemscript)
{
CScript ret;
txnouttype typ;
std::vector<std::vector<unsigned char> > vSolutions;
if (Solver(redeemscript, typ, vSolutions)) {
if (typ == TX_PUBKEY) {
unsigned char h160[20];
CHash160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160);
ret << OP_0 << std::vector<unsigned char>(&h160[0], &h160[20]);
return ret;
} else if (typ == TX_PUBKEYHASH) {
ret << OP_0 << vSolutions[0];
return ret;
}
}
uint256 hash;
CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin());
ret << OP_0 << ToByteVector(hash);
return ret;
}
这个函数的传入参数就是脚本,也就是之前构建好的脚本。这里的逻辑是针对公钥和公钥hash有不同的处理方法,以及对支付给脚本hash的处理
例子是 1)P2WPKH : OP_0 <20-byte witness program>
2)P2WSH:OP_0 <32-byte witness program>
在createtransaction中的签名是调用
if (sign)
signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
SIGHASH_ALL是签名哈希类型的一个,表示签名承诺的交易部分,all表示承诺所有的输入和输出
先来看ProduceSignature这个函数,主要逻辑是判断锁定脚本类型,然后对应处理
如果是P2SH第一次调用SignStep返回的不是签名而是赎回脚本,需要另外处理,还有隔离见证的也需要。
bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
{
CScript script = fromPubKey;
bool solved = true;
std::vector<valtype> result;
txnouttype whichType;
solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE);//SIGVERSION_BASE=0
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
if (solved && whichType == TX_SCRIPTHASH)//解析赎回脚本
{
// Solver returns the subscript that needs to be evaluated;
// the final scriptSig is the signatures from that
// and then the serialized subscript:
script = subscript = CScript(result[0].begin(), result[0].end());
solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH;
P2SH = true;
}
if (solved && whichType == TX_WITNESS_V0_KEYHASH)//隔离见证
{
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;//隔离见证脚本的组装
txnouttype subType;
solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0);
sigdata.scriptWitness.stack = result;
result.clear();
}
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)//隔离见证
{
CScript witnessscript(result[0].begin(), result[0].end());
txnouttype subType;
solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0) && 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;
result.clear();
}
if (P2SH) {//脚本公钥
result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end()));
}
sigdata.scriptSig = PushAll(result);
// Test solution
return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
}
签名数据保存在sigdata中
需要先了解SignStep,从注释来看
使用创建者(creator)签名签署scriptPubKey。 签名在scriptSigRet中返回(如果无法对scriptPubKey进行签名,则返回false),除非whichTypeRet是TX_SCRIPTHASH,在这种情况下,scriptSigRet是兑换脚本。 如果无法完全满足scriptPubKey,则返回false。
static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey,
std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion)
{
CScript scriptRet;
uint160 h160;
ret.clear();
vector<valtype> vSolutions;
if (!Solver(scriptPubKey, whichTypeRet, vSolutions))//whichTypeRet脚本类型,vSolutions保存脚本hash值
return false;
CKeyID keyID;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
return false;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, creator, scriptPubKey, ret, sigversion);//签名保存在ret
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion))
return false;
else
{
CPubKey vch;
creator.KeyStore().GetPubKey(keyID, vch);
ret.push_back(ToByteVector(vch));
}
return true;
case TX_SCRIPTHASH:
if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
return false;
case TX_MULTISIG:
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion));
case TX_WITNESS_V0_KEYHASH:
ret.push_back(vSolutions[0]);
return true;
case TX_WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
if (creator.KeyStore().GetCScript(h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
return false;
default:
return false;
}
}
可以看到有三个类型TX_SCRIPTHASH,TX_WITNESS_V0_KEYHASH,TX_WITNESS_V0_SCRIPTHASH没有调用签名函数,所以还要再次调用签名处理的。
Return public keys or hashes from scriptPubKey, for ‘standard’ transaction types.
对于“标准”事务类型,从scriptPubKey返回公钥或哈希值。
鉴于这个代码也很长,不想说了,scriptPubKey是指锁定脚本,不是单指公钥脚本,好吧稍微写一点好了
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{// Templates
static multimap<txnouttype, CScript> mTemplates;//构造模版
if (mTemplates.empty())
{
// Standard tx, sender provides pubkey, receiver adds signature
//标准交易,发送者提供公钥,接收者添加签名
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
//比特币地址交易,发送者提供公钥哈希,接收者提供签名和公钥
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
// Sender provides N pubkeys, receivers provides M signatures
//多重签名,发送者提供n个公钥,接收者提供m个签名
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
}
vSolutionsRet.clear();
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
//付费到脚本哈希的快捷方式,比其他类型更受限制:它总是OP_HASH160 20 [20字节哈希] OP_EQUAL
if (scriptPubKey.IsPayToScriptHash())//对P2SH的处理
{
typeRet = TX_SCRIPTHASH;
//20字节哈希我们是知道,前面两字节是操作OP_HASH160和代表长度的字节
vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
vSolutionsRet.push_back(hashBytes);
return true;
}
····
判断是否脚本hash,写的比较明确了
bool CScript::IsPayToScriptHash() const
{
// Extra-fast test for pay-to-script-hash CScripts:
return (this->size() == 23 &&
(*this)[0] == OP_HASH160 &&
(*this)[1] == 0x14 &&
(*this)[22] == OP_EQUAL);
}
vSolutionsRet保存的是hash值
函数运行后,typeRet会保存锁定脚本的类型,这是枚举类型
enum txnouttype
{
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEYHASH,
TX_SCRIPTHASH,
TX_MULTISIG,
TX_NULL_DATA,
TX_WITNESS_V0_SCRIPTHASH,
TX_WITNESS_V0_KEYHASH,
};
在来说点函数里的代码,接上一段代码,这里是对隔离见证的处理,隔离见证也是固定的长度,20或者32字节
int witnessversion;
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
if (witnessversion == 0 && witnessprogram.size() == 20) {
typeRet = TX_WITNESS_V0_KEYHASH;
vSolutionsRet.push_back(witnessprogram);
return true;
}
if (witnessversion == 0 && witnessprogram.size() == 32) {
typeRet = TX_WITNESS_V0_SCRIPTHASH;
vSolutionsRet.push_back(witnessprogram);
return true;
}
return false;
}
这里涉及到的判断,也写的很清楚,截取见证内容保存到program中
bool CScript::IsWitnessProgram(int& version, std::vector<unsigned char>& program) const
{
if (this->size() < 4 || this->size() > 42) {
return false;
}
if ((*this)[0] != OP_0 && ((*this)[0] < OP_1 || (*this)[0] > OP_16)) {
return false;
}
if ((size_t)((*this)[1] + 2) == this->size()) {
version = DecodeOP_N((opcodetype)(*this)[0]);
program = std::vector<unsigned char>(this->begin() + 2, this->end());
return true;
}
return false;
}
再返回函数部分,
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
typeRet = TX_NULL_DATA;
return true;
}
在这段之后就是对最开始的mTemplates的处理,对三种的处理,轮询处理
在解析出脚本类型和脚本hash或公钥后回到SignStep函数中,以公钥脚本为例,查看如何签名
keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, creator, scriptPubKey, ret, sigversion);
来看Sign1这个函数,签名保存在ret变量中
static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
{
vector<unsigned char> vchSig;
if (!creator.CreateSig(vchSig, address, scriptCode, sigversion))
return false;
ret.push_back(vchSig);
return true;
}
函数中是调用的CreateSig,不能用非压缩格式的私钥签名个隔离见证脚本
bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
CKey key;
if (!keystore->GetKey(address, key))
return false;
// Signing with uncompressed keys is disabled in witness scripts
if (sigversion == SIGVERSION_WITNESS_V0 && !key.IsCompressed())
return false;
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);//要签名的hash
if (!key.Sign(hash, vchSig))//签名的调用
return false;
vchSig.push_back((unsigned char)nHashType);//加上hash类型
return true;
}
到这里就有签名了