Skip to content

Commit 776b87f

Browse files
authored
Staking support (#39)
* Add staking support # Conflicts: # install.sh * Submission to chain works * Fix network switching * Fix magic numbers
1 parent e1e1172 commit 776b87f

File tree

1 file changed

+234
-8
lines changed

1 file changed

+234
-8
lines changed

install.sh

Lines changed: 234 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ new_user_setup=0
1212
new_network=0
1313
network_status_url=""
1414
network_identifier=""
15+
staking_url=""
1516
container_id=""
17+
latest_key_end_block=""
1618

1719
voi_logo="MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
1820
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
@@ -297,7 +299,7 @@ get_node_status() {
297299
fi
298300
}
299301
300-
set_network_url() {
302+
set_network_status_url() {
301303
case ${VOINETWORK_NETWORK} in
302304
"mainnet")
303305
network_status_url="https://mainnet-api.voi.nodly.io/v2/status"
@@ -314,6 +316,23 @@ set_network_url() {
314316
esac
315317
}
316318
319+
set_staking_url() {
320+
case ${VOINETWORK_NETWORK} in
321+
"mainnet")
322+
staking_url="https://mainnet-idx.nautilus.sh/v1/scs/accounts"
323+
;;
324+
"betanet")
325+
staking_url="https://betanet-idx.nautilus.sh/v1/scs/accounts"
326+
;;
327+
"testnet")
328+
staking_url="https://arc72-idx.nautilus.sh/v1/scs/accounts"
329+
;;
330+
*)
331+
abort "Unable to find staking URL. Exiting the program."
332+
;;
333+
esac
334+
}
335+
317336
set_network_identifier() {
318337
case ${VOINETWORK_NETWORK} in
319338
"mainnet")
@@ -331,9 +350,26 @@ set_network_identifier() {
331350
esac
332351
}
333352
353+
get_network_identifier() {
354+
case $1 in
355+
"mainnet")
356+
echo "voimain-v1.0"
357+
;;
358+
"betanet")
359+
echo "voibeta-v1.0"
360+
;;
361+
"testnet")
362+
echo "voitest-v1"
363+
;;
364+
*)
365+
echo "voitest-v1"
366+
;;
367+
esac
368+
}
369+
334370
catchup_node() {
335371
display_banner "Catching up with the network... This might take some time, and numbers might briefly increase"
336-
set_network_url
372+
set_network_status_url
337373
338374
get_node_status
339375
@@ -484,11 +520,20 @@ get_last_committed_block() {
484520
}
485521
486522
get_account_addresses() {
487-
if execute_sudo "test ! -f \"/var/lib/voi/algod/data/${network_identifier}/accountList.json\""; then
523+
local network_identifier_override
524+
local local_network_identifier
525+
network_identifier_override=$1
526+
local_network_identifier=${network_identifier}
527+
528+
if [[ -n ${network_identifier_override} ]]; then
529+
local_network_identifier=${network_identifier_override}
530+
fi
531+
532+
if execute_sudo "test ! -f \"/var/lib/voi/algod/data/${local_network_identifier}/accountList.json\""; then
488533
abort "Account list not found. Exiting the program."
489534
fi
490535
491-
accounts_json=$(execute_sudo "cat /var/lib/voi/algod/data/${network_identifier}/accountList.json")
536+
accounts_json=$(execute_sudo "cat /var/lib/voi/algod/data/${local_network_identifier}/accountList.json")
492537
number_of_accounts=$(echo "${accounts_json}" | jq '.Accounts | length')
493538
494539
if [[ $number_of_accounts -eq 0 ]]; then
@@ -509,13 +554,16 @@ generate_new_key() {
509554
end_block=$((start_block + 2000000))
510555
expiration_date=$(get_participation_expiration_eta "${end_block}" "${start_block}")
511556
address=$1
512-
echo "Generating participation key for account ${address} with start block ${start_block} and end block ${end_block}"
557+
latest_key_end_block=${end_block}
558+
echo "Generating participation key for ${address} with start block ${start_block} and end block ${end_block}"
513559
echo "New key is expected to be valid until: ${expiration_date}"
514560
execute_interactive_docker_command "/node/bin/goal account addpartkey -a ${address} --roundFirstValid ${start_block} --roundLastValid ${end_block}"
515561
}
516562
517563
ensure_accounts_are_offline() {
518-
account_addresses=$(get_account_addresses)
564+
local local_network_identifier
565+
local_network_identifier=$(get_network_identifier "$1")
566+
account_addresses=$(get_account_addresses "${local_network_identifier}")
519567
520568
if [[ -z ${account_addresses} ]]; then
521569
return
@@ -545,6 +593,48 @@ ensure_accounts_are_offline() {
545593
done
546594
}
547595
596+
get_participation_key_id_from_vote_key() {
597+
local vote_key
598+
local output
599+
vote_key=$1
600+
601+
output=$(execute_docker_command "/node/bin/goal account partkeyinfo")
602+
echo "${output}" | grep -B 10 "${vote_key}" | grep 'Participation ID:' | head -n 1 | cut -d ':' -f2- | xargs
603+
}
604+
605+
get_most_recent_participation_key_details() {
606+
local address=$1
607+
local output
608+
609+
output=$(execute_docker_command "/node/bin/goal account partkeyinfo")
610+
611+
local first_round
612+
first_round=$(echo "$output" | grep -A 4 -B 5 "${latest_key_end_block}" | grep 'First round:' | head -n 1 | cut -d ':' -f2- | xargs)
613+
local last_round
614+
last_round=$(echo "$output" | grep -A 4 -B 5 "${latest_key_end_block}" | grep 'Last round:' | head -n 1 | cut -d ':' -f2- | xargs)
615+
local key_dilution
616+
key_dilution=$(echo "$output" | grep -A 4 -B 5 "${latest_key_end_block}" | grep 'Key dilution:' | head -n 1 | cut -d ':' -f2- | xargs)
617+
local selection_key
618+
selection_key=$(echo "$output" | grep -A 4 -B 5 "${latest_key_end_block}" | grep 'Selection key:' | head -n 1 | cut -d ':' -f2- | xargs)
619+
local voting_key
620+
voting_key=$(echo "$output" | grep -A 4 -B 5 "${latest_key_end_block}" | grep 'Voting key:' | head -n 1 | cut -d ':' -f2- | xargs)
621+
local stateproof_key
622+
# shellcheck disable=SC2034
623+
stateproof_key=$(echo "$output" | grep -A 4 -B 5 "${latest_key_end_block}" | grep 'State proof key:' | head -n 1 | cut -d ':' -f2- | xargs)
624+
625+
key_info_json=$(jq --null-input --arg address "$address" --arg first_round "$first_round" --arg last_round "$last_round" --arg key_dilution "$key_dilution" --arg selection_key "$selection_key" --arg voting_key "$voting_key" --arg stateproof_key "$stateproof_key" '{
626+
"address": $address,
627+
"first_round": $first_round,
628+
"last_valid": $last_round,
629+
"key_dilution": $key_dilution,
630+
"selection_key": $selection_key,
631+
"voting_key": $voting_key,
632+
"stateproof_key": $stateproof_key
633+
}')
634+
635+
echo "${key_info_json}"
636+
}
637+
548638
generate_participation_key() {
549639
display_banner "Generating/Updating participation key"
550640
@@ -622,7 +712,9 @@ generate_participation_key() {
622712
echo "You will be asked to enter your password to activate the new key."
623713
624714
execute_interactive_docker_command "/node/bin/goal account renewpartkey -a ${account} --roundLastValid ${end_block}"
625-
execute_interactive_docker_command "/node/bin/goal account deletepartkey --partkeyid ${current_key_id}"
715+
if [[ -n ${current_key_id} ]]; then
716+
execute_interactive_docker_command "/node/bin/goal account deletepartkey --partkeyid ${current_key_id}"
717+
fi
626718
else
627719
local existing_expiration_date
628720
existing_expiration_date=$(get_participation_expiration_eta "${active_key_last_valid_round}" "${last_committed_block}")
@@ -738,6 +830,136 @@ joined_network_instructions() {
738830
fi
739831
}
740832

833+
check_staking_accounts() {
834+
account_addresses=$(get_account_addresses)
835+
if [[ $? -eq 1 ]]; then
836+
return
837+
else
838+
set_staking_url
839+
840+
for account in ${account_addresses}; do
841+
local staking_endpoint
842+
local response
843+
local http_code
844+
local json_response
845+
846+
staking_endpoint="${staking_url}?owner=${account}&deleted=0"
847+
response=$(curl -s --max-time 5 -w "%{http_code}" "${staking_endpoint}")
848+
http_code=$(echo "${response}" | awk '{print substr($0, length($0) - 2)}')
849+
json_response=$(echo "${response}" | awk '{print substr($0, 1, length($0) - 3)}')
850+
851+
if [[ "${http_code}" -eq 200 ]]; then
852+
accounts_length=$(jq '.accounts | length' <<< "${json_response}")
853+
if [[ "${accounts_length}" -gt 0 ]]; then
854+
display_banner "Staking contract detected"
855+
856+
echo "Staking contract has been detected for owner ${account}"
857+
echo ""
858+
859+
local balance
860+
balance=$(get_account_balance "${account_addr}")
861+
862+
if [[ ${balance} -lt "1000" ]]; then
863+
echo "Balance is below 1,000 microVoi. Skipping staking account setup."
864+
continue
865+
fi
866+
867+
local staking_accounts
868+
staking_accounts=$(jq -c '.accounts[]' <<< "${json_response}")
869+
for staking_account in ${staking_accounts}; do
870+
local last_committed_block
871+
local contract_address
872+
local contract_id
873+
local part_vote_k
874+
local part_vote_lst
875+
local participation_key_id
876+
877+
contract_id=$(jq -r '.contractId' <<< "${staking_account}")
878+
contract_address=$(jq -r '.contractAddress' <<< "${staking_account}")
879+
last_committed_block=$(get_last_committed_block)
880+
881+
part_vote_k=$(jq -r '.part_vote_k' <<< "${staking_account}")
882+
part_vote_lst=$(jq -r '.part_vote_lst' <<< "${staking_account}")
883+
884+
# shellcheck disable=SC2046
885+
participation_key_id=$(get_participation_key_id_from_vote_key "${part_vote_k}")
886+
887+
if [[ "${part_vote_k}" == "null" || $((part_vote_lst-last_committed_block)) -le 417104 || -z "${participation_key_id}" ]]; then
888+
889+
if [[ "${part_vote_k}" == "null" ]]; then
890+
echo "No staking participation key detected."
891+
elif [[ -z ${participation_key_id} ]]; then
892+
echo "Registered staking participation key is not installed locally."
893+
else
894+
local existing_expiration_date
895+
existing_expiration_date=$(get_participation_expiration_eta "${part_vote_lst}" "${last_committed_block}")
896+
897+
echo "Current participation key for account ${account} is expected to expire at: ${existing_expiration_date}"
898+
echo "Currently the network is at block: ${last_committed_block}"
899+
echo "Current participation key expires at block: ${part_vote_lst}"
900+
echo ""
901+
echo "This is below the required threshold of 417,104 blocks / ~14 days."
902+
fi
903+
904+
echo ""
905+
echo "Generating new key for owner ${account} and contract address ${contract_address}"
906+
907+
generate_new_key "${contract_address}"
908+
909+
local partkey_info
910+
local voting_key
911+
local selection_key
912+
local first_round
913+
local last_round
914+
local key_dilution
915+
local stateproof_key
916+
917+
partkey_info=$(get_most_recent_participation_key_details "${contract_address}")
918+
voting_key=$(jq -r '.voting_key' <<< "${partkey_info}")
919+
selection_key=$(jq -r '.selection_key' <<< "${partkey_info}")
920+
first_round=$(jq -r '.first_round' <<< "${partkey_info}")
921+
last_round=$(jq -r '.last_valid' <<< "${partkey_info}")
922+
key_dilution=$(jq -r '.key_dilution' <<< "${partkey_info}")
923+
stateproof_key=$(jq -r '.stateproof_key' <<< "${partkey_info}")
924+
925+
execute_interactive_docker_command "/node/bin/goal clerk send -a 1000 -f ${account} -t ${contract_address} --out=/tmp/payment.txn"
926+
execute_interactive_docker_command "/node/bin/goal app call --app-id ${contract_id} --from ${account} --app-arg 'b64:zSTeiA==' --app-arg 'b64:${voting_key}' --app-arg 'b64:${selection_key}' --app-arg 'int:${first_round}' --app-arg 'int:${last_round}' --app-arg 'int:${key_dilution}' --app-arg 'b64:${stateproof_key}' --out=/tmp/app_call.txn"
927+
928+
execute_docker_command "cat /tmp/{payment,app_call}.txn > /tmp/combined.txn"
929+
execute_interactive_docker_command "/node/bin/goal clerk group -i /tmp/combined.txn -o /tmp/grouped.txn > /dev/null"
930+
execute_interactive_docker_command "/node/bin/goal clerk split -i /tmp/grouped.txn -o /tmp/split.txn > /dev/null"
931+
execute_interactive_docker_command "/node/bin/goal clerk sign -i /tmp/split-0.txn -o /tmp/signed-0.txn"
932+
execute_interactive_docker_command "/node/bin/goal clerk sign -i /tmp/split-1.txn -o /tmp/signed-1.txn"
933+
execute_docker_command "cat /tmp/signed-{0,1}.txn > /tmp/signed-combined.txn"
934+
execute_interactive_docker_command "/node/bin/goal clerk rawsend -f /tmp/signed-combined.txn"
935+
936+
if [[ -n ${participation_key_id} ]]; then
937+
execute_interactive_docker_command "/node/bin/goal account deletepartkey --partkeyid ${participation_key_id}"
938+
fi
939+
else
940+
local existing_expiration_date
941+
existing_expiration_date=$(get_participation_expiration_eta "${part_vote_lst}" "${last_committed_block}")
942+
943+
echo "Current staking participation key for owner ${account}, and"
944+
echo "contract address ${contract_address} is expected to expire at: ${existing_expiration_date}"
945+
echo "This is above the required threshold of 417,104 blocks / ~14 days."
946+
echo "No new staking participation key will be generated."
947+
echo ""
948+
fi
949+
done
950+
951+
echo ""
952+
echo "${bold}To learn how to use your staking contract visit: https://staking.voi.network${normal}"
953+
else
954+
display_banner "No staking contract detected"
955+
956+
echo "No staking contracts found for owner ${account}. To learn more visit: https://staking.voi.network"
957+
fi
958+
fi
959+
done
960+
fi
961+
}
962+
741963
change_account_online_status() {
742964
local account
743965
account=$1
@@ -770,6 +992,8 @@ join_as_new_user() {
770992

771993
account_status=$(execute_docker_command "/node/bin/goal account dump -a ${account}" | jq -r .onl)
772994

995+
check_staking_accounts
996+
773997
## This step is late in the process and does require a restart of the service to take effect.
774998
## Container ID from verify_node_running will have to be re-fetched if any use of the node is to be done after this point.
775999
## Intentionally not doing this here to avoid confusion.
@@ -1171,7 +1395,7 @@ check_minimum_requirements
11711395

11721396
if [[ ${new_network} -eq 1 ]]; then
11731397
get_container_id
1174-
ensure_accounts_are_offline
1398+
ensure_accounts_are_offline "${existing_network}"
11751399
fi
11761400

11771401
get_telemetry_name
@@ -1322,6 +1546,8 @@ else
13221546
join_as_new_user
13231547
fi
13241548

1549+
check_staking_accounts
1550+
13251551
migrate_host_based_voi_setup
13261552

13271553
display_banner "Welcome to Voi!"

0 commit comments

Comments
 (0)