Всё началось с проблемы с которой мы столкнулись в BitClave: во время подготовки нашего ICO некоторый объем криптовалюты ETH (эфир) был отправлен на адрес смарт-контракта, который ранее был задеплоен в тестовую сеть Ethereum. Деньги были отправлены в главной сети на адрес не относящийся ни к одному приватному ключу, ни к одному смарт-контракту в этой сети. Сначала нам показалось, что мы просто выкинули $2000 без единой возможности вернуть наши средства



История началась с того, что мой коллега спросил у меня приватный ключ к адресу 0x9c86825280b1d6c7dB043D4CC86E1549990149f9. Я отправил ему приватный ключ к адресу 0x231A3925A014EF0a11a0DC5c33bF7cdB3bd9919f, с которого был загружен смарт-контракт по первому адресу. Мы обсудили проблему и пришли к выводу, что вернуть отправленные деньги нет никакой возможности

Каждый смарт-контракт, загружаемый в сеть Ethereum имеет уникальный адрес, который на первый взгляд выглядит как случайный, но я выяснил как именно адрес генерируется при загрузке в сеть: ethereum.stackexchange.com/a/761/3032. Проще говоря адрес загрузки – это хеш адреса отправителя транзакции и значения nonce (равного числу исходящих транзакций с этого адреса):

deployed_address = sha3(rlp.encode([sender, nonce]))


Это натолкнуло меня на мысль использовать тот же самый кошелек (тот что я использовал в тестовой сети) для загрузки нового смарт-контракта в основную сеть. Я разработал смарт-контракт простейшего кошелька, позволяющего лишь отобразить баланс и перевести утраченные средства:

contract SimpleWallet is Ownable {
    function () public payable {
    }
    function weiBalance() public constant returns(uint256) {
        return this.balance;
    }
    function claim(address destination) public onlyOwner {
        destination.transfer(this.balance);
    }
}

Затем я нашел транзакцию в тестовой сети, с помощью которой была произведена загрузка исходного контракта: 0xc4c32a3d97dbd691eb3646e4c0c404e899a632010bc48d7182d75bef6803b7bc и обнаружил, что поле nonce было равно 13. Я пополнил кошелек на 0.03 ETH в главной сети и стал заливать новый смарт-контракт раз за разом, до тех пор пока nonce не вырос с 0 до 13. И всё, я получил смарт-контракт загруженный по желаемому адресу! Тут мы можем наблюдать 2 транзакции с одинаковым nonce равным 13, который загрузили 2 различных смарт-контракта в 2 разные сети по идентичным адресам с разницей в 5 дней:


Средства были успешно получены нами после вызова метода claim, свежезалитого смарт-контракта.

Также обратите внимание, что смарт-контракт был залит в сеть на 2 дня позже того, как на него поступили средства:



Кратко. Мы отправили деньги в основной сети Ethereum на адрес смарт-контракта, который был залит в тестовую сеть Ethereum. Мы использовали тот же самый кошелёк для загрузки совершенно другого смарт-контракта в основную сеть Ethereum несколько раз, пока у транзакции поле nonce не достигло значения 13, которое как раз использовалось для загрузки смарт-контракта в тестовую сеть. Затем мы вызвали специальный метод нового смарт-контаркта, который позволил нам вывести средства на наш кошелёк. Получилось, что мы загрузили смарт-контракт по адресу, на котором его уже дожидались средства

P.S. Голосуйте апвоутом за возможность добавления Emoji в статьи на Хабре

Комментарии (10)


  1. site6893
    02.11.2017 12:33

    А вы не пробовали sendRawTransaction уже сразу с заданым нужным значением nonce?


    1. k06a Автор
      02.11.2017 12:41

      Я решил что отправить 14 транзакций старым проверенным способом будет быстрее нежели разбираться с тем, как вручную передать туда все параметры. Тем более не хотелось накосячить, ведь есть ограниченное число попыток :)


      1. site6893
        02.11.2017 12:48

        еще я не совсем понял из этого: «адрес загрузки – это хеш адреса отправителя транзакции и значения nonce (равного числу исходящих транзакций с этого адреса)»

        хеш транзакции какой вы использовали/откуда брали чтобы сгенерить адрес загрузки?


        1. k06a Автор
          02.11.2017 12:51

          Адрес загрузки геренируется сам по себе, я просто узнал о том от чего он зависит. Зависит от адреса кошелька с которого производится загрузка и от числа транзакций выполненных этим кошельком.


          1. site6893
            02.11.2017 13:10
            +1

            а блин, я понял, там 2 параметра а не 3 )))


    1. splix
      02.11.2017 21:08

      Транзакция не попадет в блокчейн пока в ней не окажутся транзакции с предыдущим nonce


  1. Chugumoto
    02.11.2017 14:05

    М — магия :)
    плюсовать пока не дано…
    но познавательно :)


  1. vvagr
    03.11.2017 04:17

    Интересные вещи, которые можно делать с помощью позднего размещения контрактов по заранее известным адресам, описаны вот тут: hackingdistributed.com/2017/08/28/submarine-sends


  1. Slann
    03.11.2017 13:11

    Интересно, а если бы вы не делали транзакции со своего адреса (то есть не увеличивали nonce), то можно было бы вернуть эфир не через 2 дня, а через сколько угодно?


    1. k06a Автор
      03.11.2017 13:13

      Nonce увеличивается с каждой транзакцией в каждой из сетей отдельно. 2 дня прошло прежде чем я узнал о проблеме и стал её решать. Вроде бы в пятницу отправили деньги, а в понедельник проблему стали решать :)