firma-cli 0.20.0

firma-cli is a command line interface tool to create bitcoin multisig wallets with private keys stored on offline devices.
[![MIT license](https://img.shields.io/github/license/RCasatta/firma)](https://github.com/RCasatta/firma/blob/master/LICENSE)
[![Crates](https://img.shields.io/crates/v/firma-cli.svg)](https://crates.io/crates/firma-cli)

# Creating a 2of2 multisig wallet p2wsh

During the following step we are going to create a multisig wallet 2of2 in testnet and we are going to sign and broadcast a transaction. 
It is required a synced bitcoin node.

In the first steps we are going to create two master keys.

## Create first Master Key

This step creates the first master key. (It is possible to create the key with dice, see `firma-offline dice --help`)

```
firma-offline random --key-name a1
```
```json
{
  "id": {
    "kind": "MasterSecret",
    "name": "a1",
    "network": "testnet"
  },
  "key": "tprv8ZgxMBicQKsPdQsGb1U22Lw7bPwhbxRkV8Q1mf8mv42q6HpJS7MW5hx1J44gKK6m2pQyC32mG1i6v1P9C97MDx7MvKZzgoXTpcwUgTSEobm"
}
```

We could have encrypted the key before saving on disk, for example leveraging existing gpg setups like so

```
 # encryption key creation and storage in encrypted gpg
  dd if=/dev/urandom bs=1 count=32 | gpg --encrypt -r 'DEADBEEF!' >encryption_key.gpg

  # bitcoin private key creation
  gpg --decrypt encryption_key.gpg | firma-offline --encrypt random --key-name a1
```
in this latter case the key file `~/.firma/testnet/keys/a1/master_secret.json` looks like this
```json
{
  "t": "encrypted",
  "c": {
    "t": "base64",
    "c": "5Vqdw61WCoxyvl6mj6WBGEPzzI/SCLxwukHbbxCYsQphkPdoGDMaPhLL7Jg7Ok4yJa7E79LiiOSaRcszjnyLH3lfskF3ii2u5qTcacQhmuh5HV8d275hGAoYejY24MU58h/4Mo5A3om6woRpIgABmAEFXGCeTsjgvvXO+iD0EJA2tse+YQJRhMQGYCMeNH7BcrnWrAhhu3eBCsZdt0j5bsrP6aX3DLQIW6uUhsP7nklscGdRstu82+NEkdwonP+hBXBrkFxfe9DCer/x4IbeZF6TGA=="
  }
}
```

In this latter case to read the plain text of the key
```
gpg --decrypt encryption_key.gpg | firma-offline --encrypt export --kind MasterSecret --name a1
```
```json
{
  "id": {
    "kind": "MasterSecret",
    "name": "a1e",
    "network": "testnet"
  },
  "key": "tprv8ZgxMBicQKsPdHXHYrBsowgZXAh1bisk2nxvJLqRakJ9tZDLTLgFSGZDH79bKF19cTkmW8LHV3ZFbRytQAxjXx1MUFrrzpdfxiFcqqfpjkf"
}
```

Note: if the wallet files are encrypted, any command need to be fed with the encryption_key to encrypt and decrypt the data.

## Create second Master Key

This one is created providing dice launches:
```
firma-offline dice --key-name a2 --faces 20 -l 12 -l 11 -l 1 -l 1 -l 16 -l 8 -l 1 -l 12 -l 7 -l 4 -l 12 -l 8 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 1 -l 18 -l 19 -l 12 -l 1 -l 16 -l 1 -l 18 -l 1 -l 13 -l 1 -l 1 -l 16 -l 4 -l 3 -l 1 -l 1 -l 1 -l 1 -l 1 -l 20 -l 19 -l 18 -l 17 -l 12 -l 2
```
```json
{
  "dice": {
    "faces": 20,
    "launches": "[12, 11, 1, 1, 16, 8, 1, 12, 7, 4, 12, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 18, 19, 12, 1, 16, 1, 18, 1, 13, 1, 1, 16, 4, 3, 1, 1, 1, 1, 1, 20, 19, 18, 17, 12, 2]",
    "value": "33146769803929392242686007705600000000000000300775117168313561497600063822621"
  },
  "id": {
    "kind": "MasterSecret",
    "name": "a2",
    "network": "testnet"
  },
  "key": "tprv8ZgxMBicQKsPdGNW7N9EPsGpWBc56L8kKncoZfxC83M5ipBV2fMhujCBnxiTx33HnqiERg6fYsgKVBdSMNKm8nEcESHfUXAUecnyWnrx6Ls"
}
```

# Create the 2of2 multisig wallet

The first time using the `firma-online` tool you need to initiate the connection parameters:
```
firma-online connect --url http://127.0.0.1:8332 --cookie-file $HOME/.bitcoin/.cookie
```
This must be done once per network, and every time node configuration change.

For the example we are using the two master_key created in the previous step. From the offline machines 
copy `$HOME/.firma/testnet/keys/a1/descriptor_public_key.json` and `$HOME/.firma/testnet/keys/a2/descriptor_public_key.json` to the 
online machine. You may choose to copy the files to the correct destination directory, or use the `import` command which would allow to optionally encrypt this data.

```
firma-online create-wallet --wallet-name firma-wallet -r 2 --key-name a1 --key-name a2
```

```json
{
  "created_at_height": 1934912,
  "descriptor": "wsh(multi(2,[2f15d226/48'/1'/0'/2']tpubDFHFgFr6HbP88U7grBQ44yvocSU1EGkXX1dArKRum1qvb4Y6hy4CpJuPqpKZSyVnHptf6zoaW4HUjFHXgmtfy2vTGF1fccPy2ioNvKeZUnq/0/*,[7938c502/48'/1'/0'/2']tpubDEJZZGYXbZKMNEWgCdG9XZDycYM19Y8WxNc2cYcxLYhnNvKpbFNkgAza1x4GCUAHLdxx28R6dX88VhjgmZscW8Dzw6pGDzJ8a4gUCqHh1ny/0/*))#jpa3vwyx",
  "id": {
    "kind": "Wallet",
    "name": "firma-wallet",
    "network": "testnet"
  }
}
```

Note wallet file `wallet.json` could be signed with one of the participant key using the `sign_wallet` command, this prevent an attacker to tamper with the watch-only wallet without getting noticed.

## Create a receiving address

Create a new address from the just generated wallet. Bitcoin node parameters are not needed anymore since have been saved in `$HOME/.firma/testnet/firma-wallet/descriptor.json`

```
firma-online get-address --wallet-name firma-wallet 
```
```json
{
  "address": "tb1qdkl3aufvvk2zst22dy3ffjt0kfdl79mhvu6jcwecm5exm6j8dveseklast",
  "path": "m/0/0"
}
```
State of indexes is saved in `$HOME/.firma/testnet/wallets/firma-wallet/indexes.json` and by calling the command again we have:
```json
{
  "address": "tb1q8m2456wjxu8mlkf708d2yvtmtlg59awvd2l3jjzkmt37gtzmx6psva9fnl",
  "path": "m/0/1"
}
```

Send some funds to `tb1qdkl3aufvvk2zst22dy3ffjt0kfdl79mhvu6jcwecm5exm6j8dveseklast`

## Check balance and coins

```
firma-online balance --wallet-name firma-wallet
```
```json
{
  "confirmed": {
    "btc": "0.00000000",
    "satoshi": 0
  },
  "pending": {
    "btc": "0.00586300",
    "satoshi": 586300
  }
}
```
```
firma-online list-coins --wallet-name firma-wallet 
```
```
{
  "coins": [
    {
      "amount": 586300,
      "outpoint": "43d3a56e8afe96eeb1c3a260bae735d064e5946190d9fb90524047bd21dbf383:0",
      "unconfirmed": true
    }
  ]
}
```

## Create the PSBT

After funds receive a confirmation we can create the PSBT specifiying the recipient and the amount, you can specify more than one recipient and you can explicitly spend specific utxo with `--coin`. See `firma-online create-tx --help`

```
firma-online create-tx --wallet-name firma-wallet --recipient tb1q8m2456wjxu8mlkf708d2yvtmtlg59awvd2l3jjzkmt37gtzmx6psva9fnl:22400 --psbt-name test
```
```json
{
  "address_reused": [],
  "funded_psbt": {
    "name": "test",
    "psbt": "cHNidP8BAH0CAAAAAaF+ubg9XAD6ZmGErqdrAdpu4ALBdz7M+TM0oPWNu5scAAAAAAD+////AqMBAAAAAAAAIgAgaMI+YcHlEUY9mvkIVax3/a4d42jXZgcjrbWqpKNFp7l4BQAAAAAAABYAFJmYo0dJtoF0R0qMtwYR5jtL6UGJAAAAAAX8bmFtZQZ0ZXN0LWEAAQEr0AcAAAAAAAAiACASrnn5+Jhe7Sivhhv6EwzbxegCw/oJSyLNg+b0h851tAEFR1IhAuOPRdJj6043K51DVaw+MIyMHEBOuEGrv89me8fOaQLpIQLgQluouXrqa42FwP/Ki9mwFxHFQy/50SN4Zcn73HSwZFKuIgYC4EJbqLl66muNhcD/yovZsBcRxUMv+dEjeGXJ+9x0sGQMeTjFAgAAAAAAAAAAIgYC449F0mPrTjcrnUNVrD4wjIwcQE64Qau/z2Z7x85pAukMyr4y1wAAAAAAAAAAAAEBR1IhAkiG9PSdcBAS08R6LIRS6iGFbQ5ZbjY2an2EMxXcmGbPIQM2XzQNCYGxaFTBlw1c4XU4hQxj7p7ntZZDaVLjrJg39VKuIgICSIb09J1wEBLTxHoshFLqIYVtDlluNjZqfYQzFdyYZs8Myr4y1wEAAAABAAAAIgIDNl80DQmBsWhUwZcNXOF1OIUMY+6e57WWQ2lS46yYN/UMeTjFAgEAAAABAAAAAAA="
  },
  "psbt_file": "$HOME/.firma/testnet/psbts/test/psbt.json",
  "qr_files": [
    "$HOME/.firma/testnet/psbts/test/qr/qr-0.png",
    "$HOME/.firma/testnet/psbts/test/qr/qr-1.png"
  ]
}
```

## Sign from node A

```
firma-offline sign --psbt-name test --key-name a1 --wallet-name firma-wallet
```
```json
{
  "balances": "",
  "fee": {
    "absolute": 193,
    "absolute_fmt": "0.00000193 BTC",
    "rate": 1.0157894736842106
  },
  "info": [
    "Added signatures"
  ],
  "inputs": [
    {
      "outpoint": "43d3a56e8afe96eeb1c3a260bae735d064e5946190d9fb90524047bd21dbf383:0",
      "signatures": [
        "2f15d226"
      ],
      "value": "0.00586300 BTC"
    }
  ],
  "outputs": [
    {
      "address": "tb1q8m2456wjxu8mlkf708d2yvtmtlg59awvd2l3jjzkmt37gtzmx6psva9fnl",
      "value": "0.00022400 BTC"
    },
    {
      "address": "tb1qye8gt2pjwpdn8mj7eh3jgnl0hnfwq23lq4cat6s55hsnka3v2kss4jxsmm",
      "value": "0.00563707 BTC"
    }
  ],
  "psbt_file": "",
  "size": {
    "estimated": 190,
    "psbt": 1083,
    "unsigned": 137
  }
}
```

The psbt.json at `~/.firma/testnet/psbts/test/psbt.json` now has 1 signature (notice the `Added signatures` in the output).

## Sign from node B

```
firma-offline sign --psbt-name test --key-name a2 --wallet-name firma-wallet
```
```json
{
  "balances": "TODO",
  "fee": {
    "absolute": 193,
    "absolute_fmt": "0.00000193 BTC",
    "rate": 1.0157894736842106
  },
  "info": [
    "Added signatures"
  ],
  "inputs": [
    {
      "outpoint": "43d3a56e8afe96eeb1c3a260bae735d064e5946190d9fb90524047bd21dbf383:0",
      "signatures": [
        "2f15d226",
        "7938c502"
      ],
      "value": "0.00586300 BTC"
    }
  ],
  "outputs": [
    {
      "address": "tb1q8m2456wjxu8mlkf708d2yvtmtlg59awvd2l3jjzkmt37gtzmx6psva9fnl",
      "value": "0.00022400 BTC"
    },
    {
      "address": "tb1qye8gt2pjwpdn8mj7eh3jgnl0hnfwq23lq4cat6s55hsnka3v2kss4jxsmm",
      "value": "0.00563707 BTC"
    }
  ],
  "psbt_file": "",
  "size": {
    "estimated": 190,
    "psbt": 1191,
    "unsigned": 137
  }
}
```

## Combine, finalize and send TX

```
firma-online send-tx --wallet-name firma-wallet --psbt-name test --broadcast
```

```
{
  "broadcasted": true,
  "hex": "0200000000010183f3db21bd47405290fbd9906194e564d035e7ba60a2c3b1ee96fe8a6ea5d3430000000000feffffff0280570000000000002200203ed55a69d2370fbfd93e79daa2317b5fd142f5cc6abf194856dae3e42c5b3683fb99080000000000220020264e85a832705b33ee5ecde3244fefbcd2e02a3f0571d5ea14a5e13b762c55a10400473044022041785595cc34a022686213b8d7b34bac2f2bfc77e37a8fa2f2f3019b0646bbfb022047e12715e32b081be49865ffdbca73829b0231e44f77c9afcd6aa25582186e300148304502210081f298aadf2e5f68e322c030b1e97e23f45a1ac9ddf5fa4b964c1a57847f37b70220129c3d54e9f5c946511bfd30fc755846014654d4d693455fa08279a994617e410147522102e43ee99d46f46cd17d25987701576ae07129ab268ca9879e53a345546537dc862102ccbef362214e9e7ece2bcd33731cdabd0c2937d9bec9db2684fb68021297f8b752ae00000000",
  "txid": "4e08b321a79465cdbba8ad811ddaa68ffe79604406413b25b55c76b9850902e5"
}
```

View tx [4e08b321a79465cdbba8ad811ddaa68ffe79604406413b25b55c76b9850902e5](https://blockstream.info/testnet/tx/4e08b321a79465cdbba8ad811ddaa68ffe79604406413b25b55c76b9850902e5)