Documentos de Académico
Documentos de Profesional
Documentos de Cultura
17 25/9/22 22:18
Votación
El siguiente contrato es bastante complejo, pero muestra muchas de las caracterís!cas de Solidity.
Implementa un contrato de votación. Por supuesto, los principales problemas del voto electrónico
son cómo asignar los derechos de voto a las personas correctas y cómo prevenir la manipulación.
No resolveremos todos los problemas aquí, pero al menos mostraremos cómo se puede hacer el
voto delegado para que el recuento de votos sea automá!co y completamente transparente al
mismo !empo.
La idea es crear un contrato por boleta, proporcionando un nombre corto para cada opción.
Entonces, el creador del contrato que se desempeña como presidente dará el derecho a votar a
cada dirección individualmente.
Las personas detrás de las direcciones pueden elegir votar por sí mismas o delegar su voto en una
persona en la que con"en.
At the end of the vo!ng !me, winningProposal() will return the proposal with the largest number
of votes.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/// @title Voting with delegation.
contract Ballot {
// This declares a new complex type which will
// be used for variables later.
// It will represent a single voter.
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 1 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 2 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
winningProposal_ = p;
}
}
}
Posibles mejoras
Currently, many transac!ons are needed to assign the rights to vote to all par!cipants. Moreover, if
two or more proposals have the same number of votes, winningProposal() is not able to register a
!e. Can you think of a way to fix these issues?
Subasta a ciegas
En esta sección, mostraremos lo fácil que es crear un contrato de subasta completamente ciego en
Ethereum. Comenzaremos con una subasta abierta donde todos puedan ver las ofertas que se
hacen y luego extenderemos este contrato a una subasta a ciegas donde no sea posible ver la oferta
real hasta que finalice el período de licitación.
La idea general del siguiente contrato de subasta simple es que todos puedan enviar sus ofertas
durante un período de licitación. Las ofertas ya incluyen el envío de dinero / Ether para vincular a
los licitadores a su oferta. Si se eleva la oferta más alta, el mejor postor anterior recuperará su
dinero. Después del final del período de licitación, el contrato debe ser llamado manualmente para
que el beneficiario reciba su dinero; los contratos no pueden ac!varse solos.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract SimpleAuction {
// Parameters of the auction. Times are either
// absolute unix timestamps (seconds since 1970-01-01)
// or time periods in seconds.
address payable public beneficiary;
uint public auctionEndTime;
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 3 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
if (highestBid != 0) {
// Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk
// because it could execute an untrusted contract.
// It is always safer to let the recipients
// withdraw their money themselves.
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit HighestBidIncreased(msg.sender, msg.value);
}
// 1. Conditions
if (block.timestamp < auctionEndTime)
revert AuctionNotYetEnded();
if (ended)
revert AuctionEndAlreadyCalled();
// 2. Effects
ended = true;
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 4 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
// 3. Interaction
beneficiary.transfer(highestBid);
}
}
Subasta a ciegas
La subasta abierta anterior se ex!ende a una subasta a ciegas a con!nuación. La ventaja de una
subasta a ciegas es que no hay presión de !empo hacia el final del período de licitación. Crear una
subasta a ciegas en una plataforma informá!ca transparente puede sonar como una contradicción,
pero la criptogra"a viene al rescate.
Durante el período de licitación, un postor en realidad no envía su oferta, sino solo una versión
hash de la misma. Dado que actualmente se considera prác!camente imposible encontrar dos
valores (suficientemente largos) cuyos valores hash son iguales, el postor se compromete a pujar
con eso. Después del final del período de licitación, los licitadores !enen que revelar sus ofertas:
envían sus valores sin cifrar y el contrato comprueba que el valor hash es el mismo que el
proporcionado durante el período de licitación.
Otro desa"o es cómo hacer que la subasta sea vinculante y ciega al mismo !empo: la única manera
de evitar que el postor simplemente no envíe el dinero después de ganar la subasta es hacer que lo
envíe junto con la oferta. Dado que las transferencias de valor no se pueden cegar en Ethereum,
cualquiera puede ver el valor.
El siguiente contrato resuelve este problema aceptando cualquier valor que sea mayor que la oferta
más alta. Dado que, por supuesto, esto solo se puede comprobar durante la fase de revelación,
algunas pujas pueden ser inválidas, y esto es a propósito (incluso proporciona una bandera explícita
para realizar pujas inválidas con altas transferencias de valor): los licitadores pueden confundir a la
competencia colocando varias pujas no válidas altas o bajas.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract BlindAuction {
struct Bid {
bytes32 blindedBid;
uint deposit;
}
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 5 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
_;
}
constructor(
uint biddingTime,
uint revealTime,
address payable beneficiaryAddress
) {
beneficiary = beneficiaryAddress;
biddingEnd = block.timestamp + biddingTime;
revealEnd = biddingEnd + revealTime;
}
/// Reveal your blinded bids. You will get a refund for all
/// correctly blinded invalid bids and for all bids except for
/// the totally highest.
function reveal(
uint[] calldata values,
bool[] calldata fakes,
bytes32[] calldata secrets
)
external
onlyAfter(biddingEnd)
onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
require(values.length == length);
require(fakes.length == length);
require(secrets.length == length);
uint refund;
for (uint i = 0; i < length; i++) {
Bid storage bidToCheck = bids[msg.sender][i];
(uint value, bool fake, bytes32 secret) =
(values[i], fakes[i], secrets[i]);
if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
// Bid was not actually revealed.
// Do not refund deposit.
continue;
}
refund += bidToCheck.deposit;
if (!fake && bidToCheck.deposit >= value) {
if (placeBid(msg.sender, value))
refund -= value;
}
// Make it impossible for the sender to re-claim
// the same deposit.
bidToCheck.blindedBid = bytes32(0);
}
payable(msg.sender).transfer(refund);
}
payable(msg.sender).transfer(amount);
}
}
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 6 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
{
if (ended) revert AuctionEndAlreadyCalled();
emit AuctionEnded(highestBidder, highestBid);
ended = true;
beneficiary.transfer(highestBid);
}
Comprar bienes de forma remota actualmente requiere que varias partes necesiten confiar entre sí.
La configuración más sencilla implica un vendedor y un comprador. Al comprador le gustaría recibir
un ar#culo del vendedor y al vendedor le gustaría recibir dinero (o un equivalente) a cambio. La
parte problemá!ca es el envío aquí: no hay forma de determinar con seguridad que el ar#culo llegó
al comprador.
Hay varias formas de resolver este problema, pero todas se quedan cortas de una forma u otra. En
el siguiente ejemplo, ambas partes !enen que poner el doble del valor del ar#culo en el contrato
como fideicomiso. Tan pronto como esto suceda, el dinero permanecerá bloqueado dentro del
contrato hasta que el comprador confirme que recibió el ar#culo. Después de eso, al comprador se
le devuelve el valor (la mitad de su depósito) y el vendedor recibe tres veces el valor (su depósito
más el valor). La idea detrás de esto es que ambas partes !enen un incen!vo para resolver la
situación o de lo contrario su dinero estará bloqueado para siempre.
Por supuesto, este contrato no resuelve el problema, pero ofrece una visión general de cómo se
pueden usar construcciones de estado !po máquina dentro de un contrato.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract Purchase {
uint public value;
address payable public seller;
address payable public buyer;
modifier onlyBuyer() {
if (msg.sender != buyer)
revert OnlyBuyer();
_;
}
modifier onlySeller() {
if (msg.sender != seller)
revert OnlySeller();
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 7 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
_;
}
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
event SellerRefunded();
buyer.transfer(value);
}
seller.transfer(3 * value);
}
}
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 8 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
Canal de micropago
Imagina que Alice quiere enviarle un poco de Ether a Bob, es decir, Alice es el remitente y Bob el
des!natario.
Alice solo necesita enviar mensajes firmados criptográficamente fuera de la cadena (por ejemplo,
por correo electrónico) a Bob y es similar a escribir cheques.
Alice y Bob usan firmas para autorizar transacciones, lo que es posible con contratos inteligentes en
Ethereum. Alice construirá un simple contrato inteligente que le permita transmi!r Ether, pero en
lugar de llamar a una función ella misma para iniciar un pago, dejará que Bob lo haga y, por lo tanto,
pagará la tarifa de transacción.
1. Alice implementa el contrato de ReceiverPays , adjuntando suficiente Ether para cubrir los
pagos que se realizarán.
2. Alice autoriza un pago firmando un mensaje con su clave privada.
3. Alice envía el mensaje firmado criptográficamente a Bob. No es necesario mantener el
mensaje en secreto (explicado más adelante), y el mecanismo para enviarlo no importa.
4. Bob reclama su pago presentando el mensaje firmado al contrato inteligente, verifica la
auten!cidad del mensaje y luego libera los fondos.
Creando la firma
Alice no necesita interactuar con la red Ethereum para firmar la transacción, el proceso está
completamente desconectado. En este tutorial, firmaremos mensajes en el navegador usando
web3.js y MetaMask, u!lizando el método descrito en EIP-712, ya que proporciona una serie de
otros beneficios de seguridad.
" Nota
The web3.eth.personal.sign prepends the length of the message to the signed data. Since we
hash first, the message will always be exactly 32 bytes long, and thus this length prefix is always
the same.
Qué firmar
Para un contrato que cumpla con los pagos, el mensaje firmado debe incluir:
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 9 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
Another type of replay a$ack can occur when the owner deploys a ReceiverPays smart contract,
makes some payments, and then destroys the contract. Later, they decide to deploy the
RecipientPays smart contract again, but the new contract does not know the nonces used in the
previous deployment, so the a$acker can use the old messages again.
Alice can protect against this a$ack by including the contract’s address in the message, and only
messages containing the contract’s address itself will be accepted. You can find an example of this
in the first two lines of the claimPayment() func!on of the full contract at the end of this sec!on.
Argumentos de embalaje
Now that we have iden!fied what informa!on to include in the signed message, we are ready to
put the message together, hash it, and sign it. For simplicity, we concatenate the data. The
ethereumjs-abi library provides a func!on called soliditySHA3 that mimics the behaviour of
Solidity’s keccak256 func!on applied to arguments encoded using abi.encodePacked . Here is a
JavaScript func!on that creates the proper signature for the ReceiverPays example:
In general, ECDSA signatures consist of two parameters, r and s . Signatures in Ethereum include
a third parameter called v , that you can use to verify which account’s private key was used to sign
the message, and the transac!on’s sender. Solidity provides a built-in func!on ecrecover that
accepts a message along with the r , s and v parameters and returns the address that was used
to sign the message.
Signatures produced by web3.js are the concatena!on of r , s and v , so the first step is to split
these parameters apart. You can do this on the client-side, but doing it inside the smart contract
means you only need to send one signature parameter rather than three. Spli%ng apart a byte array
into its cons!tuent parts is a mess, so we use inline assembly to do the job in the splitSignature
func!on (the third func!on in the full contract at the end of this sec!on).
The smart contract needs to know exactly what parameters were signed, and so it must recreate
the message from the parameters and use that for signature verifica!on. The func!ons prefixed
and recoverSigner do this in the claimPayment func!on.
El contrato completo
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 10 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract ReceiverPays {
address owner = msg.sender;
constructor() payable {}
payable(msg.sender).transfer(amount);
}
assembly {
// first 32 bytes, after the length prefix.
r := mload(add(sig, 32))
// second 32 bytes.
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes).
v := byte(0, mload(add(sig, 96)))
}
Alice ahora construye una implementación simple pero completa de un canal de pago. Los canales
de pago u!lizan firmas criptográficas para realizar transferencias repe!das de Ether de forma
segura, instantánea y sin comisiones de transacción.
Los canales de pago permiten a los par!cipantes realizar transferencias repe!das de Ether sin usar
transacciones. Esto significa que puede evitar los retrasos y las tarifas asociadas con las
transacciones. Vamos a explorar un sencillo canal de pago unidireccional entre dos partes (Alice y
Bob). Implica tres pasos:
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 11 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
1. Alice financia un contrato inteligente con Ether. Esto "abre" el canal de pago.
2. Alice firma mensajes que especifican cuánto de ese éter se debe al des!natario. Este paso se
repite para cada pago.
3. Bob "cierre" el canal de pago, re!rando su parte del Éter y enviando el resto de vuelta al
remitente.
" Nota
Solo los pasos 1 y 3 requieren transacciones de Ethereum, el paso 2 significa que el remitente
transmite un mensaje firmado criptográficamente al des!natario a través de métodos fuera de la
cadena (por ejemplo, correo electrónico). Esto significa que solo se requieren dos transacciones
para admi!r cualquier número de transferencias.
Se garan!za a Bob que recibirá sus fondos porque el contrato inteligente engarenta el Ether y honra
un mensaje firmado válido. El contrato inteligente también impone un !empo de espera, por lo que
Alice !ene la garan#a de recuperar sus fondos eventualmente incluso si el des!natario se niega a
cerrar el canal. Depende de los par!cipantes en un canal de pago decidir cuánto !empo mantenerlo
abierto. Para una transacción de corta duración, como pagar un cibercafé por cada minuto de
acceso a la red, el canal de pago puede mantenerse abierto durante un período de !empo limitado.
Por otro lado, para un pago recurrente, como pagar a un empleado un salario por hora, el canal de
pago puede mantenerse abierto durante varios meses o años.
To open the payment channel, Alice deploys the smart contract, a$aching the Ether to be escrowed
and specifying the intended recipient and a maximum dura!on for the channel to exist. This is the
func!on SimplePaymentChannel in the contract, at the end of this sec!on.
Hacer pagos
Alice realiza los pagos enviando mensajes firmados a Bob. Este paso se realiza completamente fuera
de la red Ethereum. Los mensajes son firmados criptográficamente por el remitente y luego se
transmiten directamente al des!natario.
La dirección del contrato inteligente, u!lizada para evitar ataques de repe!ción entre
contratos.
La can!dad total de éter que se le debe al des!natario hasta ahora.
Un canal de pago se cierra solo una vez, al final de una serie de transferencias. Debido a esto, solo
se canjea uno de los mensajes enviados. Esta es la razón por la que cada mensaje especifica una
can!dad total acumulada de éter adeudado, en lugar de la can!dad del micropago individual.
Naturalmente, el des!natario elegirá canjear el mensaje más reciente porque es el que !ene el total
más alto. El nonce por mensaje ya no es necesario, porque el contrato inteligente solo cumple con
un solo mensaje. La dirección del contrato inteligente todavía se u!liza para evitar que se u!lice un
mensaje des!nado a un canal de pago para un canal diferente.
Aquí está el código JavaScript modificado para firmar criptográficamente un mensaje de la sección
anterior:
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 12 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
When Bob is ready to receive his funds, it is !me to close the payment channel by calling a close
func!on on the smart contract. Closing the channel pays the recipient the Ether they are owed and
destroys the contract, sending any remaining Ether back to Alice. To close the channel, Bob needs
to provide a message signed by Alice.
The smart contract must verify that the message contains a valid signature from the sender. The
process for doing this verifica!on is the same as the process the recipient uses. The Solidity
func!ons isValidSignature and recoverSigner work just like their JavaScript counterparts in the
previous sec!on, with the la$er func!on borrowed from the ReceiverPays contract.
Solo el des!natario del canal de pago puede llamar a la función de close , que naturalmente pasa el
mensaje de pago más reciente porque ese mensaje conlleva el total adeudado más alto. Si al
remitente se le permi!era llamar a esta función, podría proporcionar un mensaje con una can!dad
menor y engañar al des!natario de lo que se le debe.
The func!on verifies the signed message matches the given parameters. If everything checks out,
the recipient is sent their por!on of the Ether, and the sender is sent the rest via a selfdestruct .
You can see the close func!on in the full contract.
Bob can close the payment channel at any !me, but if they fail to do so, Alice needs a way to
recover her escrowed funds. An expira!on !me was set at the !me of contract deployment. Once
that !me is reached, Alice can call claimTimeout to recover her funds. You can see the
claimTimeout func!on in the full contract.
Después de llamar a esta función, Bob ya no puede recibir ningún Ether, por lo que es importante
que Bob cierre el canal antes de que se alcance el vencimiento.
El contrato completo
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 13 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract SimplePaymentChannel {
address payable public sender; // The account sending payments.
address payable public recipient; // The account receiving the payments.
uint256 public expiration; // Timeout in case the recipient never closes.
/// the recipient can close the channel at any time by presenting a
/// signed amount from the sender. the recipient will be sent that amount,
/// and the remainder will go back to the sender
function close(uint256 amount, bytes memory signature) external {
require(msg.sender == recipient);
require(isValidSignature(amount, signature));
recipient.transfer(amount);
selfdestruct(sender);
}
expiration = newExpiration;
}
/// if the timeout is reached without the recipient closing the channel,
/// then the Ether is released back to the sender.
function claimTimeout() external {
require(block.timestamp >= expiration);
selfdestruct(sender);
}
/// All functions below this are just taken from the chapter
/// 'creating and verifying signatures' chapter.
assembly {
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
// second 32 bytes
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 14 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
" Nota
The func!on splitSignature does not use all security checks. A real implementa!on should
use a more rigorously tested library, such as openzepplin’s version of this code.
Verificación de pagos
1. Verifique que la dirección del contrato en el mensaje coincida con el canal de pago.
2. Verifique que el nuevo total sea la can!dad esperada.
3. Verifique que el nuevo total no exceda la can!dad de éter depositada.
4. Verifique que la firma sea válida y provenga del remitente del canal de pago.
We’ll use the ethereumjs-u!l library to write this verifica!on. The final step can be done a number
of ways, and we use JavaScript. The following code borrows the constructPaymentMessage func!on
from the signing JavaScript code above:
Contratos modulares
A modular approach to building your contracts helps you reduce the complexity and improve the
readability which will help to iden!fy bugs and vulnerabili!es during development and code review.
If you specify and control the behaviour of each module in isola!on, the interac!ons you have to
consider are only those between the module specifica!ons and not every other moving part of the
contract. In the example below, the contract uses the move method of the Balances library to
check that balances sent between addresses match what you expect. In this way, the Balances
library provides an isolated component that properly tracks balances of accounts. It is easy to verify
that the Balances library never produces nega!ve balances or overflows and the sum of all
balances is an invariant across the life!me of the contract.
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 15 de 16
Solidez por ejemplo - Documentación de Solididad 0.8.17 25/9/22 22:18
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
library Balances {
function move(mapping(address => uint256) storage balances, address from, address to,
uint amount) internal {
require(balances[from] >= amount);
require(balances[to] + amount >= balances[to]);
balances[from] -= amount;
balances[to] += amount;
}
}
contract Token {
mapping(address => uint256) balances;
using Balances for *;
mapping(address => mapping (address => uint256)) allowed;
function transferFrom(address from, address to, uint amount) external returns (bool
success) {
require(allowed[from][msg.sender] >= amount);
allowed[from][msg.sender] -= amount;
balances.move(from, to, amount);
emit Transfer(from, to, amount);
return true;
}
https://docs.soliditylang.org/en/v0.8.17/solidity-by-example.html#modular-contracts Página 16 de 16