Go Up Number!

This is the user manual for Go up Number! — a command line Bitcoin wallet for plebs, degenerates and revolutionaries.

The main design goal of gun is for it to be fun! It might also be secure and stuff but we'll have to see. gun is alpha quality in all respects. Thanks in advance for making the sacrifice of your time (and perhaps coins!) while testing this software.

The defining feature of gun is the bet command which lets you do peer-to-peer betting by copy-pasting gibberish. This is the only reason to use this wallet at the moment so you should only put what you need to play with (and are willing to lose).

Architecture

gun is written in rust and uses the Bitcoin Dev Kit (bdk) to implement the underlying wallet functionality1. It needs a trusted esplora backend server to provide blockchain data and by default it uses mempool.space. gun's strict dependence on esplora is its greatest weakness.

Contributing

gun is open source. It relies on the community to report and (if they can) fix bugs. Bugs aren't fun so please report them on the repository. You can improve this book too.

1

Right now it uses a branch of bdk at llfourn/bdk.

Install

Binary Releases

Binary releases should be available at the repository for some architectures (right now only linux-amd64). I haven't figured out how I want to go about this yet so it's better to just compile it yourself for now.

⚠️ The author of gun is the COVID QR code formally known as Lloyd "LLFourn" Fournier. He resides in Prison Island Australia and according to new legislation on the island he could be forced against his will to help the prison guards get access to his online accounts including GitHub. From there they can "alter" data to "gather evidence". The takeover can be granted without Lloyd being suspected of doing anything illegal. The target of the action can be someone else unknown to him and can even be unknown or anonymous to the guards.

So please make sure you check the signatures and more importantly prefer compiling the wallet yourself until he has figured out a more robust defense against this. Actions that publicly modify the code maliciously are far more obvious.

Import author key

Releases should be signed with this public key with this fingerprint:

28EF 6BC9 14AB BA2B AB98  B796 A270 93B5 4DA1 1F65

To verify a binary's signature import @LLFourn's key.

curl -sL https://gun.fun/llfourn.asc | gpg --import --import-options show
# ⚠️ CHECK IT MATCHES THE FINGERPRINT ABOVE

Download and verify

Download the binary and signature and verify like so:

curl -sL -o gun https://github.com/LLFourn/gun/releases/download/v0.5.0/gun-linux-amd64
curl -sL -o gun.asc https://github.com/LLFourn/gun/releases/download/v0.5.0/gun-linux-amd64.asc
gpg --verify gun.asc # check it verifies against the key with fingerprint above
chmod u+x gun
./gun --help

...verifying this is not fun so hopefully we'll figure out a way to do updating gun without doing this every time.

Compile yourself

Before compiling gun you need to install rust and cargo. Then,

git clone https://github.com/LLFourn/gun
cd gun
cargo install --path .
# Make sure ~/.cargo/bin is in your $PATH

If you have a nightly toolchain you can install it faster and download/compile less dependencies using:

cargo -Z avoid-dev-deps install --path .

The minimum supported rust version for gun is 1.54.0.

Upgrading

As long as gun --version shows >= v0.2.0 you can just go run the steps from installing again. Otherwise you should make sure that you don't have any bets in progress (gun bet list is empty) first.

Setup

Setting up a gun wallet is done through the gun init <network> command.

To initialize a mainnet wallet run:

gun init bitcoin 

Or you can bring your own BIP39 seed words:

gun init --from-existing ./my-seedwords.txt bitcoin

Or read them from stdin

gun init --from-existing - bitcoin

Or run on a different network

gun init testnet

Right now gun stores a unencrypted BIP39 seed phrase on your disk. This is not really fun and so I hope to add other options in the future.

gun init will create ~/.gun and populate with three things:

  1. seed.txt: The unencrypted BIP39 seed phrase used to derive all keys and other secret data. This should be backed up.
  2. config.json: A configuration file.
  3. database.sled: The database directory containing protocol data and wallet data.

Configuration

The main configuration file is in ~/.gun/config.json and looks something like this:

{
  "network": "bitcoin",
  "blockchain": {
    "type": "esplora",
    "base_url": "https://mempool.space/api",
    "concurrency": 4,
    "stop_gap": 10,
    "kind" : "mempool"
  },
  "kind": "p2wpkh",
  "keys": "seed-words-file"
}

Configuration keys

network

Which network the wallet operates on:

  • bitcoin: The bitcoin Bitcoin.
  • testnet: the Bitcoin testnet.
  • regtest: A local regtest node.
  • signet: A signet testnet (not supported yet: issue#21).

kind

This indicates the kind of wallet it is. Right now p2wpkh is the only option which means it uses BIP84 derivation to get p2wpkh addresses.

blockchain

Configures the blockchain backend that the wallet will use. Only esplora style backends are supported (e.g. https://mempool.space/api and https://blockstream.info/api). The only interesting option for non-developers at the moment is the base_url parameter which could be changed to your own esplora backend or another one you prefer.

keys

Indicates where keys are derived from. Right now the only option is seed-words-file.

GUN_DIR

You can override the default directory ~/.gun that gun uses to find its configuration with the GUN_DIR environment variable or by using the -d top level option:

GUN_DIR=~/my_gun_dir gun init bitcoin
# does the same thing as
gun -d ~/my_gun_dir init bitcoin

Backup and Recovery

In your gun directory (usually ~/.gun) there are a couple of files that will cause pain if you lose them.

seed.txt

seed.txt contains your secret BIP39 seed phrase. If you lose seed.txt you lose all your coins. If someone else gets their hands on it they can take all your coins. seed.txt never changes so you only ever need to take one backup.

database.sled

If you lose database.sled you lose the coins from bets you have won or will win but haven't claimed yet. It is not recommended to take backups of database.sled unless you are about to upgrade the wallet manually. The best way to avoid loss of funds here is to claim your winnings in a timely manner. Once the coins have been claimed they can be recovered from your seed.txt.

Technically, it is possible to recover these coins just with your seed.txt but this hasn't been implemented yet.

Wallet Commands

Gun supports just enough wallet functionality to make it useful. Here are the commands:

gun address

Get and view addresses.

Synopsis

gun address <subcommand>

Subcommands

These commands interact with your wallet's external addresses. Internal addresses can't be seen.

You can see change address with coins in gun utxo list.

new

gun address new

Get a new address from the wallet. The wallet remembers that it has given out this address in the database.

last-unused

gun address last-unused

Gets an address that hasn't been used yet. If they've all been used so far it gets a new one.

list

gun address list

List all addresses given out by this wallet.

show

gun address show <address>

Get more details about a particular wallet address.

gun balance

Get the balance of your wallet.

💡 Ah my brethren, these are the numbers you must make go up.

Synopsis

gun balance

But often you want to sync first

gun -s balance

Description

These are the meanings of the numbers:

  • confirmed: coins that are owned by this wallet and confirmed in the chain (but not in-use).
  • unconfirmed: coins that have been sent to your wallet but haven't been confirmed (and not in-use).
  • unclaimed: coins that you own but you haven't claimed -- if you lose your database you will lose them.
  • available: coins that are available to be used. This is just the total of the above.
  • locked: coins that are locked in some protocol execution (e.g. bet) you might get access to them or you might not!
  • in-use: coins that are being reserved for use in some protocol execution (e.g. a bet).

gun send

Sends coins to an address.

⚠️ Sending your coins gives them to someone else. This should be avoided wherever possible.

Synopsis

gun send [OPTIONS] <value> <to>

send 0.1 BTC to an address:

gun send 0.1BTC bc1qhg0xqvrg50x50qnjqgfaa8jlle8uz7q3l2c5ud 

Same as the above but with different denomination:

gun send 10000000sat bc1qhg0xqvrg50x50qnjqgfaa8jlle8uz7q3l2c5ud

Send all the available funds to the address:

gun send all bc1qhg0xqvrg50x50qnjqgfaa8jlle8uz7q3l2c5ud

Options

--fee <fee>

Set the fee for the transaction.

You can choose from:

  • --fee=rate:4.5 set the fee to 4.5 sats-per-vbyte.
  • --fee=abs:500 set the fee to exactly 500 sats.
  • --fee=in-blocks:10 set the fee so it should get into the chain some time in the next 10 blocks.

The default is in-blocks:1 i.e. set the fee so it gets into the next block.

--print-tx

Print the tx as hex instead of broadcasting it. This will assume you're going to broadcast the tx yourself.

--bump-claiming

Spend coins from any bets that are currently in the claiming state. The claiming state means there is already a pending transaction spending them so this new transaction will replace-by-fee that previous transaction. You should make sure the feerate on this one is higher than the previous.

--no-spend-unclaimed

Don't spend coins you own outside of your wallet e.g. coins from bets you won. By default gun spends these coins back into your wallet with any chance it gets but this option stops that behavior.

--spend-in-use

Spend even the coins you are using for other things.

When you make a proposal or an offer the outputs used in the bet are considered to be in-use. This option allows the transaction to spend them (but doesn't force it to).

-y, --yes

Skip all [y/n] prompts by just answering yes to all of them.

gun split

Split some coins into several outputs. This is useful when you want to have evenly sized outputs for making bet proposals.

gun split [OPTIONS] <output-size> [n]

Synopsis

Create 5 outputs of 0.05 BTC each.

gun split 0.05BTC 5

Create as many 0.05 BTC outputs as possible with all the funds in the wallet.

gun split 0.05BTC

Options

--fee <fee>

Set the fee for the transaction.

You can choose from:

  • --fee=rate:4.5 set the fee to 4.5 sats-per-vbyte.
  • --fee=abs:500 set the fee to exactly 500 sats.
  • --fee=in-blocks:10 set the fee so it should get into the chain some time in the next 10 blocks.

The default is in-blocks:1 i.e. set the fee so it gets into the next block.

--print-tx

Print the tx as hex instead of broadcasting it. This will assume you're going to broadcast the tx yourself.

--bump-claiming

Spend coins from any bets that are currently in the claiming state. The claiming state means there is already a pending transaction spending them so this new transaction will replace-by-fee that previous transaction. You should make sure the feerate on this one is higher than the previous.

--no-spend-unclaimed

Don't spend coins you own outside of your wallet e.g. coins from bets you won. By default gun spends these coins back into your wallet with any chance it gets but this option stops that behavior.

--spend-in-use

Spend even the coins you are using for other things.

When you make a proposal or an offer the outputs used in the bet are considered to be in-use. This option allows the transaction to spend them (but doesn't force it to).

-y, --yes

Skip all [y/n] prompts by just answering yes to all of them.

gun tx

Display transactions related to this wallet.

Synopsis

gun tx <subcommand>

Subcommands

For a transaction to be in the database it must spend to or from an address owned by this wallet.

list

gun tx list

Lists transactions associated with the wallet. This only keeps track of transactions that come in and out of the main wallet. For example, if you claim coins from a won bet to an unrelated address it won't show up here.

When you're waiting for a transaction to come in it can be handy to try:

gun -s tx list

show

gun tx show <txid>

Shows more details about a particular wallet transaction.

gun utxo

Display your unspent transaction outputs (UTXOs).

💡 Bitcoin is a game of UTXO acquisition. Get as many of these as you can.

Synopsis

gun utxo <subcommnd>

Subcommands

At the moment a UTXO will no longer show up here if it has been spent by an unconfirmed transaction. It will show up if it is on a

list

gun utxo list

List all your UTXOs.

show

gun utxo show <outpoint>

Show more details about a UTXO. <utxo> is given in txid:vout format e.g

gun utxo show 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0

The above command shouldn't work unless that's your UTXO (it isn't).

GuN Betting

The main reason I developed gun was to be able to experiment with Bitcoin betting through social media and messaging apps. I originally wrote down the idea in How to Make a Prediction Market on Twitter with Bitcoin. At a high level this is how it works:

  1. The first person, the proposer, proposes a bet by choosing an event to bet on (including the oracle to use) and value to risk. The proposal data also contains the inputs they will use to fund the bet. See gun bet propose.
  2. Anyone who sees the proposal may make an offer in response by choosing how much they will risk to gain the proposer's coins and which outcome they want to bet on. See gun bet offer. The offer is fully encrypted and can only be decrypted by the proposer. In fact, it might not be an offer at all since you instead can use gun bet reply to send an encrypted message instead which will be indistinguishable from an encrypted offer.
  3. The proposer decrypts any offer they get and if they want to take the other side of the bet they broadcast the bet funding transaction. They may only choose one offer. See gun bet take.
  4. Both parties monitor the state of the bet by using gun -s bet list.
  5. When the oracle publishes the outcome the winning party gains ownership of the coins. When they're ready they can move the coins into their main wallet with gun bet claim.

Bet Protocol Technical Overview

The protocol takes two messages and one on-chain transaction.

The first message, the "proposal", contains a public key (P1) which the second message, the "offer", will be encrypted under. The offer contains a public key (P2) and a ciphertext. The offer is encrypted by deriving a key from the Diffie-Hellman key between the two keys.

The two public keys are also used to construct the bet output which holds the coins until the outcome has been decided by the oracle. The script locking the coins is a 1-of-2 with keys like:

1-of-2(P1 + O1, P2 + O2)

Where P1 and O1 represent the public key of the proposer and the outcome they are betting on respectively (P2 and O2 represent the same for the offerer). If the oracle releases O1 then the proposer will get the coins otherwise if the oracle releases O2 the offerer gets them. The Diffie-Hellman keys is also used to produce secret randomness (R1 and R2) which both parties use to randomize the output script. So the output script is really something like:

1-of-2(P1 + O1 + R1, P2 + O2 + R2)

This randomization means the output keys can't bet associated with the proposal and offer keys (P1 and P2). The order of the keys in the 1-of-2 is also randomized.

When the winner learns the secret for O1 or O2 from the oracle they can claim the coins because they can construct the secret key for P1 + O1 + R1 or P2 + O2 + R2. Note there is no real hurry for them to do so except that the coins can be recovered from seed words once they have been properly claimed.

Privacy

This protocol is tries to guarantee as much privacy as possible against an observer who sees the proposal, a list of offers and the blockchain. Such an observer will be able to figure out:

  1. What event the bet was on (they can see it in the proposal). This assumes the proposer is only using the input(s) for one bet but this might not be the case.
  2. Where the bet transaction is in the blockchain and which output is the bet output and whose change output is whose (they can see the proposal's inputs so they will be able to find it).
  3. Which inputs belong to the proposer and which belong to the offerer (the inputs belonging to the proposer are in the proposal).
  4. How much was bet by each party (they know how much the proposer is risking and the value in the bet output).
  5. Which change output on the bet transaction belongs to the proposer and offer respectively (the proposer puts their change in the proposal so the other one must belong to offerer).

They should be unable to determine:

  1. Which offer was taken or whether the offer that was taken was in the list of observed offers.
  2. Whether an offer is a real offer or an encrypted message or otherwise random gibberish.
  3. Who won the bet (they don't know who bet on what).

The basic summary of this is that privacy for the offerer is relatively good but for the proposer it is relatively bad.

It's important to note that even if you do the bet privately (via direct messages) a blockchain observer can easily distinguish bet transactions from normal transactions with this protocol. Here's an example transaction which shows what it looks like when a bet is claimed.

For how this might improve in the future see the roadmap.

💡 Another important aspect of privacy is that gun uses mempool.space by default as a backend. Connecting to it through Tor would improve privacy. Even better would be connecting to your own esplora node (and safety!). This is still tricky at the moment. See roadmap.

Oracles

For this experiment I developed an oracle called olivia and deployed it at h00.ooo. olivia is badly documented and still changing a lot but you're welcome to try and run your own one.

To find events I developed a simple oracle explorer outcome.observer which can browse the events of an oracle. If you run your own oracle and want me to list it on outcome.observer make a PR to the repo.

GuN safety

The most obvious way of losing coins in a bet is betting on the wrong thing. But there are other more subtle and much less fun ways of losing your coins with gun.

The Late Bet

A malicious party may try to get a bet transaction to be confirmed after the event has already transpired and has resulted in their favor. For example, in a bet on a football match imagine if the proposer only takes the offer after the team they are betting has scored the first goal. The offerer must protect against this by canceling their offer before the game starts. See gun bet cancel.

The proposer must be careful about taking offers with a low transaction fee. If the transaction fee is low it may be stuck in the mempool until after the game starts. The offerer may then cheat by double spending his output if it looks like the game is going against him. Bet transactions enable replace-by-fee so either party can cancel it at this point by double spending one of their inputs (this is what gun bet cancel does).

Oracle cheats

The oracle may attest to the wrong outcome either maliciously or by accident. They may also do it publicly or covertly. There is nothing you can do about this other than call them out. Theoretically, you can also produce proof if the oracle misbehaved but I've left this until the Taproot version of the protocol which allows for doing this easily.

The oracle may also just disappear. If the oracle fails to attest the coins are stuck in the current protocol. This will improve with taproot (see roadmap).

💡 The oracle h00.ooo which I set up for this experiment is run by me. I will try and be accurate and timely with my attestations but the same caveat about Prison Island applies here.

Lost data

Losing data before you've won and claimed a bet is bad. Theoretically it's possible to recover the funds and bet but this hasn't been implemented yet.

see backup and recovery for more details.

gun bet oracle

Add, List and remove oracles from trusted list. For now, the security model for oracles is trust on first use (TOFU).

Synopsis

gun bet oracle <subcommand>

Subcommands

add

gun bet oracle add [options] <oracle-id>

Adds an oracle to the list of trusted oracles. For example the following command will make a request to https://h00.ooo and prompt you whether to accept it.

gun bet oracle add h00.ooo

Options

-y, --yes

Accept the keys the oracle gives you without asking.

list

gun bet oracle list

List trusted oracles.

show

gun bet oracle show <oracle-id>

Display details about an oracle. For example,

gun bet oracle show h00.ooo

TODD: document the output here

gun bet propose

Propose an event to bet on and how much you are willing to risk.

Synopsis

gun bet propose [options] <event-url>

For example

gun bet propose https://h00.ooo/random/2021-11-01T00:00:00/heads_tails.winner

Description

gun bets start with a proposal which set the oracle, event and value the proposer will risk on the bet.

The oracle and the event are embedded in the <event-url> like https://h00.ooo/random/2021-08-11T04:29:00/heads_tails.winner. Right now only https URLs are supported. The oracle for the bet is the host of the URL e.g. h00.ooo. In order to discover an oracle and event to bet on you can use an oracle explorer like outcome.observer. Note carefully that you can only bet on events with two outcomes.

The command will output the proposal as a # delimited string with the binary data encoded with base2048.

⚠️The binary data includes the on-chain input the proposer is using for the bet. These are unencrypted and viewable by anyone. Therefore, if you are posting the proposal publicly you must first ask yourself whether you really want these on-chain inputs to be linked to the identity that posts it.

Change

Currently it is possible to include a change output in the proposal where your excess over the value you want to bet will go. While the proposal is active these coins will be reserved for the bet and in-use. It is a good idea to use gun split to create an output with the exact amount of coins you want to risk as a proposer to avoid this. For technical reasons in the future change outputs for the proposer may be removed so you should get used to doing this now!

Options

-v, --value <value>

The value you want to risk on the bet. If you don't provide it you'll be prompted for it. Can be all to go "all in" with the coins in your wallet, otherwise an amount and denomination like 0.01BTC or 10000sat.

-t, --tags <tags>...

Add human readable tag to the bet as you create it.

-y, --yes

Skip all [y/n] prompts by just answering yes to all of them.

gun bet list

List bets.

Synopsis

List your bets

gun bet list

Check and update your bets online and on-chain to see if if anything has changed first:

gun -s bet list

Description

The list of bets has the following fields:

  • state: The state the bet is, which can be:
    • proposed: You've proposed this bet.
    • offered: You've made an offer on this bet.
    • unconfirmed: The bet transaction has be broadcast but hasn't been confirmed.
    • confirmed: The bet transaction has been confirmed.
    • won: You've won the bet (but haven't claimed the winnings).
    • lost: You lost! You get nothing.
    • claiming: You've started claiming a won bet.
    • claimed: You've claimed the winnings successfully.
    • canceling: You've broadcast a transaction canceling the bet.
    • canceled: This bet was canceled before confirming either (it could have been canceled by you or your counterparty)
  • outcome-time: When the oracle expects the outcome to be known
  • in: the duration between now and outcome-time in short human readable form.
  • risk: How much BTC you stand to lose in this bet.
  • reward: How much BTC you stand to gain.
  • tags: human readable strings you can attach to a bet with gun bet tag
  • oracle: The oracle for the bet.
  • i-bet: which outcome you bet on.
  • short-id: the end of the event-id of the bet.

gun bet offer

Make an offer in response to a proposal.

Synopsis

gun bet offer [options] <proposal>

Without providing any options you'll be prompted for remaining details:

gun bet offer 0.005#h00.ooo#/random/2021-10-31T02:22:00/heads_tails.winner#۸პÂ௬ɦఅɗସઇȿჱ၄ƦফЩञǴऱλ૩ԉกƻ༓LJЧ༃ཆɁঢӛႱ၃ಢͱธྈͰଢƿɧƶവϳॻŹဪ໖ǘØ

Go all in on tails and secretly communicate your confidence to the proposer

gun bet offer -m "bro I am so confident it tails atm" -c "tails" -v all 0.005#h00.ooo#/random/2021-10-31T02:22:00/heads_tails.winner#۸პÂ௬ɦఅɗସઇȿჱ၄ƦফЩञǴऱλ૩ԉกƻ༓LJЧ༃ཆɁঢӛႱ၃ಢͱธྈͰଢƿɧƶവϳॻŹဪ໖ǘØ

Description

gun bet offer generates an encrypted offer in response to a proposal. You choose the outcome you want to bet on with --choice and the --value you are willing to risk to obtain the proposal's coins. The offerer also pays and chooses the fee of the bet transaction with the --fee option.

Security

After the proposer sees your offer they may take it at their own discretion. This includes taking the offer after the outcome of the bet is already known i.e. cheating. You must protect yourself against this by canceling the offer before a malicious proposer can take advantage of this.

💡 See GuN Safety

Privacy

Your offer should be indistinguishable from random gibberish to anyone but the proposer. This doesn't mean you have to post your offer publicly. In practice it's better to send the proposer a direct message with your offer unless you are trying to advertise the fact that you are using gun for your own satisfaction.

Choosing a fee

In general you should use a low fee for your offer unless you are betting very close to the event. Proposers will generally not discriminate based on the fee and would rather you put as much value in the bet as possible.

Options

-v, --value <value>

The value you want to risk on the bet. If you don't provide it you'll be prompted for it. Can be:

  • all to go "all in" with the coins in your wallet
  • match to match whatever the proposer is risking.
  • An amount and denomination like 0.01BTC or 10000sat

-c, --choice <choice>

The outcome you want to bet on occurring. If you don't provide it you'll be prompted for it.

-m, --message <message>

Attach a message in addition to the offer. If you are making the offer in a place with limited characters (like Twitter) the command will warn you if the message is too long.

-p, --pad <pad>

Pad the ciphertext to a certain length of bytes so observes can't use the ciphertext length to determine the contents. The default value is 385 which happens to the amount that will fit the base2048 encoded ciphertext in a single tweet. If you don't want padding just add -p0.

-t, --tags <tags>...

Add human readable tags to the bet as you create it. This is useful so you a can record a short note about where you found the proposal.

--fee <fee>

Set the fee for the transaction.

You can choose from:

  • --fee=rate:4.5 set the fee to 4.5 sats-per-vbyte.
  • --fee=abs:500 set the fee to exactly 500 sats.
  • --fee=in-blocks:10 set the fee so it should get into the chain some time in the next 10 blocks.

The default is in-blocks:1 i.e. set the fee so it gets into the next block.

-y, --yes

Skip all [y/n] prompts by just answering yes to all of them.

gun bet inspect

Inspect an offer or proposal without interacting with it. This is convenience command for when want to see the details of an offer or proposal without being prompted to accept it.

Synopsis

gun bet inspect <subcommand>

Subcommands

proposal

gun bet inspect proposal <proposal>

Inspect a proposal. For example:

gun bet inspect proposal 0.005#h00.ooo#/EPL/match/2021-09-11/LEI_MCI.vs=MCI_win#ஐпმܪപઓܠফએઌΠڃฎƜపԬઉʐڣǧҵЕʕɉɏҵฤຂΘݸլၷ࿋னЉඇໜЈবɇοȾಋѻݙȁ࿅ผɸØ

offer

gun bet inspect offer <bet-id> <offer>

Inspect an offer in response to one of your proposals. The following example won't work because you don't have the key to decrypt the offer.

gun bet inspect offer 33 ପҊڧოӵիПܬߧɓئҙݒතʘȄݮɑရཪჟݿྋଟආ࿊ޕȌϐ௸ɫӂDžʉཌɭϽඨओȅଗɡވइဟЫښಊჱߓघɔȱߝŨdžชʌȋȓƥളʯǡಅਠۍॠܡԇȪݯНପżଠЋघјʝഭӏրঈॽဘɷγȌళളஜबܠܟၺѧջไ༕ڎʬƂǭදܖɓਐݢɗජฬЩݳރݺѮʃȦҰƬஹԭގఏȽම௵ڄޅ௵ੜӹɓԧڬঙਬɩצཪၛனʡҘɻ৺Ƨō

gun bet reply

Reply with an encrypted message rather than an offer to a proposal.

Synopsis

gun bet reply [OPTIONS] <proposal>

Slide into someone's DMs:

echo "follow me so I can DM you" | gun bet reply  0.01#h00.ooo#/random/2021-09-09T08:14:00/heads_tails.winner#ഩǠɢѩչਠനՎȆଞગਐעͻڢѓӫҹڣလߛϊرКɏҵฤຂΘݸլၷ࿋னЉඇໜЈবɇοȾಋѻݙȁ࿅ผɱƂइŏDZێեЯಐۇฝဆসϑ࿖ɀჵਊၒβԼјஊڠၮɈ།

Options

-m, --message <message>

Set the message for the reply with an argument. If this is omitted it will read the message from stdin.

-p, --pad <pad>

Pad the ciphertext to a certain length of bytes so observes can't use the ciphertext length to guess the contents. The default value is 385 which is number of bytes for that will fit in single tweet when base2048. If you don't want padding just add -p0.

gun bet take

Take an offer to one of your proposals.

Synopsis

gun bet take [options] <id> <encrypted-offer>

Description

gun bet take displays details about an offer and prompts you to take it or not. It is up to the user to figure out what proposal this offer was in relation to. You may use gun bet list to see the ids of all your proposals.

Taking the offer will prompt you to broadcast the bet transaction with the bet output.

Security

Try to avoid taking an offer close to when an event transpires. If the offer's fee is low the bet transaction may not be confirmed until after the event has transpired. A cheating offerer may then try to cancel the bet if things go against them before it confirms.

Options

-y, --yes

Skip all [y/n] prompts by just answering yes to all of them.

--print-tx

Print the tx as hex instead of broadcasting it. This will assume you're going to broadcast the tx yourself.

gun bet show

Show more details about a bet.

Synopsis

gun bet show [options] <id>

Options

-r, --raw

Print the raw entry in the database. This should be used for debugging only.

gun bet forget

Delete a bet from your local database.

💡There are some bets you'd rather forget about but this command can't do anything about that.

Synopsis

gun bet forget [bet-ids]...

Description

This command just deletes a bet from your local database. This is obviously dangerous depending on the state of the bet so gun bet forget will stop you from deleting your coins or warn you when you need to be careful.

⚠️ Unless you're sure you understand the impact of forgetting a bet use gun bet cancel instead.

gun bet cancel

Cancel a bet by broadcasting a double spending transaction.

Synopsis

gun bet cancel [options] [bet-ids]...

Description

To fully cancel a bet you double spend one of your declared inputs to the bet. Either the offerer or the proposer can cancel a bet before it has been confirmed.

On the proposer's side canceling is just good etiquette. By canceling the proposal everyone who has made an offer doesn't have to make a transaction canceling their offer. If a proposer is sure that no one will make an offer to a proposal they can instead forget about it.

An offerer must cancel any offer they've made that hasn't been taken (or canceled) prior to the outcome time.

To cancel a bet it must be in the proposed, offered or canceling state. You can cancel many bets at the same time. If one of the bets being canceled is in the canceling state you must make sure that the feerate set by --fee is higher than the previous canceling transaction's fee.

💡 See GuN Safety

Options

--print-tx

Print the tx as hex instead of broadcasting it. This will assume you're going to broadcast the tx yourself.

-y, --yes

Skip all [y/n] prompts by just answering yes to all of them.

--fee <fee>

Set the fee for the transaction.

You can choose from:

  • --fee=rate:4.5 set the fee to 4.5 sats-per-vbyte.
  • --fee=abs:500 set the fee to exactly 500 sats.
  • --fee=in-blocks:10 set the fee so it should get into the chain some time in the next 10 blocks.

The default is in-blocks:1 i.e. set the fee so it gets into the next block.

gun bet tag

Tag a bet with a short string.

Synopsis

Add a tag

gun bet tag add <bet-id> <tag>

Remove a tag

gun bet tag remove <bet-id> <tag>

Description

Tagging helps you remember how a bet originated. It can also be done at bet creation time with the --tags flag.

Offer tutorial

This is a tutorial that focuses on how to make an offer to a proposal which is a wacky string like:

0.005#h00.ooo#/s/EPL/match/2021-10-16/LEI_MUN.vs=MUN_win#ৠöഓཀসƵ୯รແྋŸǢঊಹహĈၐ೨ø໕Ǜœબ೮LJЧ༃ཆɁঢӛႱ၃ಢͱธྈͰଢƿɧƶവϳॻŹဪ໖ƸØ

Accounts like @GoUpNumber consistently make proposals and look for the best offers. To see how to make proposals yourself see gun bet propose.

Requirements

This tutorial is for people who want to try out betting with gun but don't want to RTFM too much. You should meet the following criteria:

  1. Comfortable with command line applications.
  2. Don't mind losing coins due to bad bets or technical malfunctions. You are happy to consider it a donation to science.
  3. Able to report bugs on GitHub.

Install

Follow instructions in install. Remember to back up your seed words in $HOME/.gun/seed.txt.

Put some coins in your wallet

💡 It's a good idea to coin join your coins before sending them to gun (or doing anything really!). If you don't feel comfortable betting coins at this stage you can join the fun by using gun bet reply to post an encrypted message that looks like an offer instead.

To get a wallet address run:

gun address new

And you'll see an address. Send a small amount of coins to it if you're just getting started.

To check when the coins come in you can do gun -s balance or gun -s utxo list or gun -s tx list. The most important thing is adding the -s flag so gun checks the blockchain before displaying the result. Without -s the command will just display what's already in the database.

Find a Proposal

A proposal contains the event that will be bet on and the amount the proposer is willing to risk on it. To promote this experiment @GoUpNumber is frequently posting proposals on twitter and on their Discord channel (invite link).

A proposal looks like this:

0.005#h00.ooo#/s/EPL/match/2021-10-16/LEI_MUN.vs=MUN_win#ৠöഓཀসƵ୯รແྋŸǢঊಹహĈၐ೨ø໕Ǜœબ೮LJЧ༃ཆɁঢӛႱ၃ಢͱธྈͰଢƿɧƶവϳॻŹဪ໖ƸØ

It contains several parts split up by the # character.

  1. 0.005: The amount in BTC that is being risked.
  2. h00.ooo: The oracle in this case is h00.ooo.
  3. /s/EPL/match/2021-10-16/LEI_MUN.vs=MUN_win: The event the proposal is on. This can be read as "Whether Manchester United beats Leicester City in their EPL (English Premier League) match on 2021-10-16". Note that gun can only bet on events with two possible outcomes.
  4. ৠöഓཀসƵ୯รແྋŸǢঊಹహĈၐ೨ø໕Ǜœબ೮LJЧ༃ཆɁঢӛႱ၃ಢͱธྈͰଢƿɧƶവϳॻŹဪ໖ƸØ: cryptographic input to the bet. A public key and list of inputs.

Trust the oracle

Before betting with h00.ooo we have to tell gun we trust it. To trust h00.ooo run:

gun bet oracle add h00.ooo

It's fine to accept whatever you are given at this stage.

Making your offer

When you've found a proposal you want to bet on make an offer to it. We'll make an offer to the proposal above. We must decide whether you think Manchester United will win or not and how much you are willing to risk.

Run the offer command like so:

gun bet offer 0.005#h00.ooo#/s/EPL/match/2021-10-16/LEI_MUN.vs=MUN_win#ৠöഓཀসƵ୯รແྋŸǢঊಹహĈၐ೨ø໕Ǜœબ೮LJЧ༃ཆɁঢӛႱ၃ಢͱธྈͰଢƿɧƶവϳॻŹဪ໖ƸØ

You'll be prompted to choose:

  1. Which outcome you want to bet on. In this case true if you think Manchester United will win otherwise false.
  2. How much you want to risk to get the 500,000 sats on offer in the proposal. You can bet as low as 1000 sats to give yourself 1 to 500 risk reward ratio. @GoUpNumber will always take it if they don't get any better offers.

The base2048 gibberish you get as output is encrypted so that only the proposer can read it. Copy it and paste it somewhere the proposer can see it e.g. reply to the tweet, send them a direct message etc.

💡 Consider posting the offer under a colorful pseudonym so not even the proposer knows who they're betting with!

Changing the default fee

As the offerer you choose the fee that the bet transaction will pay. The default fee gun chooses is quite high (it attempts to get it into the next block i.e. --fee in-blocks:1). This is inappropriate for most offers unless they are being made last minute. You can use --fee to set the fee so it should confirm some time in the next 30 blocks after it is broadcasted at the current fee rate. If you're making the offer days before the event then this is fine.

gun bet offer --fee in-blocks:30 0.005#h00.ooo#/s/EPL/match/2021-10-16/LEI_MUN.vs=MUN_win#ৠöഓཀসƵ୯รແྋŸǢঊಹహĈၐ೨ø໕Ǜœબ೮LJЧ༃ཆɁঢӛႱ၃ಢͱธྈͰଢƿɧƶവϳॻŹဪ໖ƸØ

Monitor you offer

The easiest way to monitor the offer is to do:

gun -s bet list

Immediately after it will be in the offered state. When/if the proposer has taken your offer it will transition to the unconfirmed state and then eventually the confirmed state when it ends up in the chain. If the proposer chooses a different offer it will end up in the canceled state (or if you use gun bet cancel to cancel it).

Take note of how long there is until whatever you are betting on takes place. If the bet is still not in the confirmed state a just prior to the event starting you should run gun bet cancel <bet-id> where bet id is the number in the first column of the gun bet list output. If you are forced to manually cancel your offer you should consider avoiding betting with that proposer again as it is bad manners for a proposer to leave this to you.

Claiming your winnings

If it doesn't get canceled, eventually your bet should go from the confirmed to the won or lost state. Just keep running gun -s bet list to check this.

If you go into the lost state you don't need to do anything further. Thank you for your heroic sacrifice.

If you go into the won state you can use gun bet claim to put the coins back into your main wallet. Alternatively, gun send will always spend any won bets you have open by default.

There is no strict time limit to claiming won bets but if you have data loss while the bet is in the won state the coins will be difficult to recover (See backup and recovery).

Troubleshooting

Oracle hasn't attested

If you're stuck in confirmed long after the event was due the oracle may have messed up. The easiest way is to find the event on outcome.observer but you can also manually visit the URL. For example go to https://h00.ooo/s/EPL/match/2021-10-16/LEI_MUN.vs=MUN_win in your browser and see if the attestation field is filled in.

⚠️ In the current protocol, if the oracle disappears of fails to ever attest to the event you have both lost your coins. The oracle h00.ooo is run by LLFourn so in this case you can blame him for that!

The Oracle has attested to the wrong thing

Publicly shame the operator of the oracle.

Something else

Report this as a bug on GitHub or contact LLFourn in some other way.

Formatting Output

Apart from the default human readable style format any command can be modified to format its output in one of two more computer readable formats.

JSON -j, --json

This format is output for reading into other software. Coin amounts are formatted as sats and DateTimes will be formatted as UNIX timestamps.

gun -j <command> .. will output JSON.

The following will output how much you've lost in bets

gun -j bet list | jq 'map(select(.state == "lost").risk) | add'

Tab separated -t, --tabs

This format it useful for UNIX command line tools because it tab separates each field so things like cut(1) just work.

The following will output how much you've lost in bets

gun -t bet list | awk '$2 == "lost"{ lost += $5 } END { print lost }'

Roadmap

Future features

  1. Signed releases for every platform. #29
  2. Connecting to esplora through Tor.
  3. Support for xpub wallet which produces PSBTs and can write them to SD cards for signing devices.
  4. lightning with LDK.
  5. A GUI with tauri or something similar.

Research Problems

gun is a research project that I hope develops into a useful tool.

Betting and Taproot

Taproot can improve the betting protocol in several ways. I have a protocol in mind that could achieve the following:

  1. All transactions involved would be taproot key spends.
  2. The two parties would be able to cooperatively resolve a bet if the oracle were to fail to attest.
  3. Someone who has their coins stolen from the bet with an incorrect attestation (i.e. the oracle privately leaked the attestation to the thief) can prove that this happened.

Other than that I hope I can make the whole wallet taproot-only eventually.

A better backend

gun can only use esplora as a backend. Since it is difficult to run yourself people will mostly end up using the default server mempool.space. This is a security and privacy hole.

An area of research I want to undertake is whether we can architect an easier to run and less resource intensive backend that still offers a rich enough API.