Parceros
  • 👽Recursos
    • 💻Nodos validadores
      • Buenas prácticas y seguridad de un validador
      • Montaje de nodo validador de Stargaze
      • Montaje de nodo validador de Tgrade
      • Montaje de nodo validador de Juno
      • Instalacion del Cosmovisor
      • Instalación de AutoCompound
      • Guía de instalación Prometheus y Grafana para un Validador
    • 🎓Desarrollo
      • Fundamentos en Gnu/Linux
      • Fundamentos en Blockchain
      • Billeteras
      • Introducción a Cosmos Hub
      • Guia de Inicio en Rust
      • Clases de CosmWasm
        • Introducción a CosmWasm
        • Puntos de entrada de un contrato vacio
        • Consultas - Query
      • Clases de Rust
        • Introducción a Rust
        • Ciclos
        • Funciones
        • Manejo de la memoria
        • Tipos de datos avanzados
        • Macros
        • Manejo de paquetes
        • Manejo de errores
      • Guia Archway
        • Instalación de requisitos
        • Configuración del proyecto
        • Solicitud de tokens Testnet
        • Mi primera app
          • Configuracion
          • Produciendo ejecutables wasm
          • Despliegue e instanciación de contratos en la cadena
          • Interactuar con su contrato
          • Construir el frontend de la dApp
        • Proyecto NFT
          • Creando un proyecto NFT
          • Despliegue del contrato de tokens
          • Acuñación y envío de tokens
          • Construye la Dapp NFT
        • Fee Grant
          • Comprendiendo los Fee grant
          • Grant asignación
          • Utilizando asignaciones grant
        • Multifirmas
          • Archway multi firma hub
          • Navegar por la interfaz multi firmas
      • Cosmwasm Documentacion
        • Introducción
        • Primeros pasos
          • Introducción
          • Configuración del entorno
          • Elaborar un contrato
          • Test Unitarios
          • Despliegue e interacción
          • Integración con contratos inteligentes
          • Próximos pasos
        • Arquitectura
          • ¿Qué son los contratos multicadena?
          • Modelo de actor para las convocatorias de contratos
          • Nombres y direcciones
          • Consulta del estado del contrato
          • Formatos de serialización
          • Composición del contrato
          • Comparación con los contratos de solidity
        • Contratos inteligentes
          • Semántica contractual
          • Message
            • Messages
            • Submensajes
          • State
            • Simple state
            • Complex state y maps
          • Result y option
          • Entry points
          • Query
          • Events
          • Math
          • Verificación de contratos inteligentes
          • Migration
          • Migrar una dapp a una red diferente
          • Testing
          • Ejecución Sudo
          • CosmWasm y CIB
        • Tutoriales
          • Opcion simple
            • Testing
          • Storage
            • ¿Cómo funciona el almacenamiento de valores clave?
            • Índices
            • Modelización avanzada de estados
          • Cosmwasm con ejemplos
            • Operaciones matemáticas de Cosmowasm
            • Crear una instancia de un contrato CosmWasm
            • Timelock
            • Contrato Crowdfunding
            • Respuestas y atributos en Cosmwasm
            • Lee y escribe
            • Envío de tokens
            • Token Vaults
            • Creador de mercado automático de productos constantes (AMM)
      • Guia Celestia
        • Descripcion general de celestia
          • Introduccion
          • Blockchains monolíticos vs modulares
          • Capa de disponibilidad de datos
            • La capa de disponibilidad de datos de Celestia
            • El ciclo de vida de una transacción celestia-app
            • Recuperabilidad de datos y poda
            • Disponibilidad de datos FAQ
          • Recursos adicionales
            • Aprende modular
            • Glosario de Celestia
            • Especificaciones de aplicación de celestes
            • Documentación API de nodo celestial
        • Ejecutar un nodo
          • Descripción general de los nodos en ejecución en Celestia
          • Guía de inicio rápido
            • Decidir qué nodo ejecutar
            • Entorno de desarrollo
            • Instalar celestia-node
            • Instalar celestia-app
            • 🐳 Configuración de Docker
          • Redes
            • Resumen de redes
            • Mainnet Beta
            • Mocha testnet
            • Arábica devnet
          • Tipos de nodos
            • Disponibilidad de datos
              • Nodo ligero
              • Nodo completo
              • Nodo puente
            • Consenso
            • Relay de IBC
              • Guía de retransmisión IBC
              • Relays de IBC
          • Recursos
            • nodo-celestia
              • Metricas
              • guía config.toml
              • Redes y valores personalizados
              • Solución de problemas
            • celestia-app
              • Especificaciones
              • Métricas, visualización y alertas
              • Mecánica de corte
              • Crear un testnet Celestia
              • Comandos CLI útiles
              • Monitor de Actualización
              • Carteras en celestia-app
              • Multisig
              • Crea una cuenta de adquisición
            • SystemD
            • Proceso de hardfork
        • Desarrolladores
          • Construir modular
          • Envío de blobs de datos a Celestia
          • Directrices de reenvío de transacciones
          • API de nodo
            • Celestia-node RPC CLI tutorial
            • Documentación de la API RPC de Celestia-Node
            • Rápido Scavenger
            • Page
          • Integrar con Blobstream
            • Descripción general de Blobstream
            • Integrarse con contratos de Blobstream
            • Integrar con el cliente Blobstream
            • Consultando las pruebas de Blobstream
            • Operadores locales de Blobstream X
              • Solicitar rangos de compromiso de datos
              • Nuevas implementaciones de Blobstream X
          • Implementar un rollup
            • L2s Ethereum
              • Ethereum fallback
              • Arbitro
                • Introducción a los rollups de Arbitrum con Celestia como DA
                • Implementar un arbitrum rollup devnet
                • Testnet de nitrógeno
                • Implementar un contrato inteligente sobre la implementación de Arbitrum
                • Implemente un dapp en su devnet Arbitrum rollup
                • Optimismo
                  • Introducción a la integración de OP Stack
                  • Bubs testnet
                  • Implemente un contrato inteligente en Bubs testnet
                  • Implemente un dapp en Bubs testnet
                  • Implemente un devnet OP Stack
                  • Implemente un devnet OP Stack en Celestia
                  • Auditoría
                  • Implemente un dapp con thirdweb
                  • Rollups-as-a-Servicio
                    • Caldera
            • Rollkit
            • Astria
              • Documentación
              • Implementar a Dusknet
            • SDK Soberano
            • Vistara
            • Dimensión
          • Carteras
            • Crea una billetera con celestia-node
            • Integraciones de billeteras con Celestia
          • Integre Celestia para proveedores de servicios
      • Unión
        • Arquitectura
          • CometBLS
          • Galois
          • Voyager
        • Conceptos
          • BLS Firmas
          • Clientes de Luz Condicional
          • Verificación de Consenso
          • Tecnología de validador distribuido
          • IBC
          • Sin permiso versus sin confianza
        • Infraestructura
          • Operador de nodo
            • Empezando
            • Docker Compose
            • Kubernetes
            • NixOS
            • Configuración del Nodo
        • Integracion
          • IBC Enabled Solidity
        • Demostrar
          • Dirigiendo el Union Devnet
          • PingPong
        • Unirse al testnet
          • Empezando
          • Ejecutar el binario cliente
          • Ejecución de Unionvisor
          • Obteniendo Tokens Testnet
          • Crear un Validador
          • Endpoints publicos
          • Sincronización de estado
          • Liberar un validador
          • Preguntas frecuentes
          • Historial de actualizaciones
        • Guia de estilo
          • Lista de palabras
      • Avail
        • Introducción a Avail
          • Aprovechar DA
          • Aprovechar Nexus
          • Aprovechar la fusión
        • Informacion de red
        • Más información sobre disponibilidad
          • El conseso
            • BABE
            • GRANDPA
            • NPoS
          • EIP-4844 y disponible
        • Guia de nuevo usuario
          • Cómo crear y administrar una cuenta disponible
          • Cómo utilizar el Explorador Goldberg Testnet
          • Cómo utilizar el faucet Testnet
          • Cómo establecer una identidad en cadena
          • Cómo generar una identificación de aplicación disponible
          • Cómo realizar transferencias de saldo disponibles
          • Cómo crear grupos de nominaciones disponibles
        • Construir con disponibilidad
          • Cree un paquete acumulativo con Avail
          • Comience con Avail
          • Optimium
            • OP Stack
            • Aprovechando la pila OP con Avail
            • Cómo utilizar la pila OP con Avail
            • Adaptador de pila OP 🔗
          • Validium
            • Polygon zkEVM
              • Construyendo sobre Polygon zkEVM con Avail
              • Cómo utilizar Polygon zkEVM con Avail
              • Nodo Validium 🔗
              • Contratos de Validium 🔗
              • Puente Validium 🔗
            • Madara Starknet
              • Construyendo sobre Madara Stack con Avail
              • Cómo utilizar Madara con Avail
              • Madara Starknet🔗
            • Referencia
          • Sovereign Rollups
            • Sovereign SDK 🔗
            • Rollkit 🔗
            • OpEVM 🔗
        • Glosario
        • Preguntas generales frecuentes
      • Dymension
        • Aprender
          • ELI5
          • RollApps
            • RollApps
            • Tokens
            • Gobernancia
            • Puente
            • En profundidad
              • Dymension RDK
                • Dymint
              • Gobernanza
                • Gobernador
                  • Descripción general
                  • Crear gobernador
                  • Otros comandos
                • Votación
                  • Parámetros ajustables
                  • Gasto comunitario
                  • Registro de tokens ERC-20
              • IBC Puente
                • Visión general
                • Seguridad
                • Retransmisores
          • Dymension
            • Visión general
            • DYM
              • Supply
              • Demanda
              • Crecimiento
              • Distribución
            • Seguridad
              • Estándares
              • Actualizable
              • Disponibilidad de datos
              • Pruebas de fraude
              • Resistencia a la censura
              • Page 1
            • Puentes
              • IBC
              • eIBC
            • Liquidez
              • Descripción general
              • Depositar tokens
              • Vinculación de tokens LP
              • Incentivos
              • Comisiones
            • Gobernanza
              • Descripción general
              • Preparando una propuesta
              • Proponiendo a la dimocracia
        • Construir
          • Descripción general
          • Testnet
            • EVM
              • Descripción general
            • CosmWasm
              • Descripción general
              • Información
              • Ejemplo de cosmoWasm
          • Roller CLI
            • Descripción general
            • Comenzar
              • Instalar
              • Inicializar RollApp
              • Registro
              • Correr
                • Simple
                • Avanzado
                  • Cliente ligero DA
                  • Secuenciador
                  • Retransmisor
            • Nodo en ejecución
              • Ejecutando en producción
              • Supervisión
              • Información de RollApp
              • Exportar claves
              • Mejora
              • Editar la configuración de RollApp
              • Sincronización de estado
            • Solución de problemas
              • Descripción general
              • Saldos
              • Hardware
              • Rollapp de importación/exportación
              • Acceso externo
              • Estado
              • Archivos de registro
            • RollApp local
              • Ejecute la aplicación EVM RollApp
              • Ejecute la aplicación CosmWasm RollApp
        • Validar
          • Preguntas frecuentes sobre nodos
          • Construir dimensión
          • Configuración de nodo
          • Únase a una red
          • Nodo de sincronización
          • Validador
          • Actualizaciones
          • Solución de problemas
          • Programa de delegación
            • objetivos del programa
            • Parámetros de evaluación
            • Solicitud
      • Movement
        • Desarrolladores
          • Inicio rápido
          • Configuración
            • Usando contenedores
            • Usando el instalador
          • Tutoriales
            • Desplegar
              • Módulo Aptos
              • Módulo Sui
              • Contratos EVM
                • Implementación de contratos de solidez en movimiento utilizando Foundry y Fractal
                • Implementación de contratos de solidez en M1 usando Hardhat y Fractal
                • Implementación de contratos inteligentes de Solidity en M1 utilizando el tiempo de ejecución Fractal
            • Ejecute MoveVM
              • Ejecutando M1 usted mismo
            • Construir dApp
              • Aptos Move dApp
              • Aplicación Sui Move
              • DApp de solidity
            • Interoperar
              • AptosVM<>MEVM
          • Herramientas de desarrollo
            • Movement CLI
              • Movement aptos
                • cuenta
                  • crear
                  • crear-cuenta-de-recursos
                    • derivar-dirección-de-cuenta-de-recursos
                  • fondo-con-grifo
                  • lista
                  • dirección de búsqueda
                  • rotar llave
                  • transferir
                • configuración
                  • generar-compleciones-de-shell
                  • configurar-global-config
                  • show-global-config
                  • mostrar-perfiles
                • génesis
                  • generar-admin-escritura-conjunto
                  • generar-génesis
                  • obtener direcciones de grupo
                  • generar-claves
                  • generar-plantilla-de-diseño
                  • configuración-git
                  • configuración-del-validador-de-conjuntos
                • gobernancia
                  • proponer
                  • votar
                  • propuesta de show
                  • lista-propuestas
                  • verificar-propuesta
                  • ejecutar propuesta
                  • generar-propuesta-de-actualización
                  • aprobar-ejecución-hash
                • información
                • init
                • llave
                  • generar
                  • extraer-peer
                • mover
                  • construir-publicar-carga útil
                  • limpio
                  • compilar
                  • script de compilación
                  • cobertura
                    • resumen
                    • fuente
                    • código de bytes
                  • crear-cuenta-de-recursos-y-publicar-paquete
                  • desmontar
                  • documento
                  • descargar
                  • init
                  • list
                  • probar
                  • publicar
                  • correr
                  • ejecutar guión
                  • prueba
                  • prueba transaccional
                  • verificar-paquete
                  • vista
                • multifirma
                  • aprobar
                  • crear
                  • crear-transacción
                  • ejecutar
                  • ejecutar-rechazar
                  • ejecutar con carga útil
                  • rechazar
                  • verificar-propuesta
                • nodo
                  • analizar-validador-rendimiento
                  • arranque-db
                  • verificar-conectividad-de-red
                  • conseguir-participación-pool
                  • inicializar-validador
                  • conjunto de validadores de unión
                  • conjunto de validadores de licencia
                  • mostrar-información-de-época
                  • mostrar-validador-config
                  • mostrar-conjunto-validador
                  • mostrar-validador-participación
                  • ejecutar-testnet-local
                  • actualización-clave-de-consenso
                  • actualizar-validador-direcciones-de-red
                • apostar
                  • agregar apuesta
                  • crear-contrato-de-participación
                  • distribuir-monedas-adquiridas
                  • aumentar-bloqueo
                  • inicializar-propietario de la participación
                  • solicitud-comisión
                  • establecer-votante-delegado
                  • operador de conjunto
                  • desbloquear-apuesta
                  • desbloquear-monedas-adquiridas
                  • retirar-apuesta
                • actualizar
              • movement sui
                • comenzar
                • génesis
                • ceremonia-genesis
                  • init
                  • estado de validación
                  • agregar-validador
                  • validadores de lista
                  • punto de control de compilación sin firmar
                  • examinar-punto-de-control-génesis
                  • verificar y firmar
                  • finalizar
                • herramienta clave
                  • convertir
                  • decodificar-tx-bytes
                  • decodificar-multi-sig
                  • generar
                  • importar
                  • lista
                  • par de claves de carga
                  • dirección multifirma
                  • multi-sig-combinar-sig-parcial
                  • herencia-sig-parcial-combinada-multi-sig
                  • espectáculo
                  • firmar
                  • señal-kms
                  • deshacer
                  • zk-login-firmar-y-ejecutar-tx
                  • zk-login-ingresar-token
                  • zk-login-sig-verificar
                  • zk-login-signo-inseguro-mensaje-personal
                • consola
                • cliente
                  • dirección activa
                  • entorno-activo
                  • direcciones
                  • llamar
                  • identificador de cadena
                  • campo dinámico
                  • env
                  • ejecutar-tx firmado
                  • gas
                  • fusionar moneda
                  • nueva direccion
                  • nuevo-ambiente
                  • objeto
                  • objetos
                  • pagar
                  • pago todo-sui
                  • pay-sui
                  • publicar
                  • moneda dividida
                  • cambiar
                  • bloque tx
                  • transferir
                  • transferencia-sui
                  • mejora
                  • verificar-bytecode-metro
                  • verificar-fuente
                  • transacción-repetición
                  • lote de repetición
                  • punto de control de repetición
                • validador
                  • hacer-información-validador
                  • convertirse en candidato
                  • comité conjunto
                  • comité de licencia
                  • metadatos de visualización
                  • actualizar-metadatos
                    • nombre
                    • descripción
                    • URL de la imagen
                    • URL del proyecto
                    • dirección de red
                    • dirección primaria
                    • dirección-trabajador
                    • dirección-p2p
                    • clave-pub-de-red
                    • clave-pub-trabajador
                    • protocolo-pub-clave
                  • actualizar-precio-de-gas
                  • validador de informes
                  • serializar-carga útil-pop
                  • mostrar-actualización-del-precio-del-gas-raw-txn
                • move
                  • construir
                  • cobertura
                    • resumen
                    • fuente
                    • código de bytes
                  • desmontar
                  • nuevo
                  • probar
                  • prueba
                • simulacro de incendio
                  • rotación de metadatos
              • movement ctl
              • movement manage
            • fractales
              • marco evm
          • Desarrolladores Aptos
            • Configurar la CLI de Aptos
            • Usando la CLI de Aptos
          • Desarrolladores Sui
            • Configurar Sui CLI
            • Usando Sui CLI
          • Preguntas más frecuentes
        • Ecosistema
          • Wallets
          • Tokens
          • Faucet
          • Move idioma
            • Módulos y scripts
            • Tipos primitivos
              • Enteros
              • booleano
              • DIRECCIÓN
              • Vector
              • Firmante
              • Referencias
              • Tuplas y unidad
          • Recursos de aprendizaje
          • Techpedia
            • Paralelización
            • Mover recursos
            • SDK de movement
      • Initia
        • ACERCA DE
          • Bienvenido a Inicia
          • Arquitectura Omnitia
            • Inicia (Capa 1)
            • Minitia (Capa 2)
          • Ciclo de vida de la transacción
          • Liquidez y apuestas consagradas
            • IniciaDEX
          • Programa de intereses adquiridos
        • CONSTRUIR SOBRE LA INICIATIVA
          • iniciado
          • inicia.js
          • Creando cuenta
          • Tutoriales específicos de VM
            • MoverVM
              • Implementación de módulos de movimiento
              • Creando moneda de movimiento
              • Envío de moneda de movimiento
              • Creando movimiento NFT
              • Módulos relacionados con el replanteo
              • Interactuar con Oracle en MoveVM
              • Mover ganchos IBC
            • WASMVM
              • Implementación del contrato CosmWasm
              • Interactuando con Oracle en WasmVM
              • Ganchos Wasm IBC
              • Fábrica de fichas
            • EVM
              • Implementación del contrato de solidez
              • Crear un token ERC-20 personalizado
              • Consultar estados del cosmos
              • Ejecutando mensajes de Cosmos
              • Conversión de direcciones entre EVM y Cosmos
              • Conversión entre direcciones Denom y ERC-20
              • Interactuar con Oracle en EVM
              • Ganchos EVM IBC
              • Ethereum JSON-RPC
          • Tutoriales generales
            • Oráculo: Furtivo
            • Mensajes entre cadenas
            • Saltar API
            • Miniswap
              • Interactuando con Minitswap
            • Conversión entre nombres de usuario y direcciones
            • Usando el Explorador local de Initia
            • Interactuando con InitiaDEX
            • Usando el widget de billetera Initia
        • IMPLEMENTAR MINITIA
          • Empezando
            • Implementación de su propia Minitia (Capa 2)
          • Configuración
          • Implementación de una Minitia independiente
          • Implementación completa de Minitia
            • Dirigiendo la Minitia
            • Pila de OPinit
              • Módulo OPinit: OPhost y OPchild
              • Configurar robots OPinit
                • Ejecutor del puente
                • Remitente de salida
                • Desafiador
                • Envío por lotes
                  • Envío de lotes a Inicia L1
                  • Envío de lotes a Celestia
            • Relé Hermes (IBC)
            • Habilitando oráculos
          • Retroceder
          • Agregar tokens a Initia Wallet
          • Personalizando Minitia
        • EJECUTAR EL NODO DE INICIO
          • Ejecutando el nodo de inicio
          • Arrancar un nodo de inicio
          • Conéctese a la red Inicial
          • Oráculo
          • Automatización de actualizaciones de software con Cosmovisor
          • Convertirse en un validador
        • RECURSOS
          • Registro de Iniciación
          • Información de la cadena Testnet
          • Parámetros de cadena
          • Documentación de la API
          • Documentos API (MiniMove)
          • Documentos API (MiniWasm)
          • Documentos API (MiniEVM)
      • Internet Computer
        • ¡Hola, mundo!
        • Descripción general del ICP
Powered by GitBook
On this page
  1. Recursos
  2. Desarrollo
  3. Cosmwasm Documentacion
  4. Tutoriales
  5. Storage

Índices

Previous¿Cómo funciona el almacenamiento de valores clave?NextModelización avanzada de estados

Last updated 1 year ago

Los índices son estructuras de claves que permiten iterar sobre claves primarias utilizando información sobre valores. Consideremos un modelo en el que hay varios tokens en el sistema y cada token tiene un propietario único. En este escenario, un propietario debe estar asociado a un token, y los tokens deben poder ser consultados por sus respectivos propietarios.

struct Token {
  pub owner: Addr,
  pub ticker: String
}

Los tokens pueden identificarse mediante una clave autoincrementada, que se utilizará como clave primaria. Esto garantiza que cada ficha sea única.

(TokenPK) -> Token

El índice para el propietario se estructuraría así:

(owner, TokenPK) -> Token

TokenPK apunta a los datos del token, y la clave owner:TokenPK apunta a un token concreto. Con dos accesos a la base de datos, se puede acceder a los datos de los tokens. Para recuperar todos los tokens gestionados por un propietario, podemos ejecutar una consulta de rango de prefijos como se muestra arriba.

pub const TOKENS: Map<U8Key, Token> = Map::new("tokens");
// (owner Address, Token PK) -> u8 key
pub const OWNER_INDEX: Map<(&Addr, U8Key), &[u8]> = Map::new("owner_tokenpk");

Con la información del propietario como clave, se puede acceder fácilmente a los tokens. Sin embargo, cada vez que cambia el estado de TOKENS, el campo del propietario también debe actualizarse en consecuencia.

storage-plus indexing

La solución anterior es funcional, pero subóptima debido al alto nivel de complejidad del código. Aquí es donde entra en juego. IndexedMap es un gestor de almacenamiento que proporciona indexación interna. Ofrece dos tipos de índices: Índices Únicos y Multiíndices.

Índices únicos

Garantizar la unicidad de un campo de datos en una base de datos es un requisito habitual. UniqueIndex es un ayudante de indexación que puede ayudar a conseguir esta funcionalidad."

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Token {
  pub owner: Addr,
  pub ticker: String,
  pub identifier: u8, // <---- unique value
}
// TokenIndexes structs keeps a list of indexers
pub struct TokenIndexes<'a> {
  // token.identifier
  pub identifier: UniqueIndex<'a, U8Key, Token>,
}
// IndexList is just boilerplate code for fetching a struct's indexes
impl<'a> IndexList<Token> for TokenIndexes<'a> {
  fn get_indexes(&'_ self) -> Box<dyn Iterator<Item=&'_ dyn Index<Token>> + '_> {
    let v: Vec<&dyn Index<Token>> = vec![&self.identifier];
    Box::new(v.into_iter())
  }
}
// tokens() is the storage access function.
pub fn tokens<'a>() -> IndexedMap<'a, &'a [u8], Token, TokenIndexes<'a>> {
  let indexes = TokenIndexes {
    identifier: UniqueIndex::new(|d| U8Key::new(d.identifier), "token_identifier"),
  };
  IndexedMap::new(TOKEN_NAMESPACE, indexes)
}

Repasemos el código paso a paso:

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Token {
  pub owner: Addr,
  pub ticker: String,
  pub identifier: u8, // <---- unique value
}

Un Token tiene varios valores, y entre ellos, el identifier es un valor único.

// TokenIndexes structs keeps a list of indexers
pub struct TokenIndexes<'a> {
  // token.identifier
  pub identifier: UniqueIndex<'a, U8Key, Token>,
}

TokenIndexes es una estructura para definir índices de la estructura Token.

impl<'a> IndexList<Token> for TokenIndexes<'a> {
  fn get_indexes(&'_ self) -> Box<dyn Iterator<Item=&'_ dyn Index<Token>> + '_> {
    let v: Vec<&dyn Index<Token>> = vec![&self.identifier];
    Box::new(v.into_iter())
  }
}

IndexList es una interfaz para construir los índices.

pub fn tokens<'a>() -> IndexedMap<'a, &'a [u8], Token, TokenIndexes<'a>> {
  let indexes = TokenIndexes {
    identifier: UniqueIndex::new(|d| U8Key::new(d.identifier), "token_identifier"),
  };
  IndexedMap::new(TOKEN_NAMESPACE, indexes)
}

tokens() es la función de almacenamiento utilizada para construir IndexedMap.

 identifier: UniqueIndex::new( | d| U8Key::new(d.identifier), "token_identifier"),

El código anterior es una función constructora de índices. Construye claves compuestas con la función dada, y acepta una clave para identificar el cubo del índice.

Véase el código de prueba a continuación:

#[test]
fn test_tokens() {
  let mut store = MockStorage::new();
  let owner1 = Addr::unchecked("addr1");
  let ticker1 = "TOKEN1".to_string();
  let token1 = Token {
    owner: owner1.clone(),
    ticker: ticker1,
    identifier: 0,
  };
  let token_id = increment_tokens(store.borrow_mut()).unwrap();
  tokens().save(store.borrow_mut(), &U64Key::from(token_id).joined_key(), &token1).unwrap();
  let ticker2 = "TOKEN2".to_string();
  let token2 = Token {
    owner: owner1.clone(),
    ticker: ticker2,
    identifier: 0,
  };
  let token_id = increment_tokens(store.borrow_mut()).unwrap();
  // identifier clashes, must return error
  tokens().save(store.borrow_mut(), &U64Key::from(token_id).joined_key(), &token1).unwrap();
}

La última línea se bloqueará con un error:

called `Result::unwrap()` on an **Err** value: GenericErr { msg: "Violates unique constraint on index" }
thread 'state::test::test_tokens' panicked at 'called `Result::unwrap()` on an **Err** value: GenericErr { msg: "Violates unique constraint on index" }', src/state.rs:197:90
stack backtrace:

Multiíndices

Los multiíndices se utilizan cuando la estructura está indexada por valores no únicos. Este es un caso del contrato inteligente cw721:

pub struct TokenIndexes<'a> {
  // secondary index by owner address
  // the last U64Key is the primary key which is an auto incremented token counter
  pub owner: MultiIndex<'a, (Vec<u8>, Vec<u8>), Token>,
}
// this may become a macro, not important just boilerplate, builds the list of indexes for later use
impl<'a> IndexList<Token> for TokenIndexes<'a> {
  fn get_indexes(&'_ self) -> Box<dyn Iterator<Item=&'_ dyn Index<Token>> + '_> {
    let v: Vec<&dyn Index<Token>> = vec![&self.owner];
    Box::new(v.into_iter())
  }
}
const TOKEN_NAMESPACE: &str = "tokens";
pub fn tokens<'a>() -> IndexedMap<'a, &'a [u8], Token, TokenIndexes<'a>> {
  let indexes = TokenIndexes {
    owner: MultiIndex::new(
      |d, k| (index_string(d.owner.as_str()), k),
      TOKEN_NAMESPACE,
      "tokens__owner",
    )
  };
  IndexedMap::new(TOKEN_NAMESPACE, indexes)
}

Vemos que el índice propietario es un Multiíndice. Un multiíndice puede tener valores repetidos como claves. Por eso la clave primaria se añade como último elemento de la clave multiíndice. Como su nombre indica, se trata de un índice sobre tokens, por propietario. Dado que un propietario puede tener varios tokens, necesitamos un MultiIndex para poder listar / iterar sobre todos los tokens que tiene un propietario dado.

Es importante tener en cuenta que la clave (y sus componentes, en el caso de una clave combinada) debe implementar el rasgo PrimaryKey. En este ejemplo, tanto la 2-tupla (_, _) como Vec implementan PrimaryKey:

 impl<'a, T: PrimaryKey<'a> + Prefixer<'a>, U: PrimaryKey<'a>> PrimaryKey<'a> for (T, U) {
  type Prefix = T;
  type SubPrefix = ();
  fn key(&self) -> Vec<&[u8]> {
    let mut keys = self.0.key();
    keys.extend(&self.1.key());
    keys
  }
}
 pub fn tokens<'a>() -> IndexedMap<'a, &'a str, TokenInfo, TokenIndexes<'a>> {
  let indexes = TokenIndexes {
    owner: MultiIndex::new(
      |d, k| (Vec::from(d.owner.as_ref()), k),
      "tokens",
      "tokens__owner",
    ),
  };
  IndexedMap::new("tokens", indexes)
}

Durante la creación del índice, debemos suministrar una función de índice por índice.

owner: MultiIndex::new(|d, k| (Vec::from(d.owner.as_ref()), k),

La función de índice se encarga de tomar el valor y la clave primaria (siempre en forma Vec) del mapa original y crear la clave de índice a partir de ellos. Por supuesto, esto requiere que los elementos necesarios para la clave de índice estén presentes en el valor (lo cual es lógico).

Aparte de la función de índice, también tenemos que proporcionar el espacio de nombres de la clave primaria y el espacio de nombres para el nuevo índice.

IndexedMap::new("tokens", indexes)

Aquí, el espacio de nombres de la clave primaria debe coincidir con el utilizado durante la creación del índice. Pasamos nuestros TokenIndexes (como un parámetro de tipo IndexList) como segundo argumento, conectando así el Map subyacente con la clave primaria y los índices definidos.

IndexedMap (y los otros tipos Indexed*) es simplemente una envoltura/extensión de Map que proporciona varias funciones de índice y espacios de nombres para crear índices sobre los datos originales de Map. También implementa la llamada a estas funciones de índice durante el almacenamiento/modificación/eliminación de valores, por lo que puede utilizar simplemente los datos indexados sin preocuparse de los detalles de implementación.

He aquí un ejemplo de cómo utilizar los índices en el código:

#[test]
fn test_tokens() {
  let mut store = MockStorage::new();
  let owner1 = Addr::unchecked("addr1");
  let ticker1 = "TOKEN1".to_string();
  let token1 = Token {
    owner: owner1.clone(),
    ticker: ticker1,
  };
  let ticker2 = "TOKEN2".to_string();
  let token2 = Token {
    owner: owner1.clone(),
    ticker: ticker2,
  };
  let token_id = increment_tokens(store.borrow_mut()).unwrap();
  tokens().save(store.borrow_mut(), &U64Key::from(token_id).joined_key(), &token1).unwrap();
  let token_id = increment_tokens(store.borrow_mut()).unwrap();
  tokens().save(store.borrow_mut(), &U64Key::from(token_id).joined_key(), &token1).unwrap();
  // want to load token using owner1 and ticker1
  let list: Vec<_> = tokens()
    .idx.owner
    .prefix(index_string(owner1.as_str()))
    .range(&store, None, None, Order::Ascending)
    .collect::<StdResult<_>>().unwrap();
  let (_, t) = &list[0];
  assert_eq!(t, &token1);
  assert_eq!(2, list.len());
}

Indexación múltiple compuesta

Consideremos la siguiente situación: tenemos varios lotes almacenados por su ID de lote (numérico), que puede cambiar y debe promocionarse automáticamente tras ser modificado. Queremos procesar todos los lotes pendientes con cualquier estado, desde Pendiente a Promocionado, en función de las interacciones con ellos. Además, cada lote tiene un tiempo de caducidad asociado. Sólo nos interesan los lotes pendientes que ya han caducado, para poder promocionarlos. Para ello, podemos crear un índice sobre los lotes, con una clave compuesta que incluya tanto el estado del lote como su fecha de caducidad. Utilizando esta clave compuesta, podemos excluir tanto los lotes ya promocionados como los pendientes que aún no han caducado.

Para ello, podemos crear el índice, generar la clave compuesta e iterar sobre todos los lotes pendientes que tengan una fecha de caducidad inferior a la actual.

He aquí un ejemplo de cómo hacerlo utilizando la estructura Batch:

/// A Batch is a group of members who got voted in together. We need this to
/// calculate moving from *Paid, Pending Voter* to *Voter*
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct Batch {
  /// Timestamp (seconds) when all members are no longer pending
  pub grace_ends_at: u64,
  /// How many must still pay in their escrow before the batch is early authorized
  pub waiting_escrow: u32,
  /// All paid members promoted. We do this once when grace ends or waiting escrow hits 0.
  /// Store this one done so we don't loop through that anymore.
  pub batch_promoted: bool,
  /// List of all members that are part of this batch (look up ESCROWS with these keys)
  pub members: Vec<Addr>,
}

Definiciones de IndexedMap:

// We need a secondary index for batches, such that we can look up batches that have
// not been promoted, ordered by expiration (ascending) from now.
// Index: (U8Key/bool: batch_promoted, U64Key: grace_ends_at) -> U64Key: pk
pub struct BatchIndexes<'a> {
  pub promotion_time: MultiIndex<'a, (U8Key, U64Key, U64Key), Batch>,
}
impl<'a> IndexList<Batch> for BatchIndexes<'a> {
  fn get_indexes(&'_ self) -> Box<dyn Iterator<Item=&'_ dyn Index<Batch>> + '_> {
    let v: Vec<&dyn Index<Batch>> = vec![&self.promotion_time];
    Box::new(v.into_iter())
  }
}
pub fn batches<'a>() -> IndexedMap<'a, U64Key, Batch, BatchIndexes<'a>> {
  let indexes = BatchIndexes {
    promotion_time: MultiIndex::new(
      |b: &Batch, pk: Vec<u8>| {
        let promoted = if b.batch_promoted { 1u8 } else { 0u8 };
        (promoted.into(), b.grace_ends_at.into(), pk.into())
      },
      "batch",
      "batch__promotion",
    ),
  };
  IndexedMap::new("batch", indexes)
}

Este ejemplo es similar al anterior. Las únicas diferencias son:

La clave compuesta tiene ahora tres elementos: el estado del lote, la fecha de caducidad y el ID del lote (que es la clave principal de los datos del lote). Estamos utilizando un U64Key para el id de lote / pk. Es sólo por comodidad. También podríamos haber utilizado un Vec.

Ahora, aquí es cómo utilizar los datos indexados:

let batch_map = batches();
// Limit to batches that have not yet been promoted (0), using sub_prefix.
// Iterate which have expired at or less than the current time (now), using a bound.
// These are all eligible for timeout-based promotion
let now = block.time.nanos() / 1_000_000_000;
// as we want to keep the last item (pk) unbounded, we increment time by 1 and use exclusive (below the next tick)
let max_key = (U64Key::from(now + 1), U64Key::from(0)).joined_key();
let bound = Bound::Exclusive(max_key);
let ready = batch_map
              .idx
              .promotion_time
              .sub_prefix(0u8.into())
              .range(storage, None, Some(bound), Order::Ascending)
              .collect::<StdResult<Vec<_ >>>() ?;

Un par de comentarios:

  • joined_key() se utiliza para crear la clave de rango. Esta función de ayuda transforma la clave compuesta (parcial) compuesta por la fecha de caducidad del lote y el ID del lote en un Vec con el formato adecuado. A continuación, se utiliza para crear un límite de rango.

  • sub_prefix() se utiliza para fijar el primer elemento de la clave compuesta (el estado del lote). Esto es necesario, porque prefix() en este caso (una 3-tupla), implica fijar los dos primeros elementos de la clave, y no queremos / necesitamos eso aquí.

  • La iteración procede desde Ninguno hasta la clave vinculada creada a partir de la marca de tiempo actual. De este modo, sólo se enumeran los lotes pendientes pero ya caducados.

Eso es todo. Después de eso, podemos iterar sobre los resultados y cambiar su estado de Pendiente a Promovido, o lo que necesitemos hacer.

👽
🎓
storage-plus/IndexedMap