Skip to content

Commit 1cdfe9b

Browse files
committed
feature(end-to-end): adapt tests tu support protocol configurations on chain
1 parent c8ff793 commit 1cdfe9b

11 files changed

Lines changed: 656 additions & 46 deletions

File tree

mithril-test-lab/cardano-devnet/devnet-mkfiles.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ SCRIPT_DIRECTORY=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
3737
# Generate the Mithril era scripts
3838
. ${SCRIPT_DIRECTORY}/mkfiles/mkfiles-mithril-era.sh
3939

40+
# Generate the protocol configuration scripts
41+
. ${SCRIPT_DIRECTORY}/mkfiles/mkfiles-protocol-configuration.sh
42+
4043
# Generate the Mithril payment scripts
4144
. ${SCRIPT_DIRECTORY}/mkfiles/mkfiles-mithril-payment.sh
4245

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Create Mithril protocol configuration keypair and address
2+
ADDR=protocol-config
3+
4+
## Payment address keys
5+
$CARDANO_CLI "$CARDANO_CLI_ERA" address key-gen \
6+
--verification-key-file addresses/${ADDR}.vkey \
7+
--signing-key-file addresses/${ADDR}.skey
8+
9+
## Payment addresses
10+
$CARDANO_CLI "$CARDANO_CLI_ERA" address build \
11+
--payment-verification-key-file addresses/${ADDR}.vkey \
12+
--testnet-magic "${NETWORK_MAGIC}" \
13+
--out-file addresses/${ADDR}.addr
14+
15+
## Write datums for protocol configuration address
16+
N=1
17+
SCRIPT_TX_VALUE=7200000
18+
AMOUNT_TRANSFERRED=$(( SCRIPT_TX_VALUE * 10 ))
19+
MITHRIL_PROTOCOL_CONFIG_ERROR_FILE=./protocol-config-error
20+
cat >> protocol-configuration.sh <<EOF
21+
#!/usr/bin/env bash
22+
set -e
23+
24+
# Wait for a number of blocks has elapsed
25+
function wait_for_elapsed_blocks {
26+
CARDANO_BLOCK_OFFSET=\$1
27+
CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX=30
28+
CARDANO_NEXT_BLOCK_WAIT_ROUNDS=1
29+
CARDANO_NEXT_BLOCK_WAIT_ROUND_DELAY=2
30+
CURRENT_CARDANO_BLOCK=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\
31+
--testnet-magic ${NETWORK_MAGIC} \\
32+
| jq -r '.block')
33+
while true
34+
do
35+
CARDANO_BLOCK_TARGET=\$(( \${CURRENT_CARDANO_BLOCK} + \${CARDANO_BLOCK_OFFSET} ))
36+
CARDANO_BLOCK=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\
37+
--testnet-magic ${NETWORK_MAGIC} \\
38+
| jq -r '.block')
39+
if [ \$CARDANO_BLOCK -lt \$CARDANO_BLOCK_TARGET ] ; then
40+
echo ">>>> Cardano target block not reached yet... [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]"
41+
sleep \$CARDANO_NEXT_BLOCK_WAIT_ROUND_DELAY
42+
else
43+
echo ">>>> Cardano target block is reached [current: \$CARDANO_BLOCK, target: \$CARDANO_BLOCK_TARGET] [attempt \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS]"
44+
break
45+
fi
46+
CARDANO_NEXT_BLOCK_WAIT_ROUNDS=\$(( \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS + 1 ))
47+
if [ "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS" -gt "\$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX" ] ; then
48+
echo ">>>> Timeout: Cardano target block was not reached within \$CARDANO_NEXT_BLOCK_WAIT_ROUNDS_MAX attempts"
49+
exit 1
50+
fi
51+
done
52+
}
53+
54+
# Send funds to protocol configuration address
55+
function send_funds_to_protocol_config_address {
56+
# Remove if exists previous error file
57+
rm -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE}
58+
59+
# Get current Cardano era
60+
CURRENT_CARDANO_ERA=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\
61+
--testnet-magic ${NETWORK_MAGIC} \\
62+
| jq -r '.era |= ascii_downcase | .era')
63+
echo ">>>> Current Cardano Era: \${CURRENT_CARDANO_ERA}"
64+
65+
# Get current Cardano block
66+
CURRENT_CARDANO_BLOCK=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI $CARDANO_CLI_ERA query tip \\
67+
--testnet-magic ${NETWORK_MAGIC} \\
68+
| jq -r '.block')
69+
echo ">>>> Current Cardano Block: \${CURRENT_CARDANO_BLOCK}"
70+
71+
# Send funds to protocol configuration address
72+
## Get the UTxO of utxo${N}
73+
TX_IN=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\
74+
--testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/utxo${N}.addr) --out-file /dev/stdout \\
75+
| jq -r 'to_entries | [last] | .[0].key')
76+
77+
## Build the transaction
78+
CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\
79+
--tx-in \${TX_IN} \\
80+
--tx-out \$(cat addresses/${ADDR}.addr)+${AMOUNT_TRANSFERRED} \\
81+
--change-address \$(cat addresses/utxo${N}.addr) \\
82+
--testnet-magic ${NETWORK_MAGIC} \\
83+
--invalid-hereafter 100000 \\
84+
--out-file node-pool${N}/tx/tx${N}-protocol-config-funds.txbody
85+
86+
## Sign the transaction
87+
CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\
88+
--signing-key-file addresses/utxo${N}.skey \\
89+
--testnet-magic ${NETWORK_MAGIC} \\
90+
--tx-body-file node-pool${N}/tx/tx${N}-protocol-config-funds.txbody \\
91+
--out-file node-pool${N}/tx/tx${N}-protocol-config-funds.tx
92+
93+
## Submit the transaction
94+
CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\
95+
--tx-file node-pool${N}/tx/tx${N}-protocol-config-funds.tx \\
96+
--testnet-magic ${NETWORK_MAGIC}
97+
98+
## Compute the submitted transaction id
99+
TX_ID_SUBMITTED=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction txid --tx-file node-pool${N}/tx/tx${N}-protocol-config-funds.tx)
100+
if [[ "\${TX_ID_SUBMITTED}" =~ txhash ]]; then
101+
TX_ID_SUBMITTED=\$(echo \$TX_ID_SUBMITTED | jq -r '.txhash')
102+
fi
103+
104+
## Wait at least for 10 blocks so that the transaction is confirmed
105+
wait_for_elapsed_blocks 10
106+
107+
## Wait for all pool nodes to see the new transaction
108+
for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do
109+
TOTAL_UTXOS_FOR_TX_ID=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\
110+
--testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\
111+
| jq '. | with_entries(select(.key | startswith("${TX_ID_SUBMITTED}"))) | length')
112+
echo ">>>>>> Protocol configuration address funds retrieved on node-pool\${i}: \${TOTAL_UTXOS_FOR_TX_ID}"
113+
if [ "\${TOTAL_UTXOS_FOR_TX_ID}" == "0" ]; then
114+
touch ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE}
115+
break
116+
fi
117+
done
118+
}
119+
120+
# Try to send funds to protocol configuration address
121+
function try_send_funds_to_protocol_config_address {
122+
SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX=10
123+
SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=1
124+
SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY=2
125+
while true
126+
do
127+
send_funds_to_protocol_config_address
128+
if [ -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} ]; then
129+
echo ">>>> Funds not transferred successfully to protocol configuration address, a rollback has happened [attempt \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!"
130+
sleep \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY
131+
else
132+
echo ">>>> Funds transferred successfully to protocol configuration address [attempt \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!"
133+
break
134+
fi
135+
SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=\$(( \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS + 1 ))
136+
if [ "\$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS" -gt "\$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX" ] ; then
137+
echo ">>>> Timeout: Funds were not transferred successfully to protocol configuration address within \$SEND_FUNDS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX attempts"
138+
exit 1
139+
fi
140+
done
141+
}
142+
143+
# Write datums for protocol configuration address
144+
function write_datums_for_protocol_config_address {
145+
# Remove if exists previous error file
146+
rm -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE}
147+
148+
# Fetch transactions from UTxOs of the protocol configuration address
149+
TX_IN_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\
150+
--testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\
151+
| jq -r 'to_entries | map({utxo: .key} + .value) | . [] | select(.inlineDatum | . != null and . != "") | .utxo')
152+
TX_IN_NO_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\
153+
--testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\
154+
| jq -r 'to_entries | [last] | .[0].key')
155+
156+
## Build the transaction
157+
if [ "\${TX_IN_DATUM}" == "" ]; then
158+
CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\
159+
--tx-in \${TX_IN_NO_DATUM} \\
160+
--tx-out \$(cat addresses/${ADDR}.addr)+${SCRIPT_TX_VALUE} \\
161+
--tx-out-inline-datum-file \${PROTOCOL_CONFIG_DATUM_FILE} \\
162+
--change-address \$(cat addresses/${ADDR}.addr) \\
163+
--testnet-magic ${NETWORK_MAGIC} \\
164+
--invalid-hereafter 100000 \\
165+
--out-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.txbody
166+
else
167+
CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction build \\
168+
--tx-in \${TX_IN_DATUM} \\
169+
--tx-in \${TX_IN_NO_DATUM} \\
170+
--tx-out \$(cat addresses/${ADDR}.addr)+${SCRIPT_TX_VALUE} \\
171+
--tx-out-inline-datum-file \${PROTOCOL_CONFIG_DATUM_FILE} \\
172+
--change-address \$(cat addresses/${ADDR}.addr) \\
173+
--testnet-magic ${NETWORK_MAGIC} \\
174+
--invalid-hereafter 100000 \\
175+
--out-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.txbody
176+
fi
177+
178+
## Sign the transaction
179+
CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction sign \\
180+
--signing-key-file addresses/${ADDR}.skey \\
181+
--testnet-magic ${NETWORK_MAGIC} \\
182+
--tx-body-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.txbody \\
183+
--out-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.tx
184+
185+
## Submit the transaction
186+
CARDANO_NODE_SOCKET_PATH=node-pool${N}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} transaction submit \\
187+
--tx-file node-pool${N}/tx/tx${N}-protocol-configuration-datum.tx \\
188+
--testnet-magic ${NETWORK_MAGIC}
189+
190+
## Wait at least for 10 blocks so that the transaction is confirmed
191+
wait_for_elapsed_blocks 10
192+
193+
## Wait for all pool nodes to see the new protocol configuration datum
194+
for (( i=1; i<=${NUM_POOL_NODES}; i++ )); do
195+
INLINE_DATUM=\$(CARDANO_NODE_SOCKET_PATH=node-pool\${i}/ipc/node.sock $CARDANO_CLI \${CURRENT_CARDANO_ERA} query utxo \\
196+
--testnet-magic ${NETWORK_MAGIC} --address \$(cat addresses/${ADDR}.addr) --out-file /dev/stdout \\
197+
| jq -r '. [] | select(.inlineDatum | . != null and . != "") | .inlineDatum.fields[].bytes' | xxd -r -p | jq)
198+
echo ">>>>>> Protocol configuration address inline datum retrieved on node-pool\${i}: \${INLINE_DATUM}"
199+
if [ "\${INLINE_DATUM}" == "" ]; then
200+
touch ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE}
201+
break
202+
fi
203+
done
204+
}
205+
206+
# Try to write datums for protocol configuration address
207+
function try_write_datums_for_protocol_config_address {
208+
WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX=10
209+
WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=1
210+
WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY=2
211+
while true
212+
do
213+
write_datums_for_protocol_config_address
214+
if [ -f ${MITHRIL_PROTOCOL_CONFIG_ERROR_FILE} ]; then
215+
echo ">>>> Datums not written successfully for protocol configuration address, a rollback has happened [attempt \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!"
216+
sleep \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUND_DELAY
217+
else
218+
echo ">>>> Datums successfully written for protocol configuration address [attempt \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS]!"
219+
break
220+
fi
221+
WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS=\$(( \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS + 1 ))
222+
if [ "\$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS" -gt "\$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX" ] ; then
223+
echo ">>>> Timeout: Datums were not written successfully for protocol configuration address within \$WRITE_DATUMS_PROTOCOL_CONFIG_ADDRESS_WAIT_ROUNDS_MAX attempts"
224+
exit 1
225+
fi
226+
done
227+
}
228+
229+
# Try to send funds to protocol configuration address
230+
try_send_funds_to_protocol_config_address
231+
232+
# Try to write datums for protocol configuration address
233+
try_write_datums_for_protocol_config_address
234+
235+
EOF
236+
237+
chmod u+x protocol-configuration.sh

mithril-test-lab/mithril-end-to-end/src/devnet/runner.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,20 @@ impl Devnet {
199199
.with_context(|| "Failed to read mithril era marker address file")
200200
}
201201

202+
pub fn protocol_configuration_marker_address_path(&self) -> PathBuf {
203+
self.artifacts_dir.join("addresses").join("protocol-config.addr")
204+
}
205+
206+
pub fn protocol_configuration_marker_address(&self) -> StdResult<String> {
207+
let mut marker_address_file =
208+
File::open(self.protocol_configuration_marker_address_path())?;
209+
let mut marker_address_buffer = Vec::new();
210+
marker_address_file.read_to_end(&mut marker_address_buffer)?;
211+
212+
String::from_utf8(marker_address_buffer)
213+
.with_context(|| "Failed to read protocol configuration marker address file")
214+
}
215+
202216
pub fn mithril_payments_transaction_hashes_path(&self) -> PathBuf {
203217
self.artifacts_dir.join("transaction-hashes.txt")
204218
}
@@ -371,6 +385,31 @@ impl Devnet {
371385
}
372386
}
373387

388+
pub async fn write_protocol_configuration_markers(&self, target_path: &Path) -> StdResult<()> {
389+
let run_script = "protocol-configuration.sh";
390+
let run_script_path = self.artifacts_dir.join(run_script);
391+
let mut run_command = self.build_command(&run_script_path)?;
392+
run_command.env("PROTOCOL_CONFIG_DATUM_FILE", target_path.to_str().unwrap());
393+
394+
info!("Writing protocol configuration markers on chain"; "script" => &run_script_path.display());
395+
396+
let status = run_command
397+
.spawn()
398+
.with_context(|| "Failed to write protocol configuration markers on chain")?
399+
.wait_forwarding_output_to_slog_scope(run_script)
400+
.await
401+
.with_context(|| "Error while writing protocol configuration markers on chain")?;
402+
match status.code() {
403+
Some(0) => Ok(()),
404+
Some(code) => Err(anyhow!(RetryableDevnetError(format!(
405+
"Write protocol configuration markers on chain exited with status code: {code}"
406+
)))),
407+
None => Err(anyhow!(
408+
"Write protocol configuration markers on chain terminated by signal"
409+
)),
410+
}
411+
}
412+
374413
pub async fn transfer_funds(&self) -> StdResult<()> {
375414
let run_script = "payment-mithril.sh";
376415
let run_script_path = self.artifacts_dir.join(run_script);

0 commit comments

Comments
 (0)