Выполнение операций отправки атомарно

У меня есть три адреса a1, a2, a3на которые я хочу отправить треть баланса контракта за минимальное количество транзакций (в идеале всего одну) и без хранения. Я мог бы иметь следующую функцию:

function withdraw() {
    uint split = this.balance/3;

    a1.send(split);
    a2.send(split);
    a3.send(split);
}

Проблема в атомарности и газовыделении. Насколько я понимаю , исключение out-of-gas вернет операции, но sendоперации являются исключением:

Когда во вложенном вызове возникают исключения, они автоматически «всплывают» (т. е. исключения выбрасываются повторно). Исключениями из этого правила являются sendи низкоуровневые функции call, delegatecallи callcode— те возвращают falseв случае исключения вместо «всплывания».

Так что можно a1многократно коллировать withdraw()с достаточным количеством газа для первого send, тем самым сливая себе контракт.

Можно попробовать разработать какой-нибудь механизм, чтобы вызывающий абонент уходил последним. Например:

function withdraw() {
    if(msg.sender != a1 && msg.sender != a2 && msg.sender != a3) return;
    uint split = this.balance/3;

    if(msg.sender != a1) a1.send(split);
    if(msg.sender != a2) a2.send(split);
    if(msg.sender != a3) a3.send(split);

    // msg.sender goes last
    msg.sender.send(split);
}

Проблема здесь в том, что a1и a3в сговоре можно обмануть a2. В самом деле, a3можно повторить звонок withdrawс достаточным количеством газа, чтобы слить контракт до a1.

Я думаю, что можно отслеживать изъятия, которые произошли с хранилищем, но это кажется излишним.

Есть ли способ выполнить несколько sendопераций атомарно, подобно тому, как Биткойн может иметь несколько UTXO в одной транзакции?

Ответы (1)

Вы правы насчет риска DoS от одного из получателей.

Как правило, ограничивайте взаимодействие функций одним недоверенным контрактом за раз.

Думайте о своей withdraw()функции, как о работе только с одним из них за раз. Они будут индивидуально требовать причитающееся.

Что-то вроде:

function withdraw(uint amount) returns(bool success) {
  amount = balance[msg.sender]
  balance[msg.sender] = 0;
  if(amount==0) throw;
  LogWithdrawal(msg.sender, amount);
  msg.sender.transfer(amount);
  return true;
}

Надеюсь, поможет.