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

Walkthrough on regtest using teos_cli

Salvatore Ingala edited this page Jul 17, 2020 · 7 revisions

We assume that you already have both bitcoind and teosd correctly running on regtest (Bitcoin's local test network).

In this walkthrough, we will create dummy commitment and penalty transactions that will be used to send an appointment to TEOS, and then cause a breach to observe the response.

Here we assume that bitcoin-cli is in your PATH folder; if not, you will have to adjust the commands accordingly to fully specify the path.

When you run the teosd, you will get in the shell (or in the logs if you started in daemon mode) a line like this:

16/07/2020 08:02:11 [Daemon] tower_id = 03ef3b8aaa7304f741880550a1c0ee9243d62569ffc168926b5989b17c8234149f

Your tower's id, of course, will look different. Make sure to take note of it as you will need it later. We will refer to it as <tower_id> in commands.

Create transaction

Get this Python script and save it as create_txs.py or whatever name you like.

import json
from time import sleep
from decimal import Decimal, getcontext
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException

getcontext().prec = 10


def setup_node(bitcoin_cli):
    while True:
        try:
            new_addr = bitcoin_cli.getnewaddress()
            break
        except JSONRPCException as e:
            if "Loading wallet..." in str(e):
                sleep(1)

    # This method will create a new address and mine bitcoin so the node can be used for testing
    bitcoin_cli.generatetoaddress(101, new_addr)


def create_commitment_tx(bitcoin_cli, utxo, destination=None, fee=Decimal(1 / pow(10, 5))):
    # We will set the recipient to ourselves if 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 if 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(bitcoin_cli):

    utxos = bitcoin_cli.listunspent()

    if len(utxos) == 0:
        raise ValueError("There are 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 = 18443

    bitcoin_cli = AuthServiceProxy("http://%s:%s@%s:%d" % (rpc_user, rpc_pass, rpc_host, rpc_port))

    # Make sure bitcoind is running and rpc_user / rpc_pass are correct
    try:
        bitcoin_cli.help()
    except ConnectionRefusedError:
        exit(
            'Cannot connect to bitcoind. Is bitcoind running? If so, is your rpc_user = "{}" and your rpc_pass = "{}". '
            "Change them on this script if not.".format(rpc_user, rpc_pass)
        )

    setup_node(bitcoin_cli)
    commitment_tx, commitment_txid, penalty_tx = create_txs(bitcoin_cli)

    appointment_data = {"tx_id": commitment_txid, "tx": penalty_tx, "to_self_delay": 20}

    with open("dummy_appointment_data.json", "w") as fp:
        json.dump(appointment_data, fp)

    print("Appointment data (saved to dummy_appointment_data.json):")
    print(json.dumps(appointment_data))

    print("\nCommitment tx (to be used to trigger a breach):\n{}".format(commitment_tx))

Now install python-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

The script will output to the shell the json of a valid appointment data and the hex-encoded commitment transaction that can be used to simulate a breach:

Appointment data (saved to dummy_appointment_data.json):
{"tx_id": "aa1f4f29cbfc63e08017f9040f6a971e712dbd9857a9603ddbd3774cd9b10327", "tx": "020000000001012703b1d94c77d3db3d60a95798bd2d711e976a0f04f91780e063fccb294f1faa0000000000ffffffff0130ea052a010000001600147f88b794a9df3526a0f6f8a2b43871623ecccf3f02473044022042f9f0462c269546a7df89aca94695f4728a57161d6acc8f80a2b1d94731257f02207d84cd445e782a200d7f4b4213528e56a3dd166d75aeeaf043fcfa61aea3049a012102506698a897c2ed3d63e0e670209fd525b2ae701afdc73a206e330f279e8fd1dc00000000", "to_self_delay": 20}

Commitment tx (to be used to trigger a breach):
02000000000101a08658ed4c9ebc54c1e5f55054b9ae8fea46768dae42214d4c9d73efc099ecb90000000000ffffffff0118ee052a010000001600147f88b794a9df3526a0f6f8a2b43871623ecccf3f0247304402200b0005686c304b7796811a6a05c0fae7f5445f7754906d0228d071cd28b5b05f0220248ea4cadc9cdd2e1601adb3956dc453bd74ff335eab793c688d2b4d8abf8773012102506698a897c2ed3d63e0e670209fd525b2ae701afdc73a206e330f279e8fd1dc00000000

Of course, yours will look different. The appointment data is also saved to dummy_appointment_data.json for convenience.

Take note of the commitment transaction (we'll call it <commitment_tx>).

Interacting with TEOS

If you made till here, congratulations, you're now ready to hire TEOS for your first appointment.

The commands for teos-cli in this section assume that you are on the root of the python-teos repository.

Registering with the watchtower

Before you can hire the watchtower, you first need to register yourself with it. You can do it using teos-cli for this. First you might want to run the following command to see teos_cli's documentation:

python -m cli.teos_cli help

In order to register yourself with the watchtower, run the following:

python -m cli.teos_cli register <tower_id>

replacing <tower_id> with the watchtower's instance id. The shell will confirm the successful registration.

If you're curious, you can also confirm that with the logs of teosd in ~/.teos/regtest/teos.log.

Adding the appointment

Run the following command to add the appointment we generated before (you might need to change the path to the appointment data file):

python -m cli.teos_cli add_appointment -f ./dummy_appointment_data.json

The -f flag specifies that the appointment is stored in the specified file; alternatively, you can remove the -f flag and pass the json-encoded appointment data directly in the shell. The output will be similar this:

16/07/2020 10:35:45 [Client] Sending appointment to the Eye of Satoshi
16/07/2020 10:35:45 [Client] Appointment accepted and signed by the Eye of Satoshi
16/07/2020 10:35:45 [Client] Remaining slots: 99
16/07/2020 10:35:45 [Client] Start block: 101
16/07/2020 10:35:45 [Client] Appointment saved at /home/user/.teos_cli/appointment_receipts/appointment-1594895745-aa1f4f29cbfc63e08017f9040f6a971e-7a692be412684cfeaf1add99a1758cfb.json

It might be interesting to look inside the appointment receipt:

{"appointment": {"locator": "aa1f4f29cbfc63e08017f9040f6a971e", "encrypted_blob": "588a9759f1ed082b4f998c34936ad4ee927b0f87411a800f94ea4c730be3eca29df0dbfb9a70f756498403e09e874bd80d3b69e7f7e8d6d951901b41440b901df3e52ab23997c58799382b6eadbc883e8300eeccbb160ee4c0e6cab2a37d43082f6e7a6ffe18713b065ebd6e74244c9fe2a922b6a3b6afa09556d92c2ca1a53a31b6a3a6c2e219ba262d7c6c61ccaf7a610748c6720df93f8f9f8c9cc2c3b3139b64ed1799807450c83d9e1528f9446eeb92ffb0c7ee39e740997af235465d9475a23e92fbcdfba32ac2b556dde3be", "to_self_delay": 20}, "start_block": 101, "signature": "d66eij4yz3qhempn346ck1hxn3saom7xq41y9tms7kai3b7zx4pbatqpgp34jucxs6gy9fjn9ern43sjquye8i7p39es1bfc9fyzhhab"}

Notice that the request is signed by the tower (and teos_cli does indeed verify that the signature is valid). You can later request to the tower information about an appointment by using the get_appointment command and the appointment's locator:

python -m cli.teos_cli get_appointment aa1f4f29cbfc63e08017f9040f6a971e

This will return the same information shown above, together with the status of the appointment that is now being_watched.

Triggering a breach and watching the response

The vigilant Eye of Satoshi is watching the blockchain. In order to check that he is not distracted, let's trigger a breach by broadcasting the <commitment_tx> generated by the script above.

bitcoin-cli sendrawtransaction <commitment_tx>

bitcoind will reply with a single line containing the 32 bytes (64 hexadecimal digits) of the transaction id that we just sent, which is now in the mempool.

The tower will only react to breaches that are seen in a block, and not in the mempool, therefore we need to mine a block including the commitment transaction. To do so, we first need an address to send the block subsidy to. We can create one using generatetoaddress:

bitcoin-cli getnewaddress

We will get one line, for example bcrt1q64t54uhukp40y8zznu6jc3w5d7jzshlzf7kxer, that we will refer to as <my_address> below. Let's then run the following command to generate a block:

bitcoin-cli generatetoaddress 1 <my_address>

The new block should be received by TEOS, that should detect the breach and send its response transaction. We can see it from the logs:

16/07/2020 10:45:31 [ChainMonitor] New block received via zmq  (block_hash=102bb19c2632a7ca605c66f822ddf6b1a40c547aa0e59a6840e10a4c0cc53eda)
16/07/2020 10:45:31 [Responder] New block received  (block_hash=102bb19c2632a7ca605c66f822ddf6b1a40c547aa0e59a6840e10a4c0cc53eda, prev_block_hash=59381134592736c1c3f49f76c5171f2c6983e58d40f04501d09c7c23dc0daf6d)
16/07/2020 10:45:31 [Watcher] New block received  (block_hash=102bb19c2632a7ca605c66f822ddf6b1a40c547aa0e59a6840e10a4c0cc53eda, prev_block_hash=59381134592736c1c3f49f76c5171f2c6983e58d40f04501d09c7c23dc0daf6d)
16/07/2020 10:45:31 [Watcher] List of breaches  (breaches={'aa1f4f29cbfc63e08017f9040f6a971e': 'aa1f4f29cbfc63e08017f9040f6a971e712dbd9857a9603ddbd3774cd9b10327'})
16/07/2020 10:45:31 [Watcher] Breach found for locator  (locator=aa1f4f29cbfc63e08017f9040f6a971e, penalty_txid=c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a, uuid=0c5410bf95f5fbbd13d9eac8ecdd77e961a5f146)
16/07/2020 10:45:31 [Watcher] Notifying responder and deleting appointment  (locator=aa1f4f29cbfc63e08017f9040f6a971e, penalty_txid=c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a, uuid=0c5410bf95f5fbbd13d9eac8ecdd77e961a5f146)
16/07/2020 10:45:31 [Carrier] Pushing transaction to the network  (rawtx=020000000001012703b1d94c77d3db3d60a95798bd2d711e976a0f04f91780e063fccb294f1faa0000000000ffffffff0130ea052a010000001600147f88b794a9df3526a0f6f8a2b43871623ecccf3f02473044022042f9f0462c269546a7df89aca94695f4728a57161d6acc8f80a2b1d94731257f02207d84cd445e782a200d7f4b4213528e56a3dd166d75aeeaf043fcfa61aea3049a012102506698a897c2ed3d63e0e670209fd525b2ae701afdc73a206e330f279e8fd1dc00000000, txid=c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a)
16/07/2020 10:45:31 [AppointmentsDBM] Adding tracker to Responder's db  (uuid=0c5410bf95f5fbbd13d9eac8ecdd77e961a5f146)
16/07/2020 10:45:31 [Responder] New tracker added  (dispute_txid=aa1f4f29cbfc63e08017f9040f6a971e712dbd9857a9603ddbd3774cd9b10327, penalty_txid=c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a, user_id=027572e79dc664c0d954f6bfc6bd4bb35ae9a47ecd0825945bd8363f14b94cc295)
16/07/2020 10:45:31 [AppointmentsDBM] Flagging appointment as triggered  (uuid=0c5410bf95f5fbbd13d9eac8ecdd77e961a5f146)

Great, the Eye of Satoshi wasn't distracted. In this example, the penalty transaction id is c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a. Let's see the state of the transaction:

bitcoin-cli gettransaction c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a

The output will indeed confirm that the transaction is known, but not yet confirmed:

{
  "amount": 0.00000000,
  "fee": -0.00001000,
  "confirmations": 0,
  "trusted": true,
  "txid": "c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a",
  "walletconflicts": [
  ],
  "time": 1594896331,
  "timereceived": 1594896331,
  "bip125-replaceable": "no",
  "details": [
    {
      "address": "bcrt1q07yt099fmu6jdg8klz3tgwr3vglvenel0ghv5n",
      "category": "send",
      "amount": -49.99998000,
      "label": "",
      "vout": 0,
      "fee": -0.00001000,
      "abandoned": false
    },
    {
      "address": "bcrt1q07yt099fmu6jdg8klz3tgwr3vglvenel0ghv5n",
      "category": "receive",
      "amount": 49.99998000,
      "label": "",
      "vout": 0
    }
  ],
  "hex": "020000000001012703b1d94c77d3db3d60a95798bd2d711e976a0f04f91780e063fccb294f1faa0000000000ffffffff0130ea052a010000001600147f88b794a9df3526a0f6f8a2b43871623ecccf3f02473044022042f9f0462c269546a7df89aca94695f4728a57161d6acc8f80a2b1d94731257f02207d84cd445e782a200d7f4b4213528e56a3dd166d75aeeaf043fcfa61aea3049a012102506698a897c2ed3d63e0e670209fd525b2ae701afdc73a206e330f279e8fd1dc00000000"
}

Let's mine another block and check again:

bitcoin-cli generatetoaddress 1 <my_address>

Now running the gettransaction command again should show something like this:

{
  "amount": 0.00000000,
  "fee": -0.00001000,
  "confirmations": 1,
  "blockhash": "12bb93a19bd1e64acd5f3c92bdabb94a428f1b2b6b468c3b850460124b038301",
  "blockheight": 103,
  "blockindex": 1,
  "blocktime": 1594896459,
  "txid": "c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a",
  "walletconflicts": [
  ],
  "time": 1594896331,
  "timereceived": 1594896331,
  "bip125-replaceable": "no",
  "details": [
    {
      "address": "bcrt1q07yt099fmu6jdg8klz3tgwr3vglvenel0ghv5n",
      "category": "send",
      "amount": -49.99998000,
      "label": "",
      "vout": 0,
      "fee": -0.00001000,
      "abandoned": false
    },
    {
      "address": "bcrt1q07yt099fmu6jdg8klz3tgwr3vglvenel0ghv5n",
      "category": "receive",
      "amount": 49.99998000,
      "label": "",
      "vout": 0
    }
  ],
  "hex": "020000000001012703b1d94c77d3db3d60a95798bd2d711e976a0f04f91780e063fccb294f1faa0000000000ffffffff0130ea052a010000001600147f88b794a9df3526a0f6f8a2b43871623ecccf3f02473044022042f9f0462c269546a7df89aca94695f4728a57161d6acc8f80a2b1d94731257f02207d84cd445e782a200d7f4b4213528e56a3dd166d75aeeaf043fcfa61aea3049a012102506698a897c2ed3d63e0e670209fd525b2ae701afdc73a206e330f279e8fd1dc00000000"
}

And in fact, teos.log also confirms that TEOS is still following the progress:

16/07/2020 10:47:39 [ChainMonitor] New block received via zmq  (block_hash=12bb93a19bd1e64acd5f3c92bdabb94a428f1b2b6b468c3b850460124b038301)
16/07/2020 10:47:39 [Watcher] New block received  (block_hash=12bb93a19bd1e64acd5f3c92bdabb94a428f1b2b6b468c3b850460124b038301, prev_block_hash=102bb19c2632a7ca605c66f822ddf6b1a40c547aa0e59a6840e10a4c0cc53eda)
16/07/2020 10:47:39 [Responder] New block received  (block_hash=12bb93a19bd1e64acd5f3c92bdabb94a428f1b2b6b468c3b850460124b038301, prev_block_hash=102bb19c2632a7ca605c66f822ddf6b1a40c547aa0e59a6840e10a4c0cc53eda)
16/07/2020 10:47:39 [Responder] Confirmation received for transaction  (tx=c904dd002ff2fbacfb7cfe28fb79d35503b9cc71602fffe37bf269e05e76e96a)