比特币的交易是怎么创建的?除了创币交易(Coinbase)是从挖矿产生的之外,其他的交易来源于各个地址之间的转账,或者说是从钱包发起的转账。
当用户从一个钱包发起交易的时候,将调用SendMoney函数,它在src/wallet/rpcwallet.cpp中:
static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount,
const CCoinControl& coin_control, mapValue_t mapValue, std::string fromAccount)
{
//检查转账金额,不能是负值或零,不能超支,且P2P网络正常
CAmount curBalance = pwallet->GetBalance();
if (nValue <= 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
if (nValue > curBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
//处理目的地址,生成脚本
CScript scriptPubKey = GetScriptForDestination(address);
//创建交易
CReserveKey reservekey(pwallet);
CAmount nFeeRequired;
std::string strError;
std::vector<CRecipient> vecSend;
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
CTransactionRef tx;
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
//确认并发送交易
CValidationState state;
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(fromAccount), reservekey, g_connman.get(), state)) {
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
return tx;
}
函数流程比较清晰。下面逐一解析几个重要步骤:
首先是GetScriptForDestination函数,它根据给定的地址生成脚本。其源码很简单:
CScript GetScriptForDestination(const CTxDestination& dest)
{
CScript script;
boost::apply_visitor(CScriptVisitor(&script), dest);
return script;
}
作为输入参数的CTxDestination类,允许多种输入格式,其中最常用的当然是对方的公钥了。这个时候怎么生成脚本呢?看下面:
bool operator()(const CKeyID &keyID) const {
script->clear();
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return true;
}
熟悉脚本的应该一看就明白,这是标准的支付输出脚本。脚本的详细解析留待后续文章,这里暂且略过。
回到SendMoney函数,接下来的重要步骤就是CreateTransaction,这是个大函数,相当复杂。先看笔者整理的总体流程图:上图只是总体流程,具体的各类分支判断很多,没办法一一画出。函数的详细源码及解析如下(有所删节):
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
//交易金额不能为负,至少有一个收款人
for (const auto& recipient : vecSend)
{
if (nValue < 0 || recipient.nAmount < 0)
{
strFailReason = _("Transaction amounts must not be negative");
return false;
}
nValue += recipient.nAmount;
if (recipient.fSubtractFeeFromAmount)
nSubtractFeeFromAmount++;
}
if (vecSend.empty())
{
strFailReason = _("Transaction must have at least one recipient");
return false;
}
CMutableTransaction txNew;
//把nLockTime设定为当前的区块高度,只允许新块在当前链的后面继续生成。
//这样做的目的是防止费用狙击,比如大矿工可以从当前块的前一个块开始挖,
//连续挖两个块之后就能超越当前块,那么当前块中的交易可能就无效了。
txNew.nLockTime = chainActive.Height();
//但是凡事总有例外,偶尔还得允许nLockTime往前推,
//因为确实有些交易会被耽误,比如高延迟混合网络,或者为了保护隐私的压缩交易
if (GetRandInt(10) == 0)
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));
assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
assert(txNew.nLockTime < LOCKTIME_THRESHOLD); //指向的是区块高度
FeeCalculation feeCalc;
CAmount nFeeNeeded;
int nBytes;
{
std::set<CInputCoin> setCoins;
LOCK2(cs_main, cs_wallet);
{
//找到所有可用的币
std::vector<COutput> vAvailableCoins;
AvailableCoins(vAvailableCoins, true, &coin_control);
CoinSelectionParams coin_selection_params; //选币参数
CScript scriptChange; //改变找零地址的脚本
//已设定了改变地址,直接用它
if (!boost::get<CNoDestination>(&coin_control.destChange)) {
scriptChange = GetScriptForDestination(coin_control.destChange);
} else { //没有设,生成一个新地址(钥匙)
//一般来说最好用新生成的钥匙,但如果要进行备份恢复,新钥就可能丢掉,
//所以用旧钥匙稍微好一些。下面从钥匙池中取一个新的公钥/私钥对
CPubKey vchPubKey;
bool ret = reservekey.GetReservedKey(vchPubKey, true);
if (!ret)
{
strFailReason = _("Keypool ran out, please call keypoolrefill first");
return false;
}
const OutputType change_type = TransactionChangeType(coin_control.m_change_type
? *coin_control.m_change_type : m_default_change_type, vecSend);
LearnRelatedScripts(vchPubKey, change_type);
scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type));
}
CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
//太小的找零会被放弃,算作手续费
CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator);
//计算交易需要的最低费率
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, ::mempool, ::feeEstimator, &feeCalc);
nFeeRet = 0;
bool pick_new_inputs = true;
CAmount nValueIn = 0;
//如果手续费从转账金额里走,就不使用BnB
coin_selection_params.use_bnb = nSubtractFeeFromAmount == 0;
while (true) //从零费用开始循环,直到手续费足够
{
nChangePosInOut = nChangePosRequest;
txNew.vin.clear();
txNew.vout.clear();
bool fFirst = true;
CAmount nValueToSelect = nValue;
if (nSubtractFeeFromAmount == 0)
nValueToSelect += nFeeRet; //加上另算的手续费
coin_selection_params.tx_noinputs_size = 11; //4版本号, 4锁定点, 1输入数, 1输出数, 1见证开销
for (const auto& recipient : vecSend)
{
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
if (recipient.fSubtractFeeFromAmount)
{ //如果手续费从金额走,转账金额就需要减掉手续费
assert(nSubtractFeeFromAmount != 0);
txout.nValue -= nFeeRet / nSubtractFeeFromAmount;
if (fFirst) //剩下的余数交给第一个接受者承担
{
fFirst = false;
txout.nValue -= nFeeRet % nSubtractFeeFromAmount;
}
}
//包含手续费的大小
coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, SER_NETWORK, PROTOCOL_VERSION);
if (IsDust(txout, ::dustRelayFee)) //如果交易太小
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
if (txout.nValue < 0) //金额太小,付费都不够
strFailReason = _("The transaction amount is too small to pay the fee");
else //付完费后,剩不下几个子了
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
}
else
strFailReason = _("Transaction amount too small");
return false;
}
txNew.vout.push_back(txout); //输出搞定了
}
//找用于输入的币,找不到说明金额不够了
bool bnb_used;
if (pick_new_inputs) {
nValueIn = 0;
setCoins.clear();
coin_selection_params.change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
{
if (bnb_used) {
coin_selection_params.use_bnb = false;
continue;
}
else {
strFailReason = _("Insufficient funds");
return false;
}
}
}
const CAmount nChange = nValueIn - nValueToSelect;
if (nChange > 0) //需要找零
{
CTxOut newTxOut(nChange, scriptChange); //换钥匙找零给自己
//永远不要创建垃圾输出,如果有,就加到手续费
if (IsDust(newTxOut, discard_rate) || bnb_used)
{
nChangePosInOut = -1;
nFeeRet += nChange;
}
else
{
if (nChangePosInOut == -1)
{
//把找零放到一个随机位置,可以保护隐私
nChangePosInOut = GetRandInt(txNew.vout.size()+1);
}
else if ((unsigned int)nChangePosInOut > txNew.vout.size())
{
strFailReason = _("Change index out of range");
return false;
}
std::vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;
txNew.vout.insert(position, newTxOut); //找零加入输出
}
} else {
nChangePosInOut = -1;
}
//先填上输入币,便于估算大小
for (const auto& coin : setCoins) {
txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
}
nBytes = CalculateMaximumSignedTxSize(txNew, this);
if (nBytes < 0) {
strFailReason = _("Signing transaction failed");
return false;
}
//估算最低费用
nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
}
//如果连下一遍的延迟费都搞不定,说明交易太大了,放弃
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
{
strFailReason = _("Transaction too large for fee policy");
return false;
}
if (nFeeRet >= nFeeNeeded) {
//防止手续费过高。不添加输入(大小、费用都会降低)再试一次
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; //加2作为缓冲
CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
nFeeRet = fee_needed_with_change;
continue;
}
}
//交易费输出已经有了,直接调整就行
if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
CAmount extraFeePaid = nFeeRet - nFeeNeeded;
std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
change_position->nValue += extraFeePaid;
nFeeRet -= extraFeePaid;
}
break; //有了足够费用,搞定,退出循环
}
else if (!pick_new_inputs) {
//不应当发生。要么够付手续费,要么减少转账金额,最低费用不会变
strFailReason = _("Transaction fee and change calculation failed");
return false;
}
//尝试减少找零,增加手续费
if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet;
std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
//如果找零金额比较大,够付手续费
if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) {
change_position->nValue -= additionalFeeNeeded;
nFeeRet += additionalFeeNeeded;
break; //降低找零搞定了手续费,成功退出
}
}
//如果要从收款人那减去手续费,现在已经知道费用多少,用不着重新选币了
if (nSubtractFeeFromAmount > 0) {
pick_new_inputs = false;
}
//调高手续费,再试一次
nFeeRet = nFeeNeeded;
coin_selection_params.use_bnb = false;
continue;
}
}
if (nChangePosInOut == -1) reservekey.ReturnKey();
//打乱币序,填进输入
txNew.vin.clear();
std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
std::shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
//根据BIP125,调整nSequence,允许交易自行更换
const uint32_t nSequence = coin_control.m_signal_bip125_rbf.get_value_or(m_signal_rbf)
? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
for (const auto& coin : selected_coins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
if (sign)
{
int nIn = 0;
for (const auto& coin : selected_coins)
{
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);
}
nIn++;
}
}
tx = MakeTransactionRef(std::move(txNew)); //返回生成的交易
}
return true;
}
下一节再分析交易签名。
欢迎转载,请注明出处。