Bitcoin Source Code Study Notes (I)

Bitcoin Source Code Study Notes (I)

Preface

It is not enough to engage in blockchain development without understanding its underlying core technology. Many people still do not know how Bitcoin is implemented after reading the Bitcoin white paper, because the source code of Bitcoin is exquisitely designed, and there are many designs that are not mentioned in the white paper. In addition, the documentation of Bitcoin itself is scarce, which increases the difficulty for novices to understand. Although there are many books and articles introducing blockchain, few of them start from the source code. After half a year of learning about blockchain, I started to write a tutorial on Bitcoin source code. This tutorial is easy to understand and allows developers to get started with blockchain technology in the shortest time by analyzing the most classic blockchain - Bitcoin's C++ client source code. Understanding Bitcoin source code can help developers better understand the working principle of blockchain and make modifications and adjustments in applications according to actual conditions.

The source code cited in this article is from the original version of the Bitcoin client, which is the first version of the source code released by Satoshi Nakamoto. The client includes about 16,000 lines of code. Although the Bitcoin client has undergone several major updates over the years, its data structure and principles have continued from the day it was born to this day. This article will try its best to ensure the rigor and accuracy of the text, and there will inevitably be omissions in the expression, and corrections are welcome.

Chapter 1

This section describes how the Bitcoin client generates Bitcoin addresses and creates new transactions.

Let's take a look at the GenerateNewKey() method, which is located in main.cpp.

 bool AddKey(const CKey& key)
{
    CRITICAL_BLOCK(cs_mapKeys)
{
        mapKeys[key.GetPubKey()] = key.GetPrivKey();
        mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey();
}
    return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey());
}
vector<unsigned char> GenerateNewKey()
{
CKey key;
key.MakeNewKey();
if (!AddKey(key))
        throw runtime_error("GenerateNewKey() : AddKey failed\n");
return key.GetPubKey();
}

This method generates a new public key pair by following these steps:

  • First, create a new CKey type object (line 13).

  • Call the addKey() method to add the newly created key to 1) the global map mapKeys (line 5) 2) the global map mapPubKeys (line 6) and the wallet database wallet.dat (line 8).

mapKeys establishes a one-to-one correspondence between public keys and private keys.
mapPubKeys establishes a correspondence between the hash of the public key and the public key itself.

  • Return the public key (line 16).

The public key is in an uncompressed format and is one of the OpenSSL standard formats. After obtaining the public key, the Bitcoin client passes the public key to PubKeyToAddress() and calls Hash160ToAddress() method to generate an address. The Base58 encoded string value returned at the end is a newly generated Bitcoin address. Base58 consists of 1-9 and English characters except i, l, 0, and o.

CTransaction Class

The definition of CTransaction is located in main.h. In Bitcoin, the concept of a coin is actually a combination of a series of transactions Tx. Although this method is more complicated to implement, it improves the security of Bitcoin. Users can create a new address for each transaction, and the address can be immediately invalidated after being used once. Therefore, CTransaction is one of the most important classes in the Bitcoin client.

 class CTransaction
{
public:
int nVersion;
vector<CTxIn> vin;
vector<CTxOut> vout;
int nLockTime;
//......
}

CTransaction contains two container types: input transaction vin and output transaction vout. Each vin is composed of several CTxIn objects, and each vout is composed of CTxOut objects.

The input transaction (CTxIn class) of each transaction Tx contains a COutPoint object prevout, which references the output transaction of another transaction Tx as the source transaction. The source transaction enables the current transaction Tx to obtain spendable bitcoins from another transaction. A transaction Tx can have any input transactions.

Any transaction is uniquely identified by a 256-bit uint256 hash. To reference a specific output transaction in a source transaction TxSource, we need two pieces of information: the hash of TxSource, and the position n of the output transaction in the output transaction. These two pieces of information constitute the COutPoint class. A COutPoint object points to an output transaction TxSource.vout[n] of the source transaction. If the output transaction is referenced by the input transaction at position i of another transaction Tx, for example Tx.vin[i].prevout , we call it the i-th input transaction of Tx spending the n-th output transaction in TxSource.

uint256 and uint160 classes

The definitions of these two types are located in uint.h. A uint256 class contains a 256-bit hash. It consists of an unsigned int array of length 256/32=8. A similar data structure is uint160, which is defined in the same file. Since the length of SHA-256 is 256 bits, it is not difficult for readers to infer that the function of uint160 is to store RIPEMD-160 hashes. Both uint256 and uint160 are inherited from the base_uint class.

 class base_uint {
protected:
    enum { WIDTH = BITS / 32 };
unsigned int pn[WIDTH];
public:
bool operator!() const
{
        for (int i = 0; i < WIDTH; i++)
if (pn[i] != 0)
return false;
return true;
}
//......
    unsigned int GetSerializeSize(int nType = 0, int nVersion = VERSION) const
{
return sizeof(pn);
}
    template <typename Stream>
    void Serialize(Stream& s, int nType = 0, int nVersion = VERSION) const
{
        s.write((char*)pn, sizeof(pn));
}
    template <typename Stream>
    void Unserialize(Stream& s, int nType = 0, int nVersion = VERSION)
{
        s.read((char*)pn, sizeof(pn));
}
}

This class overloads several operators. In addition, this class has three serialization member functions, GetSerializeSize() , Serialize() , and Unserialize() . We will talk about how these three methods work later.

SendMoney()

This method is located in main.cpp. The following is the source code of this method:

 bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew)
{
CRITICAL_BLOCK(cs_main)
{
int64 nFeeRequired;
        if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired))
{
string strError;
            if (nValue + nFeeRequired > GetBalance())
                strError = strprintf("Error: This is an oversized transaction that requires a transaction fee of %s ", FormatMoney(nFeeRequired).c_str());
else
                strError = "Error: Transaction creation failed ";
            wxMessageBox(strError, "Sending...");
            return error("SendMoney() : %s\n", strError.c_str());
}
        if (!CommitTransactionSpent(wtxNew))
{
            wxMessageBox("Error finalizing transaction", "Sending...");
            return error("SendMoney() : Error finalizing transaction");
}
        printf("SendMoney: %s\n", wtxNew.GetHash().ToString().substr(0,6).c_str());
// Broadcast
        if (!wtxNew.AcceptTransaction())
{
            // This must not fail. The transaction has already been signed and recorded.
            throw runtime_error("SendMoney() : wtxNew.AcceptTransaction() failed\n");
            wxMessageBox("Error: Transaction not valid", "Sending...");
            return error("SendMoney() : Error: Transaction not valid");
}
        wtxNew.RelayWalletTransaction();
}
MainFrameRepaint();
return true;
}

When a user sends bitcoins to an address, the Bitcoin client calls the SendMoney() method. This method contains three parameters:

  • scriptPubKey contains the script code OP_DUP OP_HASH160 <160-bit hash of the payee address> OP_EQUALVERIFY OP_CHECKSIG.

  • nValue represents the amount to be transferred. This amount does not include the transaction fee nTrasactionFee.

  • wtxNew is a local variable of the CWalletTx class. The variable is currently empty and will contain several CMerkleTX class objects. This class is derived from CTransaction and adds several methods. Let's ignore the details for now and just treat it as a CTransaction class.

The flow of this method is obvious:

  • First, create a new transaction (CreateTransaction(scriptPubKey, nValue, wtxNet, nFeeRequired), line 6).

  • Try to commit the transaction to the database (CommitTransactionSpent(wtxNet), line 16).

  • If the transaction is submitted successfully (wtxNew.AcceptTransaction(), line 23), it is broadcast to other peer nodes (wtxNew.RelayWalletTransaction(), line 30).

These four methods are all related to wtxNew. We introduced the first one in this chapter, and the other three will be introduced in subsequent articles.

CreateTransaction()

This method is located in main.cpp. The following is the source code of this method:

 bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, int64& nFeeRequiredRet)
{
nFeeRequiredRet = 0;
CRITICAL_BLOCK(cs_main)
{
        // txdb must be opened before the mapWallet lock
CTxDB txdb("r");
        CRITICAL_BLOCK(cs_mapWallet)
{
            int64 nFee = nTransactionFee;
loop
{
                wtxNew.vin.clear();
                wtxNew.vout.clear();
                if (nValue < 0)
                    return false;
                int64 nValueOut = nValue;
                nValue += nFee;
                // Choose coins to use
                set<CWalletTx*> setCoins;
                if (!SelectCoins(nValue, setCoins))
                    return false;
                int64 nValueIn = 0;
                foreach(CWalletTx* pcoin, setCoins)
                    nValueIn += pcoin->GetCredit();
                // Fill vout[0] to the payee
                wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey));
                // Fill vout[1] back to self with any change
                if (nValueIn > nValue)
{
                    // Use the same key as one of the coins
                    vector<unsigned char> vchPubKey;
                    CTransaction& txFirst = *(*setCoins.begin());
                    foreach(const CTxOut& txout, txFirst.vout)
                        if (txout.IsMine())
                            if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
                                break;
                    if (vchPubKey.empty())
                        return false;
                    // Fill vout[1] to ourselves
                    CScript scriptPubKey;
                    scriptPubKey << vchPubKey << OP_CHECKSIG;
                    wtxNew.vout.push_back(CTxOut(nValueIn - nValue, scriptPubKey));
}
// Fill vin
                foreach(CWalletTx* pcoin, setCoins)
                    for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
                        if (pcoin->vout[nOut].IsMine())
                            wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut));
//Sign
int nIn = 0;
                foreach(CWalletTx* pcoin, setCoins)
                    for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
                        if (pcoin->vout[nOut].IsMine())
                            SignSignature(*pcoin, wtxNew, nIn++);
                // Check that enough fee is included
                if (nFee < wtxNew.GetMinFee(true))
{
                    nFee = nFeeRequiredRet = wtxNew.GetMinFee(true);
continue;
}
                // Fill vtxPrev by copying from previous transactions vtxPrev
                wtxNew.AddSupportingTransactions(txdb);
                wtxNew.fTimeReceivedIsTxTime = true;
break;
}
}
}
return true;
}

When calling this method, it requires four parameters as follows:

  • scriptPubKey contains the script code OP_DUP OP_HASH160 <160-bit hash of the payee address> OP_EQUALVERIFY OP_CHECKSIG.

  • nValue is the amount to be transferred, and the transaction fee nTransactionFee is not included.

  • wtxNew is a new Tx instance.

  • nFeeRequiredRet is an output transaction used to pay the transaction fee, obtained after the execution of this method is completed.

The process of this method is as follows:

  • Define a local variable nValueOut = nValue to store the amount to be transferred (line 17). Add nValue to the transaction fee nFee to get a new nValue that includes the transfer fee.

  • Execute SelectCoins(nValue, setCoins) on line 21 to get a list of coins and put them into setCoins. setCoins contains transactions paid to your own address, that is, the coins you own. These transactions will become the source transactions of wtxNew.

  • Execute wtxNew.vout.push_back(CTxOut (nValueOut,sciptPubKey)) on line 27 and add an output transaction to wtxNew. This output will pay <payee address 160-bit hash> (contained in scriptPubKey) amount of coins.

  • If change is needed (nValueIn > nValue), add another output transaction to wtxNew and send the change back to me. This process includes the following steps:

    • Get the first transaction txFirst from setCoin, and check whether the transactions in txFirst.vout belong to you. If yes, extract the public key from the output transaction and put it into the local variable vchPubKey

    • Put vchPubKey into the script vchPubKey OP_CHECKSIG, and use this script code to add an output transaction for wtxNew that pays me (line 45).

    • Because setCoins includes a transaction paid to me, each transaction must include at least one transaction paid to me. This can be found in the first transaction txFirst.

  • At this point, wtxNew's output transaction container vout is ready. Now, it's time to set up the input transaction container vin. Remember that each input transaction list vin references a source transaction, and each source transaction of wtxNew can be found in setCoins. For each transaction pcoin in setCoins, traverse its output transactions pcoin->vout[nOut] one by one. If the nOutth output pays to me (which means wtxNew gets coins from this output transaction), then add a new input transaction to wtxNew (wtxNew.vin(wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut)), line 51). This input transaction points to the nOutth output transaction in pcoin, thereby connecting wtxNew.vin to the nOutth output of pcoin.

  • For each transaction pcoin in setCoins, traverse all its output transactions pcoin->vout[nOut] one by one. If the transaction belongs to me, call SignSignature(*pcoin,wtxNew, nIn++) to add a signature for the nInth input transaction. Note that nIn is the input transaction position of wtxNew.

  • If the transaction fee nFee is less than wtxNet.GetMinFee(true), nFee is set to the latter, all data in wtxNew is cleared and the whole process starts again. In the first iteration at line 11, nFee is a local copy of the global variable nTransactionFee = 0.

  • If you don't understand why it takes so much effort to refill wtxNew, GetMinFee() in the source code provides the answer: the minimum transaction fee is related to the data size of the transaction. The size of wtxNew can only be known after it is fully constructed. If the minimum transaction fee calculated by wtxNew.GetMinFee(true) is greater than the transaction fee nFee assumed when creating wtxNew, there is no other way but to rebuild wtxNew.

  • There is a chicken-and-egg situation here: if you want to create a new transaction, you must know how much the transaction fee is. The transaction fee is only known after the entire transaction is created. To break this cycle, the local variable nFee is used to hold the estimated transaction fee, and the new transaction is constructed based on it. After the construction is completed, the actual transaction fee is obtained and compared with the estimated transaction fee. If the estimated transaction fee is less than the actual transaction fee, it is replaced with the actual transaction fee and the entire transaction is reconstructed.

Here is the source code for GetMinFee():

 int64 GetMinFee(bool fDiscount=false) const
{
        unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK);
        if (fDiscount && nBytes < 10000)
return 0;
        return (1 + (int64)nBytes / 1000) * CENT;
}
  • If the calculated transaction fee is higher than the previously estimated transaction fee, the loop starting from line 11 is jumped out and the entire function is returned (line 67). Before that, the following two steps need to be performed:

    • Execute wtxNew.AddSupportingTransactions(txdb). This section will be described in more detail later.

    • Set wtxNet.fTimeReceivedIsTxTime = true (line 66).

Now let's see how to sign the newly generated transaction wtxNew through SignSignature().

SignSignature()

This method is located in script.cpp. The following is the source code of this method:

 bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq)
{
    assert(nIn < txTo.vin.size());
    CTxIn& txin = txTo.vin[nIn];
    assert(txin.prevout.n < txFrom.vout.size());
    const CTxOut& txout = txFrom.vout[txin.prevout.n];
    // Leave out the signature from the hash, since a signature can't sign itself.
    // The checksig op will also drop the signatures from its hash.
    uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType);
    if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig))
return false;
    txin.scriptSig = scriptPrereq + txin.scriptSig;
// Test solution
if (scriptPrereq.empty())
        if (!EvalScript(txin.scriptSig + CScript(OP_CODESEPARATOR) + txout.scriptPubKey, txTo, nIn))
return false;
return true;
}

The first thing to note is that this function has 5 parameters, while CreateTransaction() only has 3. This is because the last two parameters are given by default in the script.h file.

The following are the three parameters passed to CreateTransaction() :

  • txFrom is a *pcoin object. It is one of the coins in setCoins in CreateTransaction(). It is also a source transaction. Its output transactions contain the coins that the new transaction will spend.

  • txTo is the wtxNew object in CreateTransaction(). It is the new transaction that will spend the source transaction txFrom. The new transaction needs to be signed to take effect.

  • nIn is the index position pointing to the input transaction list in txTo. The input transaction list contains a reference to the output transaction list of txFrom. More precisely, txin=txTo.vin[nIn] (line 4) is the input transaction in txTo; txout=txFrom.vout[txin.prev.out.n] (line 6) is the output transaction in txFrom pointed to by txin.

Here’s what SignSignature() does:

  • Call the SignatureHash() method to generate the hash value of txTo.

  • Call the Solver() function to sign the hash just generated.

  • Call EvalScript() to run a small script and check if the signature is valid.

Let's look at these three functions together.

SignatureHash()

This method is located in script.cpp . The following is the source code of SignatureHash().

 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[i].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[i].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[i].SetNull();
        // Let the others update at will
        for (int i = 0; i < txTmp.vin.size(); i++)
if (i != nIn)
                txTmp.vin[i].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());
}

The following are the parameters required by this function:

  • txTo is the transaction to be signed. It is also the wtxNew object in CreateTransaction(). The nInth item in its input transaction list, txTo.vin[nIn] , is the target that the function will act on.

  • scriptCode is scriptPrereq + txout.scriptPubKey, where txout is the output transaction of the source transaction txFrom() defined in SignSignature(). Since scriptPrereq is empty at this time, scriptCode is actually the script code of the transaction referenced by txTo as an input transaction in the output transaction list in the source transaction txFrom. txout.scriptPubKey may contain two types of scripts:

    • Script A: OP_DUP OP_HASH160 <160-bit hash of your address> OP_EQUALVERIFY OP_CECKSIG. This script sends the coins in the source transaction txFrom to you, where <160-bit hash of your address> is your Bitcoin address.

    • Script B: <your public key> OP_CHECKSIG. This script returns the remaining coins to the initiator of the source transaction txFrom. Since the new transaction txTo/wtxNew you created will spend the coins from txFrom, you must also be the creator of txFrom. In other words, when you create txFrom, you are actually spending the coins that others have sent to you before. Therefore, <your public key> is both the public key of the creator of txFrom and your own public key.

Let's stop here for a moment and think about Script A and Script B. You may ask where these scripts come from. When Satoshi Nakamoto created Bitcoin, he added a scripting language system to Bitcoin, so all transactions in Bitcoin are completed by script code. This scripting system is actually the prototype of smart contracts. Script A comes from line 29, located in the method CSendDialog::OnButtonSend(), and Script B comes from line 44, located in the method CreateTransaction().

  • When a user initiates a transaction, the Bitcoin client calls the CSendDialog::OnButtonSend() method and adds script A to an output transaction in txFrom. Since the recipient of the output transaction is you, the <160-bit hash of the recipient's address> in the script is <160-bit hash of your address>.

  • If txFrom is created by you, script B will be added to an output transaction of txFrom in CreateTransaction(). Here, the public key vchPubKey in CreateTransaction() on line 44 is your public key.

After understanding the input transaction, let's understand how SignatureHash() works.

SignatureHash() first copies txTO to txTmp, then clears the scriptSig of each input transaction in txTmp.vin, except for txTmp.vin[nIn], where the scriptSig of the input transaction is set to scriptCode (lines 14 and 15).

Next, the function checks the value of nHashType. The caller of the function passes an enumeration value to the function nHashType = SIGHASH_ALL.

 enum
{
SIGHASH_ALL = 1,
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
    SIGHASH_ANYONECANPAY = 0x80,
};

Since nHashType = SIGHASH_ALL, all if-else conditions are not met, and the function will directly execute the last 4 lines of code.

In the last 4 lines of code, txTmp and nHashType become serialized type CDataStream objects. This type includes a character container type that holds the data. The returned hash value is the result of the Hash() method calculating the serialized data.

A transaction can contain multiple input transactions. SignatureHash() takes one of them as the target. It generates the hash by the following steps:

  • Clear all input transactions except the target transaction.

  • Copy the script of the output transaction in the source transaction that is referenced by the target transaction as an input transaction to the input transaction list of the target transaction.

  • Generate a hash value for the modified transaction.

Hash()

This method is located in util.h. The following is the source code of the method Hash() that generates a hash value:

 template<typename T1>
inline uint256 Hash(const T1 pbegin, const T1 pend)
{
uint256 hash1;
    SHA256((unsigned char*)&pbegin[0], (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
uint256 hash2;
    SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
}

This function executes the SHA256() method twice on the target data and returns the result. The declaration of SHA256() can be found in openssl/sha.h.

Solver()

This method is located in script.cpp. Solver() is executed in SignSignature() immediately after SignatureHash(). It is the function that actually generates the signature for the hash value returned by SignatureHash().

 bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
{
scriptSigRet.clear();
    vector<pair<opcodetype, valtype> > vSolution;
    if (!Solver(scriptPubKey, vSolution))
return false;
// Compile solution
    CRITICAL_BLOCK(cs_mapKeys)
{
        foreach(PAIRTYPE(opcodetype, valtype)& item, vSolution)
{
            if (item.first == OP_PUBKEY)
{
//Sign
                const valtype& vchPubKey = item.second;
                if (!mapKeys.count(vchPubKey))
                    return false;
                if (hash != 0)
{
                    vector<unsigned char> vchSig;
                    if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig))
                        return false;
                    vchSig.push_back((unsigned char)nHashType);
                    scriptSigRet << vchSig;
}
}
            else if (item.first == OP_PUBKEYHASH)
{
                // Sign and give pubkey
                map<uint160, valtype>::iterator mi = mapPubKeys.find(uint160(item.second));
                if (mi == mapPubKeys.end())
                    return false;
                const vector<unsigned char>& vchPubKey = (*mi).second;
                if (!mapKeys.count(vchPubKey))
                    return false;
                if (hash != 0)
{
                    vector<unsigned char> vchSig;
                    if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig))
                        return false;
                    vchSig.push_back((unsigned char)nHashType);
                    scriptSigRet << vchSig << vchPubKey;
}
}
}
}
return true;
}

The following are the 4 parameters required by this method:

  • The call to function SignSignature() on line 10 passes txOut.scriptPubKey, the output script of the source transaction txFrom, as input to the first parameter scriptPubKey. Remember that it may contain script A or script B.

  • The second parameter hash is the hash value generated by SignatureHash().

  • The value of the third parameter nHashType is SIGHASH_ALL.

  • The fourth parameter is the return value of the function, which is txin.scriptSig in line 12 of the call to function SignSIgnature(). Remember that txin is the newly generated transaction wtxNew (referenced as txTo in the call to function SignSignature()) and is located at the nInth input transaction. Therefore, the scriptSig of the nInth input transaction of wtxNew will store the signature returned by the function.

The function first calls another Solver() with 2 parameters. Let's examine it.

Solver() with 2 parameters

This method is located in script.cpp. The following is the source code of Solver() with 2 parameters:

 bool Solver(const CScript& scriptPubKey, vector<pair<opcodetype, valtype> >& vSolutionRet)
{
// Templates
    static vector<CScript> vTemplates;
if (vTemplates.empty())
{
        // Standard tx, sender provides pubkey, receiver adds signature
        vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG);
        // Short account number tx, sender provides hash of pubkey, receiver provides signature and pubkey
        vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG);
}
// Scan templates
    const CScript& script1 = scriptPubKey;
    foreach(const CScript& script2, vTemplates)
{
vSolutionRet.clear();
        opcodetype opcode1, opcode2;
        vector<unsigned char> vch1, vch2;
// Compare
        CScript::const_iterator pc1 = script1.begin();
        CScript::const_iterator pc2 = script2.begin();
loop
{
            bool f1 = script1.GetOp(pc1, opcode1, vch1);
            bool f2 = script2.GetOp(pc2, opcode2, vch2);
if (!f1 && !f2)
{
// Success
                reverse(vSolutionRet.begin(), vSolutionRet.end());
return true;
}
            else if (f1 != f2)
{
break;
}
            else if (opcode2 == OP_PUBKEY)
{
                if (vch1.size() <= sizeof(uint256))
break;
                vSolutionRet.push_back(make_pair(opcode2, vch1));
}
            else if (opcode2 == OP_PUBKEYHASH)
{
                if (vch1.size() != sizeof(uint160))
break;
                vSolutionRet.push_back(make_pair(opcode2, vch1));
}
            else if (opcode1 != opcode2)
{
break;
}
}
}
vSolutionRet.clear();
return false;
}

The first parameter, scriptPubKey, may contain script A or script B. Once again, it is the output script of the source transaction txFrom in SignSignature().

The second parameter is used to store output transactions. It is a container pair, each pair consists of a script operator (opcodetype type) and a script operation element (valtype type).

Lines 8-10 of this function first define two templates:

  • Template A: OP_DUP OP_HASH160 OP_PUBKEYHASH OP_EQUALVERIFY OP_CHECKSIG.

  • Template B: OP_PUBKEY OP_CHECKSIG.

Obviously, Template A and Template B correspond to Script A and Script B. For comparison purposes, here are the contents of Script A and Script B:

  • Script A: OP_DUP OP_HASH160 <your address 160-bit hash> OP_EQUALVERIFY OP_CHECKSIG.

  • Script B: <your public key> OP_CHECKSIG.

This function compares the scriptPubKey with two templates:

  • If the input script is Script A, pair the OP_PUBKEYHASH in Template A with the <your address 160-bit hash> in Script A and put the pair into vSolutionRet.

  • If the input script is script B, extract the operator OP_PUBKEY from template B and the operand <your public key> from script B, pair them up and put them into vSolutionRet.

  • If the input script does not match either template, returns false.

Back to Solver()

Let's go back to Solver() with 4 parameters and continue the analysis of this function. Now we know how this function works. It will choose one of two branches to execute, depending on whether the pair obtained from vSolutionRet comes from script A or script B. If it comes from script A, item.first == OP_PUBKEYHASH; if it comes from script B, item.first == OP_PUBKEY.

  • item.first == OP_PUBKEY (script B). In this case, item.second contains <your public key>. The global variable mapKeys maps all your public keys to their corresponding private keys. If the public key is not in mapKeys, an error is reported (line 16). Otherwise, the hash value of the newly generated transaction wtxNew is signed with the private key extracted from mapKeys as the second parameter passed in (CKey::Sign(mapKeys[vchPubKey], hash, vchSig), line 23), the result is put into vchSig, and then it is serialized into scriptSigRet (scriptSigRet << vchSig, line 24) and returned.

  • item.first == OP_PUBKEYHASH (script A). In this case, item.second contains <your 160-bit hash of your address>. This Bitcoin address is used to find its corresponding public key from the global map mapPubKeys on line 23. The global map mapPubKeys associates your address with the public key that generated them (see function AddKey()). Next, the corresponding private key is found from mapKeys using the public key and signed with the second argument hash. The signature and public key are serialized together into scriptSigRet and returned (scriptSig << vchSig << vchPubkey, line 24).

EvalScript()

This method is located in script.cpp. Now let's go back to SignSignature(). After line 12 of this function, txin.scriptsig, the scriptSig part of the nInth input transaction of wtxNew, will insert a signature. This signature may be one of the following:

  • vchSig vchPubKey (Signature A of script A)

  • vchSig (Signature B of script B)

In the following text, vchSig will be referenced as <your signature_vchSig> and vchPubKey will be referenced as <your public key_vchPubKey> to emphasize that they are your signature and public key, respectively.

We now start investigating EvalScript(), which is the last function called by SignSignature(), located on line 15. EvalScript() takes 3 parameters, namely:

  • The first parameter is txin.scriptSig + CScript(OP_CODESEPARATOR) + txout.scriptPubKey. It can be:

    • Verification scenario A: <your signature_vchSig> <your public key_vchPubKey> OP_CODESEPARATOR OP_DUP OP_HASH160 <your address 160-bit hash> OP_EQUALVERIFY OP_CHECKSIG, that is, signature A + OP_CODESEPARATOR + script A.

    • Verification scenario B: <your signature_vchSig> OP_CODESEPARATOR <your public key_vchPubKey> OP_CHECKSIG, that is, signature B + OP_CODESEPARATOR + script B.

  • The second parameter is the newly created transaction txTo, which is wtxNew in CreateTransaction().

  • The third parameter is nIn, which is the position of the transaction to be verified in the txTo input transaction list.

We will describe the verification process in detail later. In short, EvalScript() verifies whether the nInth input transaction of the newly created transaction wtxNew contains a valid signature. At this point, a new Bitcoin transaction is created.

<<:  4 Ways to Tell If Bitcoin Is in a Bubble

>>:  Alibaba takes action against counterfeiting and begins using blockchain to combat food fraud

Recommend

Is a plump waist a sign of good fortune?

The waist supports the human body, connecting the...

What kind of palm lines will bring you lots of romance in this life?

Most people hope to find their true love and end ...

ETCWin founder: China will have great potential on the ETC chain

On October 30, the “The First Practical Applicati...

Analysis of different eye shapes in women's physiognomy

As one of the traditional physiognomy techniques, ...

The man who brings bad luck to you is unreasonable and has a stern face.

A person with a bad character does not seem to be...

Detailed explanation of female palmistry

Detailed explanation of the five major lines on w...

How to tell if you are blessed

How to tell if you are blessed 1Plump red lips (l...

How to tell career from facial features

A person’s career status can be seen from his fac...

How to tell your fortune by face reading

Financial luck is one of the various fortunes in ...

Ethereum fork plan repeatedly fails, ANZ Bank openly "sings the opposite tune"

Regarding the recent DAO attack, it seems that ev...

What is the difference between Buddha eye pattern and phoenix eye pattern?

There are so many people in the world, and the li...

Which people are prone to falling out with friends?

Sometimes, when we make friends, we pay close att...