RPC Scavenger Hunt
1.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.