Haciendo operaciones de envío atómicamente

Tengo tres direcciones a1, a2, a3a las que quiero enviar un tercio del saldo de un contrato en el mínimo número de transacciones (idealmente una sola) y sin almacenamiento. Podría tener una función de la siguiente manera:

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

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

El problema está relacionado con la atomicidad y la falta de gas. Según tengo entendido , una excepción de falta de gas revertirá las operaciones, pero sendlas operaciones son una excepción:

Cuando se producen excepciones en una subllamada, "surgen" (es decir, las excepciones se vuelven a generar) automáticamente. Las excepciones a esta regla son sendy las funciones de bajo nivel call, delegatecally callcode– aquellas regresan falseen caso de una excepción en lugar de “burbujear”.

Por lo tanto, es posible a1llamar repetidamente withdraw()con suficiente gasolina para el primero send, agotando así el contrato por sí mismo.

Se podría intentar hacer algún diseño de mecanismo para que la persona que llama se retire en último lugar. Por ejemplo:

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);
}

El problema aquí es que a1y a3puede coludirse para hacer trampa a2. De hecho, a3puede repetir la llamada withdrawcon la gasolina suficiente para agotar el contrato a1.

Supongo que es posible realizar un seguimiento de los retiros que se han producido con el almacenamiento, pero eso parece una exageración.

¿Hay alguna forma de realizar varias sendoperaciones de forma atómica, similar a cómo Bitcoin puede tener múltiples UTXO en una sola transacción?

Respuestas (1)

Tiene razón sobre el riesgo de DoS de uno de los destinatarios.

Como regla general, limite las interacciones de funciones a un contrato que no sea de confianza a la vez.

Piense en su withdraw()función como si tratara solo con uno de ellos a la vez. Reclamarán individualmente lo que les corresponde.

Algo como:

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;
}

Espero eso ayude.