Pruebas en regtest con transacciones RBF

En este foro se tratan los temas de interés general.
Avatar de Usuario
jochemin
Mensajes: 46
Registrado: Dom Sep 09, 2018 8:55 pm
GnuPG: 8009 FEB1 1A79 C293 5D17 72D5 0E1E DC2D 57F8 D07F
Twitter: jochemin
Agradecido : 14 veces
Agradecimiento recibido: 38 veces

Pruebas en regtest con transacciones RBF

Mensaje por jochemin » Lun Sep 24, 2018 1:14 pm

Con RBF (Replay By Fee) siempre he creído que sólo se pueden añadir inputs a una transacción, en el canal de telegram de Reizu realizó unas pruebas que demostraban que no es así, se puede modificar también el destinatario de una transacción marcada como reemplazable.

Las pruebas fueron concluyentes, aún y todo quedaban ciertas dudas y como no había salseado con el modo regtest (Regression Test Mode) de Bitcoin he visto la oportunidad perfecta para empezar y documentarlo para que cualquiera pueda hacerlo.

Los test los he hecho en Windows (es lo que tenía a mano en ese momento).

Voy a dividir las pruebas en 2 publicaciones en este tema, esta primera es montaje y comprobación de un entorno regtest, la segunda serán las pruebas sin y con RBF.

Para comenzar instalamos el cliente Bitcoin Core si no lo tenemos ya.

Para las pruebas vamos a montar 3 nodos para ello generamos una estructura de directorios donde alojaremos los datos de cada uno de ellos:
Imagen

Dentro de cada carpeta añadiremos un fichero de configuración (bitcoin.conf) con el usuario y la contraseña para RPC:
Imagen

Lanzamos los comandos de inicio desde un cmd, uno por cada nodo.

Código: Seleccionar todo

bitcoind -regtest -port=8333 -rpcport=8332 -datadir=c:\regtest\node1 -conf=c:\regtest\node1\bitcoin.conf

Código: Seleccionar todo

bitcoind -regtest -port=8335 -rpcport=8334 -datadir=c:\regtest\node2 -conf=c:\regtest\node2\bitcoin.conf

Código: Seleccionar todo

bitcoind -regtest -port=8337 -rpcport=8336 -datadir=c:\regtest\node3 -conf=c:\regtest\node3\bitcoin.conf
Lo primero que debemos hacer es conectar los nodos entre si, en este caso desde una ventana de comandos lanzamos lo siguiente:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcport=8332 -rpcuser=node1 -rpcpassword=node1 addnode 127.0.0.1:8335 add
bitcoin-cli -regtest -rpcport=8332 -rpcuser=node1 -rpcpassword=node1 addnode 127.0.0.1:8337 add
Esto añadirá como peer los nodos 2 y 3 al nodo1. Podemos comprobar las conexiones del nodo1 con el siguiente comando:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 getconnectioncount
Imagen

Generamos 101 bloques desde nodo1 con el siguiente comando:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 -rpcport=8332 generate 101
Comprobamos el saldo:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 -rpcport=8332 getbalance
Comprobamos los nodos 2 y 3 para comprobar que están conectados y sincronizados:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node2 -rpcpassword=node2 -rpcport=8334 --getinfo
bitcoin-cli -regtest -rpcuser=node3 -rpcpassword=node3 -rpcport=8336 --getinfo
Imagen

Para comprobar que todo funciona correctamente vamos a realizar una transacción, generamos una dirección en nuestro nodo2 con

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node2 -rpcpassword=node2 -rpcport=8334 getnewaddress
Imagen

Enviamos 1BTC desde nuestro nodo1 con

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 sendtoaddress 2N6FEkFMsx8MpGwAAmyyg6yeaDjQ8nPH6XS 1
Este comando nos devuelve el identificador de la transacción (TXid) que en este caso es 60b46a9f9972b7829308e7de9617c91c42518db994a1497d41e7c4527148f448
Imagen

Comprobamos que la transacción se ha propagado a nuestros 3 nodos consultando su mempool con los siguientes comandos:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 getrawmempool
bitcoin-cli -regtest -rpcuser=node2 -rpcpassword=node2 -rpcport=8334 getrawmempool
bitcoin-cli -regtest -rpcuser=node3 -rpcpassword=node3 -rpcport=8336 getrawmempool
Imagen

Todo pinta genial, tenemos la transacción en la memoria de transacciones de nuestros 3 nodos, podemos comprobar como nuestro nodo2 al que hemos enviado la transacción tiene 1 bitcoin no confirmado, para ello utilizamos

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node2 -rpcpassword=node2 -rpcport=8334 getunconfirmedbalance
Imagen

Ok, vamos a proceder a minar un bloque para confirmar la transacción:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 generate 1
Una vez minado el bloque comprobamos que el bitcoin ya está confirmado en nuestro nodo2 y que la memoria de transacciones está vacía.

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node2 -rpcpassword=node2 -rpcport=8334 getbalance

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 getrawmempool
bitcoin-cli -regtest -rpcuser=node2 -rpcpassword=node2 -rpcport=8334 getrawmempool
bitcoin-cli -regtest -rpcuser=node3 -rpcpassword=node3 -rpcport=8336 getrawmempool
Imagen

Vale, hasta aquí vamos bien tenemos 3 nodos conectados entre si y hemos comprobado que los bloques y transacciones se propagan sin problema. Vamos con las pruebas con RBF.
Si te ha servido alguna de mis guías o simplemente me quieres invitar a un café --> https://tippin.me/@jochemin

Avatar de Usuario
jochemin
Mensajes: 46
Registrado: Dom Sep 09, 2018 8:55 pm
GnuPG: 8009 FEB1 1A79 C293 5D17 72D5 0E1E DC2D 57F8 D07F
Twitter: jochemin
Agradecido : 14 veces
Agradecimiento recibido: 38 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por jochemin » Lun Sep 24, 2018 1:14 pm

Tenemos un entorno de pruebas listo para utilizar, para comprobar si se puede sustituir la dirección de destino a una transacción con RBF primero generaremos una sin marcar como reemplazable y posteriormente una reemplazable (RBF)

Generamos una dirección en nuestro nodo3 con

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node3 -rpcpassword=node3 -rpcport=8336 getnewaddress
nos devuelve esta dirección: 2Myq4pynBLLVi5wfj7ngEEdSm1H3YY8CfMv

Para crear una transacción utilizando el método "createrawtransaction" necesitamos varios datos, entre ellos el UTXO que vamos a utilizar, podemos listarlos con el siguiente comando:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 listunspent
Imagen

Vamos a enviar 1 BTC a la dirección que hemos creado en nuestro nodo3, como he dicho vamos a utilizar el método "createrawtransaction". El comando es el siguiente:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 createrawtransaction "[{\"txid\":\"60b46a9f9972b7829308e7de9617c91c42518db994a1497d41e7c4527148f448\",\"vout\": 0}]" "{\"2Myq4pynBLLVi5wfj7ngEEdSm1H3YY8CfMv\":1,\"2NDBW6T9y8nr9Xivd6hPRdiEM8jpGtnXPnu\":47.99995616}"
Traducido cojemos un output concreto de una transacción y lo utilizamos como input, enviamos 1 bitcoin de ese input a la dirección de destino de nuestro nodo 3, 47'99995616 volverán a nuestra dirección y lo que queda hasta cubrir el vout completo será la fee.

Este comando nos devolverá una cadena en hexadecimal que contiene el detalle de nuestra transacción. Algo así:
020000000148f4487152c4e7417d49a194b98d51421cc91796dee7089382b772999f6ab4600000000000ffffffff0200e1f5050000000017a9144837402c11ab359d3622aaf868486b24b5d53dd987e01e1a1e0100000017a914daae4bf62e879ba69935f1f1a9aa770af3e599cb8700000000

Ahora tenemos que firmar la transacción, para ello utilizamos el siguiente comando:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 signrawtransaction 020000000148f4487152c4e7417d49a194b98d51421cc91796dee7089382b772999f6ab4600000000000ffffffff0200e1f5050000000017a9144837402c11ab359d3622aaf868486b24b5d53dd987e01e1a1e0100000017a914daae4bf62e879ba69935f1f1a9aa770af3e599cb8700000000
Y nos devolverá otra cadena con nuestra transacción firmada, en este caso:
0200000000010148f4487152c4e7417d49a194b98d51421cc91796dee7089382b772999f6ab4600000000017160014235a8e2a35dfddb351160372cb964614db2d40d9ffffffff0200e1f5050000000017a9144837402c11ab359d3622aaf868486b24b5d53dd987e01e1a1e0100000017a914daae4bf62e879ba69935f1f1a9aa770af3e599cb870248304502210088dfda379492eb0c5b2b2240177598c8e7791902da23f90f1e25e1bfae4257360220171611fd07dae7482d2e7eb27f6a62bec6780e52f269115f080392f55434f576012102dcc6343071498b3766a6100d9e623b4c9ae5af0de8125e83450734f622f380bc00000000
Imagen

Ya tenemos nuestra transacción firmada, ahora hay que propagarla a nuestra red, para ello utilizamos el siguiente comando:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 sendrawtransaction 0200000000010148f4487152c4e7417d49a194b98d51421cc91796dee7089382b772999f6ab4600000000017160014235a8e2a35dfddb351160372cb964614db2d40d9ffffffff0200e1f5050000000017a9144837402c11ab359d3622aaf868486b24b5d53dd987e01e1a1e0100000017a914daae4bf62e879ba69935f1f1a9aa770af3e599cb870248304502210088dfda379492eb0c5b2b2240177598c8e7791902da23f90f1e25e1bfae4257360220171611fd07dae7482d2e7eb27f6a62bec6780e52f269115f080392f55434f576012102dcc6343071498b3766a6100d9e623b4c9ae5af0de8125e83450734f622f380bc00000000
d9a7deb4c087d90423d99b78dc2f31e5b6f8e24fc0a194fc49db97e93f60c09b
Esto nos devolverá el id de la transacción y comprobaremos como lo hemos hecho en el post anterior que dicha transacción está en la memoria de transacciones de nuestros 3 nodos con:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 getrawmempool
bitcoin-cli -regtest -rpcuser=node2 -rpcpassword=node2 -rpcport=8334 getrawmempool
bitcoin-cli -regtest -rpcuser=node3 -rpcpassword=node3 -rpcport=8336 getrawmempool
Imagen

Tenemos la transacción en la memoria de nuestros 3 nodos, vamos a generar una transacción exactamente igual pero modificando fee y dirección de destino, seguimos el mismo proceso, veremos que al propagar la nueva transacción da un error claro. "18:txn-mempool-conflict"
Imagen

Bien, hemos comprobado que una transacción no marcada como reemplazable no se puede modificar y da un error claro a la hora de intentarlo. Para despejar la mempool minamos 1 nuevo bloque con el comando:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 generate 1
Repetimos todo el proceso con pero marcamos nuestra transacción como reemplazable (RBF), para hacerlo, debemos añadir al final del comando un "0" que se refiere a locktime y un "true" que se refiere a RBF. Poniendo ese "true" indicamos que es una transacción RBF.

El comando quedaría así:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 createrawtransaction "[{\"txid\": \"4a5eaf7168089bc54577f355272fe950b5bc0b7b6e5aeafcd2dc75881325b823\", \"vout\": 1}]" "{\"2Mxj2uongmNmJui2UrUeJSDPa97PuwsdvaX\":1,\"2NDBW6T9y8nr9Xivd6hPRdiEM8jpGtnXPnu\":45.99991656}" 0 true
Firmamos la transacción y la enviamos, en este caso nos devuelve como TXid 7d808fd7b6982f2a11349c2ac63aa51477357e0b2c90145498decddbea20208d. Comprobamos que la transacción existe en la memoria de transacciones de nuestros 3 nodos.
Imagen

Repetimos por última vez el proceso, generamos una nueva transacción pero modificamos la dirección de destino y elevamos la comisión. Utilizo este comando:

Código: Seleccionar todo

bitcoin-cli -regtest -rpcuser=node1 -rpcpassword=node1 -rpcport=8332 createrawtransaction "[{\"txid\": \"4a5eaf7168089bc54577f355272fe950b5bc0b7b6e5aeafcd2dc75881325b823\", \"vout\": 1}]" "{\"2N6FEkFMsx8MpGwAAmyyg6yeaDjQ8nPH6XS\":1,\"2NDBW6T9y8nr9Xivd6hPRdiEM8jpGtnXPnu\":45.99991356}" 0 true
La firmamos y enviamos.

En el log (debug.log) de los nodos vemos como añadió la primera y ahora añade la segunda:
Imagen

Si consultamos la memoria de transacciones de nuestros 3 nodos comprobaremos que ha desaparecido la primera versión de la transacción y sólo queda la segunda con diferente dirección de destino.
Imagen

Conclusiones

Tenía una idea equivocada de RBF, no sólo puedes añadir inputs a la transacción, también se pueden modificar, no sólo la dirección de destino, sino cualquier parámetro de la transacción con la única condición de elevar la fee.

Gracias a Reizu por realizar las primeras pruebas y recordarme que es mejor verificar que confiar.
Si te ha servido alguna de mis guías o simplemente me quieres invitar a un café --> https://tippin.me/@jochemin

jtimon
Mensajes: 1
Registrado: Lun Sep 24, 2018 5:50 pm
GnuPG: 4B4E 8404 5114 9DD7 FB0D 6334 77DF AB5C 3108 B9A8
Twitter:
Agradecimiento recibido: 3 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por jtimon » Lun Sep 24, 2018 6:03 pm

Muchas gracias!

Creo que es muy interesante para gente novata con regtest.
Parece que reizun tenía razón y se puede cambiar la dirección de destino y la de cambio. Supongo que si se prohibiese cada vez que quisieses aumentar la comisión tendrías que añadir un nuevo output de cambio y por eso no se hace.

Pero " también se pueden modificar, no sólo la dirección de destino, sino cualquier parámetro de la transacción con la única condición de elevar la fee." no es correcto, aquí hay un montón de tests con cosas que no se pueden modificar o restricciones sobre las modificaciones: https://github.com/bitcoin/bitcoin/blob ... ure_rbf.py

Por cierto, por la prueba veo que no necesitabas más de un nodo a menos que quieras hacer el double spend seguro incluso sin el rbf (y hubiese sido necesario si hubiese tenido razón yo sobre cómo estaba ocurriendo el double spend).

Para hacer esa prueba, si quieres, sigue los sigueintes pasos:

1) En el setup, conecta el nodo 1 al nodo 2, pero no conectes el nodo 3 con ninguno.

2) haz la tx1, sin rbf

3) hay alguna llamada para quitar una tx de la mempool, no recuerdo cuál. en el nodo 1 (el atacante), borra tx1

4) crea tx2, el doublespend, también sin rbf, el nodo 2 no la aceptará en su mempool porque ya tiene tx1

5) conecta el nodo 3 al nodo 1

6) con el nodo 3, usa "generate" para crear un bloque

tachán! double spend sin rbf, algo que puede pasar perfectamente en bcash también sin necesidad de que se "hackee" a nadie.

Avatar de Usuario
jochemin
Mensajes: 46
Registrado: Dom Sep 09, 2018 8:55 pm
GnuPG: 8009 FEB1 1A79 C293 5D17 72D5 0E1E DC2D 57F8 D07F
Twitter: jochemin
Agradecido : 14 veces
Agradecimiento recibido: 38 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por jochemin » Lun Sep 24, 2018 6:14 pm

Miraré las restricciones en las modificaciones que se pueden realizar. La verdad es que no he hecho más que modificar dirección de destino y fee.

Tengo claro el tema del doble gasto es obvio que en la propagación entran temas que no se controlan y que un nodo haya visto una tx no significa que la hayan visto todos, incluso diferentes clientes se comportarán de maneras diferentes por eso he puesto en el telegram que me encantaría hacer la prueba con los nodos de los mineros, seguramente muchos reemplacen tx sin tan siquiera estar marcadas como reemplazables.

Mi "problema" no es con el comportamiento de RBF, ni el que pensaba que tenía ni el que realmente tiene, ha sido más el que habiendo leído sobre RBF y bastante había llegado a una conclusión equivocada. Es decir tenía algo como sabido y estaba equivocado.
Si te ha servido alguna de mis guías o simplemente me quieres invitar a un café --> https://tippin.me/@jochemin

Avatar de Usuario
reizu
Mensajes: 7
Registrado: Lun Sep 17, 2018 12:32 am
GnuPG: 0x85d64b1a9931c44f
Twitter: 1reizu
Agradecimiento recibido: 6 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por reizu » Mar Sep 25, 2018 12:05 am

Que yo sepa la única restricción a la hora de sustituir la primera transacción es que el fee de la segunda transacción sea más alto. No hay más restricciones por el hecho de ser una "sustitución", simplemente las mismas que se aplican a cualquier transacción RBF. Es más, la segunda transacción no tiene por qué ser RBF, puede ser una transacción normal y esa ya no se podrá sustituir, con lo cual se puede dar el caso de que la segunda transacción (no RBF) tenga menos restricciones que la primera (sí RBF).

Por otro lado, como curiosidad: una transacción es reemplazable si cualquiera de sus inputs lo es (RBF). Podrías crear una transacción con 99 inputs no RBF y 1 input RBF, y podrás sustituir la transacción entera, eliminando esos 99 inputs "no RBF". Conclusión: para saber si una transacción es reemplazable hay que mirar todos sus inputs.

Avatar de Usuario
reizu
Mensajes: 7
Registrado: Lun Sep 17, 2018 12:32 am
GnuPG: 0x85d64b1a9931c44f
Twitter: 1reizu
Agradecimiento recibido: 6 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por reizu » Mar Sep 25, 2018 1:13 am

Bueno, ahora que lo pienso... aunque la segunda transacción no sea RBF, sigue teniendo las mismas restricciones para entrar al mempool según BIP 125 (por ejemplo la de replacement-adds-unconfirmed). Ahí creo que me he colado, en ambos casos (siendo la de reemplazo también RBF o no) tienen las mismas restricciones.

Avatar de Usuario
reizu
Mensajes: 7
Registrado: Lun Sep 17, 2018 12:32 am
GnuPG: 0x85d64b1a9931c44f
Twitter: 1reizu
Agradecimiento recibido: 6 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por reizu » Mar Sep 25, 2018 1:32 am

Y ahora que lo vuelvo a pensar... :lol:

La transacción de reemplazo (la segunda) sí que tiene restricciones que la primera transacción no tenía. Por ejemplo esa que comenté en mi post anterior (replacement-adds-unconfirmed = usar un input sin confirmar). Me había liado porque pensaba que esa/s restricción/es también se aplicaba a la primera transacción, pero sólo se aplica cuando hay un reemplazo. jtimon tenía razón.

Un saludo.

Avatar de Usuario
jochemin
Mensajes: 46
Registrado: Dom Sep 09, 2018 8:55 pm
GnuPG: 8009 FEB1 1A79 C293 5D17 72D5 0E1E DC2D 57F8 D07F
Twitter: jochemin
Agradecido : 14 veces
Agradecimiento recibido: 38 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por jochemin » Mar Sep 25, 2018 8:13 am

Reizu, las 2 dudas que tengo son:

En tus pruebas, al crear la transacción metes "sequence", 1 en la primera 2 en la siguiente. Esa "sequence" no tiene que ver con RBF, mira el BIP68. Para marcar una transacción como reemplazable solo hay que poner true en el campo "replaceable"
Imagen

Partiendo de eso, ayer pusiste lo siguiente:
Por cierto, como curiosidad: una tx RBF es cualquiera que lleve al menos un input RBF. Es decir, una tx con 99 inputs no RBF y 1 input RBF es reemplazable (cargándote los 99 inputs “no reemplazables” si quieres).
¿Como controlas que en una misma transacción haya input reemplazables y otros no? Como yo lo entiendo la característica de reemplazable es de la transacción en global.
Si te ha servido alguna de mis guías o simplemente me quieres invitar a un café --> https://tippin.me/@jochemin

Avatar de Usuario
DineroNuevo
Mensajes: 3
Registrado: Sab Sep 15, 2018 1:37 pm
GnuPG:
Twitter: dineronuevo
Agradecido : 3 veces
Agradecimiento recibido: 3 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por DineroNuevo » Mar Sep 25, 2018 11:05 am

¿Cómo podría una wallet identificar que una TX lleva RBF? ¿Podrían hacerse de forma oculta y que las wallets no se dieran cuenta?

Avatar de Usuario
jochemin
Mensajes: 46
Registrado: Dom Sep 09, 2018 8:55 pm
GnuPG: 8009 FEB1 1A79 C293 5D17 72D5 0E1E DC2D 57F8 D07F
Twitter: jochemin
Agradecido : 14 veces
Agradecimiento recibido: 38 veces

Re: Pruebas en regtest con transacciones RBF

Mensaje por jochemin » Mar Sep 25, 2018 11:25 am

Cuando marcas una transacción como reemplazable en los detalles de esa transacción lo muestra.

Esta es una transacción no reemplazable:
Imagen

Y esta es una reemplazable:
Imagen

La wallet tiene que consultar ese detalle.
Si te ha servido alguna de mis guías o simplemente me quieres invitar a un café --> https://tippin.me/@jochemin

Responder