¿Cómo puede dividir los bytes de datos de llamada en un número arbitrario de eventos de registro diferentes?

El siguiente código está incompleto. Lo que quiero hacer es dividir los bytesdatos (calldata) en dos o más funciones de registro separadas por motivos de indexación.

El problema es que no sé cómo hacerlo en Solidity. bytestiene una longitud arbitraria, por lo que no puedo copiar los datos de la llamada en la memoria, ya que no se puede asignar memoria que tenga una longitud arbitraria (AFAICT de la documentación de Solidity). También observo que el código a continuación está simplificado: no será solo la mitad, será una fracción establecida como otro parámetro para logit.

No estoy completamente seguro, pero cuando miro la salida compilada de Solidity, me parece que podría implementar esto directamente en el ensamblaje de EVM. la instrucción EVM log1toma un puntero y la longitud de los datos como argumentos de entrada, por lo que debería ser posible hacerlo. El siguiente código funciona, así que sé que de alguna manera se pueden enviar datos de llamadas de longitud arbitraria a log1. Solidity simplemente no me deja hacer la aritmética de punteros para dividir los datos (o no sé cómo). Preferiría no tener que ensamblar código... (puedo cambiar a Serpent si es necesario).

Sí, podría hacer funciones separadas y dividir los datos fuera de Ethereum. pero luego incurro en la sanción de 21.000 de gasolina por cada llamada. Estoy tratando de hacer esto por bajo costo de gasolina.

[EDITAR]: sí, podría usar hasta 7 parámetros de función (como máximo), pero eso solo me da 7 divisiones. Necesito del orden de 64. esto es lo que obtengo por tratar de simplificar demasiado la pregunta...

// 
// attempt to figure out how to split incoming byte data into separate logs
//

contract HelpLogs {

  event LogFirstHalf(bytes _data);
  event LogSecondHalf(bytes _data);

  function logit(bytes data) external {
    // can't do the pointer arithmetic to LogFirstHalf and
    // then LogSecondHalf of data.  But I can log all of it...
    LogFirstHalf(data);
  }
}
Tangencialmente: no use Serpent, está obsoleto -- ethereum.stackexchange.com/questions/21375/why-did-serpent-die
Por cierto, visto de manera genérica, esto se convierte en una pregunta más general de cómo se manejan los datos entrantes de longitud arbitraria sin tener que usar el almacenamiento para dividir los datos de manera arbitraria.

Respuestas (1)

Enfoque de división arbitraria

Editar: se agregó esta sección para abordar destinos divididos arbitrarios

En el momento de la división, debe conocer la longitud de los datos de destino. A continuación se muestra un ejemplo de implementación de la copia de los bytes a su destino dentro de Solidity, que debería ser trivialmente extensible a N cubos.

pragma solidity ^0.4.15;

contract HelpLogs {

  event LogFirstHalf(bytes _data);
  event LogSecondHalf(bytes _data);

  function logit(bytes data) external {
      uint midpoint = data.length / 2;
      bytes memory data1 = new bytes(midpoint);
      for (uint i = 0; i < midpoint; i++) {
          data1[i] = data[i];
      }
      bytes memory data2 = new bytes(data.length - midpoint);
      for (i = 0; i < data.length - midpoint; i++) {
          data2[i] = data[i + midpoint];
      }
      LogFirstHalf(data1);
      LogSecondHalf(data2);
  }
}

Tenga en cuenta que el uso de gas es más alto de lo necesario, porque funciona byte por byte. Sería más rápido usar palabras de 32 bytes, con máscara de bits. Una buena referencia es memcpyla biblioteca de utilidades de cadenas de solidez de Arachnid.


[Editar: antiguo] Enfoque de cubeta fija

Puede dividir los datos externamente sin la sobrecarga de 21 kggas. Envíe los datos predivididos como dos parámetros a una sola llamada de función:

pragma solidity ^0.4.15;

contract HelpLogs {

  event LogFirstHalf(bytes _data);
  event LogSecondHalf(bytes _data);

  function logit(bytes dataPart1, bytes dataPart2) external {
    LogFirstHalf(dataPart1);
    LogSecondHalf(dataPart2);
  }
}

Esto costará menos gas que dividir dentro del EVM.

lo siento, debería haber anticipado esta respuesta. Voy a reformular la pregunta como 'número arbitrario de divisiones'. El método que propones solo funciona (creo) en 8 divisiones. ¡Gracias!
Lo probé. Solidity se queja de 'apilar demasiado profundo' en 8 argumentos, por lo que el máximo es 7. También necesito que me envíen algún tipo de bloque de información, por lo que es más probable que sean 6.
¡gracias! Eso podría hacer el truco. Mis datos están definitivamente en límites de 32 bytes, así que gracias a la referencia a memcpy. Verificaré esto cuando tenga un código de trabajo.
la corrida inicial es de 50k de gas contra 30k de gas... ¿se está asignando el almacenamiento?
Mi ejecución dentro de Remix está usando gas 5650 durante la ejecución con una entrada de 5 bytes. ¿ Estás compilando solcsin usar --optimize?
Cambié los tipos a bytes32[]en lugar de bytesporque mis datos son realmente 32 bytes de todos modos (y eso es lo que log1 también toma) y el costo de registrar cada entrada adicional de 32 bytes es 2620 de gas. El costo original de registrarlo todo en un índice fue de 2450 de gasolina. Entonces el costo neto de la copia es 170 de gasolina. Estoy llamando a esto bueno. ¡Gracias!
Estoy usando lo que hace trufa por defecto. Gracias por el recordatorio para verificar eso.
son 68 bytes por datos pasados ​​en calldata. (32-5) * 68 = 1836 gasolina. Gtxdatanonzero es muy caro...
Sí, desafortunadamente los nodos tienen que mantener todas las transacciones para arrancar otros nodos, por lo que tiene un costo público bastante alto.