DEV Community

Cover image for Flow Blockchain — Golang: Firmar y Enviar una Transacción
Javier Soto
Javier Soto

Posted on • Edited on

Flow Blockchain — Golang: Firmar y Enviar una Transacción

Últimamente el desarrollo en las diferentes blockchain a tomado vuelo, debido a las altas ventas en NFT y diferentes proyectos que han surgido. Entre ellas esta Flow, una blockchain dedicada a desarrolladores con tarifas de transferencias mínimas (gas fees), segura y extremadamente rápida. Hoy veremos completamente la anatomía de las cuentas, firmas (signature) y como podemos enviar una transacción, todo usando Golang.

Introducción

En el siguiente tutorial veremos las partes necesarias para poder validar y enviar una transacción a la blockchain, incluyendo una breve explicación de las cuentas (accounts) y las llaves (keys). No cubrirá las especificas de una transacción, ni las del lenguaje de cadence o muchos detalles de Golang.
Se usará la documentación oficial de Flow para algunos recursos como imágenes o explicaciones.

Anatomía de una Transacción

La anatomía de una transacción puede ser representada por este diagrama. En donde la carga (payload) es donde se especifican los roles (sus cuentas de flow), el código y otros elementos necesitados para que la transacción sea valida.

Las firmas son almacenadas en los sobres, de las cuales esta el sobre de autorización y el sobre de pago.

Llaves (Keys)

Cada cuenta creada en Flow, contiene al menos una llave (key) asociada. Esta key es parte fundamental en el ciclo de vida de una transacción.

Cada relación account-key tiene un número de secuencia (sequence number), este número de secuenciá aumenta en uno cada vez que la llave se usa como la llave proponente (proposer key) en una transacción.

Las llaves también contiene un peso (weight), que es importante para los autorizadores (authorizer) de la transacción que tocare en un momento.

Cuentas (Accounts)

La primera sección que se necesita en la transacción es las cuentas que van a ser las que firman dicha transacción. Hay 3 tipos de roles, que son cuentas como cualquier otra pero toman una función distinta a la hora de firmar una transacción.

Proposer (proponedor)

El proposer siempre es el primer en firmar la transacción. El único propósito de la cuenta como proposer es de proveer la llave. En muchos casos, el proposer es la cuenta que intenta enviar la transacción y también solo se necesita una llave como proposer. En esta parte es donde el número de secuencia de la llave es importante, ya que cuando la transacción se envía y es accionada en la blockchain, el número de secuencia tiene que ser idéntico al que está en la cuenta en runtime.

Ejemplo teóricos:

Cuenta A firma como proposer la transacción X, y la proposer key contiene un número de secuencia 1. Al momento en que la transacción corre en la blockchain, se corroborara que la firma del proposer con número de secuencia 1 sea la misma de la que se encuentra en la cuenta A. Si por algún motivo, al momento en que la transacción corre y la cuenta A tenga un número de secuencia 2, la transacción fallara. Esto puede pasar cuando se firman múltiple transacciones al mismo tiempo, con la misma llave y una de ellas termina al momento que la otra corra.

Authorizer (autorizador)

El autorizador juega un rol específico dentro de la transacción, ya que es lo que dará acceso a la cuenta dentro de la transacción. El autorizador se refiera a la cuenta que se modificara o accederá a sus datos en la transacción. Te darás cuenta si una transacción necesita authorizers, y cuantos cuando veas los argumentos de la etapa de prepare.

transaction {
  prepare(acct: AuthAccount, acctSec: AuthAccount) {}
}
Enter fullscreen mode Exit fullscreen mode

En el ejemplo, la transacción necesitaría dos cuentas que funcionen como los authorizer, con sus firmas incluidas.

Otro dato muy importante de los autorizadores y sus llaves, es que para que una transacción sea considerada valida, el peso del conjunto de llaves que firma la transacción tiene que sumar 1000.

Por ejemplo:

Cuenta A tiene 4 llaves con pesos 500, 500, 300, 200 respectivamente. Firma una transacción X que requiere un solo autorizador. Para que la transacción sea valida, tendrá que firmar la transacción con cualquier numero de llaves que sume 100, este podría ser 1 y 2 (500, 500), 1 + 3 + 4 (500, 300, 200), etc.

Payer (pagador)

Cada transacción tiene que ser pagada, en este caso se considera la tarifa (gas fee) de la transacción. Esta entidad se conoce como el payer (pagador). Este rol siempre, SIEMPRE, firma al ultimo

Si tienes una billetera custodial (custodial wallet) como es Dapper o Blocto, es la que usualmente actúa como el payer cuando una cuenta se crea o cuando acciona una transacción como intermediario.

Sobres (Envelopes)

Como dicho anteriormente, hay dos sobres en la cual se almacenan las firmas. Las firmas del proposer y los authorizers se almacenan en el sobre de autorización, y la firma del payer se encuentra al último en el sobre de pago.

Firmas (Signature)

Las firmas son una estructura compuesta por 3 valores, la dirección de la cuenta flow que esta firmando (flow address), el ID de la llave (keyID) y la información de la firma (signature data). La implementación en Go también contiene un valor extra que es el indice del firmador.

type TransactionSignature struct {
 Address     Address
 SignerIndex int
 KeyIndex    int
 Signature   []byte
}
Enter fullscreen mode Exit fullscreen mode

Implementación

Ahora que se acaba las explicaciones, pasemos a cuales son los pasos para firmar y mandar tu transacción a la blockchain. Estos ejemplos son basados de la repo oficial de flow.

Primero es reunir tu código de transacción, la cuenta que se modificara y la cuenta en que va a ser el payer (si quieres probar con mas de una cuenta como authorizer, tambien puedes).

Pseudocódigo

Añadir codigo a la transaccion y argumentos
Añadir los roles necesarios
Firmar el Auhtorization Envelope con el Proposer
Firmar el Auhtorization Envelope con los Authorizers
Firmar el Payment Envelope con el Payer
Mandar la transaccion
Enter fullscreen mode Exit fullscreen mode

Ejemplo

En este ejemplo usaremos el Flow client por medio de HTTP, pero se recomienda usar el gRPC Client para el mejor rendimiento, tambien el use de las private keys va a depender de tu aplicación. Este ejemplo solo dará una plantilla de como se puede usar el Go SDK para implementar el pseudocódigo de arriba.

Crear Una Cuenta

La forma mas fácil de crear una cuenta es por el flow CLI. Necesitas un par de llaves (secreta y publica). En producción, esto se manejaría con un KMS (Key Management System) o con openssl, pero va mas allá del tutorial.

flow accounts create --key {{llave.publica}} --key-weight {{peso}}
flow accounts create --network=testenet \
--key {{llave.publica}} --key-weight {{peso}}
Enter fullscreen mode Exit fullscreen mode

En testnet te abrirá una pestaña que te terminara la creación de la cuenta y te depositara tokens

Si quieres generar un par de llaves, puedes usar paginas como esta que te explican https://techdocs.akamai.com/iot-token-access-control/docs/generate-ecdsa-keys

Crear Transacción

Necesitamos crear una plantilla para la transaccion. Esto es simplemente para inicializar lo que utilizaremos mas adelante.

codigoTx := "" // este sera tu codigo de la transaccion
tx := flow.NewTransaction().SetScript([]byte(codigoTx)) 
Enter fullscreen mode Exit fullscreen mode

Crear un Key Pair y Signer

El key (llave) y el signer son partes fundamentales de la firma de la transacción como lo comentamos anteriormente. Estos elementos se usaran para firmar el payload y/o envelope de la transacción.

Añadir Roles a la Transacción

En este paso, vamos a tomar los roles necesarios (Proposer, Authorizer, Payer) y sumarlos a la transacción.

Añadir Firmas a la Transacción

La ultima parte de empacar la transacción es la firma. Las firmas son añadidas al ultimo, primero las de Payload (proposer, authorizer) y al final el Envelope (payer)

Mandar La Transacción

El ultimo paso es mandar la transacción a la blockchain. Es simple, y es útil ver la ID de la transacción para verificar los eventos, o si fallo en run time.

 err = flowClient.SendTransaction(ctx, *tx)
 if err != nil {
  fmt.Println(err)
  return
 }

 fmt.Println("ID: " + tx.ID().String())
Enter fullscreen mode Exit fullscreen mode

Puedes usar esta herramienta para observar tu transacción

Con esto finalizamos el tutorial, Flow tiene un gran futuro para los desarrolladores y no solo en el mundo de los NFT. Espero que mas hispanohablantes se sumen y puedan desarrollar en la blockchain.

Cualquier consulta en los comentarios, y espero estar de vuelta con mas.

El código completo esta en este gist.

Top comments (0)