言語リファレンス

ポリシーを書くためのRego構文と言語機能の完全ガイド

ポリシーはRego言語で記述されます。これは、複雑なアクセス制御の決定を表現するために設計された宣言型のポリシー言語です。結果をどのように計算するかを記述する命令型言語とは異なり、Regoではどのような条件がポリシー決定をトリガーするかを記述できます。このページでは、256 Blocksでサポートされるすべての構文と言語機能を説明します。

ポリシー構造

記述する各ポリシーは、256 Blocksによって必要なボイラープレートで自動的にラップされます。これは、パッケージ宣言やデフォルト値を気にすることなく、純粋にルールの記述に集中できることを意味します。

すべてのポリシー:

  • 必要なpackageステートメントで自動的にラップされます
  • プリセットのdefault deny := false(デフォルトで許可)を持ちます
  • プリセットのdefault denyGasSponsor := false(デフォルトでスポンサー)を持ちます
  • ルールのみを定義でき、デフォルトを上書きできません

基本的なルール構文

ルールはポリシーの構成要素です。ルールはヘッド(ルール名、例:denyまたはdenyGasSponsor)とボディ(条件)で構成されます。ボディ内のすべての条件が真の場合、ルールはtrueと評価されます。

# Basic structure: rule_name if { conditions }
deny if {
    input.usd_value > 10000
}

複数の条件(AND論理)

同じルールボディに複数の条件を含める場合、ルールをトリガーするにはすべての条件が真である必要があります。各条件は暗黙的にANDで結合されます。

# Both conditions must be true to deny
deny if {
    input.chain == "ethereum"
    input.usd_value > 1000
}

複数のルール(OR論理)

同じ名前で複数のルールを定義すると、一致するルールがポリシーをトリガーします。これにより、ルール間でOR論理が提供されます。

# Either condition triggers denial
deny if {
    input.chain == "ethereum"
}
 
deny if {
    input.usd_value > 10000
}

ANDとORの組み合わせ

複雑なロジックのために両方のパターンを組み合わせることができます。各ルールボディは内部的にAND論理を使用し、複数のルールはそれらの間でOR論理を提供します。

# Deny if: (ethereum AND high-value) OR (any chain AND blocked country)
deny if {
    input.chain == "ethereum"
    input.usd_value > 5000
}
 
deny if {
    input.source_country in {"KP", "IR", "CU"}
}

変数の代入

:=演算子を使用して変数に値を代入します。変数は、値に意味のある名前を付け、再利用を可能にすることで、ポリシーをより読みやすく保守しやすくします。

ポリシーレベルの変数

ルールボディの外で定義された変数は、複数のルールから参照できる定数として機能します。これらをしきい値、許可リスト、ブロックリストに使用します。

# Policy-level constants - defined once, used everywhere
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 {
    # Calculate with a safety margin
    value_with_buffer := input.usd_value * 1.15
    value_with_buffer > 10000
}
 
deny if {
    # Estimate gas cost in wei
    gas_cost := to_number(input.gas_limit) * to_number(input.max_fee_per_gas)
    gas_cost > 1000000000000000000  # 1 ETH in wei
}

比較演算子

Regoは条件を評価するための標準的な比較演算子を提供します。これらは文字列、数値、その他の比較可能な型で機能します。

演算子説明
==等しいinput.chain == "ethereum"
!=等しくないinput.chain != "polygon"
>より大きいinput.usd_value > 1000
>=以上input.usd_value >= 1000
<より小さいinput.usd_value < 100
<=以下input.usd_value <= 100

# Block high-value transactions on expensive chains
deny if {
    input.chain == "ethereum"
    input.usd_value >= 5000
}
 
# Block transactions below a minimum value (potential spam)
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

# Apply a 10% buffer when checking limits
deny if {
    input.usd_value * 1.1 > 10000
}
 
# Block if combined value exceeds threshold
deny if {
    total := input.usd_value + 500  # Add estimated fees
    total > 5000
}

論理演算子

暗黙的なAND(複数の条件)とOR(複数のルール)を超えて、Regoはより複雑な式のための明示的な論理演算子を提供します。

not演算子

条件を否定するにはnotを使用します。これは、許可されたセットに含まれないものすべてを拒否したい許可リストパターンに特に有用です。

# Only allow specific chains (deny everything else)
allowed_chains := {"polygon", "base", "arbitrum"}
 
deny if {
    not input.chain in allowed_chains
}

例:他の条件との組み合わせ

# Deny if NOT a read-only method AND value is high
deny if {
    not input.rpc_method in {"eth_call", "eth_getBalance"}
    input.usd_value > 1000
}

ヘルパールール

denydenyGasSponsorを超えて、複雑なロジックを整理するための独自のカスタムルールを定義できます。ヘルパールールは、ポリシーをより読みやすく保守しやすくする再利用可能なビルディングブロックとして機能します。

ヘルパールールは、そのボディが一致するとtrueと評価される名前付き条件です。次に、これらのルールをdenyまたはdenyGasSponsorルールで参照でき、not演算子を含めて否定することもできます。

# Define a helper rule to check if request is from a trusted source
is_trusted if {
    input.source_country in {"US", "GB", "DE"}
    input.source_ip in {"203.0.113.10", "203.0.113.11"}
}
 
# Define another helper for high-risk transactions
is_high_risk if {
    input.usd_value > 10000
}
 
is_high_risk if {
    input.source_country in {"RU", "CN"}
}
 
# Use helper rules in deny conditions
deny if {
    not is_trusted
    is_high_risk
}

ヘルパールールは特に次の場合に有用です:

  • 再利用性: 条件を一度定義し、複数のルールで使用
  • 可読性: 複雑な条件に意味のある名前を付ける
  • 否定: notを使用して条件が満たされないときをチェック
  • 組織化: 複雑なポリシーを論理的なコンポーネントに分解

文字列操作

文字列は二重引用符("")で囲まれた文字のシーケンスです。input.chaininput.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

# Block any method containing "sign" (covers eth_sign, personal_sign, etc.)
deny if {
    contains(input.rpc_method, "sign")
}
 
# Block addresses starting with known malicious prefix
deny if {
    startswith(lower(input.to_address), "0x000000000000000000000000000000000000dead")
}
 
# Block debug and admin methods
deny if {
    startswith(input.rpc_method, "debug_")
}
 
deny if {
    startswith(input.rpc_method, "admin_")
}

配列操作

配列は角括弧[]で定義された値の順序付きリストです。セットとは異なり、配列は要素の順序を保持し、重複を含むことができます。ポリシーでは、特にリクエストに関与するすべてのコントラクトアドレスを含むinput.contract_addressesなど、配列を頻繁に使用します。

# Array syntax
my_array := ["first", "second", "third"]

Regoは配列を検査および操作するためのいくつかの方法を提供します。

関数説明戻り値
count(array)要素数number
array[index]インデックスで要素にアクセス(0ベース)element
value in array配列に値が存在するか確認boolean

# Limit the number of contracts in a single request
deny if {
    count(input.contract_addresses) > 10
}
 
# Check if a specific address is in the contract list
deny if {
    "0xbanned..." in input.contract_addresses
}
 
# Access specific array elements
deny if {
    count(input.contract_addresses) > 0
    first_contract := input.contract_addresses[0]
    startswith(first_contract, "0xdead")
}

メンバーシップテスト

メンバーシップテストはポリシーで最も一般的な操作の1つです。値が許可リスト、ブロックリスト、またはその他の許可された値のコレクションに属しているかどうかを確認するために使用します。

セット

セットは中括弧{}で定義された一意の値の順序なしコレクションです。値がセットに存在するかどうかをテストするにはin演算子を使用します。

# Set syntax
my_set := {"value1", "value2", "value3"}

セットは高速なメンバーシップテスト用に最適化されており、許可リストとブロックリストに最適です。

# Block requests from sanctioned countries
blocked_countries := {"KP", "IR", "CU", "SY", "RU"}
 
deny if {
    input.source_country in blocked_countries
}
 
# Only allow specific chains
allowed_chains := {"polygon", "base", "arbitrum"}
 
deny if {
    not input.chain in allowed_chains
}

インラインセット vs 名前付きセット

ルール内でインラインでセットを定義することも、ポリシーレベルで名前付き変数として定義することもできます。名前付きセットは可読性を向上させ、複数のルール間での再利用を可能にします。

# Inline set - good for simple, one-off checks
deny if {
    input.rpc_method in {"eth_sign", "personal_sign", "eth_signTypedData"}
}
 
# Named set - better for reusability and readability
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を使用して、コレクション内の任意の要素が条件に一致するかどうかを確認します。

# Deny if ANY contract address is in the blocklist
blocked_contracts := {"0xbanned1...", "0xbanned2..."}
 
deny if {
    some addr in input.contract_addresses
    addr in blocked_contracts
}

インデックス付き反復

反復中に各要素のインデックスにアクセスすることもできます。

# Check conditions on specific positions
deny if {
    some i, addr in input.contract_addresses
    i == 0  # First contract only
    startswith(addr, "0x000")
}

例:任意の一致を見つける

# Deny if any contract starts with a suspicious prefix
deny if {
    some addr in input.contract_addresses
    startswith(lower(addr), "0x000000000000000000000000")
}
 
# Deny if any contract is not in the approved list
approved := {"0xusdt...", "0xusdc...", "0xdai..."}
 
deny if {
    some addr in input.contract_addresses
    not addr in approved
}

everyによる反復

someが任意の要素が一致するかどうかをチェックするのに対し、everyはすべての要素が条件に一致することを要求します。これは、コレクション全体が基準を満たしていることを確保するのに役立ちます。

# Deny only if ALL contracts are from a suspicious set
# (unlikely to be legitimate if every address is suspicious)
suspicious_prefixes := {"0x0000000000000000"}
 
deny if {
    count(input.contract_addresses) > 0
    every addr in input.contract_addresses {
        some prefix in suspicious_prefixes
        startswith(addr, prefix)
    }
}

例:検証パターン

# Ensure all contracts are from approved list (inverse of some/not)
approved_contracts := {"0xusdt...", "0xusdc...", "0xdai..."}
 
# This denies if ANY contract is not approved
deny if {
    some addr in input.contract_addresses
    not addr in approved_contracts
}
 
# Equivalent using every (deny if NOT every contract is approved)
deny if {
    count(input.contract_addresses) > 0
    not every addr in input.contract_addresses {
        addr in approved_contracts
    }
}

内包表記

内包表記を使用すると、既存のコレクションを変換またはフィルタリングして新しいコレクションを作成できます。特定の値を抽出したり、派生データセットを構築したりするのに役立ちます。

配列内包表記

特定の基準に一致する要素から新しい配列を作成します。

# Extract all contract addresses that start with a specific prefix
matching_contracts := [addr |
    some addr in input.contract_addresses
    startswith(addr, "0xa")
]
 
deny if {
    count(matching_contracts) > 3
}

セット内包表記

一意の値のセットを作成します。

# Get unique prefixes from all contract addresses
contract_prefixes := {substring(addr, 0, 6) |
    some addr in input.contract_addresses
}
 
# Deny if there are too many different contract prefixes (suspicious)
deny if {
    count(contract_prefixes) > 5
}

条件付きElse

elseキーワードを使用すると、条件が満たされない場合のフォールバック値を定義できます。これは、入力に基づいて変化する派生値を計算するのに役立ちます。

# Assign risk level based on transaction value
risk_level := "critical" if {
    input.usd_value > 100000
} else := "high" if {
    input.usd_value > 10000
} else := "medium" if {
    input.usd_value > 1000
} else := "low"
 
# Use the computed risk level in deny rules
deny if {
    risk_level == "critical"
}
 
denyGasSponsor if {
    risk_level in {"high", "critical"}
}

例:チェーン固有のしきい値

# Different limits for different chains
chain_limit := 1000 if {
    input.chain == "ethereum"
} else := 5000 if {
    input.chain == "polygon"
} else := 10000
 
deny if {
    input.usd_value > chain_limit
}
言語リファレンス | 256 Blocks