# **Mina Workshop**
# Prerrequisitos
Cada tutorial ha sido probado con las últimas versiones de:
- [zkApp CLI](https://github.com/o1-labs/snarkyjs#readme)
- [o1js](https://www.npmjs.com/package/@o1labs/client-sdk) (o1js se incluye automáticamente al crear un proyecto utilizando zkApp CLI).
- Otras dependencias según se indica.
## Instalar zkApp CLI
Para instalar zkApp CLI:
$ npm install -g zkapp-cli
Para confirmar la instalación exitosa:
$ zk --version
## Dependencias
Para utilizar zkApp CLI y o1js, tu entorno requiere:
- NodeJS v16 en adelante (o NodeJS v14 usando `--experimental-wasm-threads`)
- NPM v6 en adelante
- Git v2 en adelante
Usa un gestor de paquetes para instalar las versiones requeridas y actualizar versiones anteriores si es necesario. Los gestores de paquetes para los entornos compatibles son:
- MacOS [Homebrew](https://brew.sh)
- Windows [Chocolatey](https://chocolatey.org)
- Linux
- apt, yum y otros
En Linux, podrías necesitar instalar una versión reciente de Node.js utilizando NodeSource. Usa `deb` o `rpm` tal y como es recomendado por el projecto Node.js.
Para verificar tus versiones instaladas, usa `npm -v`, `node -v` y `git -v`.
# Tutorial 1: Hola Mundo
Este tutorial de Hola Mundo te ayuda a comenzar con o1js, zkApps y la programación con pruebas de conocimiento cero.
En este tutorial paso a paso, aprenderás a codificar una zkApp de principio a fin.
Harás lo siguiente:
- Escribir un contrato inteligente básico que almacene un número como estado on-chain.
- La lógica del contrato permite que este número sea reemplazado solo por su cuadrado; por ejemplo, 3 -> 9 -> 81, y así sucesivamente.
- Crear un proyecto usando [Mina zkApp CLI](https://www.npmjs.com/package/zkapp-cli)
- Escribir el código de tu contrato inteligente
- Utilizar una blockchain local de Mina para interactuar con tu contrato inteligente.
Los tutoriales posteriores introducen más conceptos y patrones.
El código fuente completo para este tutorial se proporciona en el directorio [examples/zkapps/01-hello-world](https://github.com/o1-labs/docs2/tree/main/examples/zkapps/01-hello-world) en GitHub. Mientras estés allí, dale una estrella al repositorio `/docs2` para que otros desarrolladores zk puedan aprender a construir una zkApp!
## Prerrequisitos
Asegúrate de que tu entorno cumpla con los prerrequisitos que se mencionan al inicio de este documento.
En particular, asegúrate de tener instalado el zkApp CLI:
```sh
$ npm install -g zkapp-cli
```
## Crear un nuevo proyecto
Ahora que tienes las herramientas instaladas, puedes comenzar a construir tu aplicación.
1. Crea o cambia a un directorio donde tengas privilegios de escritura.
2. Ahora, crea un proyecto usando el comando `zk project`:
```sh
$ zk project 01-hello-world
```
El comando zk project tiene la capacidad de crear la interfaz de usuario para tu proyecto. Para este tutorial, selecciona none:
```
? Create an accompanying UI project too? …
next
svelte
nuxt
empty
❯ none
```
La salida esperada es:
```
✔ Create an accompanying UI project too? · none
✔ UI: Set up project
✔ Initialize Git repo
✔ Set up project
✔ NPM install
✔ NPM build contract
✔ Set project name
✔ Git init commit
Success!
Next steps:
cd 01-hello-world
git remote add origin <your-repo-url>
git push -u origin main
```
El comando `zk project` crea el directorio `01-hello-world` que contiene la estructura para tu proyecto, incluyendo herramientas como la herramienta de formateo de código Prettier, la herramienta de análisis de código estático ESLint y el framework de pruebas de JavaScript Jest.
3. Cambia al directorio 01-hello-world y lista los contenidos:
```
$ cd 01-hello-world
$ ls
```
La salida muestra estos resultados:
```
LICENSE
README.md
babel.config.cjs
build
config.json
jest-resolver.cjs
jest.config.js
keys
node_modules
package-lock.json
package.json
src
tsconfig.json
```
Para este tutorial, ejecutarás comandos desde la raíz del directorio `01-hello-world` mientras trabajas en el directorio `src` en archivos que contienen el código TypeScript para el contrato inteligente. Cada vez que realices actualizaciones, y luego compiles o despliegues, el código TypeScript se compila en JavaScript en el directorio `build`.
### Preparar el proyecto
Comienza eliminando los archivos predeterminados que vienen con el nuevo proyecto.
1. Para eliminar los archivos antiguos:
<pre><code class="language-sh">
$ rm src/Add.ts
$ rm src/Add.test.ts
$ rm src/interact.ts
</code></pre>
1. Ahora, crea los nuevos archivos para tu proyecto:
<pre><code class="language-sh">
$ zk file src/Square
$ touch src/main.ts
</code></pre>
- El comando `zk file` creó los archivos `src/Square.ts` y `src/Square.test.ts`.
- Sin embargo, este tutorial no incluye escribir pruebas, por lo que simplemente usarás el archivo `main.ts` como un script para interactuar con el contrato inteligente y observar cómo funciona.
En tutoriales posteriores, aprenderás a interactuar con un contrato inteligente desde el navegador, como lo haría un usuario típico.
3. Ahora, abre `src/index.ts` en un editor de texto y cámbialo para que se vea así:
<pre><code class="language-ts">
1 import { Square } from './Square.js';
2
3 export { Square };
</code></pre>
El archivo `src/index.ts` contiene todas las exportaciones que deseas hacer disponibles para su consumo desde fuera de tu proyecto de contrato inteligente, como desde una UI.
## Escribir el Contrato Inteligente zkApp
¡Ahora, la parte divertida! Escribe tu contrato inteligente en el archivo `src/Square.ts`.
Los números de línea se proporcionan para mayor comodidad. Una versión final del contrato inteligente se proporciona en el archivo de ejemplo [Square.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/01-hello-world/src/Square.ts).
Esta parte del tutorial te guía a través del código del contrato inteligente `Square` ya completado en el archivo de ejemplo `src/Square.ts`.
### Copiar el ejemplo
Copia el siguiente código en el archivo `src/Square.ts`:
```typescript
import {
Field,
SmartContract,
state,
State,
method
} from 'o1js';
export class Square extends SmartContract {
@state(Field) num = State<Field>();
init() {
super.init();
this.num.set(Field(3));
}
@method update(square: Field) {
const currentState = this.num.get();
this.num.assertEquals(currentState);
square.assertEquals(currentState.mul(currentState));
this.num.set(square);
}
}
```
Ahora estás listo para revisar las importaciones en el contrato inteligente.
### Importaciones
La declaración `import` trae otros paquetes y dependencias para usar en tu contrato inteligente.
:::info
Todas las funciones utilizadas dentro de un contrato inteligente deben operar con tipos de datos compatibles con o1js: tipos `Field` y otros tipos construidos sobre tipos `Field`.
:::
```typescript
import {
Field,
SmartContract,
state,
State,
method
} from 'o1js';
```
Estos elementos son:
- `Field`: El tipo de número nativo en o1js. Puedes pensar en los elementos Field como enteros sin signo. Los elementos Field son el tipo más básico en o1js. Todos los demás tipos compatibles con o1js se construyen sobre elementos Field.
- `SmartContract`: La clase que crea contratos inteligentes zkApp.
- `state`: Un decorador de conveniencia utilizado en contratos inteligentes zkApp para crear referencias al estado almacenado on-chain en una cuenta zkApp.
- `State`: Una clase utilizada en contratos inteligentes zkApp para crear un estado almacenado on-chain en una cuenta zkApp.
- `method`: Un decorador de conveniencia utilizado en contratos inteligentes zkApp para crear métodos de contrato inteligente como funciones. Los métodos que utilizan este decorador son los puntos de entrada del usuario final para interactuar con un contrato inteligente.
### Clase de contrato inteligente
Ahora, revisa el contrato inteligente en el archivo `src/Square.ts`.
El contrato inteligente llamado `Square` tiene un elemento de estado on-chain llamado `num` de tipo `Field` como se define en el siguiente código:
```typescript
export class Square extends SmartContract {
@state(Field) num = State<Field>();
}
```
Los zkApps pueden tener hasta ocho campos de estado on-chain. Cada campo almacena hasta 32 bytes (técnicamente, 31.875 bytes o 255 bits) de datos arbitrarios. Un tutorial posterior cubre opciones para el estado off-chain.
Ahora, este código agrega el método `init` para configurar el estado inicial del contrato inteligente al implementarlo:
```typescript
export class Square extends SmartContract {
@state(Field) num = State<Field>();
init() {
super.init();
this.num.set(Field(3));
}
```
Dado que estás extendiendo `SmartContract`, que tiene su propia inicialización, llamar a `super.init()` invoca esta función en la clase base.
Luego, `this.num.set(Field(3))` inicializa el estado on-chain `num` a un valor de `3`.
Opcionalmente, puedes especificar permisos. Consulta [setPermissions](https://docs.minaprotocol.com/zkapps/o1js-reference/classes/SmartContract#setpermissions) en la documentación de referencia de o1js.
Finalmente, este código agrega la función `update()`:
```typescript
@method update(square: Field) {
const currentState = this.num.get();
this.num.assertEquals(currentState);
square.assertEquals(currentState.mul(currentState));
this.num.set(square);
}
```
El nombre de la función `update` es arbitrario, pero tiene sentido para este ejemplo. Observa cómo se usa el decorador `@method` porque está destinado a ser invocado por usuarios finales utilizando una interfaz de usuario zkApp o, como en este caso, el script `main.ts`.
Este método contiene la lógica por la cual los usuarios finales pueden actualizar el estado on-chain de la cuenta zkApp.
Una cuenta zkApp es una cuenta en la blockchain de Mina donde se implementa un contrato inteligente zkApp. Una cuenta zkApp tiene asociada una clave de verificación.
En este ejemplo, el código especifica:
- Si el usuario proporciona un número (por ejemplo, 9) al método `update()` que es el cuadrado del estado on-chain existente denominado `num` (por ejemplo, 3), entonces actualiza el valor de `num` almacenado on-chain al valor proporcionado (en este caso, 9).
- Si el usuario proporciona un número que no cumple con estas condiciones, no podrá generar una prueba ni actualizar el estado on-chain.
Estas condiciones de actualización se logran utilizando afirmaciones dentro del método. Cuando un usuario invoca un método en un contrato inteligente, todas las afirmaciones deben ser verdaderas para generar la prueba de conocimiento cero de ese contrato inteligente. La red Mina acepta la transacción y actualiza el estado on-chain solo si la prueba adjunta es válida. Esta afirmación es cómo puedes lograr un comportamiento predecible en un modelo de ejecución off-chain.
Observa que se utilizan los métodos `get()` y `set()` para recuperar y establecer el estado on-chain.
- Un contrato inteligente recupera el estado de la cuenta on-chain cuando se invoca por primera vez si existe al menos un `get()` dentro de él.
- De manera similar, usar `set()` cambia la transacción para indicar que los cambios en este estado on-chain particular se actualizan solo cuando la transacción es recibida por la red Mina si contiene una autorización válida (generalmente, una autorización válida es una prueba).
La lógica también utiliza el método `.mul()` para la multiplicación de los valores almacenados en tipos `Field`. Puedes ver todos los [métodos](https://docs.minaprotocol.com/zkapps/o1js-reference/classes/Field#methods) disponibles en la documentación de referencia de o1js.
Recuerda que las funciones en tu contrato inteligente deben operar con tipos de datos compatibles con o1js: tipos `Field` y otros tipos construidos sobre tipos `Field`. Debido a que un contrato inteligente es realmente un circuito de conocimiento cero, las funciones de paquetes NPM arbitrarios funcionan dentro de un contrato inteligente solo si las funciones que proporciona el contrato operan con tipos de datos compatibles con o1js.
Importante, los datos pasados como entrada a un método de contrato inteligente en o1js son privados y nunca son vistos por la red.
También puedes almacenar datos públicamente on chain cuando sea necesario, como `num` en este ejemplo. Un tutorial posterior cubre un ejemplo que implementa la privacidad.
Felicidades, has revisado el código completo del contrato inteligente.
## Interactuar con un contrato inteligente
A continuación, escribirás un script que interactúa con tu contrato inteligente. Para tu referencia, el archivo de ejemplo completo se proporciona en [main.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/01-hello-world/src/main.ts). Sigue estos pasos para construir el archivo `main.ts` para que puedas interactuar con el contrato inteligente.
### Importaciones
Para este tutorial, la declaración `import` trae elementos de `o1js` que utilizas para interactuar con tu contrato inteligente.
1. Copia las siguientes líneas del archivo de ejemplo [main.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/01-hello-world/src/main.ts) en el archivo `src/main.ts`:
```typescript
import { Square } from './Square.js';
import {
Field,
Mina,
PrivateKey,
AccountUpdate,
} from 'o1js';
```
Estos elementos importados son:
- `Field`: El mismo tipo de entero sin signo de o1js que aprendiste anteriormente.
- `Mina`: Una blockchain local de Mina para desplegar el contrato inteligente para que puedas interactuar con él como lo haría un usuario.
- `PrivateKey`: Una clase con funciones para manipular claves privadas.
- `AccountUpdate`: Una clase que genera una estructura de datos que puede actualizar cuentas zkApp.
### Blockchain Local
Usar una blockchain local acelera el desarrollo y prueba el comportamiento de tu contrato inteligente localmente. Los tutoriales posteriores cubren cómo desplegar un zkApp en redes Mina en vivo.
Para inicializar tu blockchain local, agrega el siguiente código a `src/main.ts`:
```typescript
const useProof = false;
const Local = Mina.LocalBlockchain({ proofsEnabled: useProof });
Mina.setActiveInstance(Local);
const { privateKey: deployerKey, publicKey: deployerAccount } = Local.testAccounts[0];
const { privateKey: senderKey, publicKey: senderAccount } = Local.testAccounts[1];
```
Esta blockchain local también proporciona cuentas prefinanciadas. Estas líneas crean cuentas de prueba locales con MINA de prueba para usar en este tutorial:
```ts ignore
const { privateKey: deployerKey, publicKey: deployerAccount } = Local.testAccounts[0];
const { privateKey: senderKey, publicKey: senderAccount } = Local.testAccounts[1];
```
### Construir y ejecutar el contrato inteligente
Ahora que el contrato inteligente Square está completo, estos comandos ejecutan tu proyecto como una blockchain local.
Para compilar el código TypeScript en JavaScript:
```shell
$ npm run build
```
Para ejecutar el código JavaScript:
```shell
$ node build/src/main.js
```
Tienes la opción de combinar estos comandos en una sola línea:
```shell
npm run build && node build/src/main.js
```
- El comando `npm run build` crea código JavaScript en el directorio `build`.
- El operador `&&` enlaza dos comandos juntos. El segundo comando se ejecuta solo si el primer comando es exitoso.
- El comando `node build/src/main.js` ejecuta el código en `src/main.ts`.
### Inicializar tu contrato inteligente
Para inicializar tu contrato inteligente, vamos a agregar más código al archivo `src/main.ts`.
Todos los contratos inteligentes que creas con la CLI de zkApp utilizan un código similar:
- Crear un par de llaves pública/privada; la llave pública es tu dirección y donde despliegas el zkApp
- Crear una instancia de tu contrato inteligente `Square` y desplegarlo en `zkAppAddress`
- Obtener el estado inicial de `Square` después del despliegue
Los comentarios desglosan cada etapa:
```typescript
// Create a public/private key pair. The public key is your address and where you deploy the zkApp to
const zkAppPrivateKey = PrivateKey.random();
const zkAppAddress = zkAppPrivateKey.toPublicKey();
// create an instance of Square - and deploy it to zkAppAddress
const zkAppInstance = new Square(zkAppAddress);
const deployTxn = await Mina.transaction(deployerAccount, () => {
AccountUpdate.fundNewAccount(deployerAccount);
zkAppInstance.deploy();
});
await deployTxn.sign([deployerKey, zkAppPrivateKey]).send();
// get the initial state of Square after deployment
const num0 = zkAppInstance.num.get();
console.log('state after init:', num0.toString());
```
Intenta ejecutar este comando de nuevo:
```shell
$ npm run build && node build/src/main.js
```
La salida esperada es:
```shell
> 01-hello-world@0.1.0 build
> tsc
o1js loaded
state after init: 3
Shutting down
```
### Actualizar tu cuenta zkApp con una transacción
Para actualizar tu cuenta zkApp local con una transacción, agrega el siguiente código al archivo `src/main.ts`:
```typescript
const txn1 = await Mina.transaction(senderAccount, () => {
zkAppInstance.update(Field(9));
});
await txn1.prove();
await txn1.sign([senderKey]).send();
const num1 = zkAppInstance.num.get();
console.log('state after txn1:', num1.toString());
```
Este código crea una nueva transacción que intenta actualizar el campo al valor `9`. Debido a las reglas en la función `update()` que se llama en el contrato inteligente, este comando tiene éxito cuando lo ejecutas nuevamente:
```shell
$ npm run build && node build/src/main.js
...
o1js loaded
estado después de iniciar: 3
estado después de txn1: 9
Apagando
```
### Agregar una transacción que falla
Es hora de hacer algunas pruebas. La lógica del contrato permite que el número almacenado como estado on-chain sea reemplazado solo por su cuadrado. Ahora que `num` está en estado `9`, la actualización es posible solo con `81`.
Para probar un fallo, actualiza el estado a 75 en `zkAppInstance.update(Field(75))`:
```typescript
try {
const txn2 = await Mina.transaction(senderAccount, () => {
zkAppInstance.update(Field(75));
});
await txn2.prove();
await txn2.sign([senderKey]).send();
} catch (ex: any) {
console.log(ex.message);
}
const num2 = zkAppInstance.num.get();
console.log('state after txn2:', num2.toString());
```
Intenta ejecutar este comando de nuevo:
```shell
$ npm run build && node build/src/main.js
```
La salida esperada es:
```shell
$ npm run build && node build/src/main.js
...
o1js loaded
state after init: 3
state after txn1: 9
assert_equal: 75 != 81
state after txn2: 9
Shutting down
```
Y finalmente, asegúrate de cambiar tu archivo `main.ts` para incluir la actualización correcta:
```typescript
const txn3 = await Mina.transaction(senderAccount, () => {
zkAppInstance.update(Field(81));
});
await txn3.prove();
await txn3.sign([senderKey]).send();
const num3 = zkAppInstance.num.get();
console.log('state after txn3:', num3.toString());
// ----------------------------------------------------
console.log('Shutting down');
```
Ejecuta este comando de nuevo:
```shell
$ npm run build && node build/src/main.js
```
La salida esperada es:
```shell
$ npm run build && node build/src/main.js
...
o1js loaded
state after init: 3
state after txn1: 9
assert_equal: 75 != 81
state after txn2: 9
state after txn3: 81
Apagando
```
## Sigue el tutorial
Puedes seguir este video donde el especialista en criptpgrafía, David Wong, aprende a codificar un proyecto Hola Mundo: [Aprende a programar zkApps conmigo](https://www.youtube.com/embed/prx2oNpy0vo)
El video se proporciona con fines educativos y utiliza versiones anteriores de zkApp CLI y o1js, por lo que hay algunas diferencias.
## Conclusión
¡Felicidades! Has completado con éxito todos los pasos para construir tu primera zkApp con o1js.
Consulta el [Tutorial 2: Entradas Privadas y Funciones Hash](private-inputs-hash-functions) para aprender a usar entradas privadas y funciones hash con o1js.
Encuentra más tutoriales y recursos en la [documentación de zkApps](https://docs.minaprotocol.com/zkapps).
# Tutorial 2: Entradas Privadas y Funciones Hash
En el tutorial Hola Mundo, construiste un contrato inteligente zkApp básico con o1js con una única variable de estado que podía actualizarse si conocías el cuadrado de ese número.
En este tutorial, aprenderás sobre entradas privadas y funciones hash.
Con un zkApp, el dispositivo local del usuario del contrato inteligente genera una o más pruebas de conocimiento cero, que luego son verificadas por la red Mina. Cada método en un contrato inteligente o1js corresponde a la construcción de una prueba.
Todas las entradas a un contrato inteligente son privadas por defecto. Las entradas nunca son vistas por la blockchain a menos que almacenes esos valores como estado on-chain en la cuenta zkApp.
En este tutorial, construirás un contrato inteligente con una pieza de estado privado que puede ser modificada si un usuario conoce el estado privado.
## Prerrequisitos
Este tutorial ha sido probado con [Mina zkApp CLI](https://github.com/o1-labs/zkapp-cli) versión `0.9.0` y [o1js](https://www.npmjs.com/package/snarkyjs) `0.11.0`.
Asegúrate de que tu entorno cumpla con los prerrequisitos mencionados al inicio de este documento.
## Crear un proyecto
1. Crea o cambia a un directorio donde tengas privilegios de escritura.
2. Crea un proyecto usando el comando `zk project`:
```shell
$ zk project 02-private-inputs-and-hash-functions
```
El comando `zk project` tiene la capacidad de crear la interfaz de usuario para tu proyecto. Para este tutorial, selecciona `none`:
```shell
? Create an accompanying UI project too? …
next
svelte
nuxt
empty
❯ none
```
La salida esperada es:
```shell
✔ Create an accompanying UI project too? · none
✔ UI: Set up project
✔ Initialize Git repo
✔ Set up project
✔ NPM install
✔ NPM build contract
✔ Set project name
✔ Git init commit
Success!
Next steps:
cd 02-private-inputs-and-hash-functions
git remote add origin <your-repo-url>
git push -u origin main
```
El comando `zk project` crea el directorio `02-private-inputs-and-hash-functions` que contiene la estructura para tu proyecto, incluyendo herramientas como la herramienta de formateo de código Prettier, la herramienta de análisis de código estático ESLint y el framework de pruebas de JavaScript Jest.
3. Cambia al directorio `02-private-inputs-and-hash-functions` y lista los contenidos:
```shell
$ cd 02-private-inputs-and-hash-functions
$ ls
```
La salida muestra estos resultados:
```shell
LICENSE
README.md
babel.config.cjs
build
config.json
jest-resolver.cjs
jest.config.js
keys
node_modules
package-lock.json
package.json
src
tsconfig.json
```
Para este tutorial, ejecutarás comandos desde la raíz del directorio `02-private-inputs-and-hash-functions` mientras trabajas en el directorio `src` en archivos que contienen el código TypeScript para el contrato inteligente.
Cada vez que realices actualizaciones, para luego compilar o desplegar, el código TypeScript se compila en JavaScript en el directorio `build`.
### Preparar el proyecto
Comienza eliminando los archivos predeterminados que vienen con el nuevo proyecto.
1. Para eliminar los archivos generados por defecto:
```shell
$ rm src/Add.ts
$ rm src/Add.test.ts
$ rm src/interact.ts
```
2. Ahora, crea los nuevos archivos para tu proyecto:
```shell
$ zk file src/IncrementSecret
$ touch src/main.ts
```
- El comando `zk file` creó el archivo `src/IncrementSecret.ts` y el archivo de prueba `src/IncrementSecret.test.ts`.
- Sin embargo, este tutorial no incluye escribir pruebas, por lo que simplemente usarás el archivo `main.ts` como un script para interactuar con el contrato inteligente y observar cómo funciona.
3. Ahora, abre `src/index.ts` en un editor de texto y cámbialo para que se vea así:
```typescript
import { IncrementSecret } from './IncrementSecret.js';
export { IncrementSecret };
```
El archivo `src/index.ts` contiene todas las exportaciones que deseas hacer disponibles para su consumo desde fuera de tu proyecto de contrato inteligente, como desde una interfaz de usuario.
### Copiar el código de ejemplo
Copia el siguiente código en tu archivo `IncrementSecret.ts`:
```typescript
import { Field, SmartContract, state, State, method, Poseidon } from 'o1js';
export class IncrementSecret extends SmartContract {
@state(Field) x = State<Field>();
@method initState(salt: Field, firstSecret: Field) {
this.x.set(Poseidon.hash([salt, firstSecret]));
}
@method incrementSecret(salt: Field, secret: Field) {
const x = this.x.get();
this.x.assertEquals(x);
Poseidon.hash([salt, secret]).assertEquals(x);
this.x.set(Poseidon.hash([salt, secret.add(1)]));
}
}
```
Copia el siguiente código en tu archivo `main.ts`:
```typescript
import { IncrementSecret } from './IncrementSecret.js';
import {
Field,
Mina,
PrivateKey,
AccountUpdate,
} from 'o1js';
const useProof = false;
const Local = Mina.LocalBlockchain({ proofsEnabled: useProof });
Mina.setActiveInstance(Local);
const { privateKey: deployerKey, publicKey: deployerAccount } =
Local.testAccounts[0];
const { privateKey: senderKey, publicKey: senderAccount } =
Local.testAccounts[1];
const salt = Field.random();
// ----------------------------------------------------
// create a destination we will deploy the smart contract to
const zkAppPrivateKey = PrivateKey.random();
const zkAppAddress = zkAppPrivateKey.toPublicKey();
const zkAppInstance = new IncrementSecret(zkAppAddress);
const deployTxn = await Mina.transaction(deployerAccount, () => {
AccountUpdate.fundNewAccount(deployerAccount);
zkAppInstance.deploy();
zkAppInstance.initState(salt, Field(750));
});
await deployTxn.prove();
await deployTxn.sign([deployerKey, zkAppPrivateKey]).send();
// get the initial state of IncrementSecret after deployment
const num0 = zkAppInstance.x.get();
console.log('state after init:', num0.toString());
// ----------------------------------------------------
const txn1 = await Mina.transaction(senderAccount, () => {
zkAppInstance.incrementSecret(salt, Field(750));
});
await txn1.prove();
await txn1.sign([senderKey]).send();
const num1 = zkAppInstance.x.get();
console.log('state after txn1:', num1.toString());
```
## Escribir el contrato inteligente
Ahora construiremos el contrato inteligente para nuestra aplicación.
### Importaciones
La declaración `import` en el archivo `IncrementSecret.ts` trae otros paquetes y dependencias para usar en tu contrato inteligente.
:::info
Todas las funciones utilizadas dentro de un contrato inteligente deben operar con tipos de datos compatibles con o1js: tipos `Field` y otros tipos construidos sobre tipos `Field`.
:::
```typescript
import { Field, SmartContract, state, State, method, Poseidon } from 'o1js';
```
### Exportaciones
El contrato inteligente llamado `IncrementSecret` tiene un elemento de estado on-chain llamado `x` de tipo `Field` como se define en el siguiente código:
```typescript
...
export class IncrementSecret extends SmartContract {
@state(Field) x = State<Field>();
}
```
Este código agrega la estructura básica para el contrato inteligente. Ya estás familiarizado con el código de importación y exportación del Tutorial Hola Mundo descrito arriba.
### Estado Inicial
El método `initState()` está destinado a ejecutarse una vez para configurar el estado inicial en la cuenta zkApp.
```typescript
...
@method initState(salt: Field, firstSecret: Field) {
this.x.set(Poseidon.hash([ salt, firstSecret ]));
}
```
El método `initState()` acepta tu secreto y agrega un valor `salt`.
Estas entradas al método `initState()` son privadas para quien inicializa el contrato. La cuenta zkApp en el blockchain no revela cuáles son los valores `firstSecret` o `salt` en realidad.
### Actualizar el Estado
Este método actualiza el estado:
```typescript
...
@method incrementSecret(salt: Field, secret: Field) {
const x = this.x.get();
this.x.assertEquals(x);
Poseidon.hash([ salt, secret ]).assertEquals(x);
this.x.set(Poseidon.hash([ salt, secret.add(1) ]));
}
```
Mina utiliza la función de hash Poseidon que está optimizada para un rendimiento rápido dentro de los sistemas de prueba de conocimiento cero. La función de hash Poseidon toma un arreglo de Fields y devuelve un solo Field como salida.
Este contrato inteligente utiliza un número secreto y el segundo Field, `salt`.
El método `incrementSecret()` verifica que el hash del salt y el secreto sea igual al estado actual `x`:
- Si es así, agrega `1` al secreto y establece `x` como el hash del salt y este nuevo secreto.
- o1js crea una prueba de este hecho y una descripción JSON de las actualizaciones de estado que se harán en la cuenta zkApp, como almacenar el nuevo valor del hash.
- Juntos, esto forma una transacción que se puede enviar a la red Mina para actualizar la cuenta zkApp.
Debido a que los contratos inteligentes zkApp se ejecutan off-chain, tu salt y secreto permanecen privados y nunca se transmiten a ningún lugar.
Solo el resultado, actualizando el estado on-chain de `x` a `hash([ salt, secret + 1])` se revela. Debido a que el salt y el secreto no se pueden deducir de su hash, permanecen privados.
### Acerca del argumento `salt`
El salt criptográfico añade una capa adicional de seguridad a un contrato inteligente. El argumento extra `salt` previene un posible ataque al contrato inteligente. Si solo usas `secret`, el contrato es vulnerable a ser descubierto por un atacante. Un atacante podría intentar hacer hash de secretos probables y luego verificar si el hash coincide con el hash almacenado en el contrato inteligente. Si el hash coincide, entonces el atacante sabe que ha descubierto el secreto. Este escenario es particularmente preocupante si el secreto es probable que esté dentro de un subconjunto particular de valores posibles, digamos entre 1 y 10,000. En ese caso, con solo 10,000 hashes, el atacante podría descubrir el secreto.
Agregar salt como una segunda entrada al código del contrato hace que sea más difícil para un atacante hacer ingeniería inversa y acceder al contrato. El salt hace que el contrato sea más seguro y ayuda a proteger los datos almacenados en él. Para una seguridad óptima, el salt es conocido solo por ti y suele ser aleatorio.
## El archivo main.ts
El archivo `src/main.ts` es similar al tutorial Hola Mundo. Para una versión completa, consulta [main.ts](https://github.com/o1-labs/docs2/blob/main/examples/zkapps/02-private-inputs-and-hash-functions/src/main.ts).
Para este tutorial, las partes clave a discutir son la inicialización de nuestro contrato y el uso del Poseidon hash.
La inicialización del contrato inteligente esta vez es:
```typescript
...
const salt = Field.random();
...
const deployTxn = await Mina.transaction(deployerAccount, () => {
AccountUpdate.fundNewAccount(deployerAccount);
zkAppInstance.deploy();
zkAppInstance.initState(salt, Field(750));
});
await deployTxn.prove();
...
```
Nota que el método `initState()` acepta el salt y el secreto. En este caso, el secreto es el número `750`.
Este código crea una transacción de usuario y actualiza el estado on-chain:
```typescript
...
const txn1 = await Mina.transaction(senderAccount, () => {
zkAppInstance.incrementSecret(salt, Field(750));
});
...
```
Invoca la zkApp con el salt y el secreto (el número `750`).
Debido a que los zkApps son ejecutados localmente, ni el secreto ni la sal son parte de la transacción.
En su lugar, la transacción incluye solamente la prueba de que la actualización fue invocada de tal forma que todas las verificaciones pasaron y que se hizo una actualización al estado on-chain `x` donde el valor del hash está almacenado. Luego de que la transacción es procesada por la red Mina, `x` tendrá el valor `Poseidon.hash([ salt, Field(750).add(1)])`. El salt y el secreto no son revelados.
Intenta correr `main`:
```shell
$ npm run build && node build/src/main.js
```
La salida se ve algo como así:
```text
o1js loaded
state after init: 3116464240601550031577632290308565252747064306168758166756574536757280262269
state after txn1: 15333363135506653312218020664441564145350761288169575380089681962972642150348
Shutting down
```
Vamos ahora a intentar actualizar el valor del secreto con un número que no corresponde al valor on-chain. Agrega a `src/main.ts` el siguiente código:
```typescript
const txn2 = await Mina.transaction(senderAccount, () => {
zkAppInstance.incrementSecret(salt, Field(1234));
});
await txn2.prove();
await txn2.sign([senderKey]).send();
const num2 = zkAppInstance.x.get();
console.log('state after txn2:', num2.toString());
```
Deberías recibir un error similar a este:
```shell
Error: Field.assertEquals(): 3129191626806317831660800709841622162488652225442964548186218383162970769305 != 22373281554833500418354582585806200234516786908475945664577311628009557470956
```
Esto ocurre debido a que el número `1234` no corresponde con el valor secreto actualmente almacenado on-chain, que para este momento es `751`.
## Conclusión
¡Felicidades! Has construido un contrato inteligente que utiliza privacidad y funciones hash.
A continuación aprenderás cómo desplegar zkApps en una red en vivo.
# Tutorial 3: Desplegar en una red en vivo
En tutoriales anteriores, aprendiste a desplegar y ejecutar transacciones en una red local.
En este tutorial, utilizarás el comando `zk config` para crear un alias de despliegue, solicitar fondos de tMINA para pagar las tarifas de transacción y desplegar un proyecto en una red en vivo.
Las zkApps de Mina están disponibles solo en Berkeley, la testnet pública y con todas las características de Mina. Este tutorial reutiliza el contrato `Square` que creaste en el Tutorial Hola Mundo.
## Prerrequisitos
Este tutorial ha sido probado con la versión `0.15.0` de [zkApp CLI](https://github.com/o1-labs/zkapp-cli) y la versión `0.14.1` de [o1js](https://www.npmjs.com/package/o1js).
Asegúrate de que tu entorno cumpla con los Prerrequisitos descritos arriba.
Si tienes versiones anteriores de zkApp CLI y o1js instaladas, asegúrate de actualizar tu zkApp CLI a la última versión:
```shell
$ npm update -g zkapp-cli
```
## Crear un proyecto
1. Crea o cambia a un directorio donde tengas privilegios de escritura.
2. Crea un proyecto utilizando el comando `zk project`:
```shell
$ zk project 03-deploying-to-a-live-network
```
El comando `zk project` tiene la capacidad de crear la interfaz de usuario para tu proyecto. Para este tutorial, selecciona `none`:
```shell
✔ Create an accompanying UI project too? · none
✔ UI: Set up project
✔ Initialize Git repo
✔ Set up project
✔ NPM install
✔ NPM build contract
✔ Set project name
✔ Git init commit
Success!
Next steps:
cd 03-deploying-to-a-live-network
git remote add origin <your-repo-url>
git push -u origin main
```
El comando `zk project` crea el directorio `03-deploying-to-a-live-network` que contiene la estructura para tu proyecto, incluyendo herramientas como la herramienta de formateo de código Prettier, la herramienta de análisis de código estático ESLint y el framework de pruebas de JavaScript Jest.
3. Cambia al directorio `03-deploying-to-a-live-network`.
Para este tutorial, ejecutarás comandos desde la raíz del directorio `03-deploying-to-a-live-network` mientras trabajas en el directorio `src` en archivos que contienen el código TypeScript para el contrato inteligente.
Cada vez que realices actualizaciones, al compilar o desplegar, el código TypeScript se compila en JavaScript en el directorio `build`.
El comando `zk project` crea el directorio `03-deploying-to-a-live-network` que contiene la estructura para tu proyecto, incluyendo herramientas como la herramienta de formateo de código Prettier, la herramienta de análisis de código estático ESLint y el marco de pruebas de JavaScript Jest.
1. Cambia al directorio `03-deploying-to-a-live-network`.
Para este tutorial, ejecutarás comandos desde la raíz del directorio `03-deploying-to-a-live-network` mientras trabajas en el directorio `src` en archivos que contienen el código TypeScript para el contrato inteligente.
Cada vez que realices actualizaciones, y luego compiles o despliegues, el código TypeScript se compila en JavaScript en el directorio `build`.
### Preparar el proyecto
Comienza eliminando los archivos predeterminados que vienen con el nuevo proyecto.
1. Elimina los archivos generados por defecto:
```shell
$ rm src/Add.ts
$ rm src/Add.test.ts
$ rm src/interact.ts
```
2. Ahora, crea un nuevo archivo para tu contrato inteligente:
```shell
$ zk file src/Square
```
1. Copia los archivos `src/Square.ts` y `src/index.ts` de los archivos de ejemplo del primer tutorial [01-hello-world/src](https://github.com/o1-labs/docs2/tree/main/examples/zkapps/01-hello-world/src) a tu directorio local `03-deploying-to-a-live-network/src`. Si se solicita, reemplaza los archivos existentes.
Ahora que tu contrato inteligente está en su lugar, estás listo para desplegar tu contrato inteligente en la Testnet de Berkeley.
## Mina zkApp CLI
Ya instalaste Mina zkApp CLI como parte de los prerrequisitos, por lo que tienes las herramientas para gestionar despliegues.
En algunos casos, podrías necesitar crear una cuenta personalizada para tu zkApp, por ejemplo, para desplegar un zkApp con una clave diferente a la clave de pagador de tarifas, parametrizar programáticamente un zkApp antes de inicializarlo, o crear un contrato inteligente programáticamente para usuarios como parte de una aplicación. Para más detalles, consulta [Interactuar con zkApps en el lado del servidor](https://docs.minaprotocol.com/zkapps/tutorials/interacting-with-zkapps-server-side).
## Desplegar el contrato inteligente
El archivo de configuración `config.json` se creó automáticamente cuando creaste tu proyecto con el comando `zk project`. Sin embargo, el archivo de configuración generado aún no contiene el alias de despliegue.
### Alias de despliegue
Los mensajes de guía del comando `zk config` te ayudan a crear un alias de despliegue en tu archivo `config.json` del proyecto.
Puedes tener uno o más alias de despliegue para tu proyecto.
Un alias de despliegue consiste en:
- Un nombre auto-descriptivo (puede ser cualquier cosa). Utilizar patrones de nombres es útil cuando tienes más de un alias de despliegue.
- La URL de la API GraphQL de Mina que define la red que recibe tu transacción de despliegue y la transmite a la red Mina apropiada (Testnet, Devnet, Mainnet, etc.)
- La tarifa de transacción (en MINA) a usar al desplegar
- Dos pares de claves:
- Un par de claves para la cuenta zkApp. Las claves pública y privada para usar en tu aplicación se generan automáticamente en `keys/berkeley.json`.
- Un par de claves para usar como cuenta pagadora de tarifas para actualizaciones y despliegues. Las claves pública y privada se almacenan en tu computadora local y se pueden usar en varios proyectos.
- Alias de cuenta pagadora de tarifas
- Se requiere una cuenta pagadora de tarifas, puedes elegir usar una cuenta existente o crear una nueva cuenta pagadora de tarifas.
1. Para configurar tu despliegue, ejecuta el comando `zk config` y responde a las indicaciones:
```shell
$ zk config
```
Para este tutorial en la Testnet de Berkeley, utiliza:
- Nombre del alias de despliegue: `berkeley`
Este tutorial utiliza `berkeley`, pero el nombre del alias de despliegue puede ser cualquiera y no tiene que coincidir con el nombre de la red.
- URL de la API GraphQL de Mina: `https://proxy.berkeley.minaexplorer.com/graphql`
- Tarifa de transacción a utilizar al desplegar: `0.1`
- Cuenta para pagar las tarifas de transacción: Crear un nuevo par de pagadores de tarifas
2. Cuando se te pida que elijas una cuenta para pagar las tarifas de transacción, selecciona usar una cuenta diferente:
```shell
Use a different account (select to see options)
```
3. A continuación, selecciona crear un nuevo par de claves para el pagador de tarifas:
```shell
Create a new fee payer key pair
NOTE: the private key will be stored in plain text on this computer.
```
4. Cuando se te pregunte, da un alias a tu nuevo par de claves para el pagador de tarifas. Para este tutorial, utiliza `03-deploy`:
```shell
Create an alias for this account: viaje-al-blockchain
```
Tus pares de claves y el alias de despliegue se han creado:
```shell
✔ Create fee payer key pair at /Users/<username>/.cache/zkapp-cli/keys/viaje-al-blockchain.json
✔ Create zkApp key pair at keys/berkeley2.json
✔ Add deploy alias to config.json
Success!
Next steps:
- If this is a testnet, request tMINA at:
https://faucet.minaprotocol.com/?address=B62qqK5JgYAtmh2DHsQfUjUSKwQ6CFSPkGvyMZd19j1BUHfEJEqpKGo&?explorer=minaexplorer
- To deploy, run: `zk deploy berkeley`
```
5. Solicita fondos del Faucet de Testnet para financiar tu cuenta pagadora de tarifas.
Sigue las indicaciones para solicitar tMINA. Para este tutorial, tu dirección MINA se rellena en el Faucet de Testnet. tMINA llega a tu dirección cuando se produce el próximo bloque (aproximadamente 3 minutos).
6. Para desplegar tu proyecto:
```shell
$ zk deploy
```
7. En el mensaje interactivo, selecciona el alias de despliegue `berkeley`:
```shell
? Which deploy alias would you like to deploy to? …
❯ berkeley
```
Se genera una clave de verificación para tu contrato inteligente (tarda de 10 a 30 segundos).
Se muestra el proceso de despliegue:
```shell
✔ Build project
✔ Generate build.json
✔ Choose smart contract
Only one smart contract exists in the project: Square
Your config.json was updated to always use this
smart contract when deploying to this deploy alias.
✔ Generate verification key (takes 10-30 sec)
⠋ Build transaction...(node:25066) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
✔ Build transaction
```
8. Revisa y confirma los detalles de la transacción:
```shell
✖ Confirm to send transaction
┌─────────────────┬─────────────────────────────────────────────────┐
│ Deploy Alias │ berkeley2 │
├─────────────────┼─────────────────────────────────────────────────┤
│ Fee-Payer Alias │ viaje-al-blockchain │
├─────────────────┼─────────────────────────────────────────────────┤
│ URL │ https://proxy.berkeley.minaexplorer.com/graphql │
├─────────────────┼─────────────────────────────────────────────────┤
│ Smart Contract │ Square │
└─────────────────┴─────────────────────────────────────────────────┘
```
Cuando se te pregunte, escribe `yes` para confirmar y enviar la transacción.
```shell
✔ Send to network
Success! Deploy transaction sent.
Next step:
Your smart contract will be live (or updated)
as soon as the transaction is included in a block:
```
9. Para ver la transacción zkApp, ve a `https://berkeley.minaexplorer.com/transaction/<txn-hash>`, donde `<txn-hash>` es el hash de la transacción que se muestra en tu terminal.
- La Transacción zkApp Pendiente se muestra hasta que la transacción se incluya en el próximo bloque.
- La Transacción zkApp se muestra después de que la transacción se incluya en un bloque.
## Éxito
Después de que la transacción se incluya en un bloque, ¡tu contrato inteligente está desplegado!
- La cuenta Mina en esta clave pública ahora contiene la clave de verificación asociada con este contrato inteligente.
Ejecutaste el comando `zk config` para:
- Crear un alias de despliegue
- Crear un par de claves pagadoras de tarifas en `/Users/<username>/.cache/zkapp-cli/keys/03deploy.json`
- Crear un par de claves zkApp en `keys/berkeley.json`
Solicitaste tMINA para financiar tus transacciones de despliegue. Usa el tMINA restante para seguir construyendo y probando.
Ejecutaste el comando `zk deploy` para:
- Generar una clave de verificación para tu contrato inteligente
- Añadir el alias de despliegue al `config.json` del proyecto
¡Felicidades!
## Acerca de las Transacciones del Contrato Inteligente
Debido a que este tutorial utilizó el contrato inteligente del Tutorial 1: Hola Mundo, los permisos de `editState` del contrato inteligente requieren que una transacción contenga una prueba zk válida que haya sido creada por la clave privada asociada con esta cuenta zkApp.
- Cuando un usuario interactúa con este contrato inteligente proporcionando una prueba, la prueba se genera localmente en el dispositivo del usuario y se incluye en una transacción.
- Cuando la transacción se envía a la red, se verifica la prueba para asegurarse de que es correcta y coincide con la clave de verificación on-chain.
- Después de que la transacción sea aceptada, la prueba y la transacción se prueban recursivamente y se agrupan en la prueba de conocimiento cero recursiva de Mina.
Cuando cambias el código del contrato inteligente, la clave de verificación asociada también cambia. Utiliza los mismos pasos para volver a desplegar el contrato inteligente.
Para un contrato inteligente típico, los permisos se establecen para permitir solo la autorización por medio de prueba.
## Video
Mira este tutorial para obtener una guía paso a paso y explicaciones adicionales sobre cómo desplegar un zkApp.
[Cómo crear un contrato inteligente](https://www.youtube.com/embed/Sj9lTmgjDXY)
El video se proporciona con fines educativos y utiliza versiones anteriores de zkApp CLI y o1js, por lo que hay algunas diferencias. Este tutorial se ha probado con una versión específica de zkApp CLI y o1js.
## Conclusión
¡Felicidades! Has desplegado con éxito un contrato inteligente en una red en vivo.