¿Buscando datos en un contrato inteligente?

Tengo un contrato inteligente que contiene algunos datos de artículos almacenados dentro de una estructura, que se asigna con una identificación que lo identifica de forma única con una asignación, como esta:

struct Foo {
    string a;
    string b;
}

mapping (uint64 => Foo) public articles;

(Obviamente, el código anterior está simplificado, pero la estructura es la misma, aunque la estructura real tiene 7 variables en lugar de 2).

Ahora tengo que poder buscar usando cualquiera de los 7 atributos. La cadena de bloques será privada, por lo que el uso de gas no es una preocupación.

Mi primera solución fue agregar un método de búsqueda al contrato inteligente y, dentro de él, recorrer todo el mapeo y verificar qué elementos cumplen la condición. Obviamente esto es O(n) y tardaba alrededor de 8 segundos para 2000 elementos, que aunque no está mal, espero una mejor solución.

Luego pensé en tener 7 mapeos más que actuarían como índices de búsqueda, mapeando cada valor de atributo al índice del artículo que lo satisface, como por ejemplo:

mapping (string => uint64) public searchIndex;

(Tenga en cuenta que en este momento no es necesario verificar las subcadenas, solo la igualdad)

Y finalmente, estaba pensando si sería bueno mantener estos índices en Elastic Search en lugar de en la propia cadena de bloques, aunque preferiría una solución puramente basada en la cadena de bloques si es posible.

Entonces, ¿cuál de estas opciones (o de alguna otra mejor que conozcas) te parece más adecuada?

Gracias.

¿Es este motor de búsqueda de contratos inteligentes de código abierto con API M2M de algún interés? blog.secondstate.io/post/20190703-search-engine-overview ethereum.search.secondstate.io

Respuestas (2)

Está claro que no puede obtener directamente el objeto que desea sin saber su clave. La idea de las 7 asignaciones es en realidad la más obvia y la menos complicada y funciona en la mayoría de los casos, no solo en el tuyo.

La idea más útil que se me ocurre es que busques patrones en los atributos de tu estructura, patrones que te ayuden a organizar un esquema de búsqueda en caso de que tengas 10 o 20 mil elementos. p.ej ; si string alos valores están vinculados a una línea de tiempo conocida como a == "X12"hoy y el día siguiente, otra entrada tendría a == "X13". Entonces, cuando desee buscar estructuras a == X32, tendrá una mejor comprensión de dónde podría estar esa estructura y cuáles son los límites del índice de búsqueda.

Convirtiendo así la búsqueda en 20 mil elementos en una búsqueda en solo 2 mil.

Espero haber sido claro.

Una cosa que he hecho para resolver este mismo tipo de problema es reflejar los datos que entran en el contrato en una base de datos de Redis o Mongo para crear un conjunto de datos mucho más consultable. Luego, cada vez que consulto esa base de datos, verifico los datos en la cadena de bloques usando una función getter para asegurarme de que no haya sido manipulado. Me doy cuenta de que este no es el método más puro solo de blockchain, pero funciona sin correr el riesgo de bucles pesados ​​​​de gas o mapas múltiples que multiplican el costo de almacenamiento para lo que deberían ser consultas simples sobre atributos no clave.

Dado su contrato simplificado:

struct Foo {
    string a;
    string b;
}

mapping (uint64 => Foo) public articles;

Puede crear un evento para emitir cada vez que se crea una nueva adición como esta que devuelve los valores y la ID única:

event newArticleCreated(string a, string b, index uint64);

Luego, utilizando la biblioteca Ethers.js (u otra como Web3), puede configurar un oyente para capturar cualquier instancia de este evento y manejar los datos como desee desde allí. En este caso, pasaría los datos a un servicio que agrega los datos a Mongo o Redis con la clave uint64 en su contrato:

    //import the ethers.js library
    const ethers = require('ethers');

    //set your provider and create a new instance of your contract by providing 
    //the ABI and deployed address 
    let provider = new ethers.providers.JsonRpcProvider(<RPC URL for your node>); 
    contractInstance = new ethers.Contract(<contractAddress>, <abi>, provider);

    //listen for events matching this name
    contractInstance.on("newArticleCreated", (a, b, index) => {
        //do stuff when the event fires

        //set the values in the event to a JSON object structure
        let chainObj = {
            id: index, 
            a: a
            b: b
        }

        //add the data to the database
        dataService.addDataToDatabase(chainObj);

Personalmente he tenido éxito con este tipo de configuración y tiene mucha flexibilidad. Le permite consultar datos reflejados de la cadena, pero siempre verifique esos datos con el contrato utilizando ese índice/ID único.

¡Gracias!