Thông số kỹ thuật MVP giai đoạn 2 của BITE
Với BITE Giai đoạn 2, mỗi Block có thể bao gồm Giao dịch Hợp đồng-Hành động (CAT) - các giao dịch được khởi tạo bằng cách thực hiện hợp đồng thông minh trong Block trước đó.
CAT cho phép các hợp đồng thông minh giải mã dữ liệu và sau đó tự động thực hiện các hành động trên dữ liệu này.
Lợi ích chính của việc sử dụng BITE Giai đoạn 2
- Tự động hóa: Hợp đồng sẽ tự động xử lý dữ liệu đã giải mã mà không cần người dùng thực hiện giao dịch khác.
- Hiệu quả: Giải mã được thực hiện trong cùng một đợt với BITE Giai đoạn 1, do đó không có thêm chi phí hiệu suất.
- Chủ nghĩa quyết định: Việc thực hiện diễn ra theo thứ tự có thể dự đoán được (CAT chạy trước các giao dịch thông thường trong Block N+1).
Hợp đồng-Hành động-Giao dịch
Block SC trong khối N N gọi decryptAndExecute de c r y p t And Exe ecu t e biên dịch trước bằng cách truyền một mảng encryptedAruments được mã hóa và một mảng plaintextArguments pl a i n t e te Argument các đối số plaintext .
Một giao dịch CAT được thêm vào Block tiếp theo. Giao dịch CAT được đặt trước các giao dịch thông thường trong Block . Chúng không bị Gas Limit Block .
Giao dịch CAT có cùng msg.sender m s g . s e nd e r với giao dịch tạo ra chúng.
Giao dịch CAT đến trường t o là SC khởi tạo giao dịch đó. SC gửi giao dịch cho chính nó.
Giao dịch CAT luôn gọi hàm onDecrypt o n Decrypt của SC tạo ra chúng .
Giao dịch CAT được giải mã trong cùng đợt giải mã như giao dịch BITE Giai đoạn 1, trong quá trình hoàn tất Block N N. Do đó, BITE Giai đoạn 2 không thay đổi hiệu suất so với BITE Giai đoạn 1.
giải mã và thực thi biên dịch trước
Hàm này tạo ra một giao dịch 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;
Chi phí gas biên dịch trước
SẼ ĐƯỢC ĐỊNH NGHĨA SAU
cuộc gọi onDecrypt
Nếu một hợp đồng thông minh định nghĩa hàm onDecrypt() o n Dec r y p t ( ) , nó có thể bắt đầu giải mã trong Block N N và kết quả giải mã được chuyển đến onDecrypt() o n Dec r y p t ( ) trong Khối Block +1 N + 1 .
/**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;
Đặc tả đối số được mã hóa
Mỗi đối số được mã hóa sẽ có định dạng RLP tương tự như trường dữ liệu được mã hóa BITE Giai đoạn 1, nhưng sẽ bao gồm một tham số allowedDecryptorAddress bổ sung được phép Giải mã địa chỉ , chỉ định địa chỉ của hợp đồng thông minh được phép giải mã đối số này.
Ví dụ về trò chơi kéo-búa-bao.
Ví dụ dưới đây sử dụng Giao thức BITE Giai đoạn 2 để triển khai trò chơi Kéo-Búa-Bao cho hai người chơi, trong đó hợp đồng thông minh thu thập các nước đi được mã hóa từ hai người chơi, sau đó giải mã cả hai cùng một lúc.
// 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;}}}
Giải thích về trò chơi kéo búa bao
Hợp đồng này chứng minh cách BITE Giai đoạn 2 cho phép các hợp đồng thông minh giải mã dữ liệu và hoạt động tự động thông qua Giao dịch Hợp đồng-Hành động (CAT) .
Ví dụ này triển khai một trò chơi kéo-búa-bao đơn giản dành cho hai người chơi, trong đó mỗi người chơi gửi một nước đi được mã hóa và khi cả hai nước đi đều được gửi, hợp đồng sẽ tự động lên lịch giao dịch CAT để giải mã các nước đi và xác định người chiến thắng.
Luồng trò chơi
1. Tạo trò chơi
- Người chơi gọi
createGame(opponent)
để thiết lập một trò chơi mới. - Hợp đồng lưu trữ:
-
p1
(người sáng tạo), -
p2
(đối thủ), - và gán một
gameId
.
-
- Phát ra sự kiện GameCreated .
2. Gửi các bước di chuyển được mã hóa
- Mỗi người chơi gọi
submitEncryptedMove(gameId, encMove)
với nước đi được mã hóa của họ. - Các bước di chuyển được lưu trữ trong hợp đồng:
-
encMove1
cho người chơi 1, -
encMove2
cho người chơi 2.
-
- Phát ra sự kiện EncryptedMoveSubmitted .
3. Lên lịch giải mã CAT
- Khi cả hai nước đi đều được gửi:
- Hợp đồng gọi là biên dịch trước BITE Giai đoạn 2 :
BITE.decryptAndExecute(encArgs, plainArgs);
-
encArgs
=[encMove1, encMove2]
(các bước di chuyển được mã hóa). -
plainArgs
=[gameId, p1, p2]
(thông tin ngữ cảnh để tái tạo trò chơi).
- Hợp đồng gọi là biên dịch trước BITE Giai đoạn 2 :
- Điều này tạo ra một giao dịch CAT sẽ:
- Chạy vào Block tiếp theo ,
- Gọi
onDecrypt(decryptedMoves, plaintextArgs)
.
Quan trọng: Giao dịch CAT không do người dùng gửi. Chúng được giao thức tự động chèn vào Block tiếp theo, trước các giao dịch thông thường.
4. Thực thi CAT: onDecrypt
- Trong Block tiếp theo , thời gian chạy:
- Giải mã các bước di chuyển trong quá trình hoàn thiện Block ,
- Gọi lệnh gọi lại
onDecrypt
của hợp đồng:function onDecrypt(bytes[] calldata decryptedArguments, // [p1Move, p2Move]bytes[] calldata plaintextArguments // [gameId, p1, p2]) external;
- Hợp đồng:
- Phân tích cú pháp di chuyển từ
decryptedArguments
, - Tái cấu trúc ngữ cảnh (
gameId
, players) từplaintextArguments
, - Xác định người chiến thắng bằng luật kéo-búa-bao,
- Đánh dấu trò chơi đã kết thúc,
- Phát ra sự kiện WinnerDcided .
- Phân tích cú pháp di chuyển từ
Kiểm soát an ninh
Cờ CAT đang chờ xử lý
- Hợp đồng theo dõi
pendingCat = true
khi lên lịch cho CAT. - Ngăn ngừa việc lập lịch trùng lặp và đảm bảo chỉ có một CAT được mong đợi.
- Hợp đồng theo dõi
Xác minh người gọi
- Đảm bảo rằng
msg.sender
của CAT khớp với trình gọi ban đầu củadecryptAndExecute
. - Ngăn chặn các cuộc gọi bên ngoài trái phép tới
onDecrypt
.
- Đảm bảo rằng
Trạng thái trò chơi
- Cờ
finished
đảm bảo trò chơi không thể chơi lại sau khi đã xác định được người chiến thắng.
- Cờ
Sự kiện
GameCreated(gameId, p1, p2)
→ được phát ra khi một trò chơi mới được khởi tạo.EncryptedMoveSubmitted(gameId, player)
→ phát ra khi người chơi gửi nước đi được mã hóa của mình.WinnerDecided(gameId, winner, p1Move, p2Move)
→ được phát ra khi giao dịch CAT thực hiện và người chiến thắng được xác định.
Ví dụ trình tự
Block N
- Người chơi 1 gửi nước đi được mã hóa.
- Người chơi 2 gửi nước đi được mã hóa.
- Hợp đồng gọi
decryptAndExecute
, lên lịch cho CAT.
Block N+1
- Trong quá trình hoàn thiện, các bước đi được mã hóa sẽ được giải mã.
- CAT thực thi
onDecrypt
, truyền[p1Move, p2Move]
và context[gameId, p1, p2]
. - Hợp đồng quyết định người chiến thắng và phát ra WinnerDecided .
Ví dụ về Đấu giá kín (BITE Giai đoạn 2)
Ví dụ này trình bày cách triển khai phiên đấu giá kín theo giá đầu tiên bằng cách sử dụng BITE Giai đoạn 2 .
- Người đấu giá gửi giá thầu được mã hóa cùng với khoản tiền gửi ETH .
- Khi thời gian đấu thầu kết thúc, hợp đồng sẽ lên lịch Giao dịch Hợp đồng-Hành động (CAT) để giải mã tất cả các giá thầu trong Block tiếp theo .
- Sau đó, lệnh gọi lại
onDecrypt
của hợp đồng sẽ xác định người trả giá cao nhất , hoàn tất phiên đấu giá và chuyển tiền.
// 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);}}}
Luồng đấu giá
Giai đoạn đấu thầu
- Người dùng gọi
submitEncryptedBid(encBid)
với giá thầu được mã hóa và khoản tiền gửi ETH của họ. - Tiền đặt cọc đảm bảo người đấu thầu không thể thiếu vốn để đấu thầu.
Giai đoạn đóng
- Khi thời hạn trôi qua,
closeAuction()
sẽ lên lịch cho một CAT :BITE.decryptAndExecute(encArgs, plainArgs)