Pruebas con imagen de ledger
Las imágenes de ledger se pueden usar para probar smart contracts con una copia local de las entradas del ledger. Aprende a crear una imagen de ledger y usarla para pruebas de smart contracts en unos pocos pasos simples. Los ejemplos aquí cubrirán los siguientes casos de uso:
- Leer el valor almacenado de los datos del ledger
- Leer un valor almacenado llamando a una función del smart contract
- Leer el saldo de una cuenta
1. Crear un proyecto por defecto
Antes de crear una imagen de ledger y comenzar con las pruebas, primero creemos un smart contract simple que pueda usarse para los ejemplos de prueba.
Comienza creando el contrato Hello World por defecto:
stellar contract init <project_name>
El smart contract tiene dos funciones: set_value()
para almacenar un valor, y get_value()
para leer el valor almacenado. Reemplaza la función hello()
por defecto con estas dos funciones:
#![no_std]
use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec};
#[contract]
pub struct Contract;
#[contractimpl]
impl Contract {
pub fn set_value(env: Env, key: String, value: u32) {
env.storage().persistent().set(&key, &value);
}
pub fn get_value(env: Env, key: String) -> u32 {
env.storage().persistent().get(&key).unwrap_or(0)
}
}
Dado que queremos escribir una prueba que lea datos almacenados en el ledger, o más precisamente, en una imagen del ledger, debemos crear, desplegar e invocar el contrato inteligente.
Crear contrato
Usa el Stellar CLI para crear el contrato:
stellar contract build
Desplegar contrato
A continuación, desplegamos el contrato en Testnet:
stellar contract deploy \
--wasm target/wasm32v1-none/release/hello_world.wasm \
--source alice \
--network testnet
El comando deploy devolverá la dirección del contrato (p. ej., CB5QCALXDP2N6H473AQBNIFEAPCNHWCIWOASRNGTHCSC4WNC3SOROBAN
), la cual se usará en los siguientes pasos.
Invocar contrato
Finalmente, invocamos la función set_value() para almacenar un valor en el ledger de Testnet:
stellar contract invoke \
--id CB5QCALXDP2N6H473AQBNIFEAPCNHWCIWOASRNGTHCSC4WNC3SOROBAN \
--source alice \
--network testnet \
-- \
set_value \
--key "count" --value 123456
Ahora tenemos una función de contrato inteligente desplegada y en funcionamiento que ha almacenado un valor en el ledger. Puedes verificar el valor invocando la función get_value()
.
2. Crear una imagen del ledger
Se puede crear una imagen del ledger usando el Stellar CLI. El comando CLI permite personalizar y limitar el alcance de la imagen, ya que probablemente no sea necesario crear una imagen de todas las entradas del ledger. Consulta la documentación para obtener detalles completos sobre cómo limitar la imagen.
Para los ejemplos aquí usados, queremos limitar la imagen del ledger para incluir las entradas relacionadas con:
- El contrato inteligente
- El usuario
alice
- La dirección del token nativo (XLM)
Desde la raíz del proyecto del contrato inteligente, crea la imagen del ledger con este comando:
stellar snapshot create \
--output json \
--network testnet \
--address CB5QCALXDP2N6H473AQBNIFEAPCNHWCIWOASRNGTHCSC4WNC3SOROBAN \
--address alice \
--address CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
El comando almacenará la imagen como un archivo JSON llamado snapshot.json
.
Este es un ejemplo de la imagen, que muestra el valor almacenado cuando se invocó la función del contrato set_value()
:
{
"protocol_version": 23,
"sequence_number": 801343,
"timestamp": 0,
"network_id": "cee0302d59844d32bdca915c8203dd44b33fbb7edc19051ea37abedf28ecd472",
"base_reserve": 1,
"min_persistent_entry_ttl": 0,
"min_temp_entry_ttl": 0,
"max_entry_ttl": 0,
"ledger_entries": [
...
[
{
"contract_data": {
"contract": "CB5QCALXDP2N6H473AQBNIFEAPCNHWCIWOASRNGTHCSC4WNC3SOROBAN",
"key": {
"string": "count"
},
"durability": "persistent"
}
},
[
{
"last_modified_ledger_seq": 801330,
"data": {
"contract_data": {
"ext": "v0",
"contract": "CB5QCALXDP2N6H473AQBNIFEAPCNHWCIWOASRNGTHCSC4WNC3SOROBAN",
"key": {
"string": "count"
},
"durability": "persistent",
"val": {
"u32": 123456
}
}
},
"ext": "v0"
},
4294967295
]
],
]
...
}
Ahora podemos empezar a escribir un script de prueba que cargue la imagen en el entorno de prueba.
Si se vuelve a invocar la función del contrato inteligente set_value()
, ejecuta nuevamente el comando stellar snapshot create
para garantizar que la imagen refleje el ledger.
3. Leer valores almacenados y balances
Para ilustrar cómo las pruebas de contratos inteligentes pueden utilizar los datos de la imagen del ledger, leeremos el valor almacenado de dos maneras distintas y obtendremos el balance del usuario alice
.
Cargar datos de la imagen
Como punto de partida, usamos el archivo de prueba test.rs
incluido en el proyecto de contrato inteligente predeterminado que creamos y modificamos. Dado que la función hello()
fue eliminada y reemplazada por las funciones para almacenar y obtener un valor, la prueba para hello()
también puede eliminarse.
Primero, se cargan los datos de la imagen del ledger en el entorno:
#[test]
fn test() {
let env = Env::from_ledger_snapshot_file(
"../../snapshot.json",
;
}
Leer valor almacenado del ledger
En este primer ejemplo, leemos el valor almacenado directamente del ledger usando env.storage()
. El método env.as_contract()
nos permite ejecutar código en el contexto de un ID de contrato dado. Esto significa que podemos ejecutar código como si estuviéramos dentro del contrato.
Primero, definimos el ID del contrato para la prueba, que es el ID devuelto cuando se desplegó el contrato inteligente.
Luego, env.as_contract()
se usa para obtener el valor del almacenamiento, es decir, del ledger. Cuando se invocó la función del contrato set_value()
, usamos count
como clave, así que se usa la misma clave para obtener el valor.
#[test]
fn test() {
let env = Env::from_ledger_snapshot_file(
"../../snapshot.json",
);
let contract_id = Address::from_str(
&env,
"CB5QCALXDP2N6H473AQBNIFEAPCNHWCIWOASRNGTHCSC4WNC3SOROBAN"
);
env.as_contract(&contract_id, || {
let val: u32 = env.storage().persistent().get(
&String::from_str(&env, "count")
).unwrap_or(0);
assert_eq!(val, 123456);
});
}
Ahora ejecuta la prueba:
cargo test
El valor esperado es 123456
, y el valor se verifica con assert_eq!()
. Si la prueba no pasa, esta fallará con pánico.
Leer valor almacenado con función del contrato
La forma más típica de leer un valor almacenado en pruebas es llamar a la función del contrato que lee el valor. La prueba para este enfoque es la misma que si se cargara el entorno predeterminado, excepto que no necesitamos registrar el contrato en la prueba; usamos un ID real de contrato, como en el ejemplo anterior, porque en la imagen del ledger, el valor está vinculado a un ID de contrato específico.
#[test]
fn test() {
let env = Env::from_ledger_snapshot_file(
"../../snapshot.json",
);
let contract_id = Address::from_str(
&env,
"CB5QCALXDP2N6H473AQBNIFEAPCNHWCIWOASRNGTHCSC4WNC3SOROBAN"
);
let client = ContractClient::new(&env, &contract_id);
let value: u32 = client.get_value(&String::from_str(&env, "count"));
assert_eq!(value, 123456);
}
Este ejemplo muestra que solo hay diferencias menores entre probar funciones de contratos con el entorno predeterminado y el entorno de la imagen.
Leer balance de cuenta
En este último ejemplo, queremos leer el balance de una cuenta. Puede ser el balance de una cuenta de usuario o de un contrato inteligente; cualquier cuenta que pueda tener un balance. En este ejemplo, verificamos el balance de la cuenta del usuario alice
.
En este ejemplo, asumimos que alice
ya tiene un balance en XLM, y dado que los ejemplos se basan en Testnet, la cuenta alice
fue financiada por FriendBot si se siguió esta guía al crear la cuenta. La dirección de alice
se puede consultar ejecutando este comando:
stellar keys address alice
Los tokens, como XLM, están envueltos en una interfaz de contrato llamada Stellar Asset Contract (SAC). Esta interfaz proporciona una forma conveniente de consultar el balance de una cuenta. El primer paso para obtener el balance de la cuenta es crear un cliente de contrato para los tokens XLM.
La dirección SAC para XLM es una dirección reservada y será la misma para todos los proyectos. USDC y otros activos tendrán otra dirección SAC, pero estas no cambian. Usamos la dirección SAC para definir un cliente usando TokenClient
.
Luego definimos la dirección de la cuenta cuyo balance queremos consultar, y llamamos a client.balance()
con la dirección de la cuenta como argumento.
#[test]
fn test() {
let env = Env::from_ledger_snapshot_file(
"../../snapshot.json",
);
let client = TokenClient::new(
&env,
&Address::from_string(&String::from_str(
&env,
"CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
))
);
let account = Address::from_string(&String::from_str(
&env,
"GDKOYSOKU4TNHGGR763NOL6VNY52WKJL3XI33TOUKDNF4AZN7SOROBAN"
));
assert_eq!(client.balance(&account), 99602342515);
}
La cuenta puede ser un usuario, como en este caso, pero también puede ser un contrato que posea activos, como una billetera de contrato inteligente.
Guías en esta categoría:
📄️ Pruebas Unitarias
Las pruebas unitarias son pruebas pequeñas que prueban contratos inteligentes.
📄️ Simulación
Simulación de contratos dependientes en pruebas.
📄️ Prueba de Autorización
Escribe pruebas que verifiquen la autorización del contrato.
📄️ Eventos de Prueba
Escribe pruebas que verifiquen los eventos de contrato.
📄️ Pruebas de Integración
Las pruebas de integración utilizan contratos de dependencia en lugar de simulaciones.
📄️ Pruebas de Fork
Pruebas de integración utilizando datos de mainnet.
📄️ Fuzzing
Fuzzing y pruebas de propiedades para encontrar comportamientos inesperados.
📄️ Pruebas Diferenciales
Las pruebas diferenciales detectan cambios no intencionados.
📄️ Pruebas Diferenciales con Imágenes de Prueba
Prueba diferencial utilizando imágenes de prueba automáticas.
📄️ Pruebas de Mutación
La prueba de mutación encuentra código no probado.
📄️ Cobertura de Código
Las herramientas de cobertura de código encuentran código no probado.
📄️ Pruebas con imagen de ledger
Usa imágenes de ledger para probar contratos con datos del ledger