⚠️ ¡Advertencia! El siguiente artículo es un resumen sobre Mastering Ethereum, en consecuencia, me tomé la libertad de quitar las partes que no me parecieron relevantes y profundizar en lo que sí. Si tenés alguna sugerencia o corrección, no dudes en contactarme.
Transacciones - Parte 2
Continuamos analizando los campos estándar que contiene una transacción, en el capítulo anterior hablamos del nonce, gas limit y gas price. En este capítulo hablaremos del recipient, value, data y la firma digital.
Recipent o Destinatario
El destinatario de una transacción se especifica en el campo “to”. Esto contiene una dirección Ethereum de 20 bytes, que puede ser una dirección de EOA (cuenta externa controlada por una clave privada) o una dirección de contrato.
⚠️ ¡Advertencia! Ethereum no realiza una validación adicional de este campo. Cualquier valor de 20 bytes se considera válido. Si el valor de 20 bytes corresponde a una dirección sin una clave privada correspondiente o sin un contrato correspondiente, la transacción sigue siendo válida. Por lo tanto, esto sería el equivalente a quemar los tokens.
Value y Data
Una transición puede contener los dos tanto value como data, solo value, solo data, o ninguno de los dos.
- Una transacción que es solo un pago en ETH contiene solo un value.
- Una transacción que es una invocación a un contrato puede contener solo data
- También puede contener data y un value en ese caso estaría pagado por la invocación
- Una transacción que no tiene ninguno de los dos es posible, pero sería un desperdicio de gas porque no generaría ningún efecto.
Como EOA y los Contratos interpretan Value
Hay una diferencia en como se comporta las transacciones que incluyen value
dependiendo de si están destinadas a una EOA o a un contrato. En el caso de la EOA, se registra un cambio de estado y se agrega el valor enviado en la transacción.
Si la dirección de destino es un contrato, entonces la EVM ejecutará el contrato e intentará llamar a la función nombrada en el campo data
de la transacción.
Si no hay nada en el campo data
en tu transacción, la EVM llamará a una función fallback
y, si esa función es pagable, la ejecutará para determinar qué hacer a continuación.
Si no hay código en la función de fallback
, entonces el efecto de la transacción será aumentar el saldo del contrato, exactamente como un pago a una billetera.
Si no hay función fallback
o es non-payable
(por tanto, no acepta pagos), entonces la transacción se revertirá.
Como EOA y los Contratos interpretan Data
Cuando una transacción lleva un valor en el campo data
es probable que esté tratando de interactuar con un contrato, no porque sea imposible enviar una transacción a una EOA con un campo data, sino porque en este caso el protocolo ignora el contenido.
Ahora veamos qué efecto tiene incluir algo en el campo data
cuando estamos enviando una transacción a un smart contract.
Para esto tenemos que tomar en cuenta dos cosas:
Un selector de función: Los primeros 4 bytes del hash Keccak-256 del prototipo de la función. Esto permite que el contrato identifique de manera inequívoca qué función deseas invocar.
Los argumentos de la función: Los argumentos de la función, codificados según las reglas para los diversos tipos elementales definidos en la especificación ABI.
Un ejemplo de esto tomando la función withdraw
que implementamos en el contrato de faucet anteriormente sería el siguiente.
Tomamos la firma del método y le aplicamos él hash:
web3.utils.sha3("withdraw(uint256)");
>'0x2e1a7d4d13322e7b96f9a57413e1525c250fb7a9021cf91d1540d5b69f16a49f'
Seleccionamos los primeros 4 bytes, por lo tanto, sería 0x2e1a7d4d
o 2e1a7d4d
Ahora tenemos que calcular la cantidad a retirar y convertirlo a hexadecimal.
withdraw_amount = web3.utils.toWei(0.01, "ether");
>'10000000000000000'
withdraw_amount_hex = web3.utils.toHex(withdraw_amount);
>'0x2386f26fc10000'
Por último agregamos el selector de función con el parámetro en hexadecimal
Este seria el data para solicitar 0.01 ether a nuestro contrato faucet
2e1a7d4d000000000000000000000000000000000000000000000000002386f26fc10000
Transacción especial: Creación de contrato
Un tipo de traslación especial, o que tiene un comportamiento especial es la que crea un contrato en la blockchain.
Para registrar un contrato en la blockchain es necesario enviar una traslación a la dirección cero o zero address y enviar en el data
el bytecode del contrato compilado.
Si tomamos el ejemplo del contrato de facucet nuevamente:
Primero compilamos el contrato a binario
solc --bin Faucet.sol
>6060604052341561000f57600080fd5b60e58061001d6000396000f30060606040526004361060...
Ahora ejecutamos la traslación enviando eso en el campo data
src = web3.eth.accounts[0];
faucet_code = “0x6060604052341561000f57600080fd5b60e58061001d6000396000f300606...f0029”;
web3.eth.sendTransaction({from: src, to: 0, data: faucet_code, gas: 113558, gasPrice: 200000000000});
>“0x7bcc327ae5d369f75b98c0d59037eec41d44dfae75447fd753d9f2db9439124b”
Nota: Es una buena práctica especificar siempre un parámetro “to”, incluso en el caso de la creación de contratos con dirección cero, porque el costo de enviar accidentalmente tu ether a 0x0 y perderlo para siempre es demasiado alto. También debes especificar un “gasPrice” y un “gasLimit”.
Firma digital
Primero definamos que es una firma digital: es un esquema matemático para presentar la autenticidad de mensajes o documentos digitales. Una firma digital válida le da al destinatario motivo para creer que el mensaje fue creado por un remitente conocido (autenticación), que el remitente no puede negar haber enviado el mensaje (no repudio) y que el mensaje no fue alterado en tránsito (integridad).
Una firma digital cumple tres propósitos en el contexto de Ethereum.
- Demuestra que el firmante es el propietario de la cuenta (autenticación).
- Impide que la repudiación, la transacción, una vez que se ejecutó es irreversible (no repudio).
- Demuestra que los datos de la transacción no fueron adulterados (integridad)
Cómo Funcionan las Firmas Digitales
Una firma digital es un esquema matemático que consta de dos partes. La primera parte es un algoritmo para crear una firma, utilizando una clave privada (la clave de firma), a partir de un mensaje (que en nuestro caso es la transacción). La segunda parte es un algoritmo que permite a cualquiera verificar la firma utilizando solo el mensaje y una clave pública.
Creando una Firma Digital
En la implementación de ECDSA en Ethereum, el “mensaje” que se firma es la transacción, o más precisamente, el hash Keccak-256 de los datos codificados RLP de la transacción. La clave de firma es la clave privada del EOA. El resultado es la firma:
S i g = F sig ( F keccak256 ( m ) , k )
Donde:
-
k
es la clave privada de firma. -
m
es la transacción codificada en RLP. -
Fkeccak256
es la función hash Keccak-256. -
Fsig
es el algoritmo de firma. -
Sig
es la firma resultante.
La función Fsig produce una firma Sig que está compuesta por dos valores, comúnmente denominados r y s:
S i g = ( r , s )
Verificación de la Firma
Para verificar la firma, uno debe tener la firma (r y s), la transacción serializada y la clave pública que corresponde a la clave privada utilizada para crear la firma. Esencialmente, la verificación de una firma significa “solo el propietario de la clave privada que generó esta clave pública podría haber producido esta firma en esta transacción”.
El algoritmo de revisión de la firma toma el mensaje (es decir, un hash de la transacción para nuestro uso), la clave pública del firmante y la firma (valores r y s), y devuelve verdadero si la firma es válida para este mensaje y clave pública.
Nota: En Ethereum, la firma de una transacción no incluye quién la envió. Esto se puede calcular a partir de la firma. Se calculan dos posibles claves públicas a partir de la firma, luego se elige una basada en un valor especial llamado ‘v’. Este valor nos dice cuál de las dos claves públicas es la correcta.
Firma offline o Air-gap
Normalmente, cuando se envia una transacción en Ethereum, se crea, se firma y seenvia todo de una vez. Sin embargo, por razones de seguridad, es posible separar la firma y la transmisión en dos pasos. Primero, creamos una transacción sin firmar en una computadora conectada a internet. Luego, se trasnfiere esta transacción sin firmar a una computadora sin conexión a internet para firmarla. Finalmente, una vez que se tiene la transacción firmada, la trasnmitimos a la red Ethereum desde la computadora conectada a internet.
Este proceso se conoce como air-gap
y es una práctica de seguridad común. Puede realizarse en una computadora completamente desconectada de internet, en una red aislada, o sin ir mas lejos es lo que hacemos cuando usamos una hardware wallet para firmar, este proceso ofrece un nivel adicional de seguridad y es altamente recomendable implementarlo de ser posible.
EIP-155
El estándar EIP-155 especifica una codificación de transacción protegida contra ataques de repetición, que incluye un identificador de cadena dentro de los datos de la transacción, antes de la firma. Esto garantiza que las transacciones creadas para una blockchain (por ejemplo, la red principal de Ethereum) sean inválidas en otra blockchain (por ejemplo, Ethereum Classic o la red de prueba Ropsten). Por lo tanto, las transacciones transmitidas en una red no pueden ser repetidas en otra, de ahí el nombre del estándar.
Propagación de transacciones
La propagación de transacciones comienza con el nodo de Ethereum origen, creando (o recibiendo de forma offline) una transacción firmada. La transacción se valida y luego se transmite a todos los otros nodos de Ethereum que están directamente conectados al nodo origen.
En promedio, cada nodo de Ethereum mantiene conexiones con al menos 13 otros nodos, llamados sus vecinos. Cada nodo vecino valida la transacción tan pronto como la recibe. Si están de acuerdo en que es válida, almacenan una copia y la propagan a todos sus vecinos (excepto del que la recibieron).
Como resultado, la transacción se propaga desde el nodo origen, inundando la red, hasta que todos los nodos en la red tienen una copia de la transacción. Los nodos pueden filtrar los mensajes que propagan, pero por defecto propagan todos los mensajes de transacción válidos que reciben.
Conclusión
Las transacciones son el punto de partida de toda la actividad que ocurre en la red de Ethereum. Son el motor que inicia los cambios de estado en la blockchain, en el próximo capítulo pasaremos ver como programar en el lenguaje Solidity.
Referencias
1.Antonopoulos, A. M., & Wood, G. (2018). “Mastering Ethereum: Building Smart Contracts and DApps.” O’Reilly Media.