Skip to content
This repository has been archived by the owner on Sep 26, 2022. It is now read-only.

teos_cli tests with real transactions

Sergi Delgado Segura edited this page Mar 26, 2020 · 3 revisions

This documents defines how to create commitment and penalty transactions* in Bitcoin testnet3 to add to the appointment_data.json that will be used to send an appointment to the eye of satoshi 😄.

We assume that you've already been able to send a dummy appointment (meaning that you've successfully installed teos_cli and run over the examples).

Running a testnet3 node

The first thing you'll need to do is run a testnet3 node. To do so you need to get Bitcoin Core and run it with the testnet flag:

bitcoind -server -daemon -testnet -rpcuser=user -rpcpassword=passwd

It may take a while until the IDB (Initial Block Download) is completed, so go out, take a break and maybe have a nice coffee ☕️, I'll be here waiting.

You can test if your node is already up to date by checking the block height:

bitcoin-cli -testnet -rpcuser=user -rpcpassword=passwd getblockcount

And check if that matches with the latest block in any testnet explorer, such as blockcypher.

Creating a new address and get some coins

Now it's time to create a new address so you can get some sats to your wallet. Run:

bitcoin-cli -testnet -rpcuser=user -rpcpassword=passwd getnewaddress

That will output a new address four you. Now let's go to a faucet and get some coins. This one should do.

You may need to wait a bit till the transaction gets some confirmations.

Create transaction

Now it's time for the fun part. Get this Python script and save it as create_txs.py or whatever name you like.

from decimal import Decimal, getcontext
from bitcoinrpc.authproxy import AuthServiceProxy

getcontext().prec = 10


def create_commitment_tx(bitcoin_cli, utxo, destination=None, fee=Decimal(1 / pow(10, 5))):
    # We will set the recipient to ourselves is destination is None
    if destination is None:
        destination = utxo.get("address")

    commitment_tx_ins = {"txid": utxo.get("txid"), "vout": utxo.get("vout")}
    commitment_tx_outs = {destination: utxo.get("amount") - fee}

    raw_commitment_tx = bitcoin_cli.createrawtransaction([commitment_tx_ins], commitment_tx_outs)
    signed_commitment_tx = bitcoin_cli.signrawtransactionwithwallet(raw_commitment_tx)

    if not signed_commitment_tx.get("complete"):
        raise ValueError("Couldn't sign transaction. {}".format(signed_commitment_tx))

    return signed_commitment_tx.get("hex")


def create_penalty_tx(bitcoin_cli, decoded_commitment_tx, destination=None, fee=Decimal(1 / pow(10, 5))):
    # We will set the recipient to ourselves is destination is None
    if destination is None:
        destination = decoded_commitment_tx.get("vout")[0].get("scriptPubKey").get("addresses")[0]

    penalty_tx_ins = {"txid": decoded_commitment_tx.get("txid"), "vout": 0}
    penalty_tx_outs = {destination: decoded_commitment_tx.get("vout")[0].get("value") - fee}

    orphan_info = {
        "txid": decoded_commitment_tx.get("txid"),
        "scriptPubKey": decoded_commitment_tx.get("vout")[0].get("scriptPubKey").get("hex"),
        "vout": 0,
        "amount": decoded_commitment_tx.get("vout")[0].get("value"),
    }

    raw_penalty_tx = bitcoin_cli.createrawtransaction([penalty_tx_ins], penalty_tx_outs)
    signed_penalty_tx = bitcoin_cli.signrawtransactionwithwallet(raw_penalty_tx, [orphan_info])

    if not signed_penalty_tx.get("complete"):
        raise ValueError("Couldn't sign orphan transaction. {}".format(signed_penalty_tx))

    return signed_penalty_tx.get("hex")


def create_txs(rpc_user, rpc_pass, rpc_host, rpc_port):
    bitcoin_cli = AuthServiceProxy("http://%s:%s@%s:%d" % (rpc_user, rpc_pass, rpc_host, rpc_port))
    utxos = bitcoin_cli.listunspent()

    if len(utxos) == 0:
        raise ValueError("There're no UTXOs.")

    utxo = utxos.pop(0)
    while utxo.get("amount") < Decimal(2 / pow(10, 5)):
        utxo = utxos.pop(0)

    signed_commitment_tx = create_commitment_tx(bitcoin_cli, utxo)
    decoded_commitment_tx = bitcoin_cli.decoderawtransaction(signed_commitment_tx)

    signed_penalty_tx = create_penalty_tx(bitcoin_cli, decoded_commitment_tx)

    return signed_commitment_tx, decoded_commitment_tx.get("txid"), signed_penalty_tx


if __name__ == "__main__":
    rpc_user = "user"
    rpc_pass = "passwd"
    rpc_host = "localhost"
    rpc_port = 18332

    commitment_tx, commitment_txid, penalty_tx = create_txs(rpc_user, rpc_pass, rpc_host, rpc_port)

    print(
        "Commitment txid (to be included in the tx_id field of dummy_appointment_data.json): {}".format(commitment_txid)
    )
    print("Penalty tx (to be included in the tx field of dummy_appointment_data.json): {}".format(penalty_tx))
    print("Commitment tx (to be used to trigger a breach): {}".format(commitment_tx))

Now install bitcoinrpc using pip:

pip install python-bitcoinrpc

And you should be good to go. Run the script and it will create the transaction for you.

python create_txs.py

*Notice that the created transactions are not actual lightning commitment and penalty transactions, but a pair or regular parent and child transactions. However, this won't matter for the sake of the test, since once the appointment is added broadcasting the parent will trigger the tower response.