Cómo escribir reglas de Cedar

En esta página, se describe cómo crear reglas de políticas de Cedar para validar transacciones en el motor de políticas de computación multipartita (MPC).

El Policy Engine decodifica las cargas útiles de las transacciones entrantes dentro de un entorno de ejecución confiable (TEE) seguro y proyecta los atributos de la intención de la transacción en el contexto de evaluación de Cedar.

Estructura de las reglas de Cedar

Cuando escribes reglas de Cedar para Policy Engine, especificas una regla con un conjunto de condiciones anidadas:

  • Principal: Es la identidad lógica del solicitante de la transacción (el iniciador de la transacción) dentro del espacio de trabajo de computación multipartita (por ejemplo, User::"alice"), en lugar de la identidad de IAM de Google Cloud (usuario o cuenta de servicio) que llama a la API.
  • Acción: Es la acción criptográfica solicitada (por ejemplo, Action::"sign_transaction").
  • Recurso: Es la clase de recurso o de operación de destino sobre la que se opera (por ejemplo, Resource::"mpc_operation").
  • Context: Son los metadatos y los parámetros analizados de la transacción (accesibles en context.transaction).

Referencia del esquema de transacciones de Ethereum

El framework independiente del protocolo permite que las reglas omitan la verificación del esquema para que se puedan reutilizar en diferentes redes de cadena de bloques. Sin embargo, para restringir de forma explícita una regla a las cargas útiles de transacciones de Ethereum, verifica lo siguiente: context.transaction.schema == "ethereum.transaction.v2".

Se admiten los siguientes atributos dentro del registro context.transaction:

Atributo Tipo Descripción
schema String Es el esquema de formato. En el caso de las transacciones de Ethereum, siempre es "ethereum.transaction.v2".
operation String Es el tipo de operación de transacción. Valores admitidos:
  • "NATIVE_TRANSFER": Es una transferencia directa de la moneda de la cadena principal (como ETH).
  • "TOKEN_TRANSFER": Una transferencia de tokens ERC-20 (como USDC, USDT o DAI)
  • "TOKEN_APPROVE": Una aprobación de token ERC-20 para permitir que un contrato gaste tokens.
  • "CONTRACT_CALL": Es una interacción genérica con un contrato inteligente (como staking o intercambio).
chain_id String Es el ID de la red de cadena de bloques de destino (por ejemplo, "1" para la red principal de Ethereum o "11155111" para la red de prueba de Sepolia).
asset_symbol String Es el símbolo de cotización del token que se transfiere (por ejemplo, "ETH", "USDC", "DAI" o "WBTC"). Los tokens que no cotizan en bolsa devuelven "UNKNOWN".
asset_contract_address String La dirección del contrato inteligente del token ERC-20 (en formato de suma de comprobación EIP-55 con mayúsculas y minúsculas). Esto se resuelve en una cadena vacía ("") para las transferencias de ETH estándar.
recipient String Es la dirección del destinatario (en formato de suma de comprobación EIP-55 con mayúsculas y minúsculas). En el caso de las aprobaciones de tokens, representa la dirección del gastador.
spend_amount_scaled Largo Es el importe de la transferencia de tokens normalizado a una precisión fija de 9 decimales (formato de nanocoin), lo que elimina la necesidad de hacer un seguimiento de los decimales de tokens variables (como 6, 8 o 18) cuando se escriben reglas.

Cómo calcularlo: Multiplica el importe del activo legible por humanos (p.ej., 1.5 ETH o 5,000 USDC) por 1,000,000,000 (109) para obtener el valor de tu regla:
  • 0.1 ETH → 100000000 (0.1 × 109)
  • 1.0 de cualquier recurso → 1000000000 (1.0 × 109)
  • 5,000 USDC → 5000000000000 (5,000 × 109)
spend_amount_dust Largo Es el resto del importe de la transacción que está por debajo del umbral de precisión de 9 decimales. Para los activos con más de 9 decimales (como ETH, que tiene 18), este contiene el valor de sub-Gwei sin escalar (el Wei restante). Este valor siempre es 0 para los activos con 9 decimales o menos (como USDC o WBTC).
max_fee_atomic Largo Es el límite máximo de la comisión total por transacción (escalado por 109, que representa Gwei).
gas_limit Largo Es la cantidad máxima de gas asignada para la ejecución de la transacción.
destination_type String Es la clasificación de la dirección del destinatario. Valores admitidos: "EOA" (billetera de usuario estándar), "CONTRACT" (contrato inteligente) o "INTERNAL_TREASURY" (bóveda interna).
temporal_validity Largo Es el nonce de la cuenta de transacción (se usa para evitar ataques de reproducción de transacciones).

Referencia del esquema de transacciones de Bitcoin

Para restringir de forma explícita una regla a las cargas útiles de transacciones de PSBT de BIP 174 de Bitcoin, verifica lo siguiente: context.transaction.schema == "bitcoin.transaction.v2".

Se admiten los siguientes atributos dentro del registro context.transaction:

Atributo Tipo Descripción
schema String Es el esquema de formato. En el caso de las transacciones de Bitcoin, siempre es "bitcoin.transaction.v2".
operation String Es el tipo de operación de transacción. Valores admitidos:
  • "NATIVE_TRANSFER": Es una transferencia directa de Bitcoin (BTC).
  • "OP_RETURN": Es un resultado de datos nulos o de anclaje de datos que no se puede gastar.
chain_id String La parte legible por humanos (HRP) de Bech32 de la red de destino: "bc" (red principal de Bitcoin), "tb" (red de prueba / red de prueba 4) o "bcrt" (red de regresión).
asset_symbol String El símbolo de cotización del activo: "BTC".
recipient String Es la dirección de destino principal. Para las transacciones de OP_RETURN, es "unaddressable". En el caso de los barridos de consolidación interna (cambio del 100%), esto se resuelve en la primera dirección de cambio.
spend_amount_scaled Largo Es el importe neto de la transferencia saliente normalizado a una precisión fija de 9 decimales (unidades de 10-9). Dado que 1 satoshi equivale a 10 unidades de nanocoin (1 sat = 10 unidades, 1 BTC = 109 unidades), se excluyen los importes de cambio internos. En el caso de las transacciones de barrido interno o de cambio del 100%, este valor se evalúa como 0.
max_fee_atomic Largo Es el límite de la comisión del minero calculada, normalizada con una precisión de 9 decimales: (∑ Entradas − ∑ Salidas) × 10.
destination_type String La clasificación de la secuencia de comandos receptora. Valores admitidos: "P2WPKH" (SegWit nativo), "P2TR" (Taproot), "P2PKH" (heredado), "P2SH" (SegWit anidado / hash de secuencia de comandos), "TREASURY_CHANGE" (barrido de cambio interno) o "".
temporal_validity Largo El campo nLockTime de la transacción.
outputs Set<Record> Es el conjunto normalizado de todos los resultados de la transacción: [{ address: String, amount_scaled: Long, is_change: Bool }].

Conceptos básicos de la política de Bitcoin

  1. Matemática de escalamiento universal de 9 decimales: Los importes en las políticas de Bitcoin usan una precisión normalizada de nanocoins de 9 decimales (1 sat = 10 unidades, 1 BTC = 109 unidades):

    Amountscaled = Satoshis × 10 = BTC × 109

    • 0.0005 BTC (50,000 satoshis) → 500000 (50,000 × 10)
    • 0.0025 BTC (250,000 satoshis) → 2500000 (250,000 × 10)
    • 1.0 BTC (100,000,000 satoshis) → 1000000000 (100,000,000 × 10)
  2. Separación de salidas de cambio: El analizador inspecciona las rutas de derivación BIP-32 (m/.../1/*) y correlaciona las secuencias de comandos de entrada para identificar automáticamente las salidas de cambio internas. Los importes de cambio se excluyen de spend_amount_scaled, lo que garantiza que las políticas de límite de inversión se evalúen estrictamente en función del valor neto de salida transferido a terceros.

  3. Regla de destinatario único sin cambio: Para evitar ataques ambiguos de enrutamiento de varios destinatarios, las transacciones que contienen varios destinatarios externos distintos de terceros fallan de forma cerrada (ErrUnsupportedTx).


Ejemplos de reglas de Cedar

En los siguientes ejemplos, se muestra cómo configurar listas de entidades permitidas, reglas de límite y límites de repetición con Cedar para transacciones de Ethereum y Bitcoin.

Ejemplos de Ethereum

Las siguientes reglas ilustran patrones de políticas comunes para las transacciones de Ethereum y ERC-20.

1. Límite de transferencia directa de ETH

Esta regla permite que Alice transfiera ETH de la cadena base, verificando que el importe de la transferencia escalado no supere 1.0 ETH (1,000,000,000 de unidades de Gwei escaladas).

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.operation == "NATIVE_TRANSFER" &&
    context.transaction.asset_symbol == "ETH" &&
    context.transaction.spend_amount_scaled <= 1000000000
};

2. Regla de lista de tokens ERC-20 permitidos

Esta regla permite transferencias de tokens de USDC de hasta 5,000 USDC. Restringe las transferencias a una dirección de destinatario que se encuentre en una lista de entidades permitidas. Ten en cuenta que, como las cadenas de Cedar distinguen mayúsculas de minúsculas, la dirección del destinatario debe coincidir con el formato EIP-55 que combina mayúsculas y minúsculas.

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.operation == "TOKEN_TRANSFER" &&
    context.transaction.asset_symbol == "USDC" &&
    context.transaction.recipient == "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" &&
    context.transaction.spend_amount_scaled <= 5000000000000
};

3. Resguardos de llamadas de contratos inteligentes y DeFi

Esta regla permite interacciones generales con contratos (como staking o intercambio) sin restricciones de importe, siempre que no sean operaciones de transferencia que impliquen el gasto de activos.

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.operation == "CONTRACT_CALL"
};

4. Protección estricta contra la repetición (límite de ID de cadena)

Para evitar ataques de reproducción de transacciones en los que se vuelve a enviar una carga útil firmada para una cadena de testnet en mainnets de producción, puedes limitar el alcance de tu regla a un ID de cadena específico (p.ej., 11155111 para Sepolia).

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.chain_id == "11155111"
};

Ejemplos de Bitcoin

Las siguientes reglas ilustran patrones de políticas comunes para las transacciones de PSBT de Bitcoin.

1. Límite de inversión y límite de tarifa de minero

Esta regla permite transferencias directas de Bitcoin (BTC) en las que el importe neto de la transferencia saliente no supere los 0.0025 BTC (250,000 satoshis → 2,500,000 unidades escaladas) y la comisión del minero no supere los 10,000 satoshis (100,000 unidades atómicas).

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.operation == "NATIVE_TRANSFER" &&
    context.transaction.asset_symbol == "BTC" &&
    context.transaction.spend_amount_scaled <= 2500000 &&
    context.transaction.max_fee_atomic <= 100000
};

2. Lista de entidades permitidas de direcciones de SegWit (P2WPKH) y Taproot (P2TR)

Esta regla restringe las transferencias de Bitcoin a una dirección de destino aprobada de SegWit o Taproot.

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.schema == "bitcoin.transaction.v2" &&
    (context.transaction.destination_type == "P2WPKH" || context.transaction.destination_type == "P2TR") &&
    context.transaction.recipient == "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
};

3. Consolidación de UTXO y barrido de tesorería

Esta regla permite barridos de consolidación de UTXO internos en los que todos los resultados se devuelven a direcciones de cambio internas (destination_type == "TREASURY_CHANGE" y spend_amount_scaled == 0), a la vez que se limita la comisión de consolidación.

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.schema == "bitcoin.transaction.v2" &&
    context.transaction.destination_type == "TREASURY_CHANGE" &&
    context.transaction.spend_amount_scaled == 0 &&
    context.transaction.max_fee_atomic <= 50000
};

4. Inspección del conjunto de resultados

Esta regla valida que el conjunto de salidas de la transacción contenga una salida de destino y un importe específicos.

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.outputs.contains({
        address: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
        amount_scaled: 500000,
        is_change: false
    })
};

5. Alcance de la red (Mainnet frente a Testnet)

Esta regla limita la autorización a las transacciones de Bitcoin Testnet (tb), lo que evita la ejecución en Bitcoin Mainnet (bc).

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.schema == "bitcoin.transaction.v2" &&
    context.transaction.chain_id == "tb"
};

6. Anclaje de datos de OP_RETURN

Esta regla permite transacciones de anclaje de datos OP_RETURN no gastables (spend_amount_scaled == 0 y recipient == "unaddressable"), lo que aplica un límite estricto de tarifas.

permit (
    principal == User::"alice",
    action == Action::"sign_transaction",
    resource == Resource::"mpc_operation"
)
when {
    context.transaction.operation == "OP_RETURN" &&
    context.transaction.recipient == "unaddressable" &&
    context.transaction.spend_amount_scaled == 0 &&
    context.transaction.max_fee_atomic <= 10000
};

Defensas de seguridad del analizador

Para proteger las operaciones de transacciones en los entornos de producción, Policy Engine analiza las cargas útiles de forma estricta y rechaza las estructuras no válidas en la capa de la puerta de enlace antes de evaluar cualquier regla de Cedar:

  • Aplicación de la moneda base con valor cero: El analizador rechaza de forma explícita las operaciones de transferencia de tokens de Ethereum (TOKEN_TRANSFER) que también intentan agrupar activos de transferencia base que no sean cero. Esto evita que las cargas útiles maliciosas transfieran valores de activos base estándar mientras presentan la transacción como una transferencia de tokens estándar.
  • Verificación de UTXO y testigo: En el caso de las transacciones de PSBT de Bitcoin, el analizador valida que todas las entradas proporcionen datos de witness_utxo válidos, lo que evita la falsificación del cálculo de tarifas o la firma no verificada.
  • Verificación de la secuencia de comandos de salida de cambio: Las salidas de cambio internas deben coincidir con una clave pública de la secuencia de comandos de entrada o con una ruta de cambio BIP-32 conocida (m/.../1/*). Las cargas útiles que intentan falsificar las salidas de cambio a direcciones de terceros fallan de forma segura.
  • Aplicación de un único destinatario externo: Las cargas útiles de Bitcoin que contienen varias direcciones de destino externas que no son de cambio fallan de forma cerrada (ErrUnsupportedTx) para evitar la autorización ambigua de políticas de varios destinatarios.
  • Protección contra desbordamiento de enteros positivo y negativo: Se rechazan de inmediato los nonces, los tiempos de bloqueo, los límites de gas o tarifas, y los importes que superan los límites de seguridad (MaxInt64) o producen desbordamientos negativos (como un valor de salida total que supera el valor de entrada total).
  • Verificación de tarifas faltantes: Las transacciones que no definen las tarifas de minero o los parámetros de gas adecuados fallan y se cierran automáticamente.