$$\rightleftharpoonup{xx}$$
$$\longleftharp{xx}$$,
$$\longrightharp{xx}$$,
Contribuições para o estudo
Neste estudo, dois contratos inteligentes Solidity foram usados como entradas de verificação. O primeiro foi um contrato no estilo Simple DAO que foi usado como estudo de caso de reentrância. O segundo era um contrato de registro afinado/transição de estado, projetado para testar restrições em nível de contrato contra gastos duplos. O código-fonte original do Solidity servia como entrada para os processos de abstração e verificação definidos neste protocolo. O processo geral de transformação usado para tais contratos é ilustrado na Figura 1, que mostra a transformação gradual do código-fonte Solidity para os modelos FSM, Evento-B e SMV durante a verificação.
Modelagem de fronteira
A modelagem formal foca na lógica de controle-fluxo de contratos inteligentes, incluindo comportamento de entrada e saída de funções, execução interna e transições de estado em nível de contrato. A visibilidade da função (pública, externa, interna e privada) foi representada juntamente com o comportamento correspondente da pilha de chamadas relevante para a análise de reentrância. Os tipos de transições abstratas (chamada, envio, transferência) eram tratados como operações para transferir éteres.
Invariantes em nível de contrato foram definidos para garantir a unicidade da transação e evitar gastos duplos dentro do limite de abstração. Para representar os requisitos de seleção de validação e integridade do bloco, a camada de abstração lógica-protocolo foi definida em termos das transições de estado em nível de protocolo tanto da Prova de Trabalho quanto da Prova de Stake.
A fronteira de abstração não incluía elementos da camada de rede, incluindo o escalonamento das mensagens a serem entregues e atrasos causados por qualquer número de saltos, resolução de fork, adversários da rede empregando estratégias bizantinas, semântica da Máquina Virtual Ethereum e semântica de gás controlada por nós de rede, propagação de exceções, execuções assíncronas, comportamento de fallback complexo e finalização em nível de rede. Assim, os resultados da determinação de dupla despesa aplicam-se apenas a invariantes em nível de contrato e não constituem um acordo de finalização em nível de rede.
Ferramentas e configuração
A plataforma Rodin foi usada para modelar, refinar, gerar obrigações de prova e executar o modelo Event-B (versão 3.7.0), que permitia que os provadores PP, ML, SMT e Atelier-B descarregassem provas automaticamente e de forma interativa. A versão 2.0.0 do nuXmv foi executada nos modelos SMV gerados em modo completo de exploração CTL para fazer verificação de modelos CTL.
Todas as execuções de verificação foram realizadas em um ambiente computacional controlado usando Ubuntu 22.04 LTS, OpenJDK 11 e Python 3.10.12. Web3 (7.6.0), NetworkX (3.4.2), Matplotlib (3.8.0), Graphviz (0.20.3), NumPy (1.26.4) e Pandas (2.2.2) foram usados para implementar elementos de simulação e visualização. Esse projeto garantia que os resultados formais de verificação e simulação pudessem ser reproduzidos quando executados sob as mesmas condições de execução.
Fluxo de trabalho de transformação
Esse processo de verificação teve quatro fases. Contratos de solidez foram inicialmente traduzidos em uma representação da Máquina de Estados Finitos (FSM-SC). O modelo FSM-SC foi posteriormente codificado no Evento-B, com invariantes e graus de refinamento claramente especificados. A abstração FSM-SC foi traduzida em um modelo SMV no nuXmv. Os resultados da verificação foram reportados como estatísticas sobre a execução da obrigação de prova e a verificação do modelo CTL, e contraexemplos foram fornecidos como casos.
Construção de FSM
Cada contrato foi abstraído como uma Máquina de Estados Finitos definida como:

Onde:
S = conjunto de estados
S₀ = estado inicial
T = relação de transição
V = mapeamento de visibilidade
G = predicados de guarda
A = ações/atualizações de estado.
Algoritmo 1: Construção de FSM a partir da Solidez
Entrada: código-fonte Solidity
Saída: FSM-SC
1) Analisar a árvore de sintaxe abstrata do contrato de Solidity.
2) Criar o estado inicial S₀ a partir da definição do construtor.
3) Para cada função de solidez f, criar um estado de controle distinto S_f e registrar visibilidade V(f) ∈ {público, externo, interno, privado}.
4) Para cada instrução dentro da função f, derive uma transição t extraindo predicados de guarda de condições de exige/assert e ações de atualizações de variáveis de estado.
5) Adicionar a transição t a T.
6) Criar tipos explícitos de transição para operações de transferência Ether (chamada, envio, transferência), chamadas internas e externas, chamada delegada, autodestruição, uso de origem de tx, ramos condicionais e construções de loop.
7) Retorne FSM-SC = (S, S₀, T, V, G, A).
E cada função de solidez está associada a um estado de controle FSM diferente. Para compreensão de vulnerabilidades, fluxos de execução relevantes para vulnerabilidades, por exemplo, tais fluxos com chamadas externas seguidas de atualizações de balanceamento, foram explicitamente abstraídos em transições ordenadas. A Figura 2 descreve um diagrama FSM-SC de exemplo para o contrato no estilo SimpleDAO, mostrando como estados de entrada, transições de chamadas externas e sequências de atualização de estado foram abstraídos durante essa etapa de construção.
Codificação da FSM no Evento-B
A transição FSM foi representada como construtos do Evento-B. Todas as transições correspondem a eventos do Evento-B, incluindo guardas e ações específicas.
Algoritmo 2: FSM para codificação Evento-B
Entrada: FSM-SC
Saída: Máquina Evento-B e contexto
1) Definir STATE_SET e FUNÇÃO no contexto contratual.
2) Declarar variáveis que representam o estado de controle FSM e o estado em nível de contrato.
3) Representar cada estado de controle FSM s ∈ S usando current_state ∈ STATE_SET.
4) Para cada transição (s → s′, g, a), criar um evento Evento-B E_t com:
5) ONDE current_state = s ∧ g
6) ENTÃO current_state := s′ ∥ aplicar(a)
7) Codificar restrições de visibilidade usando guards derivados de V(f).
8) Definir invariantes inv1–inv9 para capturar propriedades de segurança e consistência.
9) Definir INICIALIZAÇÃO atribuindo valores S₀ e padrão.
O conjunto de estados, o estado atual, visibilidade da função, pilha de chamadas, carimbo de tempo da transação, status de transferência Ether, flag de chamada de delegado, flag de autodestruição e condição de verificação são algumas das variáveis capturadas no modelo Event-B. Os invariantes (inv1-inv9) e as ações das inicializações (act1-act6) correspondem às da especificação formal. A Figura 3 também fornece uma representação gráfica de como os eventos-chave relevantes para vulnerabilidades, especialmente transições relacionadas à reentrância, são mantidos na codificação Evento-B. Esta figura explica como os padrões estruturais do modelo FSM-SC mostrados na Figura 2 são mapeados em eventos verificáveis do Evento-B.
Estratégia de refinamento
Dois níveis de refinamento foram implementados. O fluxo de controle contratual de alto nível e os invariantes centrais foram representados no nível abstrato. O nível refinado adicionou restrições específicas de contrato, incluindo restrições de pilha de chamadas, restrições de visibilidade e condições de prevenção de reentrada.
Algoritmo 3: Refinamento e liberação de prova e obrigação
Entrada: Máquina abstrata e máquina refinada
Resultados: Obrigações e estatísticas de prova liberadas
1) Gerar obrigações de prova para a máquina abstrata em Rodin.
2) Executar provadores automáticos habilitados e registrar os resultados da descarga.
3) Gerar obrigações de prova de refinamento para a máquina refinada.
4) Aplicar avaliadores automáticos às obrigações de refinamento.
5) Cumprir as obrigações restantes de forma interativa quando necessário.
6) Estatísticas de comprovação de exportação e relatórios de status.
O relatório de prova incluiu o número de invariantes, níveis de refinamento, obrigações de prova geradas, taxa de descarga automática, taxa de descarga interativa e taxa de liberação final.
Especificação de propriedades CTL e verificação de modelos
A abstração FSM-SC foi traduzida em um modelo SMV para verificação temporal em tempo de ramificação no nuXmv.
Algoritmo 4: FSM para SMV e Verificação CTL
Saída: resultado da verificação PASS/FAIL e trilhas de contraexemplo (se houver)
1. Registrar os estados de controle FSM como um estado enumerado de variável SMV.
2. Converter transições FSM em missões próximas (de estado) protegidas.
3. Manter flags para condições relevantes à vulnerabilidade, como chamadas externas e atualizações de balanceamento.
4. Codificar propriedades CTL no nuXmv e realizar verificação de modelos.
5. Se uma propriedade falhar, gerar trilhas de contraexemplo representando sequências de transição FSM.
A verificação CTL incluía requisitos de ordem de reentrada, finalização de atualizações de estado após operações de transferência, limitação da entrada recursiva sem restrições em seções críticas e evitação de impasses.
Propriedades de segurança verificadas
O contrato no estilo Simple DAO que impede a reentrada foi confirmado garantindo ordens seguras entre chamadas externas e atualizações de estado por meio de invariantes e restrições CTL. Quando permitido, esses guardas verificavam as restrições de controle de acesso, garantindo que transições não autorizadas fossem restringidas por invariantes. O modelo do ledger reduzido verificou os invariantes da unicidade das transações e da consistência do ledger no nível de prevenção dentro do contrato.
Resultados reportados
A seção de Resultados fornece relatórios sobre métricas estruturais de FSM, métricas do modelo Evento-B e estatísticas para a verificação de prova de obrigação e CTL. Os resultados da prova formal, e os da verificação do modelo CTL, são fornecidos separadamente para distinguir a evidência de garantias de correção por invariantes e a evidência de uma verificação temporal usando verificação temporal.