之前的文章 Solidity 智能合約 - 工廠模式的重組問題 (Reorg) 說明了重組問題和解決方案,但是並不能防止搶先交易 (Front Running)。本篇文章將說明這個安全問題,以及解決方式。

搶先交易 (Front Running)

這是區塊鏈常見的一種獲利手法,也可以說是一種攻擊,監控未確認的交易來預知未來將發生的事情,讓自己的交易搶先執行而獲利。例如看到有人要用大單買入 ETH,就搶先買入後抬高價格賣給他。

問題

延續上一篇文章的情境:

  1. 使用者 A 用 salt 0xabcd... 建立一個多簽錢包 0x9876...
  2. 使用者 A 轉入 ETH 到 0x9876...

假設使用者 A 先預測了生成的錢包為 0x9876...,並連續送出第二筆交易。這時候在鏈上,存在這兩筆未確認交易,攻擊者看到後搶先送出他的交易:

  1. 攻擊者 C 用 salt 0xabcd... 建立一個多簽錢包 0x9876...
  2. 使用者 A 用 salt 0xabcd... 建立一個多簽錢包 0x9876...。已存在,交易失敗。
  3. 使用者 A 轉入 ETH 到 0x9876...

結果 ETH 跑去 C 建立的錢包去。

解決方案

等待區塊確認

如同中心化交易所入金一樣,等待足夠多的區塊確認後再進行下一步,使用者 A 可以等一段時間後再進行轉帳。

把 sender 做為參數

以之前 cloneDeterministic 的程式為基礎修改,原本:

1
2
3
4
5
function create(string memory name_, string memory symbol_, bytes32 salt) external {
address instance = Clones.cloneDeterministic(address(token), salt);
ProxyToken(address(instance)).initialize(name_, symbol_);
emit Create(instance);
}

改為

1
2
3
4
5
6
function create(string memory name_, string memory symbol_, bytes32 salt) external {
salt = keccak256(abi.encodePacked(salt, msg.sender));
address instance = Clones.cloneDeterministic(address(token), salt);
ProxyToken(address(instance)).initialize(name_, symbol_);
emit Create(instance);
}

這樣別人使用同樣的 salt 也不會產生一樣的地址,同時保證同一個 sender 的相同 salt 會產生固定的地址。

延伸閱讀

Solidity 智能合約 - 工廠模式 (Factory Pattern)
Solidity 智能合約 - 地址生成規則
Solidity 智能合約 - 工廠模式的重組問題 (Reorg)