¿Cómo puedo verificar que un contrato en blockchain coincida con el código fuente?

Dado el código fuente (Solidity) de un contrato inteligente, ¿hay alguna forma de compilar el código de manera determinista y compararlo con el código en la cadena de bloques? Quiero verificar que el contrato hace lo que dice el código fuente y que el autor no lo ha manipulado.

Una pregunta relacionada en IT Security SE.

Respuestas (7)

AFAIK, la mejor manera de hacer esto en este momento es compilar el código fuente nuevamente con exactamente la misma versión del compilador que usó el autor (por lo que esto es algo que debe revelarse) y comparar el código de bytes.

Entonces, la coincidencia que debe verificar es el código de bytes compilado contra los datos de la creación del contrato tx .

No 100%, sigue siendo problemático github.com/trufflesuite/truffle-compile/issues/77
¡Muchas gracias! El contrato se creó en v5.17.0, pero elegí v5.0.0 para verificar el código del contrato, por lo que arrojó errores. Finalmente, cambio la versión para que sea la misma con el compilador integrado v5.17.0.

Para agregar a la respuesta de @thomas-bertani, hoy etherchain.org lanzó una herramienta de verificación para los contratos de Ethereum

Aquí está el texto de la página:

La verificación del código fuente brinda transparencia a los usuarios que interactúan con los contratos inteligentes. Al cargar el código fuente, Etherscan comparará el código compilado con el de la cadena de bloques. Al igual que los contratos, un "contrato inteligente" debe proporcionar a los usuarios finales más información sobre lo que están "firmando digitalmente" y darles la oportunidad de auditar el código para verificar de forma independiente que realmente hace lo que se supone que debe hacer.

El enlace no funciona. ¿Ha cambiado?
El enlace no funciona, está caído.
Edité la respuesta para actualizar el enlace y el texto.

Actualmente el flujo de trabajo es bastante molesto. Debe compilar el contrato con la misma versión del compilador y la misma configuración (busque el indicador "optimización = verdadero" ).

Ahora tenga en cuenta que el código de bytes resultante NO coincide con el código de bytes que está almacenado en la dirección. El motivo es que el contrato compilado contiene una parte de inicialización que se ejecuta solo una vez cuando el contrato se envía a la cadena. Entonces, el código de bytes almacenado en la cadena de bloques es el código sin la parte de inicialización.

Para verificar el código tienes dos opciones:

  1. Envíe el código compilado a una cadena de bloques (virtual) y luego verifique con el comando getCode el código resultante.
  2. Compare el código compilado con la carga útil de la transacción que creó el contrato.

Etherchain ayuda a hacer este proceso.

En principio, se podrían aplicar técnicas de coincidencia de patrones para identificar un contacto incluso si no se utiliza exactamente el mismo compilador. Un proyecto que está haciendo esto y, en general, vinculando contratos de alto nivel (Serpiente/Solidez) a direcciones de Ethereum es: Etherscrape

https://etherscan.io/verifyContract es una herramienta de verificación. Al proporcionar el código fuente de Solidity, comprueba si el código de bytes generado coincide con el código de bytes del contrato (bajo la dirección dada). el usuario debe elegir el mismo compilador y habilitar o deshabilitar la optimización.

Otro enfoque sería descompilar el código de bytes y compararlo con la fuente.

Hay una herramienta llamada Porosidad que hace precisamente eso.

Estoy bastante seguro de que esto no puede funcionar a ningún nivel de eficiencia, especialmente para contratos más grandes. Los nombres de las variables no se almacenan en el código de bytes, por lo que el descompilador debe crear sus propios nombres. Intentar ver si el código descompilado es el mismo que la fuente representada sería muy difícil, creo.
El Contrato ABI contiene todos los nombres de variables.
No siempre. He visto muchos que no.
Señalado. De hecho, esto haría que la descompilación no fuera práctica para contratos complejos.
No me di cuenta de que el descompilador usaba el ABI. Eso claramente lo hace mucho más fácil.

Se recomienda utilizar la herramienta de línea de comandos de código abierto ConsenSys recién lanzada, bytecode- verifier, paquete npm respectivo

Bytecode Verifier es una práctica herramienta de línea de comandos para verificar el código de bytes compilado localmente de un contrato de Solidity de destino contra su código de bytes real almacenado en Etheruem Blockchain proporcionado su dirección de contrato.

  • integridad/corrección del código de bytes : lo que realmente se almacena en la cadena se compila correctamente a partir de un contrato en particular, lo que podría ser útil en el caso de una implementación potencial no trivial de un contrato de titular de alto valor (por ejemplo, MultiSig Wallet), especialmente el contrato se implementa a través de un tercero plataforma de fiesta

  • Esfuerzo mínimo, fácil de usar : el compilador de solidity implica horas extras con cambios menores y algunos importantes, lo que complica la verificación del código de bytes. (como preguntas recurrentes de "el código de bytes no coincide" que se hacen en Ethereum Stack Exchange). Bytecode Verifier se ha probado con la última versión desde algunos de los contratos implementados más antiguos.

  • Compatible con Testnet : la mayoría de los proyectos se inician en TestNet antes de implementar el sistema de contratos en MainNet. Esta herramienta es compatible con Rinkeby, Kovan y Ropsten Testnet, que constituyen tres redes de prueba activas y bien mantenidas que utilizan la mayoría de los desarrolladores de Ethereum.

¡Espero eso ayude!

Así es como lo hago en un script web3.

Primero , compila el código.

const solc = require('solc');

//contract sources
const contractPath = "../contracts/";

const input = {
  "ContractName.sol" : fs.readFileSync(contractPath + 'contractName.sol', 'utf8'),
}

let solcOutput = await solc.compile({ sources: input }, 1);

ahora compare byteCode

let blockCode = await web3.eth.getCode(contractAddress);
let solcCode = '0x' + (solcOutput.contracts["contractName.sol:contracName"].runtimeBytecode);}
Esto no funciona, te faltan los parámetros del constructor y los datos auxiliares.
¿Cómo lo probaste? funciona para mí en todos los contratos de la red kyber.
@mdv ¿Necesita tener parámetros de constructor coincidentes? Veo respuestas contradictorias, pero supongo que no, ya que el constructor está diseñado para almacenar valores, no para determinar la existencia de funciones o variables de estado.