aboutsummaryrefslogtreecommitdiff
path: root/contrib/devtools/test_utxo_snapshots.sh
blob: d4c49bf098f289207a0d35a02ec327881b03ce4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/usr/bin/env bash
# Demonstrate the creation and usage of UTXO snapshots.
#
# A server node starts up, IBDs up to a certain height, then generates a UTXO
# snapshot at that point.
#
# The server then downloads more blocks (to create a diff from the snapshot).
#
# We bring a client up, load the UTXO snapshot, and we show the client sync to
# the "network tip" and then start a background validation of the snapshot it
# loaded. We see the background validation chainstate removed after validation
# completes.
#

export LC_ALL=C
set -e

BASE_HEIGHT=${1:-30000}
INCREMENTAL_HEIGHT=20000
FINAL_HEIGHT=$(($BASE_HEIGHT + $INCREMENTAL_HEIGHT))

SERVER_DATADIR="$(pwd)/utxodemo-data-server-$BASE_HEIGHT"
CLIENT_DATADIR="$(pwd)/utxodemo-data-client-$BASE_HEIGHT"
UTXO_DAT_FILE="$(pwd)/utxo.$BASE_HEIGHT.dat"

# Chosen to try to not interfere with any running bitcoind processes.
SERVER_PORT=8633
SERVER_RPC_PORT=8632

CLIENT_PORT=8733
CLIENT_RPC_PORT=8732

SERVER_PORTS="-port=${SERVER_PORT} -rpcport=${SERVER_RPC_PORT}"
CLIENT_PORTS="-port=${CLIENT_PORT} -rpcport=${CLIENT_RPC_PORT}"

# Ensure the client exercises all indexes to test that snapshot use works
# properly with indexes.
ALL_INDEXES="-txindex -coinstatsindex -blockfilterindex=1"

if ! command -v jq >/dev/null ; then
  echo "This script requires jq to parse JSON RPC output. Please install it."
  echo "(e.g. sudo apt install jq)"
  exit 1
fi

DUMP_OUTPUT="dumptxoutset-output-$BASE_HEIGHT.json"

finish() {
  echo
  echo "Killing server and client PIDs ($SERVER_PID, $CLIENT_PID) and cleaning up datadirs"
  echo
  rm -f "$UTXO_DAT_FILE" "$DUMP_OUTPUT"
  rm -rf "$SERVER_DATADIR" "$CLIENT_DATADIR"
  kill -9 "$SERVER_PID" "$CLIENT_PID"
}

trap finish EXIT

# Need to specify these to trick client into accepting server as a peer
# it can IBD from, otherwise the default values prevent IBD from the server node.
EARLY_IBD_FLAGS="-maxtipage=9223372036854775207 -minimumchainwork=0x00"

server_rpc() {
  ./src/bitcoin-cli -rpcport=$SERVER_RPC_PORT -datadir="$SERVER_DATADIR" "$@"
}
client_rpc() {
  ./src/bitcoin-cli -rpcport=$CLIENT_RPC_PORT -datadir="$CLIENT_DATADIR" "$@"
}
server_sleep_til_boot() {
  while ! server_rpc ping >/dev/null 2>&1; do sleep 0.1; done
}
client_sleep_til_boot() {
  while ! client_rpc ping >/dev/null 2>&1; do sleep 0.1; done
}

mkdir -p "$SERVER_DATADIR" "$CLIENT_DATADIR"

echo "Hi, welcome to the assumeutxo demo/test"
echo
echo "We're going to"
echo
echo "  - start up a 'server' node, sync it via mainnet IBD to height ${BASE_HEIGHT}"
echo "  - create a UTXO snapshot at that height"
echo "  - IBD ${INCREMENTAL_HEIGHT} more blocks on top of that"
echo
echo "then we'll demonstrate assumeutxo by "
echo
echo "  - starting another node (the 'client') and loading the snapshot in"
echo "    * first you'll have to modify the code slightly (chainparams) and recompile"
echo "    * don't worry, we'll make it easy"
echo "  - observing the client sync ${INCREMENTAL_HEIGHT} blocks on top of the snapshot from the server"
echo "  - observing the client validate the snapshot chain via background IBD"
echo
read -p "Press [enter] to continue" _

echo
echo "-- Starting the demo. You might want to run the two following commands in"
echo "   separate terminal windows:"
echo
echo "   watch -n0.1 tail -n 30 $SERVER_DATADIR/debug.log"
echo "   watch -n0.1 tail -n 30 $CLIENT_DATADIR/debug.log"
echo
read -p "Press [enter] to continue" _

echo
echo "-- IBDing the blocks (height=$BASE_HEIGHT) required to the server node..."
./src/bitcoind -logthreadnames=1 $SERVER_PORTS \
    -datadir="$SERVER_DATADIR" $EARLY_IBD_FLAGS -stopatheight="$BASE_HEIGHT" >/dev/null

echo
echo "-- Creating snapshot at ~ height $BASE_HEIGHT ($UTXO_DAT_FILE)..."
sleep 2
./src/bitcoind -logthreadnames=1 $SERVER_PORTS \
    -datadir="$SERVER_DATADIR" $EARLY_IBD_FLAGS -connect=0 -listen=0 >/dev/null &
SERVER_PID="$!"

server_sleep_til_boot
server_rpc dumptxoutset "$UTXO_DAT_FILE" > "$DUMP_OUTPUT"
cat "$DUMP_OUTPUT"
kill -9 "$SERVER_PID"

RPC_BASE_HEIGHT=$(jq -r .base_height < "$DUMP_OUTPUT")
RPC_AU=$(jq -r .txoutset_hash < "$DUMP_OUTPUT")
RPC_NCHAINTX=$(jq -r .nchaintx < "$DUMP_OUTPUT")
RPC_BLOCKHASH=$(jq -r .base_hash < "$DUMP_OUTPUT")

# Wait for server to shutdown...
while server_rpc ping >/dev/null 2>&1; do sleep 0.1; done

echo
echo "-- Now: add the following to CMainParams::m_assumeutxo_data"
echo "   in src/kernel/chainparams.cpp, and recompile:"
echo
echo "   {${RPC_BASE_HEIGHT}, AssumeutxoHash{uint256S(\"0x${RPC_AU}\")}, ${RPC_NCHAINTX}, uint256S(\"0x${RPC_BLOCKHASH}\")},"
echo
echo
echo "-- IBDing more blocks to the server node (height=$FINAL_HEIGHT) so there is a diff between snapshot and tip..."
./src/bitcoind $SERVER_PORTS -logthreadnames=1 -datadir="$SERVER_DATADIR" \
    $EARLY_IBD_FLAGS -stopatheight="$FINAL_HEIGHT" >/dev/null

echo
echo "-- Starting the server node to provide blocks to the client node..."
./src/bitcoind $SERVER_PORTS -logthreadnames=1 -debug=net -datadir="$SERVER_DATADIR" \
    $EARLY_IBD_FLAGS -connect=0 -listen=1 >/dev/null &
SERVER_PID="$!"
server_sleep_til_boot

echo
echo "-- Okay, what you're about to see is the client starting up and activating the snapshot."
echo "   I'm going to display the top 14 log lines from the client on top of an RPC called"
echo "   getchainstates, which is like getblockchaininfo but for both the snapshot and "
echo "   background validation chainstates."
echo
echo "   You're going to first see the snapshot chainstate sync to the server's tip, then"
echo "   the background IBD chain kicks in to validate up to the base of the snapshot."
echo
echo "   Once validation of the snapshot is done, you should see log lines indicating"
echo "   that we've deleted the background validation chainstate."
echo
echo "   Once everything completes, exit the watch command with CTRL+C."
echo
read -p "When you're ready for all this, hit [enter]" _

echo
echo "-- Starting the client node to get headers from the server, then load the snapshot..."
./src/bitcoind $CLIENT_PORTS $ALL_INDEXES -logthreadnames=1 -datadir="$CLIENT_DATADIR" \
    -connect=0 -addnode=127.0.0.1:$SERVER_PORT -debug=net $EARLY_IBD_FLAGS >/dev/null &
CLIENT_PID="$!"
client_sleep_til_boot

echo
echo "-- Initial state of the client:"
client_rpc getchainstates

echo
echo "-- Loading UTXO snapshot into client..."
client_rpc loadtxoutset "$UTXO_DAT_FILE"

watch -n 0.3 "( tail -n 14 $CLIENT_DATADIR/debug.log ; echo ; ./src/bitcoin-cli -rpcport=$CLIENT_RPC_PORT -datadir=$CLIENT_DATADIR getchainstates) | cat"

echo
echo "-- Okay, now I'm going to restart the client to make sure that the snapshot chain reloads "
echo "   as the main chain properly..."
echo
echo "   Press CTRL+C after you're satisfied to exit the demo"
echo
read -p "Press [enter] to continue"

while kill -0 "$CLIENT_PID"; do
    sleep 1
done
./src/bitcoind $CLIENT_PORTS $ALL_INDEXES -logthreadnames=1 -datadir="$CLIENT_DATADIR" -connect=0 \
    -addnode=127.0.0.1:$SERVER_PORT "$EARLY_IBD_FLAGS" >/dev/null &
CLIENT_PID="$!"
client_sleep_til_boot

watch -n 0.3 "( tail -n 14 $CLIENT_DATADIR/debug.log ; echo ; ./src/bitcoin-cli -rpcport=$CLIENT_RPC_PORT -datadir=$CLIENT_DATADIR getchainstates) | cat"

echo
echo "-- Done!"