以太坊虛擬機

CREATE2

CREATE2 是一個把合約部署到「你能事先算出的地址」的操作碼——在合約存在之前,甚至在部署帳戶有過任何動作之前。原本的 CREATE 操作碼是從部署者的地址與其當前 nonce 推導新合約的地址,因此地址取決於交易歷史、難以預測。CREATE2 則純粹從你能掌控的輸入推導地址,讓部署地址完全確定。

地址是 keccak256(0xff ++ deployerAddress ++ salt ++ keccak256(initCode)) 的最後 20 個位元組,其中 salt 是你挑選的任意 32 位元組值,initCode 是合約的建立位元組碼。0xff 前綴把它與 CREATE 的地址作領域區隔。由於每個輸入都事先已知,任何人都能在不部署的情況下,精確算出某份合約將落在何處——地址是程式碼與 salt 的函式,而非部署時機或順序的函式。

這解鎖了「反事實」(counterfactual)部署:你可以把一份合約當作它已經存在,把資金送到它預先算出的地址,之後再按需、在有人要與它互動時,才真正把位元組碼部署到那裡。帳戶抽象化與智能合約錢包大量倚賴這點——使用者能立刻拿到一個錢包地址來收款,而錢包合約直到第一次交易時才被惰性部署,為那些從未被使用的帳戶省下 gas。同一套 salt 與程式碼的配方,也會在不同鏈上產生相同的合約地址。

這裡有一個鋒利的安全警語。地址承諾的是 keccak256(initCode),因此部署不同的位元組碼需要不同的 salt——但歷來,透過 CREATE2 部署的合約可能被 SELFDESTRUCT,再以相同的 salt 但不同的程式碼把新合約重新部署到完全相同的地址(「變形合約」技巧),使受信任地址上的程式碼悄悄改變。SELFDESTRUCT 的棄用大致堵上了這個漏洞,但它仍是一則警世故事,提醒人別輕易假設某地址上的程式碼是固定的。

address = keccak256(0xff ++ deployer ++ salt ++ keccak256(init_code))[12:]

部署地址完全由部署者、salt 與 init 程式碼決定。