Author

Topic: 【研究会】开源 Bitcoin P2P电子货币系统背后的技术(四)(转) (Read 1069 times)

newbie
Activity: 6
Merit: 0
同一文章為何要分四個主題? 灌水嗎?
是你误会楼猪了。
sr. member
Activity: 560
Merit: 250
同一文章為何要分四個主題? 灌水嗎?

原文就是这种篇幅。国内对于比特币理论研究的人实在不多,如此文章实在难得。
legendary
Activity: 1792
Merit: 1111
同一文章為何要分四個主題? 灌水嗎?
sr. member
Activity: 560
Merit: 250
作者: 李雪愚

Ok,接着描述交易验证的具体的过程。
涉及的脚本命令(操作符)

首先讲讲涉及到的脚本命令(操作符),描述栈顶项的变化方式如下:(n1 n2 — ) 用 “–”分隔操作符执行前后的栈中元素的变化,左边表示操作前,右边表示操作后。最右边的项表示栈顶元素。这里例子中,n2 表示栈顶,n1表示次栈顶,操作后n1,n2均被弹出。
OP_EQUAL(n1 n2 — true/false): 判断栈顶两项是否相等的脚本命令,如果相等则压入true,否则false
OP_VERIFY(true/false — NOTHING/false): 如果栈顶项为true,标记改交易单为有效,否则失败,压入false并返回
OP_CODESEPARATOR( — ): 将当前执行PC(ProgramCount)保存到变量: pbegincodehash
OP_CHECKSIG( – true/false): 校验收入来源(in)的签名是否有效。栈顶是公开密钥(PubKey),次栈顶是签名(Sig)。如果验证通过会压入True,否则压入False。
OP_EQUALVERIFY(n1 n2 — NOTHING/false): 等于 OP_EQUAL OP_VERIFY 两个操作符的联合。
交易验证

以最常见的接收地址交易转账为例描述交易验证的过程(SendMoneyToBitcoinAddress)

首先示例交易如下:
{
  "hash":"23c9e8a3140938a65c73fa0712b1d8614c8eeef486a4ab14107fcce54fbf768c",
  "ver":1,
  "vin_sz":1,
  "vout_sz":2,
  "lock_time":0,
  "size":258,
  "in":[
    {
      "prev_out":{
        "hash":"0740c9ecdef03e46e4c0f74833db5af3cb24c7d48810ef73411662252046f9c6",
        "n":0
      },
      "scriptSig":"304502204028931b310bfec094bbd04275f69beba6bf5f44ab3dbbec9564c34ef9bc1ddc022100d 37c214e978808099fc96006b41244d31b203eb4b1c69be9d57b0e2f843ad93501
      0481f6d0a51bfecb5112f223807ba668c67ac975fb096d07ef65b852a6864d9e9c18c9c16e5887c 3f03be519fd42cae52f6d1a05d10a2fa7c783b19e9b74be07d6"
    }
  ],
  "out":[
    {
      "value":"12.99000000",
      "scriptPubKey":"OP_DUP OP_HASH160 ef055cb1e8e192cbd8b6a89ccf1eb73552d9764b OP_EQUALVERIFY OP_CHECKSIG"
    },
    {
      "value":"17.00000000",
      "scriptPubKey":"OP_DUP OP_HASH160 923f3cdf648c1755455a17097d22f0be6add7c7c OP_EQUALVERIFY OP_CHECKSIG"
    }
  ]
}

ScriptSig( ): 中有两个值,一是当前拥有者的该交易单的签名,二是拥有者的公开密钥(用来验证签名)。
Sig: 304502204028931b310bfec094bbd04275f69beba6bf5f44ab3dbbec9564
c34ef9bc1ddc022100d37c214e978808099fc96006b41244d31b203eb4b1c69
be9d57b0e2f843ad93501

PubKey:0481f6d0a51bfecb5112f223807ba668c67ac975fb096d07ef65b852a6
864d9e9c18c9c16e5887c3f03be519fd42cae52f6d1a05d10a2fa7c783b19e9b7
4be07d6

脚本执行验证过程:
执行 scriptSig:
压入 :
执行 scriptPubKey:
OP_DUP 复制栈顶:
OP_HASH160 对栈顶项做HASH160(RIPEMD-160(SHA256())散列:
: 压入
OP_EQUALVERIFY: 对栈顶两个元素 做是否相等校验,如果不相等那么标记交易单无效并返回,如果等于继续:
OP_CHECKSIG: 校验收入来源的In的签名是否有效,根据pubKey去校验签名,签名的内容为交易单的Ins, Outs和脚本(从最近一次的 OP_CODESEPARATOR 到脚本结束的位置)的散列。 PubKey.Verify(SignatureHash(scriptCode, nIn, Outs), Sig)


//nIn 当前的交易单的in的索引号
//scriptCode: 保存的当前执行的代码从OP_CODESEPARATOR(如果没有就从脚本开始) 到脚本结束。
uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
    if (nIn >= txTo.vin.size())
    {
        printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn);
        return 1;
    }
    CTransaction txTmp(txTo);

    // In case concatenating two scripts ends up with two codeseparators,
    // or an extra one at the end, this prevents all those possible incompatibilities.
    scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));

    // Blank out other inputs' signatures
    for (int i = 0; i < txTmp.vin.size(); i++)
        txTmp.vin.scriptSig = CScript();
    txTmp.vin[nIn].scriptSig = scriptCode;

    // Blank out some of the outputs
    if ((nHashType & 0x1f) == SIGHASH_NONE)
    {
        // Wildcard payee
        txTmp.vout.clear();

        // Let the others update at will
        for (int i = 0; i < txTmp.vin.size(); i++)
            if (i != nIn)
                txTmp.vin.nSequence = 0;
    }
    else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
    {
        // Only lockin the txout payee at same index as txin
        unsigned int nOut = nIn;
        if (nOut >= txTmp.vout.size())
        {
            printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut);
            return 1;
        }
        txTmp.vout.resize(nOut+1);
        for (int i = 0; i < nOut; i++)
            txTmp.vout.SetNull();

        // Let the others update at will
        for (int i = 0; i < txTmp.vin.size(); i++)
            if (i != nIn)
                txTmp.vin.nSequence = 0;
    }

    // Blank out other inputs completely, not recommended for open transactions
    if (nHashType & SIGHASH_ANYONECANPAY)
    {
        txTmp.vin[0] = txTmp.vin[nIn];
        txTmp.vin.resize(1);
    }

    // Serialize and hash
    CDataStream ss(SER_GETHASH);
    ss.reserve(10000);
    ss << txTmp << nHashType;
    return Hash(ss.begin(), ss.end());
}

转自http://www.showmuch.com/a/20110608/094205.html
Jump to: