Función isContract que usa el ensamblado EVM para obtener el tamaño del código de la dirección

Mi problema es que necesito asegurarme de que mi contrato procese solo las transacciones provenientes de EOA (cuentas de propiedad externa). Para esto necesito determinar el tipo de cuenta con la que estoy tratando. En esta pregunta encontré una solución, aquí está el código:

function isContract(address addr) returns (bool) {
  uint size;
  assembly { size := extcodesize(addr) }
  return size > 0;
}

Como no entiendo el ensamblaje en absoluto, no tengo idea de lo que está pasando allí.

  • ¿Funciona esta función como se describe?
  • ¿Hay alguna manera de que un contrato lo engañe?
  • ¿Funcionará esta función cuando la red haga la transición a POS?

¡Gracias!

Una pregunta separada que explique por qué "Necesito asegurarme de que mi contrato procese solo las transacciones provenientes de EOA" también puede ser útil. En la mayoría de los casos, no se recomienda discriminar contratos porque su sistema no será tan flexible e interoperable. Por ejemplo, las cadenas de bloques pueden ser útiles con IoT porque pueden permitir que "cosas" realicen y reciban pagos.

Respuestas (2)

EDITAR: la función devolverá falso si se invoca desde el constructor de un contrato (porque el contrato aún no se ha implementado).

El código debe usarse con mucho cuidado, si es que se usa, para evitar ataques de seguridad como:

https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide/ ( archivo )

Para repetir :

No utilice la verificación EXTCODESIZE para evitar que los contratos inteligentes llamen a una función. Esto no es infalible, puede ser subvertido por una llamada de constructor, debido al hecho de que mientras el constructor se está ejecutando, EXTCODESIZE para esa dirección devuelve 0.

Consulte el código de muestra de un contrato que engaña a EXTCODESIZE para que devuelva 0.


Si desea asegurarse de que una cuenta de propiedad externa (EOA) está llamando a su contrato, una forma sencilla es require(msg.sender == tx.origin). Sin embargo, impedir un contrato es un antipatrón con consideraciones de seguridad e interoperabilidad .

Esto deberá revisarse cuando se implemente la abstracción de cuenta.


Respuesta previa.

Sí, la función funciona.

EXTCODESIZE es el código de operación EVM para obtener el tamaño del código en una dirección.

0x3b EXTCODESIZE Obtener el tamaño del código de una cuenta

Un contrato no puede engañar a EXTCODESIZE para que devuelva cero para el tamaño del contrato.

Además, una cuenta de propiedad externa (EOA) no puede engañar a este código para que piense que la EOA es un contrato. Esto se debe a que un EOA no tiene código (longitud cero) y no es más factible poner código en un EOA que encontrar la clave privada de un contrato.

La función no tiene ninguna dependencia de Prueba de participación y seguirá funcionando.

Esto no depende de POS, pero cambiará debido a la abstracción de cuenta en Serenity. No confiaría en que esto continúe funcionando para siempre, y estoy de acuerdo en que esencialmente no hay ninguna razón por la que deba importar si una cuenta es un contrato o no.
@TjadenHess ¿Qué parte cambiará? ¿O debería ser una pregunta aparte? Parece fiable que la definición de un contrato sería una cuenta que tiene código...
En Serenity, la idea es que cada cuenta sea un contrato, es decir, las cuentas normales tendrán un código de verificación ECDSA.
~EXTCODESIZE` suele ser cero en el constructor de un contrato. Es posible que desee consultar esto: reddit.com/r/ethereum/comments/916xni/…
Esta respuesta es incorrecta, consulte la respuesta de PhABC a continuación. Me pregunto cuántos desarrolladores se han equivocado al escribir contratos vulnerables debido a esta publicación.
@ Adibas03 y DanielQue Gracias por la alarma y la respuesta actualizada. Esta no será la última vez que un hacker encuentre una falla en una técnica.
@TjadenHess el blog Serenity se publicó hace casi 3 años. Hasta donde yo sé, los EOA todavía están en juego. ¿Se ha abandonado la implementación de un solo tipo de cuenta? Me gustaría restringir mi función solo a llamadas de contratos externos (sin EOA), pero si extcodesizepronto devolverá valores distintos de cero para lo que alguna vez fueron cuentas externas, necesito evitar este atajo.
La abstracción de cuentas todavía está en proceso, puede ver una discusión más reciente aquí: ethresear.ch/t/…

Este fragmento de código es correcto, aunque parece importante tener en cuenta que EXTCODESIZEdevolverá 0 cuando se llame dentro constructorde un contrato, ya que el contrato aún no se ha creado. Por lo tanto, si msg.senderes un contrato, el isContract()modificador podría devolver falso si la función de su contrato se llama dentro del constructor del msg.sendercontrato. Este vector de ataque se ha utilizado en varias ocasiones, como drenar el "cazo aéreo" ETH de FoMo3D .

Una solución a esto sería (A) requerir msg.senderque proporcione una firma ECDSA para su primera llamada de función, (B) msg.sendersolicitar que llame a su contrato en dos bloques separados o (C) agregar require(msg.sender == tx.origin). Este último sería el más económico, seguido de ECDSA y el más costoso sería el proceso de 2 pasos.

¿Podría dar más detalles sobre su solución dada? ¡Gracias!