15 KiB
18.1: Acceso a Bitcoind con Go
ℹ️ NOTA: Esta sección se ha añadido recientemente al curso y es un borrador temprano que todavía puede estar pendiente de revisión. Lectura con advertencias.
Esta sección explica cómo interactuar con bitcoind
usando el lenguaje de programación Go y el btcd rpcclient. Tenga en cuenta que tiene algunas peculiaridades y algunas limitaciones.
Configurar Go
Para prepararse para el uso de Go en su máquina UNIX, primero instale curl si aún no lo ha hecho:
$ sudo apt install curl
Luego, mire la página de descargas Go, obtenga el enlace para la última descarga y descarguelo usando curl
. Para una configuración de Debian, querrá usar la versión linux-amd64
:
$ curl -O https://dl.google.com/go/go1.15.1.linux-amd64.tar.gz
Una vez finalizada la descarga, compare el hash de la descarga con el hash en la página de descargas Go:
$ sha256sum go1.15.1.linux-amd64.tar.gz
70ac0dbf60a8ee9236f337ed0daa7a4c3b98f6186d4497826f68e97c0c0413f6 go1.15.1.linux-amd64.tar.gz
Los hashes deben coincidir. Si es así, extraiga el archivo e instale Go en su sistema:
$ tar xfv go1.15.1.linux-amd64.tar.gz
$ sudo chown -R root:root ./go
$ sudo mv go /usr/local
Ahora necesita crear una ruta Go para especificar su entorno. Abra el archivo ~/.profile
con un editor de su elección y añada lo siguiente al final de la misma:
export GOPATH=$HOME/work
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
Por último, cree el directorio de su espacio de trabajo Go:
$ mkdir $HOME/work
Configurar btcd
rpcclient
Usara el rpcclient
que viene con btcd,
una implementación de Bitcoin escrita en Go. Aunque rpcclient
fue originalmente diseñado para trabajar con el nodo completo de Bitcoin btcd
, también funciona con Bitcoin Core. Tiene algunas peculiaridades que veremos.
Puede utilizar go get
para descargarlo:
$ go get github.com/btcsuite/btcd/rpcclient
Para probar que funciona, vaya al directorio con los ejemplos de Bitcoin Core:
$ cd $GOPATH/src/github.com/btcsuite/btcd/rpcclient/examples/bitcoincorehttp
Modificar el archivo main.go
e introduzca los detalles asociados con la configuración del núcleo de Bitcoin, que se pueden encontrar en ~/.bitcoin/bitcoin.conf
:
Host: "localhost:18332",
User: "StandUp",
Pass: "6305f1b2dbb3bc5a16cd0f4aac7e1eba",
MAINNET VS TESTNET: El puerto 8332 sería para una configuración de mainnet.
Ahora puede realizar una prueba:
$ go run main.go
Deberia ver impreso el conteo de bloques:
2020/09/01 11:41:24 Block count: 1830861
Crear un proyecto rpcclient
Normalmente estará creando proyectos en su directorio ~/work/src/myproject/bitcoin
:
$ mkdir -p ~/work/src/myproject/bitcoin
$ cd ~/work/src/myproject/bitcoin
Cada proyecto deberá tener los siguientes imports
:
import (
"log"
"fmt"
"github.com/btcsuite/btcd/rpcclient"
)
Esta declaración import
le permite importar bibliotecas relevantes. Para cada ejemplo aquí, necesitará importar "log", "fmt"
y "github.com/btcsuite/btcd/rpcclient"
. Es posible que necesite importar bibliotecas adicionales para algunos ejemplos.
log
se utiliza para imprimir mensajes de error. Después de cada llamada al nodo Bitcoin, una declaraciónif
comprobará si hay algún error. Si hay errores,log
se utiliza para imprimirlos.fmt
se utiliza para imprimir la salida.rpcclient
; es obviamente la bibliotecarpcclient
Construya su conexión
Cada función bitcoind
en Go comienza con la creación de la conexión RPC, usando la función ConnConfig
:
connCfg := &rpcclient.ConnConfig{
Host: "localhost:18332",
User: "StandUp",
Pass: "431451790e3eee1913115b9dd2fbf0ac",
HTTPPostMode: true,
DisableTLS: true,
}
client, err := rpcclient.New(connCfg, nil)
if err != nil {
log.Fatal(err)
}
defer client.Shutdown()
Los parámetros connCfg
le permiten elegir el puerto RPC de Bitcoin, nombre de usuario, contraseña y si está en testnet o mainnet.
NOTA: De nuevo, asegúrese de sustituir el
User
yPass
con el que se encuentra en su~/.bitcoin/bitcon.conf
.
Por tanto la función rpcclient. New(connCfg, nil)
configura su client
para conectarse a su nodo Bitcoin.
La línea defer client.Shutdown()
es para desconectar de su nodo Bitcoin, una vez que la función main()
termina de ejecutarse.
Después de la línea defer client.Shutdown()
es donde va el material emocionante y será bastante fácil de usar.
Eso es porque rpcclient
ayuda a convertir los comandos bitcoin-cli
en funciones usando PascalCase.
Por ejemplo, bitcoin-cli getblockcount
será client.GetBlockCount
en Go.
Hacer una llamada RPC
Todo lo que se requiere ahora es hacer una llamada informativa como GetBlockCount
o GetBlockHash
usando su client
:
blockCount, err := client.GetBlockCount()
if err != nil {
log.Fatal(err)
}
blockHash, err := client.GetBlockHash(blockCount)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d\n", blockCount)
fmt.Printf("%s\n", blockHash.String())
Hacer una llamada RPC con argumentos
Las funciones rpcclient
también pueden tomar entradas; por ejemplo cliente.GetBlockHash(blockCount)
toma el conteo de bloques como una entrada.
El cliente client.GetBlockHash(blockCount)
visto anteriormente se vería como un comando bitcoin-cli
:
$ bitcoin-cli getblockhash 1830868
00000000000002d53b6b9bba4d4e7dc44a79cebd1024d1bcfb9b3cc07d6cad9c
Sin embargo, una peculiaridad con hashes en rpcclient
es que típicamente se imprimirán en una codificación diferente si se va a imprimir normalmente usando blockHash
.
Para imprimirlos como una cadena, es necesario utilizar blockHash.String()
.
Ejecute su código
Puede descargar el código completo desde el src directory.
A continuación, puede ejecutar:
$ go run blockinfo.go
1830868
00000000000002d53b6b9bba4d4e7dc44a79cebd1024d1bcfb9b3cc07d6cad9c
El último número de bloque junto con su hash debe imprimirse.
Buscar fondos
Debido a las limitaciones de la función btcd
rpcclient
, no se puede hacer un uso de la función getwalletinfo
.
Sin embargo, usted puede hacer uso de la llamada RPC getbalance
:
wallet, err := client.GetBalance("*")
if err != nil {
log.Fatal(err)
}
client.GetBalance("*")
requiere la entrada "*"
, debido a una peculiaridad con btcd
. El asterisco significa que usted quiere conseguir el saldo de todas sus carteras.
Si ejecuta el código src, debería obtener una salida similar a esta:
$ go run getbalance.go
0.000689 BTC
Crear una dirección
Puede generar direcciones en Go, pero no puede especificar el tipo de dirección:
Esto requiere el uso de una función especial chaincfg
, para especificar para qué red se están creando las direcciones.
Esta especificación solo se requiere durante la generación de direcciones, por lo que solo se utiliza en este ejemplo.
También puede incluir esto en otros ejemplos, pero no es necesario.
Asegúrese de importar "github.com/btcsuite/btcd/chaincfg"
:
import (
"log"
"fmt"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/chaincfg"
)
Luego llame a connCfG
con el parámetro chaincfg.TestNet3Params.Name
:
connCfg := &rpcclient.ConnConfig{
Host: "localhost:18332",
User: "bitcoinrpc",
Pass: "431451790e3eee1913115b9dd2fbf0ac",
HTTPPostMode: true,
DisableTLS: true,
Params: chaincfg.TestNet3Params.Name,
}
client, err := rpcclient.New(connCfg, nil)
if err != nil {
log.Fatal(err)
}
defer client.Shutdown()
MAINNET VS TESTNET: Params: chaincfg.TestNet3Params.Name,
debe ser Parameters: chaincfg.MainNetParams.Name,
en mainnet.
A continuación, puede crear su dirección:
address, err := client.GetNewAddress("")
if err != nil {
log.Fatal(err)
}
fmt.Println(address)
Una peculiaridad con client.GetNewAddress(")
es que se debe incluir una cadena vacía para que funcione.
Ejecutando la fuente se obtienen los siguientes resultados:
$ go run getaddress.go
tb1qutkcj34pw0aq7n9wgp3ktmz780szlycwddfmza
Decodificar una dirección
La creación de una dirección tomó un trabajo adicional, en especificar la cadena apropiada. Usar una dirección también lo hará porque tendrá que decodificarla antes de usarla.
Los medios que usted tendrá que importar tanto las librerías "github.com/btcsuite/btcutil"
como "github.com/btcsuite/btcd/chaincfg"
.
btcutil
permite que una dirección de Bitcoin sea decodificada de una manera que el clienterpcclient
pueda entender. Esto es necesario cuando se trabaja con direcciones enrpcclient
.chaincfg
se utiliza (de nuevo) para configurar su cadena como la cadena Testnet. Esto es necesario para la decodificación de direcciones ya que las direcciones utilizadas en Mainnet y Testnet son diferentes.
import (
"log"
"fmt"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcd/chaincfg"
)
La variable defaultNet ahora se utiliza para especificar si su nodo Bitcoin está en testnet o en mainnet. Esa información (y el objeto btcutil
) se utiliza para decodificar la dirección.
MAINNET VS TESTNET:
&chaincfg.TestNet3Params
debe ser&chaincfg.MainNetParams
en mainnet.
defaultNet := &chaincfg.TestNet3Params
addr, err := btcutil.DecodeAddress("mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha", defaultNet)
if err != nil {
log.Fatal(err)
}
NOTA: Cambie la dirección (
mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha
) para una de su billetera actual; puede utilizarbitcoin-cli listunspent
para encontrar algunas direcciones con fondos para esta prueba. Si quiere ser realmente elegante, modifique el código Go para tomar un argumento, luego escriba un script que ejecutelistunspent
, guarde la información en una variable, y ejecute el código Go con eso.
Solo después de esto, use la dirección getreceivedbyaddress
RPC, en su dirección decodificada:
wallet, err := client.GetReceivedByAddress(addr)
if err != nil {
log.Fatal(err)
}
fmt.Println(wallet)
Cuando ejecute el código, debería obtener una salida similar a:
$ go run getamountreceived.go
0.0085 BTC
Enviar una transacción
Ahora tiene todas las piezas del rompecabezas en su lugar para enviar una transacción. Va a querer:
- Importar las bibliotecas correctas, incluyendo
chaincfg
para especificar una red ybtcutil
para decodificar una dirección. - Elija una dirección para enviar.
- Descifre esa dirección.
- Ejecute
sendtoaddress
para enviar los fondos de la manera fácil.
package main
import (
"log"
"fmt"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcd/chaincfg"
)
func main() {
connCfg := &rpcclient.ConnConfig{
Host: "localhost:18332",
User: "StandUp",
Pass: "431451790e3eee1913115b9dd2fbf0ac",
HTTPPostMode: true,
DisableTLS: true,
}
client, err := rpcclient.New(connCfg, nil)
if err != nil {
log.Fatal(err)
}
defer client.Shutdown()
defaultNet := &chaincfg.TestNet3Params
addr, err := btcutil.DecodeAddress("n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi", defaultNet)
if err != nil {
log.Fatal(err)
}
sent, err := client.SendToAddress(addr, btcutil.Amount(1e4))
if err != nil {
log.Fatal(err)
}
fmt.Println(sent)
}
Cuando se ejecuta el código, el txid de la transacción se emite:
$ go run sendtransaction.go
9aa4cd6559e0d69059eae142c35bfe78b71a8084e1fcc2c74e2a9675e9e7489d
Buscar una transacción
Para buscar una transacción, como la que acaba de enviar, tendrá que volver a hacer algunas conversiones, esta vez de txid. "github.com/btcsuite/btcd/chaincfg/chainhash"
se importa para permitir que los hashes se almacenen en el código Go. chainhash.NewHashFromStr("hash")
convierte un hash en una cadena a un formato que funciona con rpcclient.
package main
import (
"log"
"fmt"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
func main() {
connCfg := &rpcclient.ConnConfig{
Host: "localhost:18332",
User: "StandUp",
Pass: "431451790e3eee1913115b9dd2fbf0ac",
HTTPPostMode: true,
DisableTLS: true,
}
client, err := rpcclient.New(connCfg, nil)
if err != nil {
log.Fatal(err)
}
defer client.Shutdown()
chash, err := chainhash.NewHashFromStr("1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df")
if err != nil {
log.Fatal(err)
}
transactions, err := client.GetTransaction(chash)
if err != nil {
log.Fatal(err)
}
fmt.Println(transactions)
}
NOTA: De nuevo, querrá cambiar el txid por uno realmente reconocido por su sistema.
Cuando ejecute el código imprimirá los detalles asociados con una transacción, como su cantidad y cuántas veces se ha confirmado:
$ go run lookuptransaction.go
{
"amount": 0.00100000,
"confirmations": 4817,
"blockhash": "000000006628870b0a8a66abea9cf0d4e815c491f079e3fa9e658a87b5dc863a",
"blockindex": 117,
"blocktime": 1591857418,
"txid": "1661ce322c128e053b8ea8fcc22d17df680d2052983980e2281d692b9b4ab7df",
"walletconflicts": [
],
"time": 1591857343,
"timereceived": 1591857343,
"bip125-replaceable": "no",
"details": [
{
"address": "mpGpCMX6SuUimDZKiVViuhd7EGyVxkNnha",
"category": "receive",
"amount": 0.00100000,
"label": "",
"vout": 0
}
],
"hex": "02000000000101e9e8c3bd057d54e73baadc60c166860163b0e7aa60cab33a03e89fb44321f8d5010000001716001435c2aa3fc09ea53c3e23925c5b2e93b9119b2568feffffff02a0860100000000001976a914600c8c6a4abb0a502ea4de01681fe4fa1ca7800688ac65ec1c000000000017a91425b920efb2fde1a0277d3df11d0fd7249e17cf8587024730440220403a863d312946aae3f3ef0a57206197bc67f71536fb5f4b9ca71a7e226b6dc50220329646cf786cfef79d60de3ef54f702ab1073694022f0618731902d926918c3e012103e6feac9d7a8ad1ac6b36fb4c91c1c9f7fff1e7f63f0340e5253a0e4478b7b13f41fd1a00"
}
Resumen: Acceso a Bitcoind con Go
Aunque btcd
y rpcclient
tienen algunos límites, aún es posible realizar los comandos principales de RPC en Go. La documentación para rpcclient
está disponible en Godoc. Si la documentación no tiene lo que está buscando, consulte también el repositorio btcd. Generalmente está bien documentado y es fácil de leer. Sobre la base de estos ejemplos debería ser capaz de incorporar Bitcoin en un proyecto Go y hacer cosas como enviar y recibir monedas.
¿Que sigue?
Obtenga más información sobre "Hablando con Bitcoin en otros lenguages" en 18.2: Accediendo a Bitcoin con Java.