BITE 第 2 階段 MVP 規範

本文為機器翻譯
展示原文

BITE 第 2 階段 MVP 規範

在 BITE 第 2 階段中,每個區塊可以包含合約行動交易 (CAT)——由前一個區塊中的智能合約執行發起的交易。

CAT 使智能合約能夠Decrypt數據,然後對這些數據自動執行操作。

使用 BITE 第 2 階段的主要優勢

  • 自動化:合約自動對解密數據進行操作,無需其他用戶交易。
  • 效率:解密與 BITE 第一階段同一批完成,因此沒有額外的性能開銷。
  • 確定性:執行以可預測的順序進行(CAT 在區塊 N+1 中的常規交易之前運行)。

合約-行動-交易

  1. N NA SC調用cryptoAndExecute編譯傳遞一個encryptedAruments數組一個plaintextArguments明文參數數組

  2. CAT 交易被添加到下一個區塊。CAT 交易在The Block中位於常規交易之前。它們不受區塊 gas 限制。

  3. CAT 交易具有與創建它們的交易相同msg.sender m s g . s end e r

  4. CAT 交易t o字段是發起該交易的 SC。SC 向自己發送交易。

  5. CAT 交易總是調用發起它們SConDecrypt函數

  6. CAT 交易與 BITE 第一階段交易在同一批次Decrypt中進行解密,即在區塊N N最終確定期間。因此,BITE 第二階段的性能與 BITE 第一階段相比沒有變化。

解密並執行預編譯

此函數創建 CAT 交易

/**Create a CAT transaction that will be decrypted and executed in the next block* @notice Decrypts the provided encrypted arguments and executes the associated logic using both decrypted and plaintext arguments in the next block* @param encryptedArguments An array of encrypted byte arrays representing the arguments that need to be decrypted before execution.* @param plaintextArguments An array of byte arrays representing the arguments that are already in plaintext and do not require decryption.*/function decryptAndExecute(bytes[] calldata encryptedArguments,bytes[] calldata plaintextArguments) external;

預編譯 gas 成本

稍後定義

onDecrypt 調用

如果智能合約定義了onDecrypt() onD e c r y p t ( )函數則可以在Block N N中發起解密,並將解密結果傳遞給Block N+1 N + 1中的onDecrypt( ) onD e c r y p t ( )

/**Execute SC call on decrypted arguments@param decryptedArguments An array of decrypted byte arrays representing the encrypted arguments that were passed to decryptAndExecute* @param plaintextArguments An array of byte arrays representing the arguments that are already in plaintext and do not require decryption.*/function onDecrypt(bytes[] calldata decryptedArguments,bytes[] calldata plaintextArguments) external;

加密參數規範

每個加密參數都將具有與 BITE 第 1 階段加密數據字段類似的RLP格式包含一個額外allowDecryptorAddress允許解密地址參數指定允許解密參數智能合約Decrypt

石頭剪刀布的例子。

下面的示例使用 BITE 協議第 2 階段為兩個玩家實現石頭剪刀布遊戲,其中智能合約從兩個玩家收集加密的動作,然後同時解密

// SPDX-License-Identifier: MITpragma solidity ^0.8.24;/*** Minimal interface to the Phase 2 precompile (void return).* Replace PRECOMPILE_ADDR with the actual address on your network.*/interface IBitePhase2 {/*** @notice Creates a CAT that will decrypt args and call onDecrypt in the next block.* @param encryptedArguments Encrypted arguments, decrypted during finalization of the current block.* @param plaintextArguments Plaintext arguments, passed through as-is.*/function decryptAndExecute(bytes[] calldata encryptedArguments,bytes[] calldata plaintextArguments) external;}contract RockPaperScissors {// -------------------- Config --------------------address constant PRECOMPILE_ADDR = 0x0000000000000000000000000000000000000100;IBitePhase2 constant BITE = IBitePhase2(PRECOMPILE_ADDR);enum Move {None, // 0Rock, // 1Paper, // 2Scissors // 3}// -------------------- Events --------------------event GameCreated(uint256 indexed gameId, address indexed p1, address indexed p2);event EncryptedMoveSubmitted(uint256 indexed gameId, address indexed player);event WinnerDecided(uint256 indexed gameId,address winner, // address(0) means drawMove p1Move,Move p2Move);// -------------------- Storage --------------------struct Game {address p1;address p2;bytes encMove1; // encrypted Move for p1bytes encMove2; // encrypted Move for p2bool p1Submitted;bool p2Submitted;// Controls to ensure the CAT callback is expectedbool pendingCat;address expectedCaller; // msg.sender that scheduled decryptAndExecutebool finished;}uint256 public nextGameId;mapping(uint256 => Game) public games;// -------------------- Game Flow --------------------function createGame(address opponent) external returns (uint256 gameId) {require(opponent != address(0) && opponent != msg.sender, "bad opponent");gameId = nextGameId++;games[gameId].p1 = msg.sender;games[gameId].p2 = opponent;emit GameCreated(gameId, msg.sender, opponent);}/*** @notice Each player submits their encrypted move (opaque bytes).* The second submission triggers decryptAndExecute in the same tx.** Expected decryption: each encrypted blob decrypts to a single byte 1..3 (Move enum).*/function submitEncryptedMove(uint256 gameId, bytes calldata encMove) external {Game storage g = games[gameId];require(!g.finished, "game finished");require(msg.sender == g.p1 || msg.sender == g.p2, "not a player");if (msg.sender == g.p1) {require(!g.p1Submitted, "p1 already submitted");g.encMove1 = encMove;g.p1Submitted = true;} else {require(!g.p2Submitted, "p2 already submitted");g.encMove2 = encMove;g.p2Submitted = true;}emit EncryptedMoveSubmitted(gameId, msg.sender);// If both moves are in and we haven't scheduled a CAT yet, schedule it now.if (g.p1Submitted && g.p2Submitted && !g.pendingCat) {g.pendingCat = true;g.expectedCaller = msg.sender; // per spec, CAT msg.sender == caller of decryptAndExecute// encryptedArguments: both encrypted movesbytes;encArgs[0] = g.encMove1;encArgs[1] = g.encMove2;// plaintextArguments: pass identifiers to reconstruct context in onDecrypt// - gameId// - p1, p2bytes;plain[0] = abi.encode(gameId);plain[1] = abi.encode(g.p1);plain[2] = abi.encode(g.p2);// Schedule CAT; no return valueBITE.decryptAndExecute(encArgs, plain);}}/*** @notice CAT callback (executed in Block N+1). Receives decrypted moves and our plaintext context.* Security notes for MVP:* - We gate by `pendingCat` and by `expectedCaller` (the account that scheduled the CAT).* - In production, consider adding a CAT nonce or blockTag in plaintext args for stronger domain separation.*/function onDecrypt(bytes[] calldata decryptedArguments, // [ p1MoveDecrypted, p2MoveDecrypted ]bytes[] calldata plaintextArguments // [ gameId, p1, p2 ]) external {// Decode contextrequire(plaintextArguments.length == 3, "bad plaintext len");(uint256 gameId) = abi.decode(plaintextArguments[0], (uint256));(address p1) = abi.decode(plaintextArguments[1], (address));(address p2) = abi.decode(plaintextArguments[2], (address));Game storage g = games[gameId];require(!g.finished, "already finished");require(g.pendingCat, "no pending CAT");require(msg.sender == g.expectedCaller, "unexpected caller (not CAT origin)");// Decode decrypted moves (each is expected to be a single byte 1..3)require(decryptedArguments.length == 2, "bad decrypted len");Move p1Move = _asMove(decryptedArguments[0]);Move p2Move = _asMove(decryptedArguments[1]);// Decide winneraddress winner = _winnerOf(p1, p2, p1Move, p2Move);// Mark finished and clear flagsg.finished = true;g.pendingCat = false;g.expectedCaller = address(0);emit WinnerDecided(gameId, winner, p1Move, p2Move);}// -------------------- Helpers --------------------function _asMove(bytes calldata b) private pure returns (Move) {require(b.length == 1, "bad move len");uint8 v = uint8(b[0]);require(v >= uint8(Move.Rock) && v <= uint8(Move.Scissors), "bad move value");return Move(v);}function _winnerOf(address p1,address p2,Move m1,Move m2) private pure returns (address) {if (m1 == m2) return address(0);// Rock(1) beats Scissors(3), Paper(2) beats Rock(1), Scissors(3) beats Paper(2)if ((m1 == Move.Rock && m2 == Move.Scissors) ||(m1 == Move.Paper && m2 == Move.Rock) ||(m1 == Move.Scissors && m2 == Move.Paper)) {return p1;} else {return p2;}}}

石頭剪刀布的解釋

該合約展示了BITE 第 2 階段如何使智能合約能夠Decrypt數據並通過合約-行動-交易 (CAT)自動採取行動。

該示例實現了一個簡單的雙人石頭剪刀布遊戲,其中每個玩家提交一個加密的動作,一旦兩個動作都提交,合約就會自動安排CAT 交易來Decrypt動作並確定獲勝者。


遊戲流程

1.遊戲創作

  • 玩家調用createGame(opponent)來設置新遊戲。
  • 合同存儲:
    • p1 (創建者),
    • p2 (對手),
    • 並分配一個gameId
  • 發出GameCreated事件。

2. 提交加密動作

  • 每個玩家使用其加密的動作調用submitEncryptedMove(gameId, encMove)
  • 動作存儲在合約中:
    • 為玩家 1 encMove1
    • 為玩家 2 encMove2
  • 發出EncryptedMoveSubmitted事件。

3. 安排CAT解密

  • 一旦兩個動作都提交了:
    • 合約調用BITE第二階段預編譯
      BITE.decryptAndExecute(encArgs, plainArgs);
    • encArgs = [encMove1, encMove2] (加密移動)。
    • plainArgs = [gameId, p1, p2] (用於重建遊戲的上下文信息)。
  • 這將創建一個CAT 交易,它將:
    • 在下一個區塊運行,
    • 調用onDecrypt(decryptedMoves, plaintextArgs)

:zap:重要提示:CAT 交易無需用戶提交。它們會由協議自動插入到下一個區塊中,位於常規交易之前。


4. CAT 執行: onDecrypt

  • 在下一個塊中,運行時:
    1. 在區塊最終確定期間解密移動,
    2. 調用合約的onDecrypt回調:
      function onDecrypt(bytes[] calldata decryptedArguments, // [p1Move, p2Move]bytes[] calldata plaintextArguments // [gameId, p1, p2]) external;
  • 合同:
    • 解析來自decryptedArguments動作,
    • 根據plaintextArguments重建上下文( gameId , players ),
    • 使用石頭剪刀布規則確定獲勝者,
    • 將遊戲標記為結束,
    • 發出WinnerDecided事件。

安全控制

  1. 待處理 CAT 標誌

    • 合同在安排 CAT 時跟蹤pendingCat = true
    • 防止重複調度並確保只需要一個 CAT。
  2. 呼叫者驗證

    • 確保 CAT 的msg.senderdecryptAndExecute的原始調用者匹配。
    • 防止未經授權的外部調用onDecrypt
  3. 遊戲狀態

    • finished標誌確保在確定獲勝者後遊戲無法重播。

活動

  • GameCreated(gameId, p1, p2)
    → 當新遊戲初始化時發出。

  • EncryptedMoveSubmitted(gameId, player)
    → 當玩家提交其加密動作時發出。

  • WinnerDecided(gameId, winner, p1Move, p2Move)
    → 當 CAT 交易執行並確定獲勝者時發出。


示例序列

  1. N座

    • 玩家 1 提交加密的動作。
    • 玩家 2 提交加密的動作。
    • 合約調用decryptAndExecute ,安排CAT。
  2. 區塊 N+1

    • 在最終確定過程中,加密的動作會被解密。
    • CAT 執行onDecrypt ,傳遞[p1Move, p2Move]和上下文[gameId, p1, p2]
    • 合約決定獲勝者併發出WinnerDecided

密封投標拍賣示例(BITE 第 2 階段)

此示例演示如何使用BITE 第 2 階段實現第一價格密封投標拍賣

  • 競標者提交其加密的出價以及ETH押金
  • 競標期結束後,合約將安排合約行動交易(CAT),在下一個區塊中解密所有競標。
  • 然後,合約的onDecrypt回調確定最高出價者,完成拍賣,並轉移資金。

// SPDX-License-Identifier: MITpragma solidity ^0.8.24;interface IBitePhase2 {function decryptAndExecute(bytes[] calldata encryptedArguments,bytes[] calldata plaintextArguments) external;}contract SealedBidAuction {// -------------------- Config --------------------address constant PRECOMPILE_ADDR = 0x0000000000000000000000000000000000000100;IBitePhase2 constant BITE = IBitePhase2(PRECOMPILE_ADDR);address public seller;uint256 public biddingDeadline;bool public finalized;// -------------------- Storage --------------------struct Bid {address bidder;bytes encBid; // encrypted bid (decrypted later)uint256 deposit; // deposit in ETH}Bid[] public bids;bool public pendingCat;address public expectedCaller;// -------------------- Events --------------------event BidSubmitted(address indexed bidder, uint256 deposit);event AuctionFinalized(address winner, uint256 amount);// -------------------- Init --------------------constructor(uint256 _biddingPeriod) {seller = msg.sender;biddingDeadline = block.timestamp + _biddingPeriod;}// -------------------- Bidding --------------------function submitEncryptedBid(bytes calldata encBid) external payable {require(block.timestamp < biddingDeadline, "bidding closed");require(msg.value > 0, "deposit required");bids.push(Bid({bidder: msg.sender,encBid: encBid,deposit: msg.value}));emit BidSubmitted(msg.sender, msg.value);}// -------------------- Close auction --------------------function closeAuction() external {require(block.timestamp >= biddingDeadline, "still open");require(!pendingCat && !finalized, "already scheduled/finalized");// Build arrays for CAT callbytes[] memory encArgs = new bytes[](bids.length);bytes ; // auction context: total bidsfor (uint256 i = 0; i < bids.length; i++) {encArgs[i] = bids[i].encBid;}plainArgs[0] = abi.encode(bids.length);pendingCat = true;expectedCaller = msg.sender;// Schedule CAT to decrypt all bids in the next blockBITE.decryptAndExecute(encArgs, plainArgs);}// -------------------- CAT callback --------------------function onDecrypt(bytes[] calldata decryptedArguments, // decrypted bid valuesbytes[] calldata plaintextArguments // [numBids]) external {require(pendingCat && !finalized, "no pending auction");require(msg.sender == expectedCaller, "unexpected caller");uint256 numBids = abi.decode(plaintextArguments[0], (uint256));require(numBids == bids.length, "mismatch");// Find highest biduint256 highestAmount = 0;uint256 winnerIndex = type(uint256).max;for (uint256 i = 0; i < numBids; i++) {uint256 amount = abi.decode(decryptedArguments[i], (uint256));if (amount > highestAmount && bids[i].deposit >= amount) {highestAmount = amount;winnerIndex = i;}}// Finalize auctionfinalized = true;pendingCat = false;expectedCaller = address(0);if (winnerIndex != type(uint256).max) {// Pay sellerpayable(seller).transfer(highestAmount);// Refund losers + excess depositfor (uint256 i = 0; i < numBids; i++) {if (i == winnerIndex) {uint256 refund = bids[i].deposit - highestAmount;if (refund > 0) payable(bids[i].bidder).transfer(refund);} else {payable(bids[i].bidder).transfer(bids[i].deposit);}}emit AuctionFinalized(bids[winnerIndex].bidder, highestAmount);} else {// No valid bids, refund everyonefor (uint256 i = 0; i < numBids; i++) {payable(bids[i].bidder).transfer(bids[i].deposit);}emit AuctionFinalized(address(0), 0);}}}

拍賣流程

投標階段

  • 用戶使用其加密出價和ETH存款調用submitEncryptedBid(encBid)
  • 押金可確保投標人的投標資金不會不足。

結束階段

  • 一旦截止日期過去, closeAuction()就會安排一次CAT
    BITE.decryptAndExecute(encArgs, plainArgs)

來源
免責聲明:以上內容僅為作者觀點,不代表Followin的任何立場,不構成與Followin相關的任何投資建議。
喜歡
收藏
評論