比特币源码解读-第6章 发起交易

 

六、发起交易

6.1 比特币脚本(智能合约)

6.1.1 自建脚本演示

解锁脚本与锁定脚本的组合为:2 << 3 << OP_ADD << 5 << OP_EQUAL;即通过调用比特币相关的脚本操作完成2 + 3 == 5的验证过程。

6.1.1.1 构建锁定脚本

//构建锁定脚本
CScript scriptLock;
scriptLock << 3 << OP_ADD << 5 << OP_EQUAL;

即构建的锁定脚本为:3 << OP_ADD << 5 << OP_EQUAL;

6.1.1.2 构建解锁脚本

//构建解锁脚本
CScript scriptUnlock;
scriptUnlock << 2;
scriptUnlock = CScript() + scriptUnlock;

即构建的解锁脚本为:2;

6.1.1.3 调用evalScript函数验证脚本

//脚本验证
if(CScript().empty())
if(!evalScript(scriptUnlock + scriptLock))
return error("Bitcoin_script : evalScript failed");

OP_ADD、OP_EQUAL等脚本操作在evalScript()函数中实现。

v OP_ADD

// (x1 x2 -- out)
if (stack.size() < 2)
return false;
CBigNum bn1(stacktop(-2));
CBigNum bn2(stacktop(-1));
CBigNum bn;
bn = bn1 + bn2;
stack.pop_back();
stack.pop_back();
stack.push_back(bn.getvch());

Ø 将堆栈(后进先出)中的数据实例化为大数bn1、bn2

Ø 进行加法操作:bn = bn1 + bn2

Ø bn1、bn2出栈,即在堆栈中删除bn1、bn2:stack.pop_back();

Ø bn入栈,完成OP_ADD操作

v OP_EQUAL

// (x1 x2 - bool)
if (stack.size() < 2)
return false;
valtype& vch1 = stacktop(-2);
valtype& vch2 = stacktop(-1);
bool fEqual = (vch1 == vch2);
stack.pop_back();
stack.pop_back();
stack.push_back(fEqual ? vchTrue : vchFalse);
if (opcode == OP_EQUALVERIFY)
{
if (fEqual)
stack.pop_back();
else
pc = pend;
}

Ø 将堆栈(后进先出)中的数据实例化vch1、vch2

Ø 进行相等判断bool fEqual = (vch1 == vch2)

Ø vch1、vch2出栈

Ø 判断fEqual,将结果入栈stack.push_back(fEqual ? vchTrue : vchFalse)

Ø 进行OP_EQUALVERIFY操作,如果fEqual为真,则上一步的结果出栈;如果fEqual为假,结束脚本操作,返回结果。

6.2 标准交易P2PK

6.2.1 创建一笔创币交易,CreateTransaction函数在P2PK.cpp文件中实现

CTransaction txNew;

CreateTransaction(txNew, key);

6.2.2 构建交易输入

tx.vin.resize(1);

tx.vin[0].prevout.SetNull();

tx.vin[0].scriptSig << nBits << ++bnExtraNonce;

6.2.3构建交易输出

tx.vout.resize(1);

tx.vout[0].nValue = 50 * COIN;

tx.vout[0].scriptPubKey << key.GetPubKey() << OP_CHECKSIG;

//debug print

tx.print();

return true;

}

6.3 标准交易P2PKH

P2PKH标准详解

6.3.1 锁定脚本scriptPubKey

CScript scriptPubKey;
scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;

即锁定脚本为:OP_DUP OP_HASH160 hash160

6.3.2 操作详解

OP_DUP:堆栈中的复制操作;

OP_HASH160:哈希160操作,得到公钥哈希(scriptPubKey)

OP_EQUALVERIFY:想等判断

OP_CHECKSIG:验证签名是否和公钥签名匹配

6.2.3 脚本验证

VerifySignature(wtxFrom, wtxNew, 0);

通过调用EvalScript()、CheckSig()函数完成验证操作。

6.4 构建交易

新建交易

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());

}

选取交易

具体选择交易使用的“随机逼近算法”在后续《比特币源码解读之选币》一文中介绍

set setCoins;
if (!SelectCoins(nValue, setCoins))
return false;

计算交易费

如果默认的交易费小于当前计算的交易费用,则需要根据当前计算的交易费重新填充交易

// Check that enough fee is included
if (nFee < wtxNew.GetMinFee(true))
{
nFee = nFeeRequiredRet = wtxNew.GetMinFee(true);
continue;
}

填充输入和输出

填充输出

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 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 ourself
CScript scriptPubKey;
scriptPubKey << vchPubKey << OP_CHECKSIG;
wtxNew.vout.push_back(CTxOut(nValueIn - nValue, scriptPubKey));
}

填充输入

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));

签名 

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++);
计算Merkle值
wtxNew.AddSupportingTransactions(txdb);
void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
{
vtxPrev.clear();
const int COPY_DEPTH = 3;
if (SetMerkleBranch() < COPY_DEPTH)
{
vector vWorkQueue;
foreach(const CTxIn& txin, vin)
vWorkQueue.push_back(txin.prevout.hash);
// This critsect is OK because txdb is already open
CRITICAL_BLOCK(cs_mapWallet)
{
map mapWalletPrev;
set setAlreadyDone;
for (int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hash = vWorkQueue[i];
if (setAlreadyDone.count(hash))
continue;
setAlreadyDone.insert(hash);
CMerkleTx tx;
if (mapWallet.count(hash))
{
tx = mapWallet[hash];
foreach(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev)
mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
}
else if (mapWalletPrev.count(hash))
{
tx = *mapWalletPrev[hash];
}
else if (!fClient && txdb.ReadDiskTx(hash, tx))
{
;
}
else
{
printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
continue;
}
int nDepth = tx.SetMerkleBranch();
vtxPrev.push_back(tx);
if (nDepth < COPY_DEPTH)
foreach(const CTxIn& txin, tx.vin)
vWorkQueue.push_back(txin.prevout.hash);
}
}
}
reverse(vtxPrev.begin(), vtxPrev.end());
}

6.5 提交交易

提交交易请求

if (!CommitTransactionSpent(wtxNew))
{
wxMessageBox("Error finalizing transaction", "Sending...");
return error("SendMoney() : Error finalizing transaction");
}

增加交易到钱包中

增加交易到钱包中并保存在mapWallet变量中,通过将选择交易写入磁盘

bool CommitTransactionSpent(const CWalletTx& wtxNew)
{
... ...
AddToWallet(wtxNew);
// Mark old coins as spent
set setCoins;
foreach(const CTxIn& txin, wtxNew.vin)
setCoins.insert(&mapWallet[txin.prevout.hash]);
foreach(CWalletTx* pcoin, setCoins)
{
pcoin->fSpent = true;
pcoin->WriteToDisk();
vWalletUpdated.push_back(make_pair(pcoin->GetHash(), false));
}
}
MainFrameRepaint();
return true;
}

6.6 有效性校验

接受交易

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");
}

检查交易是否有效

if (IsCoinBase())
return error("AcceptTransaction() : coinbase as individual tx");
if (!CheckTransaction())
return error("AcceptTransaction() : CheckTransaction failed");

检查交易是否创建成功

uint256 hash = GetHash();
CRITICAL_BLOCK(cs_mapTransactions)
if (mapTransactions.count(hash))
return false;
if (fCheckInputs)
if (txdb.ContainsTx(hash))
return false;

检查交易是否冲突

uint256 hash = GetHash();
CRITICAL_BLOCK(cs_mapTransactions)
if (mapTransactions.count(hash))
return false;
if (fCheckInputs)
if (txdb.ContainsTx(hash))
return false;
// Check for conflicts with in-memory transactions
CTransaction* ptxOld = NULL;
for (int i = 0; i < vin.size(); i++)
{
COutPoint outpoint = vin[i].prevout;
if (mapNextTx.count(outpoint))
{
// Allow replacing with a newer version of the same transaction
if (i != 0)
return false;
ptxOld = mapNextTx[outpoint].ptx;
if (!IsNewerThan(*ptxOld))
return false;
for (int i = 0; i < vin.size(); i++)
{
COutPoint outpoint = vin[i].prevout;
if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
return false;
}
break;
}
}

检查是否与已有交易冲突

map mapUnused;
int64 nFees = 0;
if (fCheckInputs && !ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), 0, nFees, false, false))
{
if (pfMissingInputs)
*pfMissingInputs = true;
return error("AcceptTransaction() : ConnectInputs failed %s", hash.ToString().substr(0,6).c_str());
}

交易保存到内存中

CRITICAL_BLOCK(cs_mapTransactions)
{
if (ptxOld)
{
printf("mapTransaction.erase(%s) replacing with new version\n", ptxOld->GetHash().ToString().c_str());
mapTransactions.erase(ptxOld->GetHash());
}
AddToMemoryPool();
}

从钱包移除旧的交易

if (ptxOld)
EraseFromWallet(ptxOld->GetHash());

6.7 交易广播

广播钱包中的交易

wtxNew.RelayWalletTransaction();

发送INV消息

void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
{
foreach(const CMerkleTx& tx, vtxPrev)
{
if (!tx.IsCoinBase())
{
uint256 hash = tx.GetHash();
if (!txdb.ContainsTx(hash))
RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
}
}
if (!IsCoinBase())
{
uint256 hash = GetHash();
if (!txdb.ContainsTx(hash))
{
printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str());
RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
}
}
}

 

你可能感兴趣的:(区块链)