¿Qué hace msg.sender.call() en Solidity?

Hola, estaba revisando la documentación de solidez. Había un código que no pude entender, incluso después de investigar mucho, no pude encontrar un resultado satisfactorio. El código es el siguiente:

contract Mutex {
  bool locked;
modifier noReentrancy() {
    require(!locked);
    locked = true;
    _;
    locked = false;
}

/// This function is protected by a mutex, which means that
/// reentrant calls from within `msg.sender.call` cannot call `f` again.
/// The `return 7` statement assigns 7 to the return value but still
/// executes the statement `locked = false` in the modifier.
  function f() public noReentrancy returns (uint) {
    require(msg.sender.call());
   return 7;
  }
}

¿Qué msg.sender.call()hace? esta llamando de f()nuevo? Si es así, ¿entonces cómo?

Respuestas (2)

Llama a la función de reserva anónima en msg.sender.

En un ataque de reentrada típico, sería algo así como una withdrawfunción haciendo msg.sender.call.value(1 ether)(). La persona que llama (un contrato inteligente) volvería a llamar a la función, de ahí el ataque de "reentrada". En este fragmento, callno parece estar haciendo nada útil, pero solo está ahí para mostrar que las lockedvariables protegen contra la reentrada.

Los comentarios no son para una discusión extensa; esta conversación se ha movido a chat .

msg.sender.call()llama a la en msg.sender.

Aquí hay un ejemplo que se extiende con una canBeAttackedfunción.

contract Mutex {
  bool locked;
modifier noReentrancy() {
    require(!locked);
    locked = true;
    _;
    locked = false;
}

  function canBeAttacked() public returns (uint) {
    require(msg.sender.call.value(1 ether)());
   return 7;
  }

/// This function is protected by a mutex
  function f() public noReentrancy returns (uint) {
    require(msg.sender.call());
   return 7;
  }
}

2 cosas a tener en cuenta del código anterior.

  1. Un atacante puede crear un contrato que tenga un respaldo

de:

// attacker fallback
function() {
  Mutex(msg.sender).canBeAttacked();
}

El contrato puede tener otra función que llame a canBeAttacked(), y esto seguirá reingresando canBeAttacked()y enviando al atacante 1 éter cada vez. (El respaldo necesita más código para evitar un bucle infinito y quedarse sin gasolina). Este es un ejemplo de un ataque de reentrada.

Ahora imagine un contrato de atacante que tiene una reserva de:

// attacker fallback
function() {
  Mutex(msg.sender).f();
}
  1. Cuando el atacante llama f(), el respaldo del atacante fallará porque lockedes true, por lo require(!locked)que generará una excepción y revertirá la transacción completa.

También tenga en cuenta que noReentrancyevita que un atacante llame f()y, en el respaldo del atacante, llame a canBeAttacked. (Pero pueden atacar como el n. ° 1 anterior).

No explicaste por qué alguien querría llamar a la función de respaldo del remitente en primer lugar. ¿Para qué sirve esto?
@LukeHutchison Tiene razón en que, en casi todos los casos, uno no quiere llamar a la función de reserva. Es posible que se pregunte algo similar. Me preguntaba por qué enviar ether a un contrato siempre invoca la función de respaldo. ethereum.stackexchange.com/questions/1505/…