У меня есть три адреса 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 в одной транзакции?
Вы правы насчет риска 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;
}
Надеюсь, поможет.