Manera eficiente de verificar e insertar una dirección de matriz que es única

Tengo un requisito siguiente que debe atenderse en dapp.

  1. Insertar matriz de direcciones
  2. La matriz debe ser única. Si una de las direcciones de la matriz ya existe, rechace y revierta toda la operación .
  3. dapp debe poder recuperar la lista de direcciones de usuario
  4. dapp debe poder detectar si existe una dirección de usuario en función de la entrada de una dirección de usuario

Nota:

  1. El requisito anterior debe ser satisfecho.
  2. La verificación de validación completa debe realizarse mediante un contrato inteligente (no se puede confiar solo en la verificación del lado del cliente).

Tratar de usar 3 for loop no parece ser una forma muy inteligente para esto, ya que se convierte en una operación costosa. Desafortunadamente, esta es la única solución que se me ocurre para satisfacer la condición anterior. ¿Alguien tiene experiencia en lidiar con esto?

// probably only one of mapping or list is needed
address[] public addressList;
mapping (address => bool) public userAddr; 


    function insertAddress(address[] addressUser) public returns (bool) {
        // loop addressUser and check if it is unique
        // loop addressUser and check if it exists in mapping
        // loop and push addressUser to addressList + insert addressUser to userAddr


        return true;
    }

Respuestas (2)

Al ver su requerimiento, haría:

address[] public addressList;
mapping (address => bool) public userAddr; 

function insertAddress(address[] addressUser) public returns (bool) {
    // used to check adressUser uniqueness
    mapping (address => bool) memory uniq;
    for(uint i = 0; i < addressUser.length, i++) {
        address addr = addressUser[i];
        // check if addressUser is unique
        require(uniq[addr] == false);
        uniq[addr] = true;
        // if addr is not already list
        if (userAddr[addr] == false) {
            userAddr[addr] = true;
            addressList.push(addr);
        }
    }

    return true;
}

Editar:

Después de ver otro contrato, puede usar el método aquí y solicitar que todas las direcciones se envíen en orden creciente. Probablemente sea menos costoso en términos de gas ya que menos asignación de memoria.

Eso haría:

function insertAddress(address[] addressUser) public returns (bool) {
    // used to check adressUser uniqueness
    address lastAddr = address(0);
    for(uint i = 0; i < addressUser.length, i++) {
        address addr = addressUser[i];
        // check if addressUser is unique
        // by forcing all address to be sent 
        // in increasing order
        require(addr > lastAddr);
        lastAddr = addr;
        // if addr is not already in list
        if (userAddr[addr] == false) {
            userAddr[addr] = true;
            addressList.push(addr);
        }
    }

    return true;
}

 

He creado contratos como este antes y he visto muchos de los problemas que está teniendo. En resumen, estas operaciones que está intentando realizar son costosas . Por lo tanto, la mejor manera de hacer esto es simplemente hacerlo de manera eficiente.

Para cumplir con los requisitos, colocaría toda la función en un ciclo que itera sobre cada dirección que se pasa. Para que sea lo más eficiente posible, puede usar una ifdeclaración para verificar si la dirección ya existe. Si es así, simplemente puede pasar a la siguiente dirección sin realizar más cálculos en la dirección actual . Dependiendo de cómo esté pasando las direcciones, esto puede suponer un gran ahorro de gasolina.

Como ejemplo, podrías hacer:

address[] public addressList;
mapping (address => bool) public userAddr; 


    function insertAddress(address[] addressUser) public returns (bool) 
    {
        for (uint256 i = 0; i < addressUser.length; i++) {
            if (address does not exist) {
                push addressUser to addressList
                insert addressUser to userAddr
            }
        }

        return true;
    }

Editar según los nuevos requisitos

La matriz debe ser única. Si una de las direcciones de la matriz ya existe, rechace y revierta toda la operación.

Si este es el caso, entonces lo anterior no se aplica y lo que tenía originalmente es la mejor manera de hacerlo.

Editar basado en comentarios

Pensándolo lógicamente, para verificar la matriz debe verificar cada dirección. Esto requiere códigos de operación que verifiquen cada elemento, y no puede evitarlo. Después de realizar estas comprobaciones, debe escribir cada elemento en la cadena de bloques, que también requiere códigos de operación para cada uno de ellos. Todo este proceso es computacionalmente costoso y es la razón por la cual gran parte de esta lógica generalmente se sugiere fuera de la cadena.

Una cosa que puede hacer es comparar los valores hash de las matrices (una que está enviando y otra que está siendo revisada). Puede tomar el keccack256de cada matriz para garantizar un hash único. Puede almacenar este hash en el contrato inteligente y enviar un hash para que se verifique, en lugar de la matriz completa. Al hacer esto, ahora solo está realizando una verificación, en lugar de N verificaciones (N es la cantidad de elementos en la matriz).

Aún se le pedirá que realice un bucle para agregar todos los elementos al contrato inteligente, pero ahora ha eliminado efectivamente un bucle.

Un ejemplo sería:

address[] public addressList;
mapping (address => bool) public userAddr;
mapping (bytes32 => bool) public doesHashExist; 


    function insertAddress(address[] addressUser) public returns (bool) 
    {
        bytes32 newHash = keccak256(addressUser);
        require(!doesHashExist[newHash]);
        for (uint256 i = 0; i < addressUser.length; i++) {
                push addressUser to addressList
                insert addressUser to userAddr
            }
        }
        doesHashExist[newHash] = True;
        return true;
    }

Verá que hay una nueva asignación que se usa para almacenar hashes que existen en el contrato. La función ahora solo realiza una única verificación (así como un hashing) para confirmar la existencia de la matriz. Finalmente, guarda el hash.

La matriz u anterior es para omitir e insertar una dirección no única que he visto que se practica mucho. pero el requisito para el dapp es rechazar toda la operación, incluso si una de ellas es única.
Veo. Si ese es el caso, entonces esta es una función muy simple y la que describiste funciona. La función simplemente verificará si las direcciones son únicas y, si no, las agregará a la matriz.
Sí, sé que funciona, pero requiere al menos 3 bucles for (uno para verificar si el pase de matriz en sí mismo es único, uno para verificar si el pase de matriz tiene un valor existente, uno para insertar). Estoy tratando de buscar una manera más rentable.
La forma más eficiente sería realizar la verificación fuera de la cadena en la capa Dapp. Puede verificar fácilmente si una matriz es única sin escribir nada en la cadena de bloques. Si es único, entonces puede enviar una transacción a la red y no tener que realizar una de las de Solidity.
El requisito debe cumplirse en el propio contrato inteligente según el requisito. No debe depender de la validación externa. Entre, ¿hay alguna manera de reducir 3 for loop a 2? (por ejemplo: for loop para verificar que la matriz en sí es única, ¿se puede verificar de otra manera más eficiente?)
He actualizado la respuesta anterior.
¿Por qué se sugeriría hacer fuera de la cadena? La validación fuera de la cadena se puede omitir fácilmente llamando directamente a la función de contrato inteligente desde la dirección de la billetera del usuario
Mi nueva sugerencia con hashes evita esta preocupación.
You can take the keccak256 of each array in order to ensure a unique hash.Realmente no entendí cómo esto reducirá uno para el ciclo. keccak256(array[] addressList1) === keccak256(array[] addressList2)solo puede verificar si dos listas de matrices son iguales (en términos de longitud, valor y orden de valor). No puede identificar si un elemento de una matriz existe en otra matriz o no entiendo bien la implementación.
Enviaría un hash y lo compararía con un mapeo existente. Agregaré código a la respuesta.