Persistir datos
Entradas del ledger
Los contratos pueden acceder a entradas del ledger de tipo CONTRACT_DATA
. Se proporcionan funciones del host para sondear, leer, escribir y eliminar entradas del ledger CONTRACT_DATA
.
Cada entrada del ledger CONTRACT_DATA
está indexada en el ledger por el ID del contrato que la posee, su tipo de almacenamiento (Persistent
, Temporary
, Instance
) así como un único valor elegido por el usuario, del tipo de valor estándar. Esto significa que la clave elegida por el usuario puede ser un valor simple como un símbolo, número o blob binario, o puede ser un valor estructurado más complejo como un vector o mapa con múltiples subvalores.
Cada entrada del ledger CONTRACT_DATA
también mantiene (además de su clave) un solo valor asociado con la clave. De nuevo, este valor puede ser simple como un símbolo o número, o puede ser complejo como un vector o mapa con muchos subvalores.
No se requiere serialización o deserialización en el código del contrato al acceder a las entradas del ledger CONTRACT_DATA
: el host automáticamente serializa y deserializa cualquier entrada del ledger accedida, intercambiándola con el contrato como valores deserializados. Si un contrato desea utilizar un formato de serialización personalizado, puede almacenar una entrada del ledger CONTRACT_DATA
con valor binario y proporcionar su propio código para serializar y deserializar, pero Soroban ha sido diseñado con la intención de minimizar la necesidad de que los contratos hagan esto.
Control de Acceso
Los contratos solo pueden leer y escribir entradas del ledger CONTRACT_DATA
que son propiedad del contrato: aquellas indexadas por el mismo ID de contrato que el contrato que realiza la lectura o escritura. Intentar acceder a otras entradas del ledger CONTRACT_DATA
hará que una transacción falle.
No hay control de acceso para las operaciones de extensión de TTL. Cualquier usuario puede invocar ExtendFootprintTTLOp
en cualquier LedgerEntry.
Granularidad
Una entrada del ledger CONTRACT_DATA
se lee o se escribe desde el ledger en su totalidad; no hay forma de leer o escribir "solo una parte" de una entrada del ledger CONTRACT_DATA
. También hay un costo fijo adicional al acceder a cualquier entrada del ledger CONTRACT_DATA
. Por lo tanto, los contratos son responsables de dividir lógicamente estructuras de datos "grandes" en "piezas" con una granularidad de tamaño apropiada, para usar al leer y escribir. Si las piezas son demasiado grandes, puede haber costos innecesarios por leer y escribir datos no utilizados, así como contendencia innecesaria en la ejecución paralela; pero si las piezas son demasiado pequeñas, puede haber costos innecesarios por el costo fijo de cada entrada.
Huella y contendencia paralela
Los contratos solo pueden acceder a entradas del ledger especificadas en la huella de su transacción. Las transacciones con huellas superpuestas se dice que contienden, y solo se ejecutarán secuencialmente entre sí, en un solo hilo. Las transacciones con huellas no superpuestas pueden ejecutarse en paralelo. Esto significa que una granularidad más fina de las entradas del ledger CONTRACT_DATA
puede reducir la contendencia artificial entre transacciones que utilizan un contrato, y así aumentar el paralelismo.
Prácticas recomendadas para datos de contrato
Estado de cuenta vs. Estado compartido
Si bien no hay distinción entre el estado de "cuenta" y el estado "compartido" a nivel de protocolo, puede ser útil pensar en los datos en estos términos al decidir qué tipo de almacenamiento y estrategia de extensión de TTL usar para un caso de uso determinado. Como pauta, el estado de cuenta y el estado compartido se pueden describir de la siguiente manera:
- La mayoría del estado del contrato puede asociarse con una cuenta específica o compartirse entre múltiples interesados ("bien público")
- Estado específico de la cuenta
- Balances, posiciones, asignaciones, etc.
- Estado compartido
- Entrada de administrador, valores de piscina, etc.
- Estado específico de la cuenta
- Hay dos subcategorías de estado compartido
- "Estado global" compartido por todos los usuarios del contrato
- Instancia de contrato, contrato wasm, o un administrador global
- Estado compartido solo por un subconjunto específico de usuarios
- Valores de piscina de AMM En un contrato monolítico de AMM (estilo Uniswap V4)
- "Estado global" compartido por todos los usuarios del contrato
- Nota: A veces, el estado de cuenta y el estado compartido pueden fusionarse cuando el alcance del contrato es pequeño
- Es decir. billetera inteligente o un solo nft, donde se genera una nueva instancia de contrato para cada cuenta
Contratos con propietario vs. Contratos autónomos
Además de los tipos de estado, también es útil considerar el tipo de instancia de contrato que se está utilizando. De nuevo, estos tipos no se aplican a nivel de protocolo, pero pueden ser útiles.
- Contratos con propietario
- Instancias de contrato que tienen un propietario claro
- Una billetera inteligente o un solo nft, donde se genera una nueva instancia de contrato para cada cuenta
- Activos respaldados por reservas personalizados (USDC, etc.)
- Instancias de contrato que tienen un propietario claro
- Contratos autónomos
- Instancia de contrato que no tiene un propietario claro, o tiene un grupo descentralizado de propietarios
- La mayoría de los protocolos DeFi, especialmente los que no son actualizables
- Instancia de contrato que no tiene un propietario claro, o tiene un grupo descentralizado de propietarios
Prácticas recomendadas
- Preferir
Temporary
sobre almacenamientoPersistent
eInstance
- Cualquier cosa que pueda tener un tiempo de espera debe ser
Temporal
con el TTL establecido en el tiempo de espera. Ver la tabla de límites de recursos para el máximo actual de TTL/tiempo de espera. Consulta la tabla de límites de recursos para el máximo actual de TTL/tiempo de espera. - Ejemplo: Las firmas de autenticación de Soroban tienen un ledger de expiración absoluto, por lo que los nonces se pueden almacenar en entradas
Temporary
sin riesgos de seguridad- Ejemplo: las firmas de autenticación de Soroban tienen un ledger de caducidad absoluto, por lo que los nonces se pueden almacenar en entradas
Temporales
sin riesgos de seguridad - Ejemplo: La asignación de SAC que vive solo hasta un ledger dado (para que alguna firma de asignación antigua no se pueda utilizar en el futuro si no se agota)
- Ejemplo: las firmas de autenticación de Soroban tienen un ledger de caducidad absoluto, por lo que los nonces se pueden almacenar en entradas
- Cualquier cosa que pueda tener un tiempo de espera debe ser
- Todo estado global que no pueda ser
Temporary
debe estar en almacenamientoInstance
- Esto garantiza que el TTL de la instancia de contrato y todos los globals relevantes estén atados juntos
- Dado que el estado global se utiliza a menudo, esto asegura que el estado global nunca necesite ser restaurado, lo que lleva a invocaciones de contrato más baratas y eficientes.
- Los contratos autónomos deben extender el TTL de cualquier estado compartido tocado por una invocación a través de la función de host
extend_ttl()
- Dado que estos contratos no tienen propietarios, dejar la extensión de TTL a clientes benevolentes podría llevar a una situación de “tragedia de los comunes”
- La mayoría de los usuarios no extienden el TTL porque no es necesario, pero aún se benefician del cliente benevolente que sí extiende el TTL.
- Los propietarios de contratos con propietario deben subsidiar las tarifas de extensión de TTL del estado compartido al enviar manualmente operaciones de extensión.
- Los propietarios deben llevar un registro de los TTL de todo el estado compartido.
- Esto podría implementarse a través de algo como un trabajo cron donde se envía periódicamente un
ExtendFootprintTTLOp
para todo el estado compartido relevante (es decir, una vez al mes) - Alternativamente, esto también podría implementarse a través de una función de contrato inteligente de administración llamada rutinariamente por el propietario.
- Los clientes (Billeteras/Dapps) deben identificar el estado que se relaciona con su respectiva cuenta, presentar información de TTL a los usuarios y sugerir extensiones de TTL cuando sea necesario.
- Nunca debe dependerse de las extensiones de TTL para funcionalidad o seguridad.
- Ejemplo no seguro: Una entrada debe ser permanente, pero en lugar de usar almacenamiento
Persistente
, un contrato utiliza almacenamientoTemporal
pero extenderá continuamente la entrada para que siempre esté en vivo.- Debido a que no hay una interfaz automática para la extensión de TTL, y cada extensión de TTL debe provenir de una invocación de contrato inteligente o de un
ExtendFootprintTTLOp
, debe asumirse que el TTL de una entrada puede llegar a 0 y la entrada ser eliminada. - Las tarifas de extensión de TTL son variables, y puede volverse demasiado caro extender el TTL de entradas
Temporal
pseudo “persistentes” si las tarifas aumentan inesperadamente. - Si el proceso de extensión de TTL es automatizado (es decir, un trabajo cron), la cuenta que envía automáticamente los
ExtendFootprintTTLOps
puede quedarse sin fondos inesperadamente si las tarifas de extensión de TTL aumentan, causando que la entradaTemporal
agote su TTL y sea eliminada permanentemente.
- Debido a que no hay una interfaz automática para la extensión de TTL, y cada extensión de TTL debe provenir de una invocación de contrato inteligente o de un
- Ejemplo no seguro: Una entrada debe ser permanente, pero en lugar de usar almacenamiento
- La agotamiento de TTL de la entrada nunca debería confiarse para funcionalidad o seguridad.
- Ejemplo no seguro: un nonce debe expirar en 7 días, por lo que un contrato crea una entrada
Temporary
y extiende el TTL a 7 días sin otra imposición de duración en su lugar. - Cualquiera puede enviar una operación de extensión de TTL sobre cualquier entrada sin autorización, lo que significa que cualquier usuario puede extender indefinidamente su TTL.
- Si una entrada necesita ser invalidada después de un período de tiempo determinado, esto debe implementarse manualmente por el contrato.
- Ejemplo no seguro: un nonce debe expirar en 7 días, por lo que un contrato crea una entrada