Referência da Linguagem

Guia completo de sintaxe Rego e recursos da linguagem para escrever políticas

As políticas são escritas em Rego, uma linguagem de políticas declarativa projetada para expressar decisões complexas de controle de acesso. Ao contrário de linguagens imperativas onde você descreve como computar um resultado, Rego permite descrever quais condições devem acionar uma decisão de política. Esta página cobre toda a sintaxe e recursos da linguagem suportados na 256 Blocks.

Estrutura de Políticas

Cada política que você escreve é automaticamente envolvida com o boilerplate necessário pela 256 Blocks. Isso significa que você pode se concentrar puramente em escrever suas regras sem se preocupar com declarações de pacote ou valores padrão.

Todas as políticas:

  • São automaticamente envolvidas com a declaração package necessária
  • Têm um default deny := false predefinido (permitir por padrão)
  • Têm um default denyGasSponsor := false predefinido (patrocinar por padrão)
  • Podem apenas definir regras, não substituir padrões

Sintaxe Básica de Regras

Regras são os blocos de construção de políticas. Uma regra consiste em um cabeçalho (o nome da regra, ex: deny ou denyGasSponsor) e um corpo (as condições). Quando todas as condições no corpo são verdadeiras, a regra avalia para true.

Exemplo

# Estrutura básica: nome_da_regra if { condições }
deny if {
    input.usd_value > 10000
}

Múltiplas Condições (Lógica AND)

Quando você inclui múltiplas condições no mesmo corpo de regra, TODAS as condições devem ser verdadeiras para a regra ser acionada. Cada condição é implicitamente unida com AND.

Exemplo

# Ambas as condições devem ser verdadeiras para negar
deny if {
    input.chain == "ethereum"
    input.usd_value > 1000
}

Múltiplas Regras (Lógica OR)

Quando você define múltiplas regras com o mesmo nome, QUALQUER regra correspondente acionará a política. Isso fornece lógica OR entre regras.

Exemplo

# Qualquer condição aciona a negação
deny if {
    input.chain == "ethereum"
}
 
deny if {
    input.usd_value > 10000
}

Combinando AND e OR

Você pode combinar ambos os padrões para lógica complexa. Cada corpo de regra usa lógica AND internamente, enquanto múltiplas regras fornecem lógica OR entre elas.

Exemplo

# Negar se: (ethereum E alto-valor) OU (qualquer chain E país bloqueado)
deny if {
    input.chain == "ethereum"
    input.usd_value > 5000
}
 
deny if {
    input.source_country in {"KP", "IR", "CU"}
}

Atribuição de Variáveis

Use o operador := para atribuir valores a variáveis. Variáveis tornam suas políticas mais legíveis e mantíveis ao dar nomes significativos a valores e permitir reutilização.

Variáveis de Nível de Política

Variáveis definidas fora de corpos de regras atuam como constantes que podem ser referenciadas por múltiplas regras. Use-as para limites, listas de permissão e listas de bloqueio.

Exemplo

# Constantes de nível de política - definidas uma vez, usadas em todos os lugares
max_transaction_value := 50000
blocked_countries := {"KP", "IR", "CU", "SY"}
approved_senders := {"0x742d35cc...", "0xa0b86991..."}
 
deny if {
    input.usd_value > max_transaction_value
}
 
deny if {
    input.source_country in blocked_countries
}
 
deny if {
    not input.from_address in approved_senders
}

Variáveis Locais

Variáveis definidas dentro de um corpo de regra são locais a essa regra. Use-as para cálculos intermediários ou para melhorar a legibilidade.

Exemplo

deny if {
    # Calcular com uma margem de segurança
    value_with_buffer := input.usd_value * 1.15
    value_with_buffer > 10000
}
 
deny if {
    # Estimar custo de gas em wei
    gas_cost := to_number(input.gas_limit) * to_number(input.max_fee_per_gas)
    gas_cost > 1000000000000000000  # 1 ETH em wei
}

Operadores de Comparação

Rego fornece operadores de comparação padrão para avaliar condições. Estes funcionam com strings, números e outros tipos comparáveis.

OperadorDescriçãoExemplo
==Igual ainput.chain == "ethereum"
!=Diferente deinput.chain != "polygon"
>Maior queinput.usd_value > 1000
>=Maior ou igualinput.usd_value >= 1000
<Menor queinput.usd_value < 100
<=Menor ou igualinput.usd_value <= 100

Exemplo

# Bloquear transações de alto valor em chains caras
deny if {
    input.chain == "ethereum"
    input.usd_value >= 5000
}
 
# Bloquear transações abaixo de um valor mínimo (potencial spam)
deny if {
    input.usd_value != null
    input.usd_value < 1
}

Operadores Aritméticos

Você pode realizar cálculos aritméticos dentro de suas condições de política. Isso é útil para computar limites, aplicar multiplicadores ou combinar valores.

OperadorDescriçãoExemplo
+Adiçãoinput.usd_value + 100
-Subtraçãoinput.usd_value - fees
*Multiplicaçãoinput.usd_value * 1.1
/Divisãoinput.usd_value / 2
%Módulo (resto)input.gas_limit % 1000

Exemplo

# Aplicar um buffer de 10% ao verificar limites
deny if {
    input.usd_value * 1.1 > 10000
}
 
# Bloquear se o valor combinado exceder o limite
deny if {
    total := input.usd_value + 500  # Adicionar taxas estimadas
    total > 5000
}

Operadores Lógicos

Além do AND implícito (múltiplas condições) e OR (múltiplas regras), Rego fornece operadores lógicos explícitos para expressões mais complexas.

O Operador not

Use not para negar uma condição. Isso é particularmente útil para padrões de lista de permissão onde você quer negar qualquer coisa QUE NÃO esteja em um conjunto permitido.

Exemplo

# Permitir apenas chains específicas (negar tudo mais)
allowed_chains := {"polygon", "base", "arbitrum"}
 
deny if {
    not input.chain in allowed_chains
}

Exemplo: Combinando com Outras Condições

# Negar se NÃO for um método somente leitura E o valor for alto
deny if {
    not input.rpc_method in {"eth_call", "eth_getBalance"}
    input.usd_value > 1000
}

Regras Auxiliares

Além de deny e denyGasSponsor, você pode definir suas próprias regras personalizadas para organizar lógica complexa. Regras auxiliares atuam como blocos de construção reutilizáveis que tornam suas políticas mais legíveis e mantíveis.

Uma regra auxiliar é simplesmente uma condição nomeada que avalia para true quando seu corpo corresponde. Você pode então referenciar essas regras em suas regras deny ou denyGasSponsor, inclusive com o operador not para negá-las.

Exemplo

# Definir uma regra auxiliar para verificar se a solicitação é de uma fonte confiável
is_trusted if {
    input.source_country in {"US", "GB", "DE"}
    input.source_ip in {"203.0.113.10", "203.0.113.11"}
}
 
# Definir outra auxiliar para transações de alto risco
is_high_risk if {
    input.usd_value > 10000
}
 
is_high_risk if {
    input.source_country in {"RU", "CN"}
}
 
# Usar regras auxiliares em condições de negação
deny if {
    not is_trusted
    is_high_risk
}

Regras auxiliares são particularmente úteis para:

  • Reutilização: Defina uma condição uma vez, use em múltiplas regras
  • Legibilidade: Dê nomes significativos a condições complexas
  • Negação: Use not para verificar quando uma condição NÃO é atendida
  • Organização: Divida políticas complexas em componentes lógicos

Operações com Strings

Strings são sequências de caracteres entre aspas duplas (""). A maioria dos campos de entrada como input.chain, input.rpc_method e endereços são strings. Rego fornece várias funções integradas para trabalhar com strings, essenciais para correspondência de padrões em endereços, nomes de métodos e outros campos de texto.

FunçãoDescriçãoRetorna
contains(string, substring)Verifica se string contém substringboolean
startswith(string, prefix)Verifica se string começa com prefixoboolean
endswith(string, suffix)Verifica se string termina com sufixoboolean
lower(string)Converter para minúsculasstring
upper(string)Converter para maiúsculasstring
substring(string, start, length)Extrair porção da stringstring
sprintf(format, values)Formatar string com valoresstring

Exemplo

# Bloquear qualquer método contendo "sign" (cobre eth_sign, personal_sign, etc.)
deny if {
    contains(input.rpc_method, "sign")
}
 
# Bloquear endereços começando com prefixo malicioso conhecido
deny if {
    startswith(lower(input.to_address), "0x000000000000000000000000000000000000dead")
}
 
# Bloquear métodos debug e admin
deny if {
    startswith(input.rpc_method, "debug_")
}
 
deny if {
    startswith(input.rpc_method, "admin_")
}

Operações com Arrays

Arrays são listas ordenadas de valores, definidas com colchetes []. Ao contrário de conjuntos, arrays preservam a ordem dos elementos e podem conter duplicatas. Você trabalhará frequentemente com arrays em políticas, especialmente input.contract_addresses que contém todos os endereços de contratos envolvidos em uma solicitação.

# Sintaxe de array
my_array := ["first", "second", "third"]

Rego fornece várias maneiras de inspecionar e trabalhar com arrays.

FunçãoDescriçãoRetorna
count(array)Número de elementosnumber
array[index]Acessar elemento por índice (base 0)element
value in arrayVerificar se valor existe no arrayboolean

Exemplo

# Limitar o número de contratos em uma única solicitação
deny if {
    count(input.contract_addresses) > 10
}
 
# Verificar se um endereço específico está na lista de contratos
deny if {
    "0xbanned..." in input.contract_addresses
}
 
# Acessar elementos específicos do array
deny if {
    count(input.contract_addresses) > 0
    first_contract := input.contract_addresses[0]
    startswith(first_contract, "0xdead")
}

Teste de Pertencimento

O teste de pertencimento é uma das operações mais comuns em políticas. Você o usará para verificar se valores pertencem a listas de permissão, listas de bloqueio ou outras coleções de valores permitidos.

Conjuntos

Conjuntos são coleções não ordenadas de valores únicos, definidos com chaves {}. Use o operador in para testar se um valor existe em um conjunto.

# Sintaxe de conjunto
my_set := {"value1", "value2", "value3"}

Conjuntos são otimizados para teste rápido de pertencimento, tornando-os ideais para listas de permissão e bloqueio.

Exemplo

# Bloquear solicitações de países sancionados
blocked_countries := {"KP", "IR", "CU", "SY", "RU"}
 
deny if {
    input.source_country in blocked_countries
}
 
# Permitir apenas chains específicas
allowed_chains := {"polygon", "base", "arbitrum"}
 
deny if {
    not input.chain in allowed_chains
}

Conjuntos Inline vs Nomeados

Você pode definir conjuntos inline dentro de regras ou como variáveis nomeadas no nível de política. Conjuntos nomeados melhoram a legibilidade e permitem reutilização em múltiplas regras.

Exemplo

# Conjunto inline - bom para verificações simples e únicas
deny if {
    input.rpc_method in {"eth_sign", "personal_sign", "eth_signTypedData"}
}
 
# Conjunto nomeado - melhor para reutilização e legibilidade
approved_contracts := {
    "0xdac17f958d2ee523a2206206994597c13d831ec7",  # USDT
    "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",  # USDC
    "0x6b175474e89094c44da98b954eedeac495271d0f"   # DAI
}
 
deny if {
    some addr in input.contract_addresses
    not addr in approved_contracts
}

Iteração com some

A palavra-chave some permite iterar sobre coleções e verificar condições em cada elemento. Isso é essencial ao trabalhar com arrays como input.contract_addresses.

Iteração Básica

Use some para verificar se QUALQUER elemento em uma coleção corresponde a uma condição.

Exemplo

# Negar se QUALQUER endereço de contrato estiver na lista de bloqueio
blocked_contracts := {"0xbanned1...", "0xbanned2..."}
 
deny if {
    some addr in input.contract_addresses
    addr in blocked_contracts
}

Iteração com Índice

Você também pode acessar o índice de cada elemento durante a iteração.

Exemplo

# Verificar condições em posições específicas
deny if {
    some i, addr in input.contract_addresses
    i == 0  # Apenas primeiro contrato
    startswith(addr, "0x000")
}

Exemplo: Encontrando Qualquer Correspondência

# Negar se qualquer contrato começar com um prefixo suspeito
deny if {
    some addr in input.contract_addresses
    startswith(lower(addr), "0x000000000000000000000000")
}
 
# Negar se qualquer contrato não estiver na lista aprovada
approved := {"0xusdt...", "0xusdc...", "0xdai..."}
 
deny if {
    some addr in input.contract_addresses
    not addr in approved
}

Iteração com every

Enquanto some verifica se QUALQUER elemento corresponde, every exige que TODOS os elementos correspondam a uma condição. Isso é útil para garantir que coleções inteiras atendam seus critérios.

Exemplo

# Negar apenas se TODOS os contratos forem de um conjunto suspeito
# (improvável de ser legítimo se cada endereço for suspeito)
suspicious_prefixes := {"0x0000000000000000"}
 
deny if {
    count(input.contract_addresses) > 0
    every addr in input.contract_addresses {
        some prefix in suspicious_prefixes
        startswith(addr, prefix)
    }
}

Exemplo: Padrões de Validação

# Garantir que todos os contratos sejam da lista aprovada (inverso de some/not)
approved_contracts := {"0xusdt...", "0xusdc...", "0xdai..."}
 
# Isso nega se QUALQUER contrato não for aprovado
deny if {
    some addr in input.contract_addresses
    not addr in approved_contracts
}
 
# Equivalente usando every (negar se NÃO todo contrato for aprovado)
deny if {
    count(input.contract_addresses) > 0
    not every addr in input.contract_addresses {
        addr in approved_contracts
    }
}

Compreensões

Compreensões permitem criar novas coleções transformando ou filtrando existentes. Elas são úteis para extrair valores específicos ou construir conjuntos de dados derivados.

Compreensão de Array

Criar um novo array a partir de elementos que correspondem a certos critérios.

Exemplo

# Extrair todos os endereços de contratos que começam com um prefixo específico
matching_contracts := [addr |
    some addr in input.contract_addresses
    startswith(addr, "0xa")
]
 
deny if {
    count(matching_contracts) > 3
}

Compreensão de Conjunto

Criar um conjunto de valores únicos.

Exemplo

# Obter prefixos únicos de todos os endereços de contratos
contract_prefixes := {substring(addr, 0, 6) |
    some addr in input.contract_addresses
}
 
# Negar se houver muitos prefixos de contratos diferentes (suspeito)
deny if {
    count(contract_prefixes) > 5
}

Else Condicional

A palavra-chave else permite definir valores de fallback quando condições não são atendidas. Isso é útil para computar valores derivados que variam com base na entrada.

Exemplo

# Atribuir nível de risco com base no valor da transação
risk_level := "critical" if {
    input.usd_value > 100000
} else := "high" if {
    input.usd_value > 10000
} else := "medium" if {
    input.usd_value > 1000
} else := "low"
 
# Usar o nível de risco computado em regras de negação
deny if {
    risk_level == "critical"
}
 
denyGasSponsor if {
    risk_level in {"high", "critical"}
}

Exemplo: Limites Específicos por Chain

# Limites diferentes para chains diferentes
chain_limit := 1000 if {
    input.chain == "ethereum"
} else := 5000 if {
    input.chain == "polygon"
} else := 10000
 
deny if {
    input.usd_value > chain_limit
}
Referência da Linguagem | 256 Blocks