Справочник по языку

Полное руководство по синтаксису Rego и возможностям языка для написания политик

Политики написаны на Rego, декларативном языке политик, предназначенном для выражения сложных решений по контролю доступа. В отличие от императивных языков, где вы описываете как вычислить результат, Rego позволяет вам описать какие условия должны вызвать политическое решение. Эта страница охватывает весь поддерживаемый синтаксис и возможности языка, поддерживаемые в 256 Blocks.

Структура политики

Каждая политика, которую вы пишете, автоматически обертывается необходимым шаблонным кодом 256 Blocks. Это означает, что вы можете сосредоточиться исключительно на написании своих правил, не беспокоясь об объявлениях пакетов или значениях по умолчанию.

Все политики:

  • Автоматически обертываются необходимым оператором package
  • Имеют предустановленное default deny := false (разрешить по умолчанию)
  • Имеют предустановленное default denyGasSponsor := false (спонсировать по умолчанию)
  • Могут определять только правила, а не переопределять значения по умолчанию

Базовый синтаксис правил

Правила являются строительными блоками политик. Правило состоит из головы (имя правила, например: deny или denyGasSponsor) и тела (условия). Когда все условия в теле истинны, правило оценивается как true.

Пример

# Базовая структура: rule_name if { conditions }
deny if {
    input.usd_value > 10000
}

Множественные условия (логика И)

Когда вы включаете несколько условий в одно тело правила, ВСЕ условия должны быть истинными для срабатывания правила. Каждое условие неявно соединяется с И.

Пример

# Оба условия должны быть истинными для блокировки
deny if {
    input.chain == "ethereum"
    input.usd_value > 1000
}

Множественные правила (логика ИЛИ)

Когда вы определяете несколько правил с одинаковым именем, ЛЮБОЕ совпадающее правило вызовет политику. Это обеспечивает логику ИЛИ между правилами.

Пример

# Любое условие вызывает блокировку
deny if {
    input.chain == "ethereum"
}
 
deny if {
    input.usd_value > 10000
}

Комбинирование И и ИЛИ

Вы можете комбинировать оба шаблона для сложной логики. Каждое тело правила использует логику И внутренне, в то время как несколько правил обеспечивают логику ИЛИ между ними.

Пример

# Блокировать если: (ethereum И высокая стоимость) ИЛИ (любая цепочка И заблокированная страна)
deny if {
    input.chain == "ethereum"
    input.usd_value > 5000
}
 
deny if {
    input.source_country in {"KP", "IR", "CU"}
}

Присваивание переменных

Используйте оператор := для присваивания значений переменным. Переменные делают ваши политики более читаемыми и поддерживаемыми, давая значимые имена значениям и обеспечивая повторное использование.

Переменные уровня политики

Переменные, определенные вне тел правил, действуют как константы, на которые могут ссылаться несколько правил. Используйте их для пороговых значений, белых списков и черных списков.

Пример

# Константы уровня политики - определены один раз, используются везде
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
}

Локальные переменные

Переменные, определенные внутри тела правила, являются локальными для этого правила. Используйте их для промежуточных вычислений или для улучшения читаемости.

Пример

deny if {
    # Вычислить с запасом безопасности
    value_with_buffer := input.usd_value * 1.15
    value_with_buffer > 10000
}
 
deny if {
    # Оценить стоимость газа в wei
    gas_cost := to_number(input.gas_limit) * to_number(input.max_fee_per_gas)
    gas_cost > 1000000000000000000  # 1 ETH в wei
}

Операторы сравнения

Rego предоставляет стандартные операторы сравнения для оценки условий. Они работают со строками, числами и другими сравниваемыми типами.

ОператорОписаниеПример
==Равноinput.chain == "ethereum"
!=Не равноinput.chain != "polygon"
>Больше чемinput.usd_value > 1000
>=Больше или равноinput.usd_value >= 1000
<Меньше чемinput.usd_value < 100
<=Меньше или равноinput.usd_value <= 100

Пример

# Блокировать высокостоимостные транзакции в дорогих цепочках
deny if {
    input.chain == "ethereum"
    input.usd_value >= 5000
}
 
# Блокировать транзакции ниже минимальной стоимости (потенциальный спам)
deny if {
    input.usd_value != null
    input.usd_value < 1
}

Арифметические операторы

Вы можете выполнять арифметические вычисления в условиях вашей политики. Это полезно для вычисления пороговых значений, применения множителей или комбинирования значений.

ОператорОписаниеПример
+Сложениеinput.usd_value + 100
-Вычитаниеinput.usd_value - fees
*Умножениеinput.usd_value * 1.1
/Делениеinput.usd_value / 2
%Остаток от деленияinput.gas_limit % 1000

Пример

# Применить буфер 10% при проверке лимитов
deny if {
    input.usd_value * 1.1 > 10000
}
 
# Блокировать, если комбинированная стоимость превышает порог
deny if {
    total := input.usd_value + 500  # Добавить оценочные комиссии
    total > 5000
}

Логические операторы

Помимо неявного И (несколько условий) и ИЛИ (несколько правил), Rego предоставляет явные логические операторы для более сложных выражений.

Оператор not

Используйте not для отрицания условия. Это особенно полезно для шаблонов белых списков, где вы хотите блокировать все, что НЕ входит в разрешенный набор.

Пример

# Разрешать только определенные цепочки (блокировать все остальное)
allowed_chains := {"polygon", "base", "arbitrum"}
 
deny if {
    not input.chain in allowed_chains
}

Пример: Комбинирование с другими условиями

# Блокировать, если НЕ метод только для чтения И стоимость высокая
deny if {
    not input.rpc_method in {"eth_call", "eth_getBalance"}
    input.usd_value > 1000
}

Вспомогательные правила

Помимо deny и denyGasSponsor, вы можете определять свои собственные пользовательские правила для организации сложной логики. Вспомогательные правила действуют как многократно используемые строительные блоки, которые делают ваши политики более читаемыми и поддерживаемыми.

Вспомогательное правило - это просто именованное условие, которое оценивается как true, когда его тело совпадает. Затем вы можете ссылаться на эти правила в ваших правилах deny или denyGasSponsor, в том числе с оператором not для их отрицания.

Пример

# Определить вспомогательное правило для проверки, от доверенного источника ли запрос
is_trusted if {
    input.source_country in {"US", "GB", "DE"}
    input.source_ip in {"203.0.113.10", "203.0.113.11"}
}
 
# Определить другое вспомогательное правило для высокорисковых транзакций
is_high_risk if {
    input.usd_value > 10000
}
 
is_high_risk if {
    input.source_country in {"RU", "CN"}
}
 
# Использовать вспомогательные правила в условиях deny
deny if {
    not is_trusted
    is_high_risk
}

Вспомогательные правила особенно полезны для:

  • Повторное использование: Определите условие один раз, используйте его в нескольких правилах
  • Читаемость: Дайте значимые имена сложным условиям
  • Отрицание: Используйте not для проверки, когда условие НЕ выполнено
  • Организация: Разбейте сложные политики на логические компоненты

Строковые операции

Строки - это последовательности символов, заключенные в двойные кавычки (""). Большинство полей ввода, таких как input.chain, input.rpc_method и адреса, являются строками. Rego предоставляет несколько встроенных функций для работы со строками, необходимых для сопоставления шаблонов с адресами, именами методов и другими текстовыми полями.

ФункцияОписаниеВозвращает
contains(string, substring)Проверить, содержит ли строка подстрокуboolean
startswith(string, prefix)Проверить, начинается ли строка с префиксаboolean
endswith(string, suffix)Проверить, заканчивается ли строка суффиксомboolean
lower(string)Преобразовать в нижний регистрstring
upper(string)Преобразовать в верхний регистрstring
substring(string, start, length)Извлечь часть строкиstring
sprintf(format, values)Форматировать строку со значениямиstring

Пример

# Блокировать любой метод, содержащий "sign" (охватывает eth_sign, personal_sign и т.д.)
deny if {
    contains(input.rpc_method, "sign")
}
 
# Блокировать адреса, начинающиеся с известного вредоносного префикса
deny if {
    startswith(lower(input.to_address), "0x000000000000000000000000000000000000dead")
}
 
# Блокировать методы debug и admin
deny if {
    startswith(input.rpc_method, "debug_")
}
 
deny if {
    startswith(input.rpc_method, "admin_")
}

Операции с массивами

Массивы - это упорядоченные списки значений, определенные квадратными скобками []. В отличие от наборов, массивы сохраняют порядок элементов и могут содержать дубликаты. Вы часто будете работать с массивами в политиках, особенно с input.contract_addresses, который содержит все адреса контрактов, участвующие в запросе.

# Синтаксис массива
my_array := ["first", "second", "third"]

Rego предоставляет несколько способов проверки и работы с массивами.

ФункцияОписаниеВозвращает
count(array)Количество элементовnumber
array[index]Доступ к элементу по индексу (начиная с 0)element
value in arrayПроверить, существует ли значение в массивеboolean

Пример

# Ограничить количество контрактов в одном запросе
deny if {
    count(input.contract_addresses) > 10
}
 
# Проверить, находится ли конкретный адрес в списке контрактов
deny if {
    "0xbanned..." in input.contract_addresses
}
 
# Доступ к конкретным элементам массива
deny if {
    count(input.contract_addresses) > 0
    first_contract := input.contract_addresses[0]
    startswith(first_contract, "0xdead")
}

Проверка членства

Проверка членства - одна из самых распространенных операций в политиках. Вы будете использовать ее для проверки, принадлежат ли значения белым спискам, черным спискам или другим коллекциям разрешенных значений.

Наборы

Наборы - это неупорядоченные коллекции уникальных значений, определенные фигурными скобками {}. Используйте оператор in для проверки, существует ли значение в наборе.

# Синтаксис набора
my_set := {"value1", "value2", "value3"}

Наборы оптимизированы для быстрой проверки членства, что делает их идеальными для белых и черных списков.

Пример

# Блокировать запросы из санкционированных стран
blocked_countries := {"KP", "IR", "CU", "SY", "RU"}
 
deny if {
    input.source_country in blocked_countries
}
 
# Разрешать только определенные цепочки
allowed_chains := {"polygon", "base", "arbitrum"}
 
deny if {
    not input.chain in allowed_chains
}

Встроенные и именованные наборы

Вы можете определять наборы встроенно внутри правил или как именованные переменные на уровне политики. Именованные наборы улучшают читаемость и позволяют повторное использование в нескольких правилах.

Пример

# Встроенный набор - хорош для простых, разовых проверок
deny if {
    input.rpc_method in {"eth_sign", "personal_sign", "eth_signTypedData"}
}
 
# Именованный набор - лучше для повторного использования и читаемости
approved_contracts := {
    "0xdac17f958d2ee523a2206206994597c13d831ec7",  # USDT
    "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",  # USDC
    "0x6b175474e89094c44da98b954eedeac495271d0f"   # DAI
}
 
deny if {
    some addr in input.contract_addresses
    not addr in approved_contracts
}

Итерация с some

Ключевое слово some позволяет вам итерировать по коллекциям и проверять условия для каждого элемента. Это необходимо при работе с массивами, такими как input.contract_addresses.

Базовая итерация

Используйте some для проверки, ЛЮБОЙ ли элемент в коллекции соответствует условию.

Пример

# Блокировать, если ЛЮБОЙ адрес контракта находится в черном списке
blocked_contracts := {"0xbanned1...", "0xbanned2..."}
 
deny if {
    some addr in input.contract_addresses
    addr in blocked_contracts
}

Итерация с индексом

Вы также можете получить доступ к индексу каждого элемента во время итерации.

Пример

# Проверить условия для конкретных позиций
deny if {
    some i, addr in input.contract_addresses
    i == 0  # Только первый контракт
    startswith(addr, "0x000")
}

Пример: Поиск любого совпадения

# Блокировать, если любой контракт начинается с подозрительного префикса
deny if {
    some addr in input.contract_addresses
    startswith(lower(addr), "0x000000000000000000000000")
}
 
# Блокировать, если любой контракт не находится в списке одобренных
approved := {"0xusdt...", "0xusdc...", "0xdai..."}
 
deny if {
    some addr in input.contract_addresses
    not addr in approved
}

Итерация с every

В то время как some проверяет, ЛЮБОЙ ли элемент совпадает, every требует, чтобы ВСЕ элементы соответствовали условию. Это полезно для обеспечения того, чтобы все коллекции соответствовали вашим критериям.

Пример

# Блокировать, только если ВСЕ контракты из подозрительного набора
# (маловероятно, что это легитимно, если каждый адрес подозрительный)
suspicious_prefixes := {"0x0000000000000000"}
 
deny if {
    count(input.contract_addresses) > 0
    every addr in input.contract_addresses {
        some prefix in suspicious_prefixes
        startswith(addr, prefix)
    }
}

Пример: Шаблоны валидации

# Убедиться, что все контракты из списка одобренных (обратное от some/not)
approved_contracts := {"0xusdt...", "0xusdc...", "0xdai..."}
 
# Это блокирует, если ЛЮБОЙ контракт не одобрен
deny if {
    some addr in input.contract_addresses
    not addr in approved_contracts
}
 
# Эквивалент с использованием every (блокировать, если НЕ каждый контракт одобрен)
deny if {
    count(input.contract_addresses) > 0
    not every addr in input.contract_addresses {
        addr in approved_contracts
    }
}

Выражения-генераторы

Выражения-генераторы позволяют вам создавать новые коллекции, преобразуя или фильтруя существующие. Они полезны для извлечения конкретных значений или построения производных наборов данных.

Генератор массива

Создайте новый массив из элементов, соответствующих определенным критериям.

Пример

# Извлечь все адреса контрактов, начинающиеся с определенного префикса
matching_contracts := [addr |
    some addr in input.contract_addresses
    startswith(addr, "0xa")
]
 
deny if {
    count(matching_contracts) > 3
}

Генератор набора

Создайте набор уникальных значений.

Пример

# Получить уникальные префиксы из всех адресов контрактов
contract_prefixes := {substring(addr, 0, 6) |
    some addr in input.contract_addresses
}
 
# Блокировать, если слишком много различных префиксов контрактов (подозрительно)
deny if {
    count(contract_prefixes) > 5
}

Условный Else

Ключевое слово else позволяет вам определять резервные значения, когда условия не выполнены. Это полезно для вычисления производных значений, которые меняются в зависимости от ввода.

Пример

# Назначить уровень риска на основе стоимости транзакции
risk_level := "critical" if {
    input.usd_value > 100000
} else := "high" if {
    input.usd_value > 10000
} else := "medium" if {
    input.usd_value > 1000
} else := "low"
 
# Использовать вычисленный уровень риска в правилах deny
deny if {
    risk_level == "critical"
}
 
denyGasSponsor if {
    risk_level in {"high", "critical"}
}

Пример: Пороговые значения для конкретных цепочек

# Различные лимиты для различных цепочек
chain_limit := 1000 if {
    input.chain == "ethereum"
} else := 5000 if {
    input.chain == "polygon"
} else := 10000
 
deny if {
    input.usd_value > chain_limit
}
Справочник по языку | 256 Blocks