Saltar al contenido principal

Autorización de Contratos Inteligentes

La autorización es el proceso de juzgar qué operaciones "deben" o "no deben" permitirse; se trata de juzgar permisos.

La autorización se diferencia de la autenticación, que es el problema más estrecho de juzgar si una persona "es quien dice ser", o si un mensaje que afirma provenir de una persona "realmente" provino de ella.

La autorización a menudo utiliza autenticación criptográfica (a través de firmas) para respaldar sus juicios, pero es un proceso más amplio y general.

## Marco de Autorización de Soroban

Soroban tiene como objetivo proporcionar un marco ligero, pero flexible y extensible que permite a los contratos implementar reglas de autorización arbitrariamente complejas, mientras proporciona una implementación incorporada para algunas tareas comunes (como la prevención de repeticiones). El marco consta de los siguientes componentes:

    • Autorización específica de contratos: reglas de autorización personalizadas implementadas por contratos utilizando el almacenamiento privado de contratos y cuentas abstractas.
    • Abstracción de cuentas: permite a los usuarios personalizar sus reglas de autenticación y definir políticas de autorización universales a través de contratos de cuentas personalizados (esto incluye el soporte incorporado para las cuentas Stellar).
    • Biblioteca de autorización basada en el host: asegura la integridad entre las cuentas personalizadas y los contratos regulares. También define el formato de carga útil de firma estructurada, asegura la prevención de repeticiones y se encarga de proporcionar los contextos de firma correctos. Also defines the structured signature payload format, ensures replay prevention and takes care of providing the correct signature contexts.

Soroban host also provides some cryptographic functions (signature verification, hashing) which may be useful for the custom account implementation.

También es más fácil para las aplicaciones cliente escribir código genérico para interactuar con el marco de autorización de Soroban. Por ejemplo, las billeteras pueden implementar una forma generalizada de presentar y firmar cargas útiles de Soroban. Nos damos cuenta de que no es posible cubrir cada caso, pero esperamos que la gran mayoría de los contratos puedan operar dentro del marco y así contribuir a construir un ecosistema más cohesivo. For example, wallets can implement a generalized way to present and sign Soroban payloads.

Nos damos cuenta de que no es posible cubrir cada uno de los casos, pero esperamos que la gran mayoría de los contratos pueda operar dentro del marco y así contribuir a crear un ecosistema más cohesivo. Los marcos de autorización personalizados todavía son posibles de implementar, pero no son recomendables (a menos que no haya alternativas).

### Autorización Específica de Contratos

#### Almacenamiento de Contratos

Los contratos tienen acceso exclusivo de lectura y escritura a su almacenamiento en el ledger. Esto permite a los contratos controlar y gestionar de manera segura el acceso del usuario a sus datos. Por ejemplo, un contrato de token puede asegurarse de que solo el administrador pueda acuñar más tokens almacenando la identidad del administrador en su almacenamiento. De manera similar, puede asegurarse de que solo un propietario del saldo pueda transferir ese saldo. De manera similar, puede asegurarse de que solo un propietario del saldo pueda transferir ese saldo.

#### Address

El enfoque basado en almacenamiento descrito en la sección anterior requiere una manera de representar las identidades de los usuarios y autenticarles. Address es un tipo gestionado por el host que realiza estas funciones.

Desde la perspectiva del contrato, Address es un tipo de identificador opaco. La lógica del contrato no necesita depender de la representación interna del Address (ver sección Abstracción de Cuentas a continuación para más detalles).

Address tiene dos métodos similares en Soroban SDK: require_auth y require_auth_for_args (estos métodos llaman a la respectiva función del host de Soroban). La única diferencia entre las funciones es la capacidad de personalizar los argumentos de invocación. Consulta [ejemplo de autenticación] que demuestra cómo utilizar estas funciones.

Ambas funciones aseguran que el Address ha autorizado la llamada de la función actual dentro del contexto actual (donde el contexto está definido por las llamadas a require_auth en la pila de llamadas actual; consulta la definición más formal en la sección a continuación). Las reglas de autenticación para esta autorización están definidas por el Address y son aplicadas por el host de Soroban. La protección contra repeticiones también se implementa en el host, es decir, normalmente no hay necesidad de que un contrato gestione sus propias nonces.

#### Autorización de Llamadas a Subcontratos

Una de las características clave del Marco de Autorización de Soroban es la capacidad de realizar fácilmente llamadas a subcontratos autorizadas. Por ejemplo, es posible que un contrato llame a require_auth para un Address y luego llame a token.xfer autorizado para el mismo Address (ver [ejemplo de bloqueo temporal] que demuestra este patrón).

Los contratos no necesitan hacer nada especial para beneficiarse de esta característica. Simplemente llamando a un subcontrato que llama a require_auth garantizará que la llamada al subcontrato haya sido debidamente autorizada.

#### Cuándo utilizar require_auth

La principal decisión relacionada con la autorización que un escritor de contratos necesita tomar para cualquier Address dado es si necesita llamar a require_auth para él. Si bien la decisión tiene que tomarse caso por caso, aquí hay algunas reglas generales:

    • Si el acceso a los datos del Address en este contrato es solo de lectura, entonces probablemente no se necesite require_auth.
  • If the Address data in this contract is being modified in a way that's not strictly beneficial to the user, then require_auth is probably needed (e.g. reducing the user's token balance needs to be authorized, while increasing it doesn't need to be authorized)
    • Si un contrato llama a otro contrato que llamará a require_auth para el Address (por ejemplo, token.xfer), entonces agregar require_auth en el llamador asegurará que la autorización para la llamada interna no pueda ser reutilizada fuera de tu contrato. Por ejemplo, si quieres hacer algo positivo para el usuario, pero solo cuando haya transferido algún token a tu contrato, entonces la llamada al contrato en sí debería require_auth.

#### Autorización de Múltiples Addresses

No hay una restricción explícita sobre cuántas entidades Address utiliza el contrato y cuántas Addresses tienen require_auth llamado. Eso significa que es posible autorizar una llamada a contrato en nombre de múltiples usuarios, que pueden incluso tener diferentes contextos de autorización (personalizados a través de argumentos en require_auth_for_args). [Intercambio atómico] es un ejemplo que trata sobre la autorización de dos Addresses.

Nota que los contratos que manejan múltiples Address autorizados necesitan un soporte un poco más complejo en el lado del cliente (para recoger y adjuntar las firmas adecuadas).

### Abstracción de Cuentas

La abstracción de cuentas es una forma de desacoplar la lógica de autenticación de las reglas de autorización específicas del contrato. El Address definido anteriormente es, de hecho, un identificador de una cuenta 'abstracta'. Es decir, los contratos conocen el Address y pueden requerir autorización de él, pero no saben cómo exactamente está implementado.

Por ejemplo, imagina un contrato de token. Sus responsabilidades son gestionar los saldos de múltiples usuarios (transferir, acuñar, quemar, etc.). Realmente no hay nada sobre estas responsabilidades que tenga que ver con cómo exactamente el usuario autorizó la transacción que modifica el saldo. Los usuarios pueden querer utilizar alguna clave de hardware que soporte una nueva generación de algoritmos criptográficos (que ni siquiera tienen que existir hoy en día) o pueden querer tener un esquema de multisig personalizado y nada de esto realmente tiene que ver con la lógica del token.

La abstracción de cuentas proporciona un punto de extensión conveniente para cada contrato que utiliza Address para autorización. No resuelve automáticamente todos los problemas: las herramientas del lado del cliente aún pueden necesitar ser adaptadas para soportar diferentes esquemas de autenticación o diferentes billeteras. Pero el estado on-chain no necesita ser modificado y modificar el estado on-chain es un problema mucho más difícil.

#### Tipos de Implementaciones de Cuentas

Conceptualmente, cada cuenta abstracta es un contrato especial que define reglas de autenticación y potencialmente algunas políticas de autorización específicas de cuenta adicionales. Sin embargo, con el fin de optimizar e integrar con las cuentas Stellar existentes, Soroban admite 4 tipos diferentes de implementaciones de cuentas.

A continuación se presentan las descripciones generales de estas implementaciones. Consulta la guía de transacciones para obtener información concreta sobre cómo se representan las diferentes cuentas.

##### Cuenta Stellar

Corresponde a Address::Account.

Este es un 'contrato de cuenta' especial incorporado que maneja todas las cuentas Stellar. No es un contrato real y no necesita ser desplegado.

Esto soporta el multisig Stellar con umbral medio. Consulta la documentación de Stellar para obtener más detalles sobre el multisig y los umbrales.

##### Invocador de Transacciones

Corresponde a Address::Account.

Esta también es una cuenta Stellar, pero su firma se infiere de la cuenta de origen de la transacción Stellar (o operación, si tiene una).

Esta es puramente una optimización de la Cuenta Stellar que puede omitir una firma en caso de que la cuenta de origen de la transacción también autorice la invocación del contrato.

##### Invocador de Contrato

Corresponde a Address::Contract.

Este es un caso especial de una 'cuenta' que puede aparecer solo cuando un contrato llama a otro contrato. Consideramos que, dado que el contrato realiza una llamada, entonces debe estar autorizando la misma (de lo contrario, no debería haber hecho esa llamada). Por lo tanto, todas las llamadas a require_auth realizadas en nombre del contrato Address de la directa invocador se consideran autorizadas (pero no se consideran autorizadas las llamadas en nombre del contrato más profundo en la pila).

##### Cuenta Personalizada

Corresponde a Address::Contract.

Este es el punto de extensión de la abstracción de cuentas. La cuenta personalizada es un contrato especial que implementa el método __check_auth. Si algún contrato llama a require_auth para el Address de este contrato, el host de Soroban llamará a __check_auth con los argumentos correspondientes.

__check_auth recibe una carga útil de firma, una lista de firmas (en cualquier formato definido por el usuario) y una lista de las invocaciones de contrato que están siendo autorizadas por estas firmas. Su responsabilidad es realizar la autenticación verificando las firmas y también (opcionalmente) aplicar una política de autorización personalizada. Por ejemplo, se puede implementar un sistema de pesos de firma similar al de Stellar, pero también puede tener reglas personalizables para los pesos, por ejemplo, para permitir gastar más de X unidades del token Y solo dado un peso de firma Z.

La cuenta personalizada también puede ser tratada como una billetera inteligente custodial. Sostiene los fondos del usuario (saldos de tokens, NFTs, etc.) and provides the user(s) with ways to authorize operations on these funds. Dicho esto, nada impide que las cuentas personalizadas autoricen operaciones que no tengan que ver con ningún saldo; por ejemplo, puede utilizarse para realizar funciones administrativas para tokens (no olvides, la cuenta personalizada simplemente define qué hacer cuando se llama a require_auth).

Para la interfaz exacta y más detalles, consulta el [ejemplo de cuenta personalizada].

### Secp256r1, Claves de Paso y Billeteras Inteligentes

Después de una exitosa votación de validadores públicos para actualizar el Mainnet de Stellar al Protocolo 21, se habilitó el esquema de firma secp256r1 para las transacciones de contratos inteligentes. Esto permite a los desarrolladores implementar claves de paso para firmar transacciones en lugar de utilizar claves secretas o frases semilla. La documentación oficial está en progreso, consulta toda la información sobre claves de paso y billeteras inteligentes en la página de Billeteras Inteligentes en la documentación.

### Conceptos Avanzados

La mayoría de los contratos no deberían necesitar los conceptos descritos en esta sección. Consulta esto al desarrollar contratos complejos que traten con árboles de llamadas a contratos profundos y/o múltiples Addresses.

#### Detalles de implementación de require_auth

Cuando se ejecuta una transacción de Soroban on-chain, el host recopila una lista de entradas SorobanAuthorizationEntry de la transacción (XDR). Estas entradas contienen credenciales de autorizar firmadas y árboles de invocación autorizados. El host utiliza estas entradas para verificar la autorización durante la ejecución del contrato.

Cada vez que se llama a la función del host require_auth/require_auth_for_args para una cuenta que no es invocador del contrato, ocurren los siguientes pasos:

    • Encontrar un árbol de invocación autorizado que coincida con la llamada require_auth. El proceso de coincidencia es bastante complicado y se describe en la sección a continuación.
    • Si la autenticación no ha ocurrido aún para este árbol, entonces realízala:
    • Verifica la caducidad de la firma. Las firmas caducadas no son válidas.
    • Verifica y consume la nonce. Nonce es un número arbitrario que debe ser único entre todas las firmas no caducadas de la dirección.
    • Build the expected signature payload preimage and compute its SHA-256 hash to get the final signature payload
    • Call __check_auth of the account contract corresponding to the Address using the signature payload and the invocations from the authorization tree
  • Marca la invocación como 'agotada' en su árbol de invocación autorizado. 'Invocaciones agotadas' se omitirán al coincidir con futuras llamadas require_auth.

Si alguno de los pasos anteriores falla, entonces la autorización se considera fallida.

Ten en cuenta que la autenticación ocurre solo una vez por árbol, ya que todo el árbol necesita ser firmado.

#### Coincidencia de Árboles de Invocación Autorizada

Para que las autorizaciones tengan éxito, todas las llamadas require_auth/require_auth_for_args deben estar cubiertas por los árboles de SorobanAuthorizedInvocation correspondientes en una transacción (definidos en [XDR] de transacción invocation-xdr).

Formalmente, esta correspondencia se define de la siguiente manera.

Dados una invocación de contrato de nivel superior I, podemos construir un 'árbol de invocación de contrato' T rastreando todas las llamadas a subcontratos (un borde dirigido A->B en el árbol significa 'la función de contrato A llama a la función de contrato B). Ten en cuenta que solo consideramos las funciones que están implementadas en diferentes contratos; es decir, cualquier llamada de función que no involucre una invocación de contrato a través del host call se considera perteneciente al mismo nodo.

Digamos que se requiere autorización de las direcciones A_1..A_N. Luego, para cada dirección A_i, hay dos tipos de nodos en el árbol de invocación T: nodos R que tuvieron una llamada a require_auth para A_i y nodos N que no tuvieron tal llamada. Luego, eliminamos todos los nodos N y todos los bordes de T y agregamos los bordes dirigidos que conectan los nodos R restantes, de modo que el borde vaya de R_j a R_k si hubo un camino entre R_j y R_k en T que no contenga ningún otro nodo R. Como resultado, obtenemos un bosque de árboles SorobanAuthorizedInvocation para A_i. Ten en cuenta que estos árboles no tienen que tener su raíz como nodo I (es decir, la llamada al contrato de nivel superior), por lo que es posible, por ejemplo, agrupar la llamada autorizada sin requerir la firma de la función de agrupación.

En términos más simples, los árboles SorobanAuthorizedInvocation para un Address son subconjuntos del árbol de invocación completo que están 'condensados' para contener solo invocaciones que tienen llamada a require_auth para ese Address.

Durante el proceso de coincidencia que ocurre para cada require_auth, el host intenta hacer coincidir el camino actual en T con un árbol SorobanAuthorizedInvocation para el Address correspondiente. El camino se considera coincidente solo cuando hay un camino correspondiente de nodos R agotados que conduce a la llamada actual. Esto significa que si el Address firma una secuencia de llamadas A.foo->B.bar->C.baz, entonces su verificación de autorización fallará en caso de que A.foo llame directamente a C.baz porque C.baz debe ser llamado estrictamente desde B.bar.

##### Direcciones Duplicadas

En caso de que la misma función de contrato llame a require_auth para la misma Address múltiples veces (por ejemplo, cuando se agrupan múltiples operaciones del mismo usuario), cada llamada a require_auth aún debe tener un nodo correspondiente en el árbol SorobanAuthorizedInvocation. Debido a esto, puede haber múltiples árboles válidos que hagan que todas las verificaciones de autorización pasen. No hay nada de malo en eso: la dirección aún debe haber autorizado todas las invocaciones. El único requisito para que tales casos se manejen correctamente es asegurarse de que las llamadas require_auth para un Address ocurran antes de las llamadas al subcontrato correspondientes.