Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Contract

scripts/contract.livemd

Contract

exfacto_root = Path.join(__DIR__, "..")

Mix.install(
  [
    {:exfacto, path: exfacto_root, env: :dev}
  ],
  config_path: Path.join(exfacto_root, "config/config.exs"),
  lockfile: Path.join(exfacto_root, "mix.lock")
)
==> exfacto
Compiling 1 file (.ex)
:ok

Imports & Constants

alias ExFacto.{Gambler, Oracle, Event, Contract, Messaging, Utils, Builder}
alias ExFacto.Oracle.{Announcement, Attestation}
alias ExFacto.Contract.{Offer, Accept}
alias Bitcoinex.{Transaction, Script}
alias Bitcoinex.Secp256k1.{PrivateKey, Point, Signature, Schnorr}

network = :testnet
p2tr_keyspend_witness_len = 64
default_sequence = 0xFFFFFFFE

new_p2tr_keyonly = fn ->
  sk = Utils.new_private_key()
  pk = PrivateKey.to_point(sk)

  {:ok, p2tr_script} = Script.create_p2tr(pk)
  {sk, p2tr_script}
end

fake_tx = fn out ->
  %Transaction{
    version: 2,
    inputs: [
      %Transaction.In{
        prev_txid: "bcda2fe66ca90e922c3679b224b7b38e34660049a597a8193b850e951c90b268",
        prev_vout: rem(Utils.new_rand_int(), 10),
        sequence_no: default_sequence,
        script_sig: ""
      }
    ],
    outputs: [out],
    lock_time: 0
  }
end
#Function<42.3316493/1 in :erl_eval.expr/6>

Set Up Oracle

oracle = Oracle.new(Utils.new_private_key())
%ExFacto.Oracle{
  sk: %Bitcoinex.Secp256k1.PrivateKey{
    d: 49210819581175817739796205276126045948765995434495284179134470827825907128590
  },
  pk: %Bitcoinex.Secp256k1.Point{
    x: 112502138587442934194068684128318852147239071099056347765908050043529251109604,
    y: 14372472142056461307751746689712105593257677661447046211315250490323145961828,
    z: 0
  }
}

Setup Event

outcomes = ["CHIEFS WIN", "EAGLES WIN"]
event_descriptor = %{outcomes: outcomes}

{nonce_sk, event} =
  Event.new_event_from_enum_event_descriptor(
    event_descriptor,
    1_678_498_879,
    &amp;ExFacto.Utils.new_private_key/0
  )
{%Bitcoinex.Secp256k1.PrivateKey{
   d: 11099716068458697126781019421672042870075979202855107091004132003591057325542
 },
 %ExFacto.Event{
   id: "339dddff3c187228b8c46b4d6c7e7722e80aef998e6cdcceec2aa9c0b4b7bdaf",
   nonce_points: [
     %Bitcoinex.Secp256k1.Point{
       x: 53007777715105910724780386918899673925950264896437873703088252280660935563344,
       y: 29493127903056445117114273794631617853089395679184317995663376207111954618218,
       z: 0
     }
   ],
   descriptor: %{outcomes: ["CHIEFS WIN", "EAGLES WIN"]},
   maturity_epoch: 1678498879
 }}

Now, the oracle will sign the event for authentication purposes, The signed event is a complete Announcement, which can be broadcast to DLC users.

oracle_info = Oracle.sign_event(oracle, event)
%{
  announcement: %ExFacto.Oracle.Announcement{
    signature: %Bitcoinex.Secp256k1.Signature{
      r: 9817884557788043311116236364298648127266359087879543768110447890793925641833,
      s: 33263763681618801616927220214888551996816683182144900389480185689251560573126
    },
    public_key: %Bitcoinex.Secp256k1.Point{
      x: 112502138587442934194068684128318852147239071099056347765908050043529251109604,
      y: 14372472142056461307751746689712105593257677661447046211315250490323145961828,
      z: 0
    },
    event: %ExFacto.Event{
      id: "339dddff3c187228b8c46b4d6c7e7722e80aef998e6cdcceec2aa9c0b4b7bdaf",
      nonce_points: [
        %Bitcoinex.Secp256k1.Point{
          x: 53007777715105910724780386918899673925950264896437873703088252280660935563344,
          y: 29493127903056445117114273794631617853089395679184317995663376207111954618218,
          z: 0
        }
      ],
      descriptor: %{outcomes: ["CHIEFS WIN", "EAGLES WIN"]},
      maturity_epoch: 1678498879
    }
  }
}

Anyone can verify the signature of this announcement like so

Announcement.verify(oracle_info.announcement)
true

Introducing Alice

Meet Alice. she has a coin (from a transaction that we made up). She wants to use this coin to bet on this Oracle Announcement.

{alice_input_sk, alice_input_script} = new_p2tr_keyonly.()

alice_input_tx =
  fake_tx.(%Transaction.Out{
    value: 100_000_000,
    script_pub_key: Script.to_hex(alice_input_script)
  })

alice_funding_inputs = [
  Messaging.new_funding_input_info(
    alice_input_tx,
    0,
    default_sequence,
    p2tr_keyspend_witness_len,
    nil,
    100_000_000
  )
]
[
  %{
    amount: 100000000,
    max_witness_len: 64,
    prev_tx: %Bitcoinex.Transaction{
      version: 2,
      inputs: [
        %Bitcoinex.Transaction.In{
          prev_txid: "bcda2fe66ca90e922c3679b224b7b38e34660049a597a8193b850e951c90b268",
          prev_vout: 5,
          script_sig: "",
          sequence_no: 4294967294
        }
      ],
      outputs: [
        %Bitcoinex.Transaction.Out{
          value: 100000000,
          script_pub_key: "51203757539bd5fe5c5077a11b3d9fac440cc6ae7b720e998177a05506802c7c48bf"
        }
      ],
      witnesses: nil,
      lock_time: 0
    },
    prev_vout: 0,
    redeem_script: nil,
    sequence: 4294967294
  }
]

Alice Makes an Offer

Alice will create an offer using this Event.

# we don't care where the money goes after the contract in this example
{_alice_change_sk, alice_change_script} = new_p2tr_keyonly.()
{:ok, alice_change_addr} = Script.to_address(alice_change_script, network)
{_alice_payout_sk, alice_payout_script} = new_p2tr_keyonly.()
{:ok, alice_payout_addr} = Script.to_address(alice_payout_script, network)

# Gambler is instantiated once per contract.
alice =
  Gambler.new(
    alice_funding_inputs,
    alice_change_addr,
    alice_payout_addr,
    &amp;Utils.new_private_key/0
  )
%ExFacto.Gambler{
  network: %Bitcoinex.Network{
    name: :testnet,
    hrp_segwit_prefix: "tb",
    p2pkh_version_decimal_prefix: 111,
    p2sh_version_decimal_prefix: 196
  },
  funding_inputs: [
    %{
      amount: 100000000,
      max_witness_len: 64,
      prev_tx: %Bitcoinex.Transaction{
        version: 2,
        inputs: [
          %Bitcoinex.Transaction.In{
            prev_txid: "bcda2fe66ca90e922c3679b224b7b38e34660049a597a8193b850e951c90b268",
            prev_vout: 5,
            script_sig: "",
            sequence_no: 4294967294
          }
        ],
        outputs: [
          %Bitcoinex.Transaction.Out{
            value: 100000000,
            script_pub_key: "51203757539bd5fe5c5077a11b3d9fac440cc6ae7b720e998177a05506802c7c48bf"
          }
        ],
        witnesses: nil,
        lock_time: 0
      },
      prev_vout: 0,
      redeem_script: nil,
      sequence: 4294967294
    }
  ],
  fund_sk: %Bitcoinex.Secp256k1.PrivateKey{
    d: 59767214921211262063831426568980088969872177011134049327557565075950451087792
  },
  fund_pk: %Bitcoinex.Secp256k1.Point{
    x: 114564553767010798500476798454396468895738181561405739508394323762080218726613,
    y: 110979531653360282726091406237515411029843107390241081018546762591892756861498,
    z: 0
  },
  change_script: %Bitcoinex.Script{
    items: [
      81,
      32,
      <<7, 45, 89, 196, 83, 247, 156, 202, 241, 164, 110, 112, 116, 63, 0, 73, 69, 116, 204, 227,
        38, 199, 176, 84, 243, 124, 146, 220, 172, 139, 177, 146>>
    ]
  },
  payout_script: %Bitcoinex.Script{
    items: [
      81,
      32,
      <<158, 98, 237, 65, 136, 118, 116, 108, 133, 112, 202, 213, 151, 85, 89, 109, 162, 224, 173,
        4, 13, 10, 24, 75, 22, 249, 54, 38, 24, 134, 93, 205>>
    ]
  }
}

Alice sets a few parameters for the bet. She sets the total bet value to 100M sats, decides she will put up 50M sats (making the odds 50/50), and that she will bet on the EAGLES WIN outcome. She also sets the fee rate to 2 sats/vByte.

total_collateral = 100_000_000
# alice will put up half (.5BBTC)
offer_collateral = 50_000_000
# alice decides she wants to bet on the seoncd outcome "EAGLES WIN"
payouts = [0, 100_000_000]
# refund will become available 1 week after the event maturity
refund_locktime_delta = 1_204_000
# sats/vByte
fee_rate = 2
2

Then she creates the offer

offer =
  Gambler.create_offer(
    alice,
    oracle_info,
    payouts,
    offer_collateral,
    total_collateral,
    refund_locktime_delta,
    fee_rate
  )
%ExFacto.Contract.Offer{
  version: 0,
  contract_flags: 0,
  chain_hash: <<0, 0, 0, 0, 9, 51, 234, 1, 173, 14, 233, 132, 32, 151, 121, 186, 174, 195, 206, 217,
    15, 163, 244, 8, 113, 149, 38, 248, 215, 127, 73, 67>>,
  offer_id: <<88, 163, 193, 174, 33, 93, 94, 72, 210, 161, 18, 144, 108, 153, 88, 15, 186, 68, 103,
    85, 79, 200, 75, 170, 170, 84, 194, 56, 26, 251, 165, 103>>,
  contract_info: %ExFacto.Contract{
    total_collateral: 100000000,
    descriptor: [{"CHIEFS WIN", 0}, {"EAGLES WIN", 100000000}],
    oracle_info: %{
      announcement: %ExFacto.Oracle.Announcement{
        signature: %Bitcoinex.Secp256k1.Signature{
          r: 9817884557788043311116236364298648127266359087879543768110447890793925641833,
          s: 33263763681618801616927220214888551996816683182144900389480185689251560573126
        },
        public_key: %Bitcoinex.Secp256k1.Point{
          x: 112502138587442934194068684128318852147239071099056347765908050043529251109604,
          y: 14372472142056461307751746689712105593257677661447046211315250490323145961828,
          z: 0
        },
        event: %ExFacto.Event{
          id: "339dddff3c187228b8c46b4d6c7e7722e80aef998e6cdcceec2aa9c0b4b7bdaf",
          nonce_points: [
            %Bitcoinex.Secp256k1.Point{
              x: 53007777715105910724780386918899673925950264896437873703088252280660935563344,
              y: 29493127903056445117114273794631617853089395679184317995663376207111954618218,
              z: 0
            }
          ],
          descriptor: %{outcomes: ["CHIEFS WIN", "EAGLES WIN"]},
          maturity_epoch: 1678498879
        }
      }
    }
  },
  funding_pubkey: %Bitcoinex.Secp256k1.Point{
    x: 114564553767010798500476798454396468895738181561405739508394323762080218726613,
    y: 110979531653360282726091406237515411029843107390241081018546762591892756861498,
    z: 0
  },
  payout_script: %Bitcoinex.Script{
    items: [
      81,
      32,
      <<158, 98, 237, 65, 136, 118, 116, 108, 133, 112, 202, 213, 151, 85, 89, 109, 162, 224, 173,
        4, 13, 10, 24, 75, 22, 249, 54, 38, 24, 134, 93, 205>>
    ]
  },
  collateral_amount: 50000000,
  funding_inputs: [
    %{
      amount: 100000000,
      max_witness_len: 64,
      prev_tx: %Bitcoinex.Transaction{
        version: 2,
        inputs: [
          %Bitcoinex.Transaction.In{
            prev_txid: "bcda2fe66ca90e922c3679b224b7b38e34660049a597a8193b850e951c90b268",
            prev_vout: 5,
            script_sig: "",
            sequence_no: 4294967294
          }
        ],
        outputs: [
          %Bitcoinex.Transaction.Out{
            value: 100000000,
            script_pub_key: "51203757539bd5fe5c5077a11b3d9fac440cc6ae7b720e998177a05506802c7c48bf"
          }
        ],
        witnesses: nil,
        lock_time: 0
      },
      prev_vout: 0,
      redeem_script: nil,
      sequence: 4294967294
    }
  ],
  change_script: %Bitcoinex.Script{
    items: [
      81,
      32,
      <<7, 45, 89, 196, 83, 247, 156, 202, 241, 164, 110, 112, 116, 63, 0, 73, 69, 116, 204, 227,
        38, 199, 176, 84, 243, 124, 146, 220, 172, 139, 177, 146>>
    ]
  },
  fee_rate: 2,
  cet_locktime: 1678498879,
  refund_locktime: 1679702879,
  tlvs: nil
}

This offer can be serialized to base64 and broadcast publically, or shared directly with a counterparty Alice wants to bet against. It is one-time use, since the offer specifies Alice’s coins, which cannot be spent more than once.

Meet Bob

This is Bob. Bob says hi. This is Bob when an offer comes by. Bob also has a coin (also fake), and wants to bet against Alice.

{bob_input_sk, bob_input_script} = new_p2tr_keyonly.()

bob_input_tx =
  fake_tx.(%Transaction.Out{
    value: 100_000_000,
    script_pub_key: Script.to_hex(bob_input_script)
  })

bob_funding_inputs = [
  Messaging.new_funding_input_info(
    bob_input_tx,
    0,
    default_sequence,
    p2tr_keyspend_witness_len,
    nil,
    100_000_000
  )
]

# we don't care where the money goes after the contract in this example
{_bob_change_sk, bob_change_script} = new_p2tr_keyonly.()
{:ok, bob_change_addr} = Script.to_address(bob_change_script, network)
{_bob_payout_sk, bob_payout_script} = new_p2tr_keyonly.()
{:ok, bob_payout_addr} = Script.to_address(bob_payout_script, network)

# Gambler is instantiated once per contract.
bob = Gambler.new(bob_funding_inputs, bob_change_addr, bob_payout_addr, &amp;Utils.new_private_key/0)
%ExFacto.Gambler{
  network: %Bitcoinex.Network{
    name: :testnet,
    hrp_segwit_prefix: "tb",
    p2pkh_version_decimal_prefix: 111,
    p2sh_version_decimal_prefix: 196
  },
  funding_inputs: [
    %{
      amount: 100000000,
      max_witness_len: 64,
      prev_tx: %Bitcoinex.Transaction{
        version: 2,
        inputs: [
          %Bitcoinex.Transaction.In{
            prev_txid: "bcda2fe66ca90e922c3679b224b7b38e34660049a597a8193b850e951c90b268",
            prev_vout: 2,
            script_sig: "",
            sequence_no: 4294967294
          }
        ],
        outputs: [
          %Bitcoinex.Transaction.Out{
            value: 100000000,
            script_pub_key: "51205d59ddb45ebbf4095a84957bc72cf281e91d38b0780c143f734fa57fbfe2ea22"
          }
        ],
        witnesses: nil,
        lock_time: 0
      },
      prev_vout: 0,
      redeem_script: nil,
      sequence: 4294967294
    }
  ],
  fund_sk: %Bitcoinex.Secp256k1.PrivateKey{
    d: 44604019854985784808865556248399850115653801163419028119626154994820038954610
  },
  fund_pk: %Bitcoinex.Secp256k1.Point{
    x: 14150459797792531712934683190431462043133654649141591757944928049719459820297,
    y: 70535129904109218451398605642616567932518110756700997188868515215226319975698,
    z: 0
  },
  change_script: %Bitcoinex.Script{
    items: [
      81,
      32,
      <<188, 91, 116, 226, 28, 56, 122, 200, 133, 30, 15, 253, 136, 67, 166, 43, 203, 68, 205, 41,
        157, 61, 242, 38, 115, 182, 226, 226, 119, 150, 163, 244>>
    ]
  },
  payout_script: %Bitcoinex.Script{
    items: [
      81,
      32,
      <<158, 78, 211, 190, 255, 130, 152, 176, 216, 8, 39, 135, 81, 152, 101, 203, 78, 52, 175, 205,
        227, 227, 106, 142, 28, 158, 82, 89, 223, 112, 148, 40>>
    ]
  }
}

Bob Accepts Alice’s Offer

Alice can send Bob an encoding of her offer, or a coordinator can help Bob find Alice’s offer. In either case, Bob will now create an Accept to Alice’s Offer.

From the Offer, Bob has all the info he needs to construct the full Funding Tx, CETs for each outcome, and a refund tx.

{accept, outcomes_cet_txs, refund_tx} = Gambler.create_accept(bob, offer)
{%ExFacto.Contract.Accept{
   version: 0,
   chain_hash: <<0, 0, 0, 0, 9, 51, 234, 1, 173, 14, 233, 132, 32, 151, 121, 186, 174, 195, 206,
     217, 15, 163, 244, 8, 113, 149, 38, 248, 215, 127, 73, 67>>,
   contract_id: <<117, 190, 36, 128, 131, 184, 152, 18, 54, 67, 139, 101, 30, 31, 143, 209, 176,
     173, 113, 146, 164, 142, 251, 191, 231, 135, 179, 232, 82, 129, 36, 143>>,
   offer_id: <<88, 163, 193, 174, 33, 93, 94, 72, 210, 161, 18, 144, 108, 153, 88, 15, 186, 68, 103,
     85, 79, 200, 75, 170, 170, 84, 194, 56, 26, 251, 165, 103>>,
   funding_pubkey: %Bitcoinex.Secp256k1.Point{
     x: 14150459797792531712934683190431462043133654649141591757944928049719459820297,
     y: 70535129904109218451398605642616567932518110756700997188868515215226319975698,
     z: 0
   },
   dummy_tapkey_tweak: 16934846483378894890565234504791733207279513267057194057672482455920119488381,
   payout_script: %Bitcoinex.Script{
     items: [
       81,
       32,
       <<158, 78, 211, 190, 255, 130, 152, 176, 216, 8, 39, 135, 81, 152, 101, 203, 78, 52, 175,
         205, 227, 227, 106, 142, 28, 158, 82, 89, 223, 112, 148, 40>>
     ]
   },
   change_script: %Bitcoinex.Script{
     items: [
       81,
       32,
       <<188, 91, 116, 226, 28, 56, 122, 200, 133, 30, 15, 253, 136, 67, 166, 43, 203, 68, 205, 41,
         157, 61, 242, 38, 115, 182, 226, 226, 119, 150, 163, 244>>
     ]
   },
   collateral_amount: 50000000,
   funding_inputs: [
     %{
       amount: 100000000,
       max_witness_len: 64,
       prev_tx: %Bitcoinex.Transaction{
         version: 2,
         inputs: [
           %Bitcoinex.Transaction.In{
             prev_txid: "bcda2fe66ca90e922c3679b224b7b38e34660049a597a8193b850e951c90b268",
             prev_vout: 2,
             script_sig: "",
             sequence_no: 4294967294
           }
         ],
         outputs: [
           %Bitcoinex.Transaction.Out{
             value: 100000000,
             script_pub_key: "51205d59ddb45ebbf4095a84957bc72cf281e91d38b0780c143f734fa57fbfe2ea22"
           }
         ],
         witnesses: nil,
         lock_time: 0
       },
       prev_vout: 0,
       redeem_script: nil,
       sequence: 4294967294
     }
   ],
   cet_adaptor_signatures: [
     %{
       adaptor_signature: %Bitcoinex.Secp256k1.Signature{
         r: 90978364178544021802367793214640889112083314403552040032988742271135857961808,
         s: 20210761486218253650978587387536842852559547852748243162444564273083923057445
       },
       was_negated: true
     },
     %{
       adaptor_signature: %Bitcoinex.Secp256k1.Signature{
         r: 72700873725788662581715484951228162046687252252910922338188035078958333453017,
         s: 79997523302327456534399266389595263700690459020378308845703842663096594313908
       },
       was_negated: true
     }
   ],
   refund_signature: %Bitcoinex.Secp256k1.Signature{
     r: 33562964401042321607265418039138833973912693330612906096379732073245043857195,
     s: 92439541549377783547920350267587245088469595146959933231437123707760839802995
   }
 },
 [
   {"CHIEFS WIN",
    %Bitcoinex.Transaction{
      version: 2,
      inputs: [
        %Bitcoinex.Transaction.In{
          prev_txid: "2d1de52ea2e5c65ae4e299f57286d7de0ae916c7eb46b0154dd371d0487a81ea",
          prev_vout: 2,
          script_sig: "",
          sequence_no: 4294967294
        }
      ],
      outputs: [
        %Bitcoinex.Transaction.Out{
          value: 0,
          script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
        },
        %Bitcoinex.Transaction.Out{
          value: 100000000,
          script_pub_key: "51209e4ed3beff8298b0d8082787519865cb4e34afcde3e36a8e1c9e5259df709428"
        }
      ],
      witnesses: nil,
      lock_time: 1678498879
    }},
   {"EAGLES WIN",
    %Bitcoinex.Transaction{
      version: 2,
      inputs: [
        %Bitcoinex.Transaction.In{
          prev_txid: "2d1de52ea2e5c65ae4e299f57286d7de0ae916c7eb46b0154dd371d0487a81ea",
          prev_vout: 2,
          script_sig: "",
          sequence_no: 4294967294
        }
      ],
      outputs: [
        %Bitcoinex.Transaction.Out{
          value: 100000000,
          script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
        },
        %Bitcoinex.Transaction.Out{
          value: 0,
          script_pub_key: "51209e4ed3beff8298b0d8082787519865cb4e34afcde3e36a8e1c9e5259df709428"
        }
      ],
      witnesses: nil,
      lock_time: 1678498879
    }}
 ],
 %Bitcoinex.Transaction{
   version: 2,
   inputs: [
     %Bitcoinex.Transaction.In{
       prev_txid: "2d1de52ea2e5c65ae4e299f57286d7de0ae916c7eb46b0154dd371d0487a81ea",
       prev_vout: 2,
       script_sig: "",
       sequence_no: 4294967294
     }
   ],
   outputs: [
     %Bitcoinex.Transaction.Out{
       value: 50000000,
       script_pub_key: "51209e4ed3beff8298b0d8082787519865cb4e34afcde3e36a8e1c9e5259df709428"
     },
     %Bitcoinex.Transaction.Out{
       value: 50000000,
       script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
     }
   ],
   witnesses: [],
   lock_time: 1679702879
 }}

Alice ACKs the Accept, Signs Funding Transaction

Once Alice receives the Accept message from Bob, she has enough information to sign the funding transaction and encrypted_sign the CETs. She will then send those results back to Bob, who will broadcast the Funding transaction.

Bob gets a free option here (for now). In the future, we will integrate a Barrier Oracle to eliminate this, but that requires another round or synchronous communication.

{ack, signed_funding_tx, outcomes_cet_txs, cet_adaptor_signatures, refund_tx, refund_signature} =
  Gambler.offerer_ack_accept(alice, offer, accept)
{%ExFacto.Contract.Acknowledge{
   contract_id: <<117, 190, 36, 128, 131, 184, 152, 18, 54, 67, 139, 101, 30, 31, 143, 209, 176,
     173, 113, 146, 164, 142, 251, 191, 231, 135, 179, 232, 82, 129, 36, 143>>,
   funding_witnesses: nil,
   cet_adaptor_signatures: [
     %{
       adaptor_signature: %Bitcoinex.Secp256k1.Signature{
         r: 71545299047403584280350093102881688899045651630875972501665480063499441775354,
         s: 113255400802490240213356401307713237895933173450941633902558963401455761658868
       },
       was_negated: true
     },
     %{
       adaptor_signature: %Bitcoinex.Secp256k1.Signature{
         r: 113943090579295950747425423646814870534250816951008286503127944112158272850817,
         s: 98411006057455546361992457794195164115046965386001900636803840984332865348491
       },
       was_negated: true
     }
   ],
   refund_signature: %Bitcoinex.Secp256k1.Signature{
     r: 68365117727113787193406509136072374812719626143900228734004104953954332899542,
     s: 79004258105906074360111001897659133199729191643905471892954206592133776135360
   }
 },
 %Bitcoinex.Transaction{
   version: 2,
   inputs: [
     %Bitcoinex.Transaction.In{
       prev_txid: "4d6b6f988719d9a42ccf73f0af7778500479cf3b7ceaa25b3028f3f17a4adfc5",
       prev_vout: 0,
       script_sig: "",
       sequence_no: 4294967294
     },
     %Bitcoinex.Transaction.In{
       prev_txid: "e7e61fc5c41fbf524515868a8a067fc77a6c97fcc8e70779bae4196b4e4e94da",
       prev_vout: 0,
       script_sig: "",
       sequence_no: 4294967294
     }
   ],
   outputs: [
     %Bitcoinex.Transaction.Out{
       value: 49999490,
       script_pub_key: "5120072d59c453f79ccaf1a46e70743f00494574cce326c7b054f37c92dcac8bb192"
     },
     %Bitcoinex.Transaction.Out{
       value: 49999490,
       script_pub_key: "5120072d59c453f79ccaf1a46e70743f00494574cce326c7b054f37c92dcac8bb192"
     },
     %Bitcoinex.Transaction.Out{
       value: 100000512,
       script_pub_key: "5120979e5db0269867f168a1fec854d2f57cdd8cb6dfc01cfc303bbc2ec86590a5da"
     }
   ],
   witnesses: nil,
   lock_time: 0
 },
 [
   {"CHIEFS WIN",
    %Bitcoinex.Transaction{
      version: 2,
      inputs: [
        %Bitcoinex.Transaction.In{
          prev_txid: "87df44a985bc5b6a2a18b26bf01481fa69750d955e6eab6efc6ba671203b7ef9",
          prev_vout: 2,
          script_sig: "",
          sequence_no: 4294967294
        }
      ],
      outputs: [
        %Bitcoinex.Transaction.Out{
          value: 0,
          script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
        },
        %Bitcoinex.Transaction.Out{
          value: 100000000,
          script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
        }
      ],
      witnesses: nil,
      lock_time: 1678498879
    }},
   {"EAGLES WIN",
    %Bitcoinex.Transaction{
      version: 2,
      inputs: [
        %Bitcoinex.Transaction.In{
          prev_txid: "87df44a985bc5b6a2a18b26bf01481fa69750d955e6eab6efc6ba671203b7ef9",
          prev_vout: 2,
          script_sig: "",
          sequence_no: 4294967294
        }
      ],
      outputs: [
        %Bitcoinex.Transaction.Out{
          value: 100000000,
          script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
        },
        %Bitcoinex.Transaction.Out{
          value: 0,
          script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
        }
      ],
      witnesses: nil,
      lock_time: 1678498879
    }}
 ],
 [
   %{
     adaptor_signature: %Bitcoinex.Secp256k1.Signature{
       r: 71545299047403584280350093102881688899045651630875972501665480063499441775354,
       s: 113255400802490240213356401307713237895933173450941633902558963401455761658868
     },
     was_negated: true
   },
   %{
     adaptor_signature: %Bitcoinex.Secp256k1.Signature{
       r: 113943090579295950747425423646814870534250816951008286503127944112158272850817,
       s: 98411006057455546361992457794195164115046965386001900636803840984332865348491
     },
     was_negated: true
   }
 ],
 %Bitcoinex.Transaction{
   version: 2,
   inputs: [
     %Bitcoinex.Transaction.In{
       prev_txid: "87df44a985bc5b6a2a18b26bf01481fa69750d955e6eab6efc6ba671203b7ef9",
       prev_vout: 2,
       script_sig: "",
       sequence_no: 4294967294
     }
   ],
   outputs: [
     %Bitcoinex.Transaction.Out{
       value: 50000000,
       script_pub_key: "51209e4ed3beff8298b0d8082787519865cb4e34afcde3e36a8e1c9e5259df709428"
     },
     %Bitcoinex.Transaction.Out{
       value: 50000000,
       script_pub_key: "51209e62ed418876746c8570cad59755596da2e0ad040d0a184b16f9362618865dcd"
     }
   ],
   witnesses: [],
   lock_time: 1679702879
 },
 %Bitcoinex.Secp256k1.Signature{
   r: 68365117727113787193406509136072374812719626143900228734004104953954332899542,
   s: 79004258105906074360111001897659133199729191643905471892954206592133776135360
 }}

01:03:28.183 [debug] Tzdata polling for update.

01:03:28.448 [info] tzdata release in place is from a file last modified Tue, 29 Nov 2022 17:25:53 GMT. Release file on server was last modified Fri, 24 Mar 2023 03:10:55 GMT.

01:03:28.450 [debug] Tzdata downloading new data from https://data.iana.org/time-zones/tzdata-latest.tar.gz

01:03:28.528 [debug] Tzdata data downloaded. Release version 2023b.

01:03:28.863 [info] Tzdata has updated the release from 2022g to 2023b

01:03:28.863 [debug] Tzdata deleting ETS table for version 2022g

01:03:28.864 [debug] Tzdata deleting ETS table file for version 2022g

12:23:57.925 [debug] Tzdata polling for update.

12:23:58.021 [debug] Tzdata polling shows the loaded tz database is up to date.

15:04:27.539 [debug] Tzdata polling for update.

15:04:27.635 [info] tzdata release in place is from a file last modified Fri, 24 Mar 2023 03:10:55 GMT. Release file on server was last modified Tue, 28 Mar 2023 20:25:39 GMT.

15:04:27.635 [debug] Tzdata downloading new data from https://data.iana.org/time-zones/tzdata-latest.tar.gz

15:04:27.727 [debug] Tzdata data downloaded. Release version 2023c.

15:04:28.073 [info] Tzdata has updated the release from 2023b to 2023c

15:04:28.073 [debug] Tzdata deleting ETS table for version 2023b

15:04:28.075 [debug] Tzdata deleting ETS table file for version 2023b

Bob Signs Funding Transaction