Как вы можете разделить байты данных вызова на произвольное количество различных событий журнала?

Следующий код неполный. Что я хочу сделать, так это разделить bytesданные (calldata) на две или более отдельных функций журнала для целей индексации.

Проблема в том, что я не могу понять, как это сделать в Solidity. bytesимеет произвольную длину, поэтому я не могу скопировать данные вызова в память, поскольку нельзя выделить память произвольной длины (AFAICT из документации Solidity). Я также отмечаю, что приведенный ниже код упрощен — это будет не просто половина, это будет некоторая дробь, установленная в качестве другого параметра для logit.

Я не совсем уверен, но когда я смотрю на скомпилированный вывод Solidity, мне кажется, что я мог бы реализовать это непосредственно в сборке EVM. инструкция EVM log1принимает указатель и длину данных в качестве входных аргументов, поэтому это должно быть возможным. Приведенный ниже код работает, поэтому я знаю, что каким-то образом данные вызова произвольной длины могут быть отправлены в log1. Solidity просто не позволяет мне выполнять арифметику указателя для разделения данных (или я не могу понять, как это сделать). Я бы предпочел не писать код на ассемблере... (при необходимости я могу переключиться на Serpent).

Да, я мог бы сделать отдельные функции и разделить данные за пределами Ethereum. но тогда я несу штраф в размере 21 000 газа за каждый звонок. Я пытаюсь сделать это для низкой стоимости газа.

[EDIT] - да, я мог бы использовать до 7 параметров функции (максимум), но это дает мне только 7 разделений. Мне нужно порядка 64. Это то, что я получаю за попытку слишком упростить вопрос...

// 
// 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);
  }
}
По касательной: не используйте Serpent, он устарел -- ethereum.stackexchange.com/questions/21375/why-did-serpent-die
Кстати, при общем рассмотрении это превращается в более общий вопрос о том, как обрабатывать входящие данные произвольной длины без необходимости использовать хранилище для произвольного разделения данных.

Ответы (1)

Произвольный раздельный подход

Изменить: добавлен этот раздел для решения произвольных разделенных пунктов назначения.

К моменту разбиения вы должны знать длину целевых данных. Ниже приведен пример реализации копирования байтов в место назначения внутри Solidity, которое должно быть тривиально расширено до N сегментов.

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);
  }
}

Обратите внимание, что потребление газа выше, чем должно быть, потому что оно работает побайтно. Было бы быстрее использовать 32-байтовые слова с битовой маской. Хорошей ссылкой является memcpyбиблиотека утилит Solidity-String от Arachnid.


[Изменить: старое] Подход с фиксированным ведром

Вы можете разделить данные извне без накладных расходов в 21 кг газа. Отправьте предварительно разделенные данные в виде двух параметров в один вызов функции:

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);
  }
}

Это будет стоить меньше газа, чем расщепление внутри EVM.

извините, я должен был предвидеть этот ответ. Я перефразирую вопрос как «произвольное количество расщеплений». Метод, который вы предлагаете, работает только (я полагаю) на 8 шпагатов. Спасибо хоть!
Я проверил это. Solidity жалуется на «слишком глубокий стек» при 8 аргументах, поэтому максимальное значение равно 7. Мне также нужно отправить какой-то информационный блок, поэтому, скорее всего, будет 6.
Спасибо! Это может просто помочь. Мои данные определенно находятся на 32-байтовых границах, так что благодаря ссылке на memcpy. Я проверю это, когда у меня будет рабочий код.
начальный запуск - 50 тыс. газа по сравнению с 30 тыс. газа... выделяется ли хранилище?
Мой запуск внутри Remix использует газ 5650 во время выполнения с 5-байтовым вводом. Вы компилируете solcбез использования --optimize?
Я изменил типы на bytes32[]вместо , bytesтак как мои данные в любом случае действительно 32 байта (и это то, что также принимает log1), а стоимость регистрации каждой дополнительной 32-байтовой записи составляет 2620 газа. Первоначальная стоимость записи всего этого в один индекс составляла 2450 газа. Таким образом себестоимость копии составляет 170 газа. Я называю это хорошим. Спасибо!
Я использую то, что делает трюфель по умолчанию. Спасибо за напоминание проверить это.
это 68 байтов на данные, переданные в calldata. (32-5)*68=1836 газ. Gtxdatanonzero очень дорогой...
Да, к сожалению, ноды должны поддерживать все транзакции, чтобы запускать другие ноды, поэтому это сопряжено с довольно высокими публичными затратами.