bdl 2024: solving the challenges

1. RPC Scavenger Hunt

Only bitcoin-cli commands and CLI tools allowed:

1.1. Hash of block 654,321

#!/bin/sh

bitcoin-cli getblockhash 654321

000000000000000000058452bbe379ad4364fe8fda68c45e299979b492858095

1.2. Verify signature

address: 1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa
message: 1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa
signature: HCsBcgB+Wcm8kOGMH8IpNeg0H4gjCrlqwDf/GlSXphZGBYxm0QkKEPhh9DTJRp2IDNUhVr0FhP9qCqo2W0recNM=

#!/bin/sh

ADDRESS="1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa"
MESSAGE="1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa"
SIGNATURE="HCsBcgB+Wcm8kOGMH8IpNeg0H4gjCrlqwDf/GlSXphZGBYxm0QkKEPhh9DTJRp2IDNUhVr0FhP9qCqo2W0recNM="

bitcoin-cli verifymessage $ADDRESS $SIGNATURE $MESSAGE

false

1.3. UTXOs created on 123,456

#!/bin/sh

# simple & native way:
# bitcoin-cli getblockstats 123456 | jq .outs

# ad hoc way
# get hash of block 123,456
hash=$(bitcoin-cli getblockhash 123456)

# get block's txid array
txs=$(bitcoin-cli getblock $hash | jq -r .tx[])

# for each txid, get the raw transaction, decode it, and get the vout lenght
n_utxos=0
for tx in $txs
do
    tx_hex=$(bitcoin-cli getrawtransaction $tx)

    tx_utxos=$(bitcoin-cli decoderawtransaction $tx_hex | jq '.vout | length')

    n_utxos=$((n_utxos + tx_utxos))
done

echo $n_utxos

24

1.4. Taproot address from XPUB

#!/bin/sh

# Using descriptors, compute the taproot address at index 100 derived from this extended public key:
#   `xpub6Cx5tvq6nACSLJdra1A6WjqTo1SgeUZRFqsX5ysEtVBMwhCCRa4kfgFqaT2o1kwL3esB1PsYr3CUdfRZYfLHJunNWUABKftK2NjHUtzDms2`

# derivation path primer:
# master_key/purpose’/coin_type’/account’/change/address_index

# taproot uses this derviation path: m/86h/0h/0h
# the 101st (index starts from 0) taproot address will have this path: m/86h/0h/0h/0/100

XPUB="xpub6Cx5tvq6nACSLJdra1A6WjqTo1SgeUZRFqsX5ysEtVBMwhCCRa4kfgFqaT2o1kwL3esB1PsYr3CUdfRZYfLHJunNWUABKftK2NjHUtzDms2"

DESCRIPTOR=$(bitcoin-cli getdescriptorinfo "tr($XPUB/100)" | jq -r .descriptor)

TAPROOT_100=$(bitcoin-cli deriveaddresses $DESCRIPTOR | jq -r .[0])

echo $TAPROOT_100

bc1p3yrtpvv6czx63h2sxwmeep8q98h94w4288fc4cvrkqephalydfgszgacf9

1.5. 1-of-4 P2SH from the pubkeys in the inputs of transaction

#!/bin/sh

# Create a 1-of-4 P2SH multisig address from the public keys in the four inputs of this tx:
#   `37d966a263350fe747f1c606b159987545844a493dd38d84b070027a895c4517`

# get the 4 public keys
TXID="37d966a263350fe747f1c606b159987545844a493dd38d84b070027a895c4517"
TX=$(bitcoin-cli getrawtransaction $TXID 1)

PUBKEY_0=$(echo $TX | jq .vin[0].txinwitness[1])
PUBKEY_1=$(echo $TX | jq .vin[1].txinwitness[1])
PUBKEY_2=$(echo $TX | jq .vin[2].txinwitness[1])
PUBKEY_3=$(echo $TX | jq .vin[3].txinwitness[1])

# make a 1-of-4 p2sh multisig address from these keys
P2SH_ADDRESS=$(bitcoin-cli createmultisig 1 "[$PUBKEY_0, $PUBKEY_1, $PUBKEY_2, $PUBKEY_3]" "legacy" | jq -r .address)

echo $P2SH_ADDRESS

3GyWg1CCD3RDpbwCbuk9TTRQptkRfczDz8

1.6. Transaction on 257,343 that spends the coinbase of 256,128

#!/bin/sh

# Which tx in block 257,343 spends the coinbase output of block 256,128?
BLOCKHASH_CREATED=$(bitcoin-cli getblockhash 256128)

# coinbase output of 256,128 has the outpoint 611c5a0972d28e421a2308cb2a2adb8f369bb003b96eb04a3ec781bf295b74bc:0
COINBASE_TXID=$(bitcoin-cli getblock $BLOCKHASH_CREATED | jq -r .tx[0])
COINBASE_VOUT=0

# iterate over transaction inputs and check if txid and vout are a match for the coinbase from 256,128
BLOCKHASH_SPENT=$(bitcoin-cli getblockhash 257343)

# get transactions
TRANSACTIONS=$(bitcoin-cli getblock $BLOCKHASH_SPENT | jq -r .tx[])

# check each input on each transaction for a spend of the coinbase from 256,128
for TXID in $TRANSACTIONS
do
    readarray -t INPUTS < <(bitcoin-cli getrawtransaction $TXID 1 | jq -c .vin[])
    
    for INPUT in "${INPUTS[@]}"
    do
        CANDIDATE_PREVOUT=$(echo "$INPUT" | jq -r .txid)
        CANDIDATE_VOUT=$(echo "$INPUT" | jq -r .vout)
        if [[ $COINBASE_TXID == $CANDIDATE_PREVOUT && $COINBASE_VOUT == $CANDIDATE_VOUT ]]
        then
            echo $TXID
            exit 0
        fi
    done
done

c54714cb1373c2e3725261fe201f267280e21350bdf2df505da8483a6a4805fc

1.7. Address of only UTXO left from 123,321

#!/bin/sh

# Only a single output remains unspent from block 123,321. What address was it sent to?

BLOCKHASH=$(bitcoin-cli getblockhash 123321)
readarray -t TRANSACTIONS < <(bitcoin-cli getblock $BLOCKHASH | jq -r .tx[])

# iterate over all outputs on the block, call `gettxout` and return address
for TXID in "${TRANSACTIONS[@]}"
do  
    TX=$(bitcoin-cli getrawtransaction "$TXID" 1 | jq -c .)    # Added -c here to get compact output
    readarray -t OUTPUTS < <(echo "$TX" | jq -c .vout[])

    for OUTPUT in "${OUTPUTS[@]}"
    do
        # check if this output is in the UTXO set
        # null if not so
        VOUT_INDEX=$(echo $OUTPUT | jq -r .n)
        UNSPENT=$(bitcoin-cli gettxout $TXID $VOUT_INDEX)

        if [[ $UNSPENT ]]
        then
            ADDRESS=$(echo $UNSPENT | jq -r .scriptPubKey.address)
            echo $ADDRESS
        fi
    done
done

1FPDNNmgwEnKuF7GQzSqUcVQdzSRhz4pgX

1.8. What pubkey signed an input

#!/bin/sh

# Which public key signed input 0 in this tx `e5969add849689854ac7f28e45628b89f7454b83e9699e551ce14b6f90c86163`?

TXID="e5969add849689854ac7f28e45628b89f7454b83e9699e551ce14b6f90c86163"
TRANSACTION=$(bitcoin-cli getrawtransaction $TXID 1)

# get the pubkey from witness' TLV
# OF_IF OP_PUSHBYTES_33 <PUBKEY> ...
PUBKEY=$(echo $TRANSACTION | jq -r .vin[0].txinwitness[2][4:70])

echo $PUBKEY

025d524ac7ec6501d018d322334f142c7c11aa24b9cffec03161eca35a1e32a71f

2. Recover Wallet State

We're given a descriptor and must compute the wallet balance on a custom signet.

To do this we have to implement secp256k1 operations for pubkey derivation and BIP32 for hierarchical deterministic key generation.


3. Send a Multisig Transaction


4. Connect to the network


Follow the White Rabbit