언어 참조

정책 작성을 위한 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.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

예시

# 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")
}

멤버십 테스트

멤버십 테스트는 정책에서 가장 일반적인 작업 중 하나입니다. 값이 허용 목록, 차단 목록 또는 기타 허용된 값 컬렉션에 속하는지 확인하는 데 사용합니다.

세트

세트는 중괄호 {}로 정의된 고유 값의 정렬되지 않은 컬렉션입니다. 값이 세트에 존재하는지 테스트하려면 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