基礎指南:zkEVM、EVM相容和Rollup是什麼?

語言: CN / TW / HK

作者:Immutable X聯合創始人兼CTO Alex Connolly

來源: medium

ZK-rollup長期以來一直被視為以太坊擴充套件的終局。然而,儘管它們對以太坊擴充套件路線圖很重要,但在幾個關鍵點上仍然存在廣泛的不確定性:

zk-rollup到底是什麼?

特定於應用程式的rollup和通用rollup之間有什麼區別?

zk-EVM rollup是什麼?EVM等效和EVM相容等術語的實際含義是什麼麼,它們如何適用於rollup?

zk-rollup生態系統的現狀怎麼樣,對我的專案意味著什麼?

如果你是一名希望瞭解以太坊擴充套件的下一階段的開發人員,本文(希望)會有所幫助。

ZK-Rollup

ZK-rollup是通過一個簡單的觀察成為可能的: STARKs或SNARKs 等證明系統允許以次線性處理來驗證線性數量的交易。我們可以利用這一特性來建立大規模可擴充套件的區塊鏈交易處理,具體如下:

1、使用者在L1上的zk-rollup智慧合約中鎖定他們的資產。

2、使用者將涉及這些資產的交易提交給L2的sequencer,該sequencer將這些交易收整合有序的批次,併為每個批次生成一個有效性證明(例如STARK/SNARK)和聚合狀態更新。

3、這個狀態更新和證明被提交給我們的L1 zk-rollup智慧合約並由其驗證,用於更新我們的L1狀態。

4、使用者可以使用這個L1狀態(取決於不同的資料可用性機制)來檢索他們的資產,從而實現完全的自我託管和 "以太坊安全性"。

簡化的ZK-Rollup架構

驗證證明的gas成本與被證明的交易數量呈次線性關係,與直接使用L1相比,允許更大的規模。要想更詳細地瞭解這個過程,我推薦閱讀Vitalik的 Rollup不完整指南 或Delphi新發布的 Rollup全面指南

特定於應用的Rollup

到目前為止,所有生產級的zk-rollup都是我們稱之為 "特定於應用的rollup"。在特定於應用的rollup中,rollup支援固定數量的 "狀態轉換"(例如交易),由rollup執行者定義。這對於過度優化常見的用例是非常有用的,例如:

Loopring —支付和交換

Immutable —NFT鑄造和交易,遊戲

dydx —永續合約交易

特定於應用的rollup在擴充套件特定的、已熟知的問題方面非常出色。如果你的專案需求可以通過特定於應用的rollup來滿足,你的用例可能會得到更好的效能,更好的使用者體驗和更好定價,因為它們缺乏通用性是一個巨大的優勢。例如,在Immutable,我們能夠通過對NFT交易費用補貼免費的NFT鑄造和轉移來消除gas費用——這種權衡只因rollup的狀態轉換的可預測性質而成為可能。

然而,許多專案希望能夠建立自己的自定義邏輯和智慧合約,獨立於rollup運營商,這在特定於應用的rollup中是不可能的。此外,許多DeFi專案需要 "可組合性",或與其他專案進行互動的能力(例如,許多DeFi專案使用Uniswap作為價格預言機)。只有當你的rollup不僅支援自定義程式碼,而且支援可以由任何使用者部署的原生智慧合約時,可組合性才有可能實現。為了實現這一點,我們需要修改我們的zk-rollup架構,以擴大每個元件的運用。

這種靈活性的增加要付出幾個代價:效能大大降低,rollup引數的可定製性降低,以及更高的費用。然而,最大的代價是根本沒有實現通用的zk-rollup,當然也沒夠實現批量生產的zk-rollup。但這種情況正在開始改變。

StarkNet 目前已經在主網上執行。

3個獨立的專案( zkSyncPolygon Hermez/zkEVMScroll )都在ETH CC 2022大會上宣佈,他們將成為第一個到達主網的 "zkEVM"。

這些公告值得深入研究,因為這些團隊不只是宣佈了通用的rollup,他們還宣佈了 "zkEVM"。隨後,推特上出現了很多圍繞 "EVM相容性"、"EVM等效性"、"真正的zkEVM "以及哪種方法更優越的爭論。對於應用開發者來說,這些對話往往是噪音——所以本文的目的是解析這些術語、設計決策和理念,並解釋它們對開發者的實際影響。

讓我們從頭開始:什麼是EVM?

瞭解EVM

以太坊虛擬機器(EVM)是執行以太坊交易的執行環境,最初在 以太坊黃皮書 中定義,後來被一系列 以太坊改進提案 (EIP)修改。它由以下部分組成:

一個用於執行程式的標準 "機器",每筆交易都有的易失性 "儲存器",可以寫入交易的永續性 "儲存器 "和一個操作 "堆疊"

~約140個定價的 "操作碼",在該機器中執行狀態轉換。

圖表來自 https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf

我們虛擬機器的一些操作碼例子:

堆疊操作—PUSH1 (向堆疊中新增東西)

算術操作—ADD(新增數字),SUBTRACT。

狀態操作—SSTORE(儲存資料),SLOAD(載入資料)。

交易操作— CALLDATA, BLOCKNUMBER (返回關於當前執行的交易的資訊)

一個EVM程式只是一系列操作碼和引數。當這些程式被表示為一個連續的程式碼區塊時,我們稱其結果為 "位元組碼"(通常表示為十六進位制字串)。

通過將大量這些操作碼組合成一個執行序列,我們可以建立任意的程式。以太坊使用定製的虛擬機器,而不是調整現有的虛擬機器,因為它有獨特的需求:

每個操作都必須有 "成本",以防止濫用(因為所有節點執行所有交易)

每個操作都必須是確定性的(因為所有節點都必須在交易執行後就狀態達成一致)

我們需要區塊鏈特有的概念(如智慧合約、交易)

一些複雜的操作必須是原語(如密碼學)

交易必須沙盒化,沒有I/O或外部狀態訪問

EVM是第一個圖靈完整的區塊鏈虛擬機器,於2015年釋出。它有一些設計上的侷限性,但其巨大的先發優勢和隨後的廣泛採用為以太坊創造了巨大的獨特性——它是迄今為止整個領域中最經得起考驗的智慧合約基礎設施。

由於以太坊的主導地位,許多後來的區塊鏈都直接採用了這個執行時環境。例如,Polygon和BNBChain是以太坊的直接分叉,因此使用EVM作為其執行時間。值得注意的是,EVM並不是一成不變的,在 EIP1559 等升級中經常被修改。由於其他區塊鏈需要時間來更新,或者在一些地方與以太坊有分歧,它們往往執行的是一個稍微過時的EVM版本,並且可能難以跟上變化——這一事實會 讓以太坊的核心開發者感到沮喪

以太坊相容性

然而,人們所說的 "EVM鏈 "通常不僅僅是反映這個執行時環境。以下是一些始於以太坊的標準,它們已成為事實上的全球標準:

Solidity (一種高階語言,可編譯成EVM位元組碼)

以太坊的JSON-RPC客戶端API (用於與以太坊節點互動)

ERC20/ERC721(Ethereum代幣標準)

ethers.js (一個與以太坊互動的網路庫)

以太坊的密碼學(例如,keccak256作為雜湊函式, secp256k1 上的ECDSA簽名)

從技術上講,你的鏈可以有EVM執行時間,而不支援上述的一些或全部。然而,遵守這些標準使得在你的新鏈上使用以太坊工具變得非常容易。一個很好的例子是Polygon,它除了使用上述所有工具外,還能夠執行分叉版本的Etherscan( Polygonscan ),使用 Hardhat 等以太坊開發者工具,並在Metamask等錢包中被支援為不同的以太坊 "網路"。 NansenDune 等工具最初都是針對以太坊的,因此增加對新的EVM區塊鏈的支援很簡單。新的錢包,新的NFT市場——如果以太坊的介面和你的鏈的介面之間的唯一區別是鏈的ID,你很可能會成為第一個且最容易新增的。話雖如此,這些工具是為以太坊打造的——只要你開始修改你的區塊鏈(例如更大的區塊,更快的區塊時間),你就面臨破壞它們的風險。沒有完美的相容性這回事。

儘管如此,針對以太坊標準的工具和應用程式的數量為新的區塊鏈創造了巨大的動力,使其只是反映以太坊標準。任何不支援上述標準的區塊鏈在涉及開發者工具時都會自動落後,並有可能隨著EVM生態系統的發展而進一步落後。

我認為,"EVM相容 "一詞實際上不足以描述這裡的網路效應——我們實際上描述的是 "以太坊相容",並遠遠超出智慧合約執行環境,延伸到整個以太坊生態系統和工具集。

為了應對這一點,Solana等非EVM區塊鏈不得不建立完全平行的生態系統,這將降低它們的速度,並使其更難吸引現有的開發者。然而,不需要遵守這些標準確實讓非EVM區塊鏈有能力對以太坊工具集進行更根本的改變,從而更積極地將自己與以太坊區分開。建立一個EVM區塊鏈非常簡單——但為什麼有人會使用你的區塊鏈而不是其他數百個 "速度快的EVM區塊鏈 "中的一個。如果你能克服需要建立一個成功的平行鏈和生態系統的困難,Solana已經表明,a)你可以吸引優秀的原生應用程式(如MagicEden,Phantom),b)如果商業激勵足夠,EVM起源專案仍然會支援你(如 Opensea增加Solana支援 )。

ZK-EVM

公共通用rollup都有一個共同的目標:開發者和使用者儘可能快地生成網路效果。這需要創造最高效能的rollup技術,擁有最好的BD團隊,並進行最早或最有效的營銷。然而,所有rollup團隊(出於上述原因)都對以下問題感到非常擔憂:

將現有以太坊合約(和開發者)遷移到其rollup中

由現有EVM工具支援(如錢包、市場等)

實現這兩個目標的最簡單方法是建立一個“zkEVM”:一個通用rollup,將EVM作為其智慧合約引擎執行,並保持與上述以太坊生態系統的通用介面的相容性。

然而,這並不像我們從頭開始建立新的L1區塊鏈時那樣容易。我們的目標是執行EVM位元組碼——但ZK證明需要將它們證明的所有計算語句轉換為非常特定的格式——一個“ 代數線路 (algebraic circuit)”,然後可以編譯成STARK或SNARK。為了快速瞭解“線路”,這裡有一個例子(使用更直觀的布林電路( boolean circuit )作為代數線路的特例)。在基於這個簡單電路的zkSNARK系統中,我們的證明者希望說服驗證者他們瞭解產生真輸出的輸入( 1 = 1, 2 = 1, 3=0)。這是一個非常簡單的電路,具有有限數量的邏輯閘( logic gates )——我相信你可以想象編碼一個電路需要多少邏輯閘,證明覆雜的智慧合約互動,特別是涉及密碼學的互動!

為了理解這個編譯過程的每一步,我推薦閱讀Vitalik的 從零到英雄的SNARK指南 ,以及Eli Ben-Sasson關於 不同證明系統的討論 。然而,這種更深層次的理解對於我們的目的來說並不是必要的——只需記住,為了支援EVM計算,我們必須將所有EVM程式轉換為這些電路,以便以後能夠證明它們。

一般來說,有以下幾種方法可以做到這一點:

通過將EVM執行軌跡轉換為可驗證電路,直接證明EVM執行跟蹤

建立一個自定義虛擬機器,將EVM操作碼對映到該虛擬機器的操作碼中,然後在該自定義環境中證明跟蹤的正確性

建立一個自定義虛擬機器,將Solidity轉換為自定義虛擬機器的位元組碼(直接或通過自定義高階語言),並在你的自定義環境中進行驗證

方案1:證明EVM執行軌跡

Scroll

讓我們從最直觀的方法開始:證明EVM執行軌跡本身,這種方法目前正由 Scroll 團隊(與Ethereum基金會的 Privacy Scaling Group 一起)進行研究。為了實現這個目標, 我們將需要

為一些密碼累加器設計一個電路(允許我們驗證我們正在準確地讀取儲存和載入正確的位元組碼)

設計一個電路,將位元組碼與真正的執行跟蹤連線起來

為每個操作碼設計一個電路(使我們能夠證明每個操作碼的讀、寫和計算的正確性)

在電路中直接實現每一個EVM操作碼是具有挑戰性的,但由於這種方法完全反映了EVM,它對可維護性和工具支援有很大好處。下圖顯示,Scroll和Ethereum之間唯一的理論差異是實際的執行環境。然而,值得注意的是,Scroll目前並沒有通過這種機制支援所有的EVM操作碼。

Optimism團隊 對此寫了一篇精彩的討論,儘管是在optimistic rollup的背景下。Optimism最初建立了一個定製的Optimistic虛擬機器(OVM),作為他們rollup的執行環境。OVM是 "以太坊相容的",這意味著它可以執行修改後的Solidity程式碼,但低水平失配的幾個領域意味著以太坊工具和複雜的程式碼經常需要重新編寫。由於這個原因,Optimism改用 "EVM等效",直接使用確切的EVM標準,並正在開發第一個EVM等效的欺詐證明系統。然而,optimistic rollup不需要擔心電路或驗證器的效率——Optimism的正確選擇可能不是我們rollup的正確選擇。

方案2:自定義虛擬機器+操作碼支援

這種認識促使團隊採用上面提到的 "EVM相容"的方法:建立一個具有優化效能的自定義虛擬機器,然後將EVM位元組碼直接轉換為虛擬機器的位元組碼。

Polygon

一個專注於這種方法的團隊是Polygon Hermez(最近改名為Polygon zkEVM)。Polygon的方法是建立一個zkEVM( "操作碼級的等價物" ),這聽起來最初與Scroll採取的方法相似。然而,與Scroll不同的是,Polygon的備用執行時("zkExecutor")執行 定製的 "zkASM''操作碼 ,而不是EVM操作碼,來優化EVM解析(即減少限制的數量,直接證明EVM)。Hermez團隊將此描述為 "基於操作碼的方法",因為核心挑戰是在他們的定製虛擬機器中重新建立每一個EVM操作碼(你可以在 這裡 檢視程式碼),這樣它們就可以快速從EVM位元組碼變成可驗證的格式。

這些中間步驟為維護和潛在的錯誤創造了更大的表面積,但對於實現高效能的證明來說是必要的。最終,重要的是要清楚,你的程式不是在反映電路中的EVM的zkEVM中執行,它們是在備用的 "zkExecutor "執行時中執行,它與EVM本身相似但不同。

由於這個原因,可能會與在此係統上執行的現有L1應用程式和工具有一些不相容,儘管大多數Solidity程式碼可以照常執行。Polygon宣稱與 "100%的現有以太坊工具"相容,並承諾遵守JSON-RPC,他們在 文件 中提到了這一點。在實踐中,這種說法可能是理想的,這將依賴於以太坊本身變得對SNARK更加友好。

Polygon的方法產生了比Scroll更高效能的rollup(當然是在中短期內):

大量的自定義程式碼,因為我們需要建立zkASM

可能需要開發人員修改他們的L1程式碼或工具框架

隨著時間的推移,與以太坊的偏移可能會擴大

方案3:自定義虛擬機器+轉譯器

上述解決方案在 "使EVM適用於zk-rollup"方面投入了大量的開發時間,將相容性置於長期效能和擴充套件性之上。還有一個選擇:建立一個全新的、專門的虛擬機器,然後在上面新增對以太坊工具的支援,作為一個附加層。

StarkNet

這是StarkWare對StarkNet中取的方法,它是目前進展最快的通用rollup。StarkNet執行一個定製的智慧合約虛擬機器(Cairo VM),並有自己的低階語言(Cairo),兩者都是為智慧合約rollup而建。這意味著StarkNet沒有開箱即用的以太坊相容性——正如我們之前看到的,即使是操作碼級別的虛擬機器級別的相容性也也可能會影響rollup的效能。

然而,Nethermind團隊(與StarkWare合作)建立了 Warp轉譯器 ,它能夠將任意的Solidity程式碼轉換為Cairo VM位元組碼。Warp的目標是使普通的Solidity合約可以移植到StarkNet上——實現許多以太坊開發者在 "EVM相容性 "方面的主要目標。然而,在實踐中,有一些Solidity的功能是Warp不支援的,包括低級別的呼叫(完整的列表可以在 這裡 找到)。

這種構建智慧合約rollup的方法是保持 "Solidity相容":你沒有在EVM內執行程式,也沒有保持與任何其他以太坊介面的相容性,但Solidity開發人員將能夠編寫可用於你的rollup的程式碼。因此,你可以保持與以太坊類似的開發者體驗,而不必損害你的rollup的基礎層。

然而,這種方法需要做出幾項折衷。首先,建立自己的虛擬機器是具有挑戰性的——以太坊團隊已經有超過五年的時間來解決EVM的問題,並且仍然在頻繁地進行升級和修復。更多的自定義rollup將允許更好的效能,但你將失去由每條其他鏈和rollup對EVM進行的集體改進的好處。

其次,通過轉譯器支援Solidity有可能失去可組合性——如果開發者同時用CAIRO和Solidity編寫合約,那麼支援兩者之間介面的工具有很大的可能性是脆弱的。到目前為止,絕大多數的StarkNet專案都直接使用CAIRO,它們可能不容易與未來的Solidity專案組合。最後,可能也是最重要的一點,StarkNet團隊目前的目標不是與其他以太坊元件相容——他們正在推出自己的客戶端API、javascript庫和錢包系統,這將迫使相容以太坊的工具手動新增StarkNet支援。這極具挑戰性,但並非不可能——如上所述,Solana已經成功地使其自定義標準得到一些以太坊工具的尊重,但將依靠StarkWare團隊的能力來吸引那些不介意重建的開發者。

然而,如果他們能夠成功地做到這一點,StarkWare團隊將尋求通過第一個為Zk-rollup優化的智慧合約虛擬機器複製EVM的先發優勢。

zkSync

另一個採用這種策略的專案是 zkSync 。zkSync已經建立了他們自己的虛擬機器(SyncVM),它是基於暫存器的,並定義了自己的AIR (代數中間程式碼表示) 。然後,他們建立了一個 專門的編譯器 ,將 Yul (一種中間語言,可以為不同的EVM版本編譯成位元組碼,就像低級別的Solidity一樣)編譯成LLVM-IR,然後將其編譯成指令,用於他們的自定義虛擬機器。這與StarkWare採取的方法類似,但理論上提供了圍繞基礎語言的更多靈活性(儘管目前只支援Solidity 0.8.x)。zkSync團隊最初建立了他們自己的類似CAIRO的語言 ( Zinc ),但他們已經將大部分精力轉移到了Solidity編譯器上,以使L1開發者的遷移更加簡單。總的來說,他們的策略是重用更多的以太坊工具集——我希望他們的客戶端API等也能更 "相容以太坊"。

zkSync利用這個自定義虛擬機器來提供非EVM相容的功能,如賬戶抽象( Account Abstraction ),這一直是以太坊核心協議的目標。這是自定義虛擬機器所提供的好處的一個很好的例子——你不必等待以太坊建立新的功能!

下圖進行了總結,你可以清楚地看到每個團隊所遵循的不同策略:

Vitalik的zkEVM型別

Vitalik Buterin 關於zkEVM的部落格文章 強調了rollup團隊目前面臨的基本困境:EVM並不是為 "可驗證 "的程式建立的。事實上,正如我們通過上面的分析所顯示的,你越是尋求與以太坊相容,你的 "可驗證格式 "中的程式的效能就越差。Vitalik根據與現有EVM基礎設施的相容程度,確定了通用rollup的幾個大類別。

我對其觀點的唯一拓展是注意到,即使在每個 "型別"中,也有很大程度的變化——我們正在處理一個範圍,而不是完全細分的類別。從開發者體驗的角度來看,對應用層進行單一的、小的改變的第3類rollup與第2類rollup有更多的共同點,第3類rollup對應用層進行了全面的改變,但在技術上沒有引入新的虛擬機器而成為第4類。

智慧合約Rollup的現狀

鑑於理解上述內容所需的細節,我們發明了一堆關於以太坊相容性的語言,這並不奇怪。事實上,沒有一個zk-rollup能完美地反映EVM在所有情況下的行為——這是一個程度問題,當涉及到可維護性和效能而不僅僅是相容性時,每個團隊所做的詳細選擇最終將是最重要的。我的觀點是,下面的定義是最清晰和最一致的:

至關重要的是要明白,上述方法中沒有任何一種在本質上是優越的——這是分類,而不是分等級。它們都做出了不同的權衡:更容易建立、維護和升級,效能更高,更容易與現有工具相容。最終,領先的rollup也將由更好的分銷和營銷來決定,而不是純粹的技術能力。話雖如此,做出正確的基本技術決定無疑有很大的優勢。Scroll對EVM標準的熱忱承諾是否能使他們輕鬆應對任何EVM的升級?另一個團隊更務實的方法是否能幫助他們更快進入市場?StarkWare的自定義虛擬機器+轉譯器的方法是否會被證明是一個更堅實的長期基礎?另一個團隊會不會最終從這個領域的先行者無疑會犯的錯誤中學到東西,並將他們擊敗?歸根結底,以太坊發展的當前時刻的美妙之處在於,我們有不同的團隊在以不同的方法推動一個共同的目標。

但在我們得意忘形之前,也應該對目前智慧合約rollup的準備情況保持清醒的頭腦。每個團隊都有強烈的動機將自己營銷為 "即將接管世界"——但以太坊上最早也要到2022年底才會有 "生產級 "的智慧合約推出,而其中許多團隊要到2023年才會準備好。根據StarkNet的發展歷程,我們應該能預期從一個rollup進入測試網開始,至少要經過一年的迭代,才能使該rollup準備好支援主網上一致的生產級交易量。

由於這種不成熟的狀態,對於那些需要規模而又不影響以太坊安全的開發者來說,特定於應用的rollup仍然是最有力的選擇。事實上,即使是在通用rollup可用和更廣泛地整合後,我預計在可預見的未來,特定於應用的rollup的效能、定製和可靠性仍將在某些用例(如交易所、NFT鑄造/交易)中佔據優勢。

額外的Rollup因素

儘管本文的主要重點是,這並不全是關於以太坊生態系統相容性與效能的問題!還有一些其他因素影響著你是否應該在特定的通用rollup上構建。我建議考慮的幾個主要附加標準如下:

費用:這些rollup會以原生代幣、ETH、還是兩者的複雜組合來收取費用?費用結構 對使用者和開發者的體驗有很大的影響 ,因為rollup通常需要擁有費用代幣來支付計算費用。

證明和排序:所有的rollup都需要一個實體,負責給交易排序和產生證明。今天,大多數特定於應用的rollup是 "單排序器",它以彈性為代價產生更高的吞吐量。大多數通用rollup最初是作為單排序器rollup開始的,但他們通常有計劃隨著時間的推移將這個排序器去中心化。

自我託管:Zk-rollup的核心承諾是能夠在保持以太坊安全的同時釋放規模。然而,許多通用的rollup目前沒有一個明確的機制,能夠在出現惡意或不可用的排序器的情況下恢復使用者資產。

資料可用性:正如介紹中提到的,自我託管的保證取決於故障情況下狀態資料的可用性。然而,完全的資料可用性為使用者引入了額外的成本。這在特定於應用的rollup世界中已經被廣泛使用(例如Validiums,Volitions),但每個通用的rollup需要單獨新增這個功能。

總結

智慧合約rollup是以太坊擴充套件路線圖中令人難以置信的一部分。在與現有的以太坊工具集的關係中,這些rollup所做的不同權衡證明了以太坊開發者生態系統多樣性。

然而,目前關於EVM相容性的討論通常沒有抓住重點。從開發者的角度來看,所有這些rollup都將支援Solidity程式碼。真正的以太坊相容是一個更大的挑戰,但它實際上有實質性的權衡,開發者應該在投入到rollup之前意識到這一點。

為了提高透明度,我希望看到每個rollup團隊對以下問題提供更清晰的答案:

L1和L2在執行時間上的確切差異是什麼?哪些操作碼將在L2上被修改?任何其他的虛擬機器特性(如費用結構)與L1相比是否會有所不同?

你的自定義虛擬機器的形式規約在哪裡,它的效能比其他方案強還是弱?

這次升級將對其他以太坊介面(如客戶端API)做出多少改變,會破壞以太坊工具嗎?

你的rollup什麼時候在測試網上線?什麼時候在主網上線?能否支援持續的生產吞吐量,即1000+自定義合約吞吐量?

你預計什麼時候能支援使用者資產的完全自我託管,以及在通用的rollup環境下會是什麼樣的?

一旦這些rollup在測試網上釋出,這些問題應該更容易回答。在此之前,我希望看到這些團隊繼續釋出更多的技術細節,說明他們的解決方案將作出的確切權衡,以及這將如何影響智慧合約和工具開發者。

隨著合併在即,經過戰鬥考驗的特定於應用程式的rollup在生產中,並且通用的rollup將在明年進入主網,以太坊擴充套件的未來就在眼前。