Skip to content

Commit

Permalink
Merge pull request CryptozombiesHQ#119 from vacilagodzila/patch-5
Browse files Browse the repository at this point in the history
Lesson 4 Translated to Spanish (completed)
  • Loading branch information
enlight authored Feb 26, 2018
2 parents 27f3484 + 3e19260 commit 40fedb5
Show file tree
Hide file tree
Showing 14 changed files with 168 additions and 162 deletions.
10 changes: 5 additions & 5 deletions es/4/00-overview.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: Zombie Battle System
title: Sistema de Batalla Zombie
header: "Lesson 4: Zombie Battle System"
roadmap: roadmap4.jpg
---

The time has come, human...
Ha llegado el momento, humano...

Time to make your zombies FIGHT!
¡Es hora de hacer que tus zombies PELEEN!

But zombie battles aren't for the faint of heart...
Pero las batallas zombie no son para aquellos con problemas de corazón...

In this lesson, we're going to be putting together a lot of the concepts you've learned in previous chapters to build out a zombie battle function. We're also going to learn about **payable** functions, and how to build DApps that can accept money from players.
En esta lección vamos estar ensamblando muchos conceptos que ha aprendido en capítulos anteriores para construir una función de batalla zombie. También vamos a aprender sobre funciones **payable**, y cómo construir DApps que puedan aceptar dinero de los jugadores.
18 changes: 9 additions & 9 deletions es/4/battle-01.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Zombie Battles
title: Batallas Zombie
actions: ['checkAnswer', 'hints']
requireLogin: true
material:
Expand Down Expand Up @@ -207,18 +207,18 @@ material:
}
---

Now that we've learned about payable functions and contract balances, it's time to add functionality for zombie battles!
Ahora que hemos aprendido sobre funciones payable y balances de contrato ¡Es hora de añadir funcionalidad para las batallas zombie!

Following the format from previous chapters, we'll organize our code by creating a new file / contract for the attack functionality that imports from the previous contract.
Siguiendo el formato de capitulos pasados, organizaremos nuestro código creando un nuevo archivo / contrato para la funcionalidad de ataque que se importa desde nuestro contrato anterior.

## Put it to the test
## Pongalo a prueba

Let's review creating a new contract. Repetition leads to mastery!
Hagamos un repaso de cómo crear un nuevo contrato ¡La repetición hace al maestro!

If you can't remember the syntax for doing these, check `zombiehelper.sol` for the syntax — but try to do it without peeking first to test your knowledge.
Si no puede recordar la sintaxis para hacerla, vea `zombiehelper.sol` para ponerse al día — pero primero trate de hacerlo sin fijarse para poner a prueba su conocimiento.

1. Declare at the top of the file that we're using Solidity version `^0.4.19`.
1. Anuncie al principio del archivo que estamos utilizando Solidity en su versión `^0.4.19`.

2. `import` from `zombiehelper.sol`.
2. `import` (importe) desde `zombiehelper.sol`.

3. Declare a new `contract` called `ZombieBattle` that inherits from `ZombieHelper`. Leave the contract body empty for now.
3. Anuncie un nuevo `contract` llamado `ZombieBattle` que herede de `ZombieHelper`. Deje vacío el cuerpo del contrato por ahora.
56 changes: 28 additions & 28 deletions es/4/battle-02.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Random Numbers
title: Números Aleatorios
actions: ['checkAnswer', 'hints']
requireLogin: true
material:
Expand Down Expand Up @@ -216,19 +216,19 @@ material:
---

Great! Now let's figure out the battle logic.
¡Grandioso! Ahora descifremos la lógica de batalla

All good games require some level of randomness. So how do we generate random numbers in Solidity?
Todo buen juego necesita algún nivel de aleatoriedad. Entonces ¿Cómo generamos números aleatorios en Solidity?

The real answer here is, you can't. Well, at least you can't do it safely.
La respuesta correcta es que no puede. Bueno, al menos no puede hacerlo de una manera segura.

Let's look at why.
Veamos por qué.

## Random number generation via `keccak256`
## La generación aleatoria de números a través de `keccak256`

The best source of randomness we have in Solidity is the `keccak256` hash function.
La mejor fuente de aleatoriedad que tenemos en solidity es la función hash `keccak256`.

We could do something like the following to generate a random number:
Podríamos hacer algo como lo siguiente para generar un número aleatorio:

```
// Generate a random number between 1 and 100:
Expand All @@ -238,42 +238,42 @@ randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
```

What this would do is take the timestamp of `now`, the `msg.sender`, and an incrementing `nonce` (a number that is only ever used once, so we don't run the same hash function with the same input parameters twice).
Lo que esto haría es tomar la marca de tiempo de `now`, el `msg.sender`, y un `nonce` (un número que sólo se utiliza una vez, para que no ejecutemos dos veces la misma función hash con los mismos parámetros de entrada) en incremento.

It would then use `keccak` to convert these inputs to a random hash, convert that hash to a `uint`, and then use `% 100` to take only the last 2 digits, giving us a totally random number between 0 and 99.
Luego entonces utilizaría `keccak` para convertir estas entradas a un hash aleatorio, convertir ese hash a un `uint` y luego utilizar `% 100` para tomar los últimos 2 dígitos solamente, dándonos un número totalmente aleatorio entre 0 y 99.

### This method is vulnerable to attack by a dishonest node
### Este método es vulnerable a ataques de nodos deshonestos

In Ethereum, when you call a function on a contract, you broadcast it to a node or nodes on the network as a **_transaction_**. The nodes on the network then collect a bunch of transactions, try to be the first to solve a computationally-intensive mathematical problem as a "Proof of Work", and then publish that group of transactions along with their Proof of Work (PoW) as a **_block_** to the rest of the network.
En Ethereum, cuando llama a una función en un contrato, lo transmite a un nodo o nodos en la red como una **_transacción_**. Los nodos en la red luego recolectan un montón de transacciones, intentan ser el primero en resolver el problema de matemática intensamente informático como una "Prueba de Trabajo", para luego publicar ese grupo de transacciones junto con sus Pruebas de Trabajo (PoW) como un **_bloque_** para el resto de la red.

Once a node has solved the PoW, the other nodes stop trying to solve the PoW, verify that the other node's list of transactions are valid, and then accept the block and move on to trying to solve the next block.
Una vez que un nodo ha resuelto la PoW, los otros nodos dejan de intentar resolver la PoW, verifican que las transacciones en la lista de transacciones del otro nodo son validas, luego aceptan el bloque y pasan a tratar de resolver el próximo bloque.

**This makes our random number function exploitable.**
**Esto hace que nuestra función de números aleatorios sea explotable **.

Let's say we had a coin flip contract — heads you double your money, tails you lose everything. Let's say it used the above random function to determine heads or tails. (`random >= 50` is heads, `random < 50` is tails).
Digamos que teníamos un contrato coin flip — cara y duplica su dinero, sello y pierde todo. Digamos que utilizó la función aleatorea anterior para determinar cara o sello. (`random >= 50` es cara, `random < 50` es sello).

If I were running a node, I could publish a transaction **only to my own node** and not share it. I could then run the coin flip function to see if I won — and if I lost, choose not to include that transaction in the next block I'm solving. I could keep doing this indefinitely until I finally won the coin flip and solved the next block, and profit.
Si yo estuviera ejecutando un nodo, podría publicar una transacción **a mi propio nodo solamente** y no compartirla. Luego podría ejecutar la función coin flip para ver si gané — y si perdí, escojo no incluir esa transacción en el próximo bloque que estoy resolviendo. Podría seguir haciendo esto indefinidamente hasta que finalmente gané el lanzamiento de la moneda y resolví el siguiente bloque, beneficiandome de ello.

## So how do we generate random numbers safely in Ethereum?
## Entonces ¿Cómo generamos números aleatorio de manera segura en Ethereum?

Because the entire contents of the blockchain are visible to all participants, this is a hard problem, and its solution is beyond the scope of this tutorial. You can read <a href="https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract" target=_new>this StackOverflow thread</a> for some ideas. One idea would be to use an **_oracle_** to access a random number function from outside the Ethereum blockchain.
Ya que todos los contenidos de la blockchain son visibles para todos los participantes, este es un problema dificil, y su solución está más allá del rango de este tutorial. Puede leer <a href="https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract" target=_new>este hilo de StackOverflow</a> para que se haga un idea. Una idea sería utilizar un **_oráculo_** para ingresar una función de número aleatorio desde fuera de la blockchain de Ethereum.

Of course, since tens of thousands of Ethereum nodes on the network are competing to solve the next block, my odds of solving the next block are extremely low. It would take me a lot of time or computing resources to exploit this profitably — but if the reward were high enough (like if I could bet $100,000,000 on the coin flip function), it would be worth it for me to attack.
Por supuesto, debido a que cientos de miles de nodos de Ethereum en la red están compitiendo por resolver el próximo bloque, mis probabilidades de resolver el siguiente bloque son extremadamente escasas. Me tomaría mucho tiempo o recursos informáticos para explotar esto y que sea beneficioso — pero si la recompensa fuera lo suficientemente alta (como si pudiera apostar $100,000,000 en la función coin flip), para mi valdría la pena atacar.

So while this random number generation is NOT secure on Ethereum, in practice unless our random function has a lot of money on the line, the users of your game likely won't have enough resources to attack it.
Así que mientras esta generación de número aleatorio NO sea segura en Ethereum, en la práctica a menos que nuestra función aleatoria tenga mucho dinero en riesgo, es probable que los usuarios de su juego no tengan suficientes recursos para atacarla.

Because we're just building a simple game for demo purposes in this tutorial and there's no real money on the line, we're going to accept the tradeoffs of using a random number generator that is simple to implement, knowing that it isn't totally secure.
Ya que sólo estamos construyendo un juego simple para propósitos de demo en este tutorial y no hay dinero real en riesgo, vamos a asumir el riesgo de utilizar un generador de números aleatorios que es simple de implementar, sabiendo que no es totalmente seguro.

In a future lesson, we may cover using **_oracles_** (a secure way to pull data in from outside of Ethereum) to generate secure random numbers from outside the blockchain.
En una lección futura, puede que cubramos el uso de los **_oráculos_** (una manera segura de incorporar datos desde afuera de Ethereum) para generar números aleatorios seguros desde afuera de la blockchain.

## Put it to the test
## Pongalo a prueba

Let's implement a random number function we can use to determine the outcome of our battles, even if it isn't totally secure from attack.
Implementemos una función de número aleatorio que podamos utilizar para determinar el resultado de nuestras batallas, incluso si no está totalmente seguro de ataques.

1. Give our contract a `uint` called `randNonce`, and set it equal to `0`.
1. Dé a nuestro contrato un `uint` llamado `randNonce`, y configurarlo como igual a `0`.

2. Create a function called `randMod` (random-modulus). It will be an `internal` function that takes a `uint` named `_modulus`, and `returns` a `uint`.
2. Cree una función llamada `randMod` (random-modulus). Será una función `internal` que tome un `uint` llamado `_modulus`, y `returns` un `uint`.

3. The function should first increment `randNonce` (using the syntax `randNonce++`).
3. La función debería primero incrementar `randNonce` (utilizando la sintaxis `randNonce++`).

4. Finally, it should (in one line of code) calculate the `uint` typecast of the `keccak256` hash of `now`, `msg.sender`, and `randNonce`and `return` that value `% _modulus`. (Whew! That was a mouthful. If you didn't follow that, just take a look at the example above where we generated a random numberthe logic is very similar).
4. Finalmente, debería (en una línea de código) calcular el encasillado de `uint` del hash `keccak256` de `now`, `msg.sender` y `randNonce`y `return` (arrojar) ese valor `% _modulus`. (¡Vaya! Eso fue un trabalenguas. Si pudo comprender eso, tan sólo eche un vistazo al ejemplo de arriba donde generamos un número aleatoriola lógica es muy similar).
28 changes: 14 additions & 14 deletions es/4/battle-03.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Zombie Fightin'
title: Pelea Zombie
actions: ['checkAnswer', 'hints']
requireLogin: true
material:
Expand Down Expand Up @@ -227,23 +227,23 @@ material:
}
---

Now that we have a source of some randomness in our contract, we can use it in our zombie battles to calculate the outcome.
Ahora que tenemos una fuente de aleatoriedad en nuestro contrato, podemos utilizar en nuestras batallas zombie para calcular el resultado.

Our zombie battles will work as follows:
Nuestras batallas zombie funcionarán de la siguiente manera:

- You choose one of your zombies, and choose an opponent's zombie to attack.
- If you're the attacking zombie, you will have a 70% chance of winning. The defending zombie will have a 30% chance of winning.
- All zombies (attacking and defending) will have a `winCount` and a `lossCount` that will increment depending on the outcome of the battle.
- If the attacking zombie wins, it levels up and spawns a new zombie.
- If it loses, nothing happens (except its `lossCount` incrementing).
- Whether it wins or loses, the attacking zombie's cooldown time will be triggered.
- Usted escoge a uno de sus zombies y escoge un zombie del oponente para atacar.
- Si usted es el zombie que ataca, tendrá un 70% de oportunidad de ganar. El zombie que defiende tendrá un 30% de oportunidad de ganar.
- Todos los zombies (atacando y defendiendo) tendrán un `winCount` y un `lossCount` que incrementará dependiendo del resultado de la batalla.
- Si el zombie que ataca gana, sube de nivel y genera un nuevo zombie.
- Si pierde, no ocurre nada (solamente incrementa su `lossCount`)
- Sea que gane o que pierda, el tiempo de enfriamiento o recarga del zombie que ataca empezará a correr.

This is a lot of logic to implement, so we'll do it in pieces over the coming chapters.
Esto es mucha lógica a implementar así que lo haremos por partes en los siguientes capítulos.

## Put it to the test
+## Pongalo a prueba

1. Give our contract a `uint` variable called `attackVictoryProbability`, and set it equal to `70`.
1. Dele a nuestro contrato una variable `uint` llamada `attackVictoryProbability`, y configurela como igual a `70`.

2. Create a function called `attack`. It will take two parameters: `_zombieId` (a `uint`) and `_targetId` (also a `uint`). It should be an `external` function.
2. Cree una función llamada `attack`. Tomará dos parametros: `_zombieId` (una `uint`) y `_targetId` (también una `uint`). Debería ser una función `external`.

Leave the function body empty for now.
Deje vacío el cuerpo de función por ahora.
30 changes: 15 additions & 15 deletions es/4/battle-04.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Refactoring Common Logic
title: Refactorizando Lógica Común
actions: ['checkAnswer', 'hints']
requireLogin: true
material:
Expand Down Expand Up @@ -276,34 +276,34 @@ material:
}
---

Whoever calls our `attack` function — we want to make sure the user actually owns the zombie they're attacking with. It would be a security concern if you could attack with someone else's zombie!
Quien sea que llame a nuestra función `attack` — queremos asegurarnos de que el usuario sea realmente el dueño del zombie con el que está atacando ¡Sería un problema de seguridad si se pudiera atacar con el zombie de alguien más!

Can you think of how we would add a check to see if the person calling this function is the owner of the `_zombieId` they're passing in?
¿Puede pensar en cómo podríamos añadir un chequeo para ver si la persona que llama a esta función es el dueño del `_zombieId` que está pasando?

Give it some thought, and see if you can come up with the answer on your own.
Pienselo por un rato, vea si puede conseguir una respuesta usted mismo por su lado.

Take a moment... Refer to some of our previous lessons' code for ideas...
Tomese un momento... Revise el código de algunas de las lecciones anteriores para conseguir ideas...

Answer below, don't continue until you've given it some thought.
Más abajo la respuesta, no continúe hasta que lo haya pensado realmente.

## The answer
## La respuesta

We've done this check multiple times now in previous lessons. In `changeName()`, `changeDna()`, and `feedAndMultiply()`, we used the following check:
Hemos hecho este chequeo varias veces en lecciones anteriores. En `changeName()`, `changeDna()`, y `feedAndMultiply()`, utilizamos el siguiente chequeo:

```
require(msg.sender == zombieToOwner[_zombieId]);
```

This is the same logic we'll need for our `attack` function. Since we're using the same logic multiple times, let's move this into its own `modifier` to clean up our code and avoid repeating ourselves.
Esta es la misma lógica que necesitaremos para nuestra función `attack`. Ya que estamos utilizando multiples veces la misma lógica, movamos esto a su propio `modifier` para limpiar nuestro código y evitar redundancias.

## Put it to the test
## Pongalo a prueba

We're back to `zombiefeeding.sol`, since this is the first place we used that logic. Let's refactor it into its own `modifier`.
Regresamos a `zombiefeeding.sol`, dado que este es el primer lugar dónde usamos esa lógica. Vamos a refactorizarlo en su propio `modifier`.

1. Create a `modifier` called `ownerOf`. It will take 1 argument, `_zombieId` (a `uint`).
1. Cree un `modifier` llamado `ownerOf`. Tomará 1 argumento, `_zombieId` (un `uint`).

The body should `require` that `msg.sender` is equal to `zombieToOwner[_zombieId]`, then continue with the function. You can refer to `zombiehelper.sol` if you don't remember the syntax for a modifier.
El cuerpo debería `require` (requerir) que `msg.sender` sea igual a `zombieToOwner[_zombieId]` y luego continuar con la función. Puede consultar `zombiehelper.sol` si no recuerda la sintaxis para un modificador.

2. Change the function definition of `feedAndMultiply` such that it uses the modifier `ownerOf`.
2. Cambie la definición de función de `feedAndMultiply` de manera que utilice el modificador `ownerOf`.

3. Now that we're using a `modifier`, you can remove the line `require(msg.sender == zombieToOwner[_zombieId]);`
3. Ahora que estamos utilizando un `modifier`, puede eliminar la línea `require(msg.sender == zombieToOwner[_zombieId]);`
10 changes: 5 additions & 5 deletions es/4/battle-05.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: More Refactoring
title: Más Refactorización
actions: ['checkAnswer', 'hints']
requireLogin: true
material:
Expand Down Expand Up @@ -257,10 +257,10 @@ material:
}
---

We have a couple more places in `zombiehelper.sol` where we need to implement our new `modifier` `ownerOf`.
Tenemos un par de lugares más en `zombiehelper.sol` donde necesitamos implementar nuestro nuevo `modifier` `ownerOf`.

## Put it to the test
## Pongalo a prueba

1. Update `changeName()` to use `ownerOf`
1. Actualice `changeName()` para utilizar `ownerOf`

2. Update `changeDna()` to use `ownerOf`
2. Actualice `changeDna()` para utilizar `ownerOf`
Loading

0 comments on commit 40fedb5

Please sign in to comment.