Доступ к прошлым значениям переменных смарт-контракта

Я хотел бы знать, возможно ли запросить значения переменных, используя старые блоки.

Например, у меня есть

contract Simple {
    string32 message public;

    function Simple() {
        message = msg.sender;
    }
}

Можно ли получить значение сообщения 1 месяц назад? Есть ли метод, который позволяет мне проверять такую ​​переменную на определенной высоте блока?

Существует ли метод, который извлекает все различные значения, найденные в блокчейне?

Большое спасибо.

Ответы (3)

Контракт не может получить доступ к переменной, которая больше не находится в хранилище. Однако вы можете получить доступ к этим переменным из web3.

Я бы порекомендовал использовать Events , которые вы можете индексировать по отметке времени, чтобы упростить поиск.

С web3 есть возможное решение (вероятно, не самое быстрое или самое элегантное, но оно должно показать, как это можно сделать). Мы получаем первую переменную контракта (индекс 0), которая имеет тип uint:

contract = "0x6d363cd2eb21ebd39e50c9a2f94a9724bf907d13";
maxBlocks = 1000;

startBlock = eth.blockNumber;
for (var i = 1; i < maxBlocks; i++) { /* Be careful: we go *back* in time */
    current = web3.eth.getStorageAt(contract, 0, startBlock-i);
    if (current != previous) {
        /* TODO Where to find msg.sender? We probably have to loop
         * over the transactions in the block can call
         * web3.eth.getTransaction */
        blockDate = new Date(web3.eth.getBlock(startBlock-i+1).timestamp*1000);
        console.log("Block #" + (startBlock-i+1) +  " (" + web3.eth.getBlock(startBlock-i+1).timestamp + " " + blockDate.toString()
            +  ") : " + web3.toDecimal(previous));
        /* What if there are two changes in a single block? The
         * documentation of getStorageAt seems silent about that */
        previous = current;
    }
}
blockDate = new Date(web3.eth.getBlock(startBlock-maxBlocks).timestamp*1000);
console.log("Somewhere before block #" +(startBlock-maxBlocks) +  " (block of " + blockDate.toString()
        +  ") : " + web3.toDecimal(previous));

Вы можете попробовать это в Testnet, где был развернут контракт с этим адресом.

Я не уверен, но одобрил редактирование; при желании отмените его, и @bortzmeyer должен опубликовать другой ответ.

Теоретически вы могли бы придумать невероятно длинное и сложное доказательство Меркла, чтобы доказать контракту, что данная учетная запись имеет определенное значение в определенном номере блока, и вы могли бы немного повысить эффективность, если бы у вас был контракт, в котором хранится столько же предыдущих максимально блокировать хэши; однако это было бы довольно громоздко реализовать. Помимо этого, самым простым краткосрочным решением может быть просто выяснить, к каким именно значениям вы хотите иметь постоянный доступ из контракта, и просто постоянно хранить их в хранилище в массиве. Если вы просто хотите получить доступ к прошлым значениям из интерфейса, я рекомендую события, как упоминалось выше.

Возможно, низкотехнологичный вариант — установить новый экземпляр geth и загрузить блокчейн из основного экземпляра geth, проверяя данные в каждом новом блоке. Я не знаю, можно ли контролировать/останавливать синхронизацию после каждого блока. Если подумать, я, возможно, только что ELI5ed то, что говорил предыдущий постер.
Привет, Виталик и другие, спасибо. Я видел Events и уже имел в виду хранить перезаписанные переменные в другом массиве, но я думаю, что это сильно увеличит необходимое хранилище. Поскольку для этого варианта использования предыдущие значения не являются информацией, которую мне нужно использовать в самом контракте, как вы думаете, с помощью внешнего программного обеспечения, такого как блок-эксплорер, я мог бы легко получить историю значений переменных? Если я использую ether.camp, я могу видеть модификации хранилища для каждой транзакции. Как вы думаете, это было бы лучше, чем хранить предыдущие значения в другом массиве внутри контракта?

Это зависит от ваших потребностей, но я так понимаю, что-то вроде: debug.setHead(800000)может помочь.

Он «перематывает» последний блок вашего блокчейна до блока 800 000 (просто пример).

Он не знает, работает ли он для запроса прошлых смарт-контрактов, но я полагаю, что он будет работать: я часто использовал его (в консоли geth JS) для запроса прошлых балансов для определенных адресов.

Проблема в том, что тогда вам нужно повторно синхронизировать до последнего блока: вот почему я говорю, что это зависит от вашего варианта использования. Этот метод не масштабируется, но если ваши требования просты, этого может быть достаточно.

Например, если я хочу запросить баланс адреса (адрес, который может быть смарт-контрактом, так что это более сложно, чем просто проверка количества ETH в «обычном» адресе), примерно раз в неделю я начинаю с текущего блока (последнего one), затем я отлаживаю.setHead(...) за неделю до текущей даты, затем за неделю до этого, промываю и повторяю. Это очень быстро. Но в конце вам нужно повторно синхронизироваться с того места, где вы оказались.