為什麼 OP_CHECKLOCKTIMEVERIFY 被最大序列號禁用?
在我的程式碼中,
OP_CHECKLOCKTIMEVERIFY
我注意到如果 txin 序列號被最大化,那麼腳本將無法驗證。我想知道這是什麼意思?為什麼有人會向網路送出無法驗證的交易?這是程式碼的相關部分:
// Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making // the opcode ineffective. // // Testing if this vin is not final is sufficient to // prevent this condition. Alternatively we could test all // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. if (txTo->vin[nIn].IsFinal()) return false;
我也對程式碼中的註釋感到困惑。他們說每個序列號都必須被最大化才能使腳本無法驗證,但在我看來這不是真的 - 看起來只需要最大化一個序列號然後整個事務(所有txins) 將失敗。我認為這意味著交易不會因此包含在區塊鏈中?但這與程式碼註釋背道而馳。
!
如果有一個on if 條件,那將是有意義的。
的目的與
OP_CHECKLOCKTIMEVERIFY
目的相反tx.nLockTime
。tx.nLockTime
防止具有未來日期的交易進入區塊鏈,同時OP_CHECKLOCKTIMEVERIFY
使某人能夠凍結資金,以便它們只能在給定的時間戳或塊高度之後使用。
tx.nLockTime
tx.nLockTime
由函式IsFinalTx()
驗證src/main.cpp
:bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if (tx.nLockTime == 0) return true; if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, tx.vin) if (!txin.IsFinal()) return false; return true; }
在哪裡:
txin.IsFinal()
_src/primitives/transaction.h
bool IsFinal() const { return (nSequence == std::numeric_limits<uint32_t>::max()); }
如果 tx 鎖定時間低於門檻值,則將其視為塊高度,如果高於門檻值,則將其視為時間戳。無論哪種方式,事務鎖定時間值都必須小於相關約束。如果它更大,那麼礦工必須等待才能將交易包含在一個塊中。
繞過此事務鎖定時間約束的唯一方法是通過將所有 txin 序列號設置為 maxint 來完全禁用事務鎖定時間。完成此操作後,即使尚未達到鎖定時間,礦工也會立即包含交易。
交易鎖定時間的想法是在交易被鎖定之前(即在區塊高度或時間戳趕上 tx 鎖定時間之前),有人可以對交易進行修改。每次他們進行更改時,他們都必須增加序列號,以讓礦工知道哪個修改接踵而至。
一個案例可能是數字遺囑。如果您想在您去世時專門將錢轉給其他人,那麼您可以創建一個鎖定時間為一年的交易,然後將其提供給幾個朋友。如果您去世,他們可以在一年後在網路上廣播此交易,並相應地發送資金。在一年的這個時間段之前廣播交易不會使他們能夠收到資金,因為礦工將忽略該交易,直到該時間段生效(並且顯然朋友無法更改該交易的功能,因為它是由您的私鑰簽名的你從不透露)。
如果您沒有死,那麼您可以通過在網路上廣播不同的交易,將資金花到您自己選擇的不同地址。然後,您的朋友將無法使用您給他們的原始交易,因為這將是雙花,礦工不允許。對於您廣播以取消遺囑的交易,您將更改鎖定時間以使其更快,並增加序列號。或者,您可以將鎖定時間設置為 0 或將序列號設置為 maxint 以立即使用。
OP_CHECKLOCKTIMEVERIFY
OP_CHECKLOCKTIMEVERIFY
有非常不同的用途。它在以下功能EvalScript()
中src/script/interpreter.cpp
得到驗證:case OP_CHECKLOCKTIMEVERIFY: { if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { // not enabled; treat as a NOP2 if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); } break; } if (stack.size() < 1) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); // Note that elsewhere numeric opcodes are limited to // operands in the range -2**31+1 to 2**31-1, however it is // legal for opcodes to produce results exceeding that // range. This limitation is implemented by CScriptNum's // default 4-byte limit. // // If we kept to that limit we'd have a year 2038 problem, // even though the nLockTime field in transactions // themselves is uint32 which only becomes meaningless // after the year 2106. // // Thus as a special case we tell CScriptNum to accept up // to 5-byte bignums, which are good until 2**39-1, well // beyond the 2**32-1 limit of the nLockTime field itself. const CScriptNum nLockTime(stacktop(-1), fRequireMinimal, 5); // In the rare event that the argument may be < 0 due to // some arithmetic being done first, you can always use // 0 MAX CHECKLOCKTIMEVERIFY. if (nLockTime < 0) return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME); // Actually compare the specified lock time with the transaction. if (!checker.CheckLockTime(nLockTime)) return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME); break; }
它依賴於
CheckLockTime()
同一文件中的功能:bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether // nLockTime < LOCKTIME_THRESHOLD. // // We want to compare apples to apples, so fail the script // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) )) return false; // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. if (nLockTime > (int64_t)txTo->nLockTime) return false; // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making // the opcode ineffective. // // Testing if this vin is not final is sufficient to // prevent this condition. Alternatively we could test all // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. if (txTo->vin[nIn].IsFinal()) return false; return true; }
這裡將事務鎖定時間與堆棧上的值進行比較。要成功驗證,兩者必須在門檻值的同一側(即兩者都必須解釋為塊高度,或都解釋為時間戳),並且腳本僅在堆棧值低於 tx 鎖定時間時進行驗證。或者換一種說法,腳本只會驗證事務鎖定時間是否已超過堆棧值。
鑑於
IsFinalTx()
目前阻止具有鎖定時間的交易被包含在區塊鏈中,OP_CHECKLOCKTIMEVERIFY
凍結區塊鏈中的資金,以便它們只能在未來的指定時間之後使用。請注意,用於比較的堆棧值在放在 scriptPubKey 中時最有用。用於與堆棧值比較的鎖定時間是簽名交易的鎖定時間。這迫使花費者等待區塊或時間戳才能花費資金。
如前所述,
IsFinalTx()
確實允許探勘鎖定時間高於目前塊高度或時間戳的交易——前提是序列號被最大化,從而禁用 tx 鎖定時間。送出具有最大序列號的此類交易將是接收方在發送方在 txout 腳本中指定的時間之前花費資金的一種偷偷摸摸的方式。因此,為了防止OP_CHECKLOCKTIMEVERIFY
繞過條件,當序列號禁用 tx 鎖定時間時,腳本驗證必須失敗。