Script

OP_IF 和 OP_ELSE 的內部工作

  • April 19, 2021

具體是如何OP_ELSE工作的?有else if腳本嗎?


根據<https://en.bitcoin.it/wiki/Script#Flow_control>,an 之後的語句OP_ELSE在三種情況下執行。它說:

如果前面的 OP_IF 或 OP_NOTIF 或 OP_ELSE 沒有被執行,那麼這些語句是。

我想了解第三種情況是如何工作的,即OP_ELSE沒有遵循前面的情況。考慮這個腳本和True堆棧頂部的 a:

OP_IF
   &lt;foo&gt;
OP_ELSE
   &lt;bar&gt;
OP_ELSE
   &lt;baz&gt;
OP_ENDIF

如果我們按照對這封信的引用的解釋,那麼這意味著&lt;foo&gt;並且&lt;baz&gt;被執行。然而,這非常違反直覺。如果是這種情況,那麼第二個OP_ELSE是多餘的,因為&lt;baz&gt;可以簡單地追求&lt;foo&gt;相同的效果並且使用更少的操作碼。

我還缺少其他解釋嗎?

我在嘗試編寫具有 3 個不同分支的腳本時偶然發現了這個細節,例如

OP_IF top stack item is 0
   &lt;foo&gt;
OP_ELSEIF top stack item is 1
  &lt;bar&gt;
OP_ELSE
  &lt;baz&gt;
OP_ENDIF

我知道上述不是有效的腳本,但可以在不訴諸以下的情況下實現這種效果嗎?

OP_IF
   &lt;foo&gt;
OP_ELSE
   OP_IF
       &lt;bar&gt;
   OP_ELSE
       &lt;baz&gt;
   OP_ENDIF
OP_ENDIF

請注意,這不僅因為它具有更多操作碼,而且因為在見證中需要兩個堆棧項才能執行&lt;bar&gt;or ,因此成本更高&lt;baz&gt;

比特幣腳本中條件的內部工作

比特幣腳本中的條件在概念上是使用一堆布爾值實現的,每個布爾值指示在針對條件的每個深度(嵌套的 IF / ELSE)迭代腳本時是否執行目前操作碼。

為了掌握功能,讓我們首先考慮操作碼的單布爾條件執行。true解釋器將通過在開始時將布爾值設置為(執行操作)來遍歷腳本。當遇到 an 時,IF它會將布爾值設置為 之前的推送數據的值IF,轉換為布爾值(CastToBool在參考實現中)。

如果是true這樣,它將繼續執行以下操作碼,有效地執行“if {}分支”。

如果是false,它將暫停執行,直到 …. 它遇到一個ELSE! 遇到 anELSE將切換全域布爾暫停執行。如果是以前false,它將成為true並有效地執行“else {}分支”。相反如果是true,它將成為false 並暫停執行“else {}分支”。

使用一點 ASCII 藝術(fExec是全域布爾值,告訴是否實際執行 OP):

Value of fExec:  true    true      true         true  true    true         false      false            ???

Script        : |start| OP_BLABLA OP_DOSOMETHING &lt;1&gt; OP_IF OP_DOSOMESTUFF OP_ELSE OP_DOANOTHERSTUFF OP_ENDIF
Value of fExec:  true    true      true         true  false    false       true      true             ???

Script        : |start| OP_BLABLA OP_DOSOMETHING &lt;0&gt; OP_IF OP_DOSOMESTUFF OP_ELSE OP_DOANOTHERSTUFF OP_ENDIF

請注意我在遇到時如何標記fExecas的值。這是因為實際的實現稍微複雜一些:為了支持嵌套條件,這不是一個布爾值,而是一個用於確定執行的布爾值向量。那是:???``OP_ENDIF

  • OP_IF將一個新的布爾值推送到向量
  • OP_ELSE 將切換向量的最後一個布爾值
  • OP_ENDIF將彈出向量的最後一個元素

走得更遠

多個ELSE為一個IF

請注意,ELSE作為單個切換的實現意味著沒有什麼可以阻止連續切換兩次。也就是說,您可以ELSEENDIF. 兩個後續ELSEs 將有效地切換最後一個布爾值兩次,因此執行以下操作碼,就像“if {}分支”一樣。

因此,在您的問題範例中:

OP_IF &lt;foo&gt; OP_ELSE &lt;bar&gt; OP_ELSE &lt;baz&gt; OP_ENDIF

確實如此&lt;foo&gt;&lt;baz&gt;這將被執行。

fExec從布爾向量計算時的二次行為

Pieter Wuille 於 2020 年修改了條件邏輯的實現,以修復Segio Demian Lerner 發現的二次行為

因此,實際實現實際上不再為布爾值使用堆棧,而是將“頂部布爾值”儲存在模仿先前實現的專用資料結構中。

稍微相關的 Tapscript 規則

作為提議的 Taproot 軟分叉(Tapscript) 的一部分,提議的對腳本規則的新增內容提升了201OP 程式碼的限制。這是通過消除上述二次行為而成為可能的。

引用自:https://bitcoin.stackexchange.com/questions/103844