Com a fase 2 da mordida, cada bloco pode incluir transações com contrato (CATs)-transações iniciadas pela execução de contratos inteligentes no bloco anterior.

Os gatos permitem contratos inteligentes para descriptografar dados e executar ações automaticamente nesses dados.

Principais benefícios do uso da fase 2 da mordida

  • Automação: Os contratos atuam em dados descriptografados automaticamente, sem exigir outra transação do usuário.
  • Eficiência: A descriptografia é feita no mesmo lote da fase 1 da mordida, portanto, nenhuma sobrecarga extra de desempenho.
  • Determinismo: A execução acontece em uma ordem previsível (os gatos são executados antes das transações regulares no bloco N+1).
  1. Um sc em bloco N chamadas descriptografarexecute pré -compilado passando um EncrypteDaruments Array e um PlainTexTarguments Array de argumentos de texto simples.

  2. Uma transação de gato é adicionada ao próximo bloco. As transações CAT são colocadas na frente de transações regulares no bloco. Eles não estão sujeitos a bloquear o limite de gás.

  3. As transações de CAT têm o mesmo msg.sender como a transação que os criou.

  4. Transação de gato para O campo é o SC que o originou. O SC envia uma transação para si mesma.

  5. Transação de gato sempre chama OnDecrypt função do SC que os originou.

  6. As transações de CAT são descriptografadas durante o mesmo lote descriptografando que a transação da fase 1 da mordida, durante a finalização do bloco N. Portanto, a fase 2 da mordida não muda o desempenho em comparação com a fase 1 da mordida.

Esta função cria uma transação de gato

/**
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;

Custo de gás pré -compilado

Para ser definido mais tarde

Se um contrato inteligente define um ONDECRYPT () função, ele pode iniciar a descriptografia no bloco Ne os resultados de descriptografia são passados ​​para ONDECRYPT () em bloco 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;

Cada argumento criptografado terá formato RLP semelhante ao campo de dados criptografado da fase 1, mas incluirá um adicional PermitidoDecryPtoraddress parâmetro, especificando o endereço do contrato inteligente que pode descriptografar este argumento

O exemplo abaixo usa o Bite Protocol Fase 2 para implementar jogos de paper-tesouros para dois jogadores, onde o contrato inteligente coleta movimentos criptografados de dois jogadores e depois descriptografa ambos ao mesmo tempo

// SPDX-License-Identifier: MIT
pragma 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,       // 0
        Rock,       // 1
        Paper,      // 2
        Scissors    // 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 draw
        Move p1Move,
        Move p2Move
    );

    // -------------------- Storage --------------------
    struct Game {
        address p1;
        address p2;
        bytes encMove1;   // encrypted Move for p1
        bytes encMove2;   // encrypted Move for p2
        bool p1Submitted;
        bool p2Submitted;

        // Controls to ensure the CAT callback is expected
        bool pendingCat;
        address expectedCaller; // msg.sender that scheduled decryptAndExecute
        bool 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 moves
            bytes;
            encArgs(0) = g.encMove1;
            encArgs(1) = g.encMove2;

            // plaintextArguments: pass identifiers to reconstruct context in onDecrypt
            // - gameId
            // - p1, p2
            bytes;
            plain(0) = abi.encode(gameId);
            plain(1) = abi.encode(g.p1);
            plain(2) = abi.encode(g.p2);

            // Schedule CAT; no return value
            BITE.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 context
        require(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 winner
        address winner = _winnerOf(p1, p2, p1Move, p2Move);

        // Mark finished and clear flags
        g.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;
        }
    }
} 

Este contrato demonstra como Fase 2 de mordida Ativa contratos inteligentes para descriptografar dados e agir automaticamente via Transações de contrato (gatos).

O exemplo implementa um simples Jogo de paper-tesores de dois jogadores onde cada jogador envia um movimento criptografadoe uma vez que os dois movimentos são enviados, o contrato agenda automaticamente um Transação de gato Descriptografar os movimentos e determinar o vencedor.


Fluxo de jogo

1. Criação de jogos

  • Um jogador liga createGame(opponent) Para configurar um novo jogo.
  • As lojas de contrato:
    • p1 (criador),
    • p2 (adversário),
    • e atribui a gameId.
  • Emite Gamecreated evento.

2. Enviar movimentos criptografados

  • Cada jogador liga submitEncryptedMove(gameId, encMove) com eles movimento criptografado.
  • Os movimentos são armazenados no contrato:
    • encMove1 Para o jogador 1,
    • encMove2 para o jogador 2.
  • Emite EncryptedMoveSubmitt evento.

3. AGENDAR DESCRIPTIÇÃO DE CAT

  • Uma vez Ambos os movimentos são enviados:
  • Isso cria um Transação de gato isso vai:
    • Correr no Próximo blocoAssim,
    • Chamar onDecrypt(decryptedMoves, plaintextArgs).

IMPORTANTE: As transações de CAT não são submitidas pelo usuário. Eles são inseridos automaticamente no próximo bloco pelo protocolo, antes das transações regulares.


4. Execução de gatos: onDecrypt

  • No Próximo blocoo tempo de execução:
    1. Descriptografa os movimentos durante a finalização do bloco,
    2. Invoca o contrato onDecrypt ligar de volta:
      function onDecrypt(
          bytes() calldata decryptedArguments, // (p1Move, p2Move)
          bytes() calldata plaintextArguments  // (gameId, p1, p2)
      ) external;
      
  • O contrato:
    • Parses se move de decryptedArgumentsAssim,
    • Reconstrua o contexto (gameIdjogadores) de plaintextArgumentsAssim,
    • Determina o vencedor usando regras de papel-paper-tesors,
    • Marca o jogo como acabado,
    • Emite Vencedor decidido evento.

Controles de segurança

  1. Bandeira de gato pendente

    • O contrato rastreia pendingCat = true Ao agendar um gato.
    • Impede a programação duplicada e garante que apenas um gato seja esperado.
  2. Verificação do chamador

    • Garante que o gato msg.sender corresponde ao chamador original de decryptAndExecute.
    • Evita chamadas externas não autorizadas para onDecrypt.
  3. Estado de jogo

    • finished A bandeira garante que um jogo não possa ser reproduzido depois que um vencedor é decidido.

Eventos

  • GameCreated(gameId, p1, p2)
    → Emitido quando um novo jogo é inicializado.

  • EncryptedMoveSubmitted(gameId, player)
    → Emitido quando um jogador envia seu movimento criptografado.

  • WinnerDecided(gameId, winner, p1Move, p2Move)
    → emitido quando a transação CAT é executada e o vencedor é determinado.


Exemplo de sequência

  1. Bloco n

    • O jogador 1 envia um movimento criptografado.
    • O jogador 2 envia um movimento criptografado.
    • Chamadas contratadas decryptAndExecuteagendar um gato.
  2. Bloco n+1

    • Durante a finalização, os movimentos criptografados são descriptografados.
    • CAT executa onDecryptpassando (p1Move, p2Move) e contexto (gameId, p1, p2).
    • Contrato decide o vencedor e emite Vencedor decidido.

Este exemplo demonstra como implementar um Leilão de Bid-Bid de primeiro preço usando Fase 2 de mordida.

  • Licitantes envie o seu lances criptografados junto com um ETH depósito.
  • Quando o período de licitação termina, o contrato agenda um Transação de contrato (CAT) que descriptografa todas as ofertas no Próximo bloco.
  • O contrato onDecrypt retorno de chamada então determina o mais alto lancefinaliza o leilão e transfere os fundos.

// SPDX-License-Identifier: MIT
pragma 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 call
        bytes() memory encArgs = new bytes()(bids.length);
        bytes ; // auction context: total bids

        for (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 block
        BITE.decryptAndExecute(encArgs, plainArgs);
    }

    // -------------------- CAT callback --------------------
    function onDecrypt(
        bytes() calldata decryptedArguments, // decrypted bid values
        bytes() 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 bid
        uint256 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 auction
        finalized = true;
        pendingCat = false;
        expectedCaller = address(0);

        if (winnerIndex != type(uint256).max) {
            // Pay seller
            payable(seller).transfer(highestAmount);

            // Refund losers + excess deposit
            for (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 everyone
            for (uint256 i = 0; i < numBids; i++) {
                payable(bids(i).bidder).transfer(bids(i).deposit);
            }
            emit AuctionFinalized(address(0), 0);
        }
    }
}

Fluxo de leilão

Fase de licitação

  • Os usuários ligam submitEncryptedBid(encBid) com sua oferta criptografada e depósito de ETH.
  • Os depósitos garantem que os licitantes não possam sub -financiar sua oferta.

Fase de fechamento

Fontesethresear

By victor

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *