Удобно ли использовать сопоставления в качестве временных экземпляров KeyValue?

Рассмотрим следующий контракт, который иллюстрирует проблему. Здесь у нас есть динамический массив сопоставлений, add()предназначенный для добавления нового нового сопоставления в конец массива, возврата значения для ключа сопоставления 0и изменения этого значения на true. remove()в свою очередь предназначен для удаления последнего отображения из массива.

contract ClearMapping {
    mapping(uint => bool)[] state;

    // 1. add -> false
    // 2. remove
    // 3. add -> true
    function add() returns (bool) {
        uint pos = state.length++;
        bool curr = state[pos][0];
        state[pos][0] = true;
        return curr;
    }

    function remove() {
        state.length--;
    }
}

Можно подумать, что удаление элемента сопоставления из массива эффективно очищает это сопоставление, и что добавление нового сопоставления вместо старого будет иметь новый key* -> falseэлемент all . Оказывается, это неправда. То же самое произойдет, если вы попытаетесь поменять местами элементы, внутри которых есть сопоставления, все будет заменено, кроме сопоставлений.

Единственное решение, которое я вижу, чтобы справиться с этим, - это вручную удалить / переназначить каждый используемый ключ сопоставления, но это очень быстро становится дорогостоящим с точки зрения использования газа.

Вопросы, на которые я ищу ответы:

  1. Это ошибка в EVM/Solidity?
  2. Есть ли удобный/эффективный способ очистить/поменять местами сопоставления?
  3. Не следует ли использовать сопоставления в качестве временных экземпляров KeyValue?

Спасибо за внимание!

Ответы (1)

Чтобы ответить на ваш вопрос, позвольте мне объяснить, как на самом деле выглядит стек EVM: сам по себе это просто карта от ключа до значения, оба длиной 32 байта.

карта солидности, сопоставляется с sha3(mapId .key) с заданным значением в стеке evm. Это также причина, по которой невозможно перебрать все ключи на карте, потому что они «рандомизированы» по всему стеку evm.

массив солидности сопоставляется с sha3(arrayId) + index со значением. Здесь мы можем выполнить итерацию, если мы знаем идентификатор массива, просто увеличив индекс.

(Здесь я не совсем уверен, сохраняется ли длина массива.)

Нижеследующее является просто предположением, поскольку я полностью уверен, как солидность решает эту проблему: если у вас теперь есть массив карт, то, что вы на самом деле делаете, это mapId = sha3(arrayId) + index. Вы можете перебирать их. Однако, если вы сохраняете что-то на карту, вы делаете следующее:

sha3((sha3(arrayId) + index) . key ) = value

Но, удалив последний элемент карты, вы потеряете идентификатор массива для карты, но поскольку нельзя перебирать карты, не зная ключей, ваше значение сохраняется.

Однако у мультимассивов нет этой проблемы:

import "dapple/test.sol";

contract A is Test {
  uint[][] multiarray;

  function testMultiArray() {
    //@log multiarray length: `uint multiarray.length`
    //@log incrementing multiarray
    multiarray.length++;
    //@log multiarray length: `uint multiarray.length`
    //@log multiarray[0] length: `uint multiarray[0].length`
    //@log incrementing multiarray[0]
    multiarray[0].length++;
    multiarray[0].length++;
    //@log multiarray[0][0]: `uint multiarray[0][0]`
    //@log set value to 1
    multiarray[0][0] = 1;
    multiarray[0][1] = 1;
    //@log multiarray[0] length: `uint multiarray[0].length`
    //@log multiarray[0][0]: `uint multiarray[0][0]`
    //@log multiarray[0][1]: `uint multiarray[0][1]`
    //@log decrementing multiarray
    multiarray.length--;
    //@log multiarray[0] length: `uint multiarray.length`
    //@log incrementing multiarray
    multiarray.length++;
    //@log multiarray[0] length: `uint multiarray[0].length`
    multiarray[0].length++;
    multiarray[0].length++;
    //@log multiarray[0][0]: `uint multiarray[0][0]`
    //@log multiarray[0][1]: `uint multiarray[0][1]`
  }
}

Выведет следующий вывод:

  test multi array
  LOG:  multiarray length: 0
  LOG:  incrementing multiarray
  LOG:  multiarray length: 1
  LOG:  multiarray[0] length: 0
  LOG:  incrementing multiarray[0]
  LOG:  multiarray[0][0]: 0
  LOG:  set value to 1
  LOG:  multiarray[0] length: 2
  LOG:  multiarray[0][0]: 1
  LOG:  multiarray[0][1]: 1
  LOG:  decrementing multiarray
  LOG:  multiarray[0] length: 0
  LOG:  incrementing multiarray
  LOG:  multiarray[0] length: 0
  LOG:  multiarray[0][0]: 0
  LOG:  multiarray[0][1]: 0
Спасибо за объяснение! Теперь я могу сказать, что с этой архитектурой хранения это не ошибка (вопрос 1). И я знал, что это нормально для массивов, хотя вы не можете эффективно заменить все виды отображений массивами. А вопрос 2? Что касается 3, я решил пока не использовать сопоставления во временных сущностях.
2. простой => нет. Если вы хотите очистить сопоставление, вы должны запомнить и перебрать все ключи и удалить его вручную с помощью delete map[key]. Существует итерируемый шаблон, состоящий из mppping и массива, который позволяет вам перебирать вашу карту, которую можно использовать для удаления элементов. Также вы можете придумать свои собственные структуры данных для удаления отображаемых элементов, но в evm нет встроенной сборки мусора, вам придется делать это вручную.
Это то, что я делал после того, как узнал об этом. Но потребление газа резко возрастает, что не идет на пользу производству. Может быть есть способ их заменить? Например, для замены key => boolможно использовать uint[]набор пакетов по 256 логических значений в каждом. А как же key => uintдругие?