diff --git a/cardano/testnet/00_common.sh b/cardano/testnet/00_common.sh index 1a7a590..01b729c 100755 --- a/cardano/testnet/00_common.sh +++ b/cardano/testnet/00_common.sh @@ -131,18 +131,18 @@ cropTxOutput="yes" #yes/no to crop the unsigned/signed txfile outputs on transa # ############################################################################################################################## - #------------------------------------------------------- -#DisplayMajorErrorMessage +#Display MajorError Message directly to the error output majorError() { -echo -e "\e[97m\n" > $(tty) -echo -e " _ ._ _ , _ ._\n (_ ' ( \` )_ .__)\n ( ( ( ) \`) ) _)\n (__ (_ (_ . _) _) ,__)\n \`~~\`\\ ' . /\`~~\`\n ; ;\n / \\ \n_____________/_ __ \\___________________________________________\n" > $(tty) -echo -e "\e[35m${1}\n\nIf you think all is right at your side, please check the GitHub repo if there\nis a newer version/bugfix available, thx: https://github.com/gitmachtl/scripts\e[0m\n" > $(tty); exit 1; +echo -e "\e[97m\n" >&2 +echo -e " _ ._ _ , _ ._\n (_ ' ( \` )_ .__)\n ( ( ( ) \`) ) _)\n (__ (_ (_ . _) _) ,__)\n \`~~\`\\ ' . /\`~~\`\n ; ;\n / \\ \n_____________/_ __ \\___________________________________________\n" >&2 +echo -e "\e[35m${1}\n\nIf you think all is right at your side, please check the GitHub repo if there\nis a newer version/bugfix available, thx: https://github.com/gitmachtl/scripts\e[0m\n" >&2; exit 1; } #------------------------------------------------------- -#API Endpoints and Network-Settings for the various chains +#get the terminal output device - may vary depending on the write access +echo -n "" 2> /dev/null > $(tty); if [[ $? -eq 0 ]]; then termTTY=$(tty); else termTTY="/dev/tty"; fi network=${network:-mainnet} #sets the default network to mainnet, if not set otherwise unset _magicparam _addrformat _byronToShelleyEpochs _tokenMetaServer _transactionExplorer _koiosAPI _adahandlePolicyID _adahandleAPI _lightModeParametersURL @@ -160,11 +160,13 @@ workMode=${workmode:-"${workMode}"} koiosApiToken=${koiosapitoken:-"${koiosApiToken}"} #Set the list of preconfigured networknames -networknames="mainnet, preprod, preview, sancho" +networknames="mainnet, preprod, preview, sancho, guildnet" #Check if there are testnet parameters set but network is still "mainnet" if [[ "${magicparam}${addrformat}" == *"testnet"* && "${network,,}" == "mainnet" ]]; then majorError "Mainnet selected, but magicparam(${magicparam})/addrformat(${addrformat}) have testnet settings!\n\nPlease select the right chain in the '00_common.sh', '${scriptDir}/common.inc', '$HOME/.common.inc' or './common.inc' file by setting the value for the parameter network to one of the preconfiged networknames:\n${networknames}\n\nThere is no need anymore, to set the parameters magicparam/addrformat/byronToShelleyEpochs for the preconfigured networks. Its enough to specify it for example with: network=\"preprod\"\nOf course you can still set them and also set a custom networkname like: network=\"vasil-dev\""; exit 1; fi +#API Endpoints and Network-Settings for the various chains + #Preload the variables, based on the "network" name case "${network,,}" in @@ -285,7 +287,7 @@ if [[ "${adahandleAPI: -1}" == "/" ]]; then adahandleAPI=${adahandleAPI%?}; fi # if [[ "${magicparam}" == "" || ${addrformat} == "" || ${byronToShelleyEpochs} == "" ]]; then majorError "The 'magicparam', 'addrformat' or 'byronToShelleyEpochs' is not set!\nOr maybe you have set the wrong parameter network=\"${network}\" ?\nList of preconfigured network-names: ${networknames}"; exit 1; fi #Don't allow to overwrite the needed Versions, so we set it after the overwrite part -minCliVersion="9.2.1" #minimum allowed cli version for this script-collection version +minCliVersion="9.3.0" #minimum allowed cli version for this script-collection version maxCliVersion="99.99.9" #maximum allowed cli version, 99.99.9 = no limit so far minNodeVersion="9.1.0" #minimum allowed node version for this script-collection version maxNodeVersion="99.99.9" #maximum allowed node version, 99.99.9 = no limit so far @@ -713,7 +715,8 @@ ask() { echo -ne "$1 [$prompt] " # Read the answer (use /dev/tty in case stdin is redirected from somewhere else) - read reply $(tty) #redirect to the tty output +# echo -ne "${1}: " > $(tty) #redirect to the tty output + echo -ne "${1}: " > ${termTTY} #redirect to the tty output IFS= read -s pass #read in the password but don't show it local hidden=$(sed 's/./*/g' <<< ${pass}) - echo -ne "${hidden}" > $(tty) #show stars for the chars +# echo -ne "${hidden}" > $(tty) #show stars for the chars + echo -ne "${hidden}" > ${termTTY} #show stars for the chars echo -n "${pass}" #pass the password to the calling instance unset pass #unset the variable } @@ -1127,6 +1132,73 @@ fi } #------------------------------------------------------- +#------------------------------------------------------- +#Convert an action/proposal UTXO and IDX value into the CIP-129 bech representation like gov_action1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsq6dmejn +convert_actionUTXO2Bech() { + local govActionID="${1}" +# local govActionUTXO=$(trimString "${1%%#*}"); govActionUTXO=${govActionUTXO,,} #takes the part before the # separator +# local govActionIdx=$(trimString "${1#*#}"); #takes the part after the # separator + if [[ "${govActionID}" =~ ^([[:xdigit:]]{64}+#[[:digit:]]{1,})$ ]]; then + local govActionUTXO=${govActionID:0:64}; govActionUTXO=${govActionUTXO,,} #make sure its lower case + local govActionIdx=$(( ${govActionID:65} + 0 )) #make sure to have single digits if provided like #00 #01 #02... + local govActionIdxHex="00$(bc <<< "obase=16;ibase=10;${govActionIdx}")"; govActionIdxHex=${govActionIdxHex: -$(( (${#govActionIdxHex}-1)/2*2 ))} #make sure its with a leading zero and always in pairs like 03, 04af + local govActionBech=$(${bech32_bin} "gov_action" <<< "${govActionUTXO}${govActionIdxHex}" 2> /dev/null) + echo -n "${govActionBech}" + else + echo -e "\n\e[35mERROR - Please provide a valid Governance-Action-ID in the format like: \e[0m365042be18639f776520fca54e9cb2df04ab9ecd43bf50078045d8cc6ee491be#0\n"; exit 1; + fi +} +#------------------------------------------------------- + +#------------------------------------------------------- +#Convert a CIP129 action bech into UTXO#IDX format +convert_actionBech2UTXO() { + local govActionBech="${1}" + if [[ "${govActionBech}" != "gov_action1"* ]]; then exit 1; fi + local govActionBechHex=$(${bech32_bin} <<< "${govActionBech}" 2> /dev/null) + local govActionUTXO=${govActionBechHex:0:64} + local govActionIdx=$(bc <<< "obase=10;ibase=16;${govActionBechHex:64}") +echo -n "${govActionUTXO}#${govActionIdx}" +} +#------------------------------------------------------- + +#------------------------------------------------------- +#Convert a bech drep, committee or pool into the CIP129 bech format +convert_actionBech2CIP129() { + local bechFormat="${1}" + local hexFormat=$(${bech32_bin} <<< "${bechFormat}") + case "${bechFormat}" in + "cc_hot1"*) local cip129BechFormat=$(${bech32_bin} "cc_hot" <<< "02${hexFormat}");; + "cc_hot_script1"*) local cip129BechFormat=$(${bech32_bin} "cc_hot" <<< "03${hexFormat}");; + "cc_cold1"*) local cip129BechFormat=$(${bech32_bin} "cc_cold" <<< "12${hexFormat}");; + "cc_cold_script1"*) local cip129BechFormat=$(${bech32_bin} "cc_cold" <<< "13${hexFormat}");; + "drep1"*) local cip129BechFormat=$(${bech32_bin} "drep" <<< "22${hexFormat}");; + "drep_script1"*) local cip129BechFormat=$(${bech32_bin} "drep" <<< "23${hexFormat}");; + *) echo -n "ERROR - cannot convert the given bech input"; exit 1 ;; + esac +echo -n "${cip129BechFormat}" +} +#------------------------------------------------------- + +#------------------------------------------------------- +#Convert a CIP129 bech drep, committee or pool into the standard bech format +convert_actionCIP1292Bech() { + local cip129BechFormat="${1}" + local hexFormat=$(${bech32_bin} <<< "${cip129BechFormat}") + case "${hexFormat:0:2}${cip129BechFormat}" in + "02cc_hot"*) local bechFormat=$(${bech32_bin} "cc_hot" <<< "${hexFormat:2}");; + "03cc_hot"*) local bechFormat=$(${bech32_bin} "cc_hot_script" <<< "${hexFormat:2}");; + "12cc_cold"*) local bechFormat=$(${bech32_bin} "cc_cold" <<< "${hexFormat:2}");; + "13cc_cold"*) local bechFormat=$(${bech32_bin} "cc_cold_script" <<< "${hexFormat:2}");; + "22drep"*) local bechFormat=$(${bech32_bin} "drep" <<< "${hexFormat:2}");; + "23drep"*) local bechFormat=$(${bech32_bin} "drep_script" <<< "${hexFormat:2}");; + *) echo -n "ERROR - cannot convert the given cip129 bech input"; exit 1 ;; + esac +echo -n "${bechFormat}" +} +#------------------------------------------------------- + + #------------------------------------------------------- #Calculate the minimum UTXO value that has to be sent depending on the assets and the minUTXO protocol-parameters @@ -1411,7 +1483,7 @@ queryLight_UTXO() { #${1} = address to query # # makes an online query via koios API and returns and output like a cli stake-address-info query # -queryLight_stakeAddressInfo() { #${1} = address to query +queryLight_stakeAddressInfo() { #${1} = stakeaddress(bech) to query local addr=${1} local errorcnt=0 @@ -1447,17 +1519,89 @@ queryLight_stakeAddressInfo() { #${1} = address to query else local delegation; local rewardAccountBalance; local delegationDeposit; local voteDelegation; #define local variables so we can read it in one go with the next jq command - { read delegation; read rewardAccountBalance; read delegationDeposit; read voteDelegation; } <<< $(jq -r ".[0].delegated_pool // \"null\", .[0].rewards_available // \"null\", .[0].deposit // \"null\", .[0].vote_delegation // \"null\"" <<< "${responseJSON}" 2> /dev/null) + { read delegation; read rewardAccountBalance; read delegationDeposit; read voteDelegation; } <<< $(jq -r ".[0].delegated_pool // \"null\", .[0].rewards_available // \"null\", .[0].deposit // \"null\", .[0].delegated_drep // \"null\"" <<< "${responseJSON}" 2> /dev/null) #deposit value, always 2000000 lovelaces until conway if [[ ${delegationDeposit} == null ]]; then delegationDeposit=2000000; fi + #convert from CIP129 to regular format if its a normal drep delegation + if [[ "${voteDelegation}" == "drep1"* ]]; then voteDelegation=$(convert_actionCIP1292Bech ${voteDelegation}); fi + + #convert bech-voteDelegation into keyHash-/scriptHAsh-voteDelegation + case "${voteDelegation}" in + "drep1"*) voteDelegation="keyHash-$(${bech32_bin} <<< ${voteDelegation})" + ;; + "drep_script1"*) voteDelegation="scriptHash-$(${bech32_bin} <<< ${voteDelegation})" + ;; + "drep_always_abstain") voteDelegation="alwaysAbstain" + ;; + "drep_always_no_confidence") + voteDelegation="alwaysNoConfidence" + ;; + *) voteDelegation="null" + ;; + esac - #convert bech-voteDelegation into keyHash-voteDelegation - if [[ ${voteDelegation} != null ]]; then voteDelegation="keyHash-$(${bech32_bin} <<< ${voteDelegation})"; fi + jsonRet="[ { \"address\": \"${addr}\", \"stakeDelegation\": \"${delegation}\", \"delegationDeposit\": ${delegationDeposit}, \"rewardAccountBalance\": ${rewardAccountBalance}, \"voteDelegation\": \"${voteDelegation}\" } ]" #compose a json like the cli output + #return the composed json + printf "${jsonRet}" + fi + unset jsonRet response responseCode responseJSON addr error errorcnt - jsonRet="[ { \"address\": \"${addr}\", \"stakeDelegation\": \"${delegation}\", \"delegationDeposit\": ${delegationDeposit}, \"rewardAccountBalance\": ${rewardAccountBalance}, \"voteDelegation\": \"${voteDelegation}\" } ]" #compose a json like the cli output +} +#------------------------------------------------------- + + + +#------------------------------------------------------- +#queryLight_drepInfo function +# +# makes an online query via koios API and returns and output like a cli drep-state query +# +queryLight_drepInfo() { #${1} = drep-id(bech) to query + + local drepID="${1}"; + if [[ ${#drepID} -eq 56 || ${#drepID} -eq 63 ]]; then drepID=$(convert_actionBech2CIP129 ${drepID}); fi #if given drep id is in standard format, convert it to CIP129 format for koios query + local errorcnt=0 + local error=-1 + while [[ ${errorcnt} -lt 5 && ${error} -ne 0 ]]; do #try a maximum of 5 times to request the information via koios API + error=0 + response=$(curl -sL -m 30 -X POST -w "---spo-scripts---%{http_code}" "${koiosAPI}/drep_info" -H "${koiosAuthorizationHeader}" -H "Accept: application/json" -H "Content-Type: application/json" -d "{\"_drep_ids\":[\"${drepID}\"]}" 2> /dev/null) + if [ $? -ne 0 ]; then error=1; fi; + errorcnt=$(( ${errorcnt} + 1 )) + done + if [[ ${error} -ne 0 ]]; then echo -e "Query of the Koios-API via curl failed, tried 5 times."; exit 1; fi; #curl query failed + + #Split the response string into JSON content and the HTTP-ResponseCode + if [[ "${response}" =~ (.*)---spo-scripts---([0-9]*)* ]]; then + local responseJSON="${BASH_REMATCH[1]}" + local responseCode="${BASH_REMATCH[2]}" + else + echo -e "Query of the Koios-API via curl failed. Could not separate Content and ResponseCode."; exit 1; #curl query failed + fi + + #Check the responseCode + case ${responseCode} in + "200" ) ;; #all good, continue + * ) echo -e "HTTP Response code: ${responseCode}"; exit 1; #exit with a failure and the http response code + esac; + + jsonRet=$(jq -r . <<< "${responseJSON}" 2> /dev/null) + if [ $? -ne 0 ]; then echo -e "Query via Koios-API (${koiosAPI}) failed, not a JSON response."; exit 1; fi; #reponse is not a json file + + #check if the drepID is registered, if not, return an empty array + if [[ $(jq -r ".[0].registered" <<< "${responseJSON}" 2> /dev/null) != "true" ]]; then + printf "[]"; #drepID not registered on chain, return an empty array + else + + local hasScript; local delegatedStake; local drepDeposit; local drepHex; local drepExpiry; local drepAnchorUrl; local drepAnchorHash; #define local variables so we can read it in one go with the next jq command + { read hasScript; read delegatedStake; read drepDeposit; read drepHex; read drepExpiry; read drepAnchorUrl; read drepAnchorHash; } <<< $(jq -r "\"\(.[0].has_script)\" // \"null\", .[0].amount // \"null\", .[0].deposit // \"null\", .[0].hex // \"null\", .[0].expires_epoch_no // \"null\", .[0].url // \"-\", .[0].hash // \"-\"" <<< "${responseJSON}" 2> /dev/null) + + #set the hash-type + if [[ "${hasScript}" == "false" ]]; then hashType="keyHash"; else hashType="scriptHash"; fi + + jsonRet="[ [ { \"${hashType}\": \"${drepHex}\" }, { \"anchor\": { \"url\": \"${drepAnchorUrl}\", \"dataHash\": \"${drepAnchorHash}\" } , \"deposit\": ${drepDeposit}, \"expiry\": ${drepExpiry}, \"stake\": ${delegatedStake} } ] ]" #compose a json like the cli output #return the composed json printf "${jsonRet}" fi @@ -1469,6 +1613,242 @@ queryLight_stakeAddressInfo() { #${1} = address to query +#------------------------------------------------------- +#queryLight_committeeState function +# +# makes an online query via koios API and returns and output like: cardano-cli conway query committee-state +# +queryLight_committeeState() { #for filtering, ${1} = committeeHASH + + local committeeHASH="${1}" + local errorcnt=0 + local error=-1 + while [[ ${errorcnt} -lt 5 && ${error} -ne 0 ]]; do #try a maximum of 5 times to request the information via koios API + error=0 + response=$(curl -sL -m 30 -X GET -w "---spo-scripts---%{http_code}" "${koiosAPI}/committee_info" -H "${koiosAuthorizationHeader}" -H "Accept: application/json" -H "Content-Type: application/json" 2> /dev/null) + if [ $? -ne 0 ]; then error=1; fi; + errorcnt=$(( ${errorcnt} + 1 )) + done + if [[ ${error} -ne 0 ]]; then echo -e "Query of the Koios-API via curl failed, tried 5 times."; exit 1; fi; #curl query failed + + #Split the response string into JSON content and the HTTP-ResponseCode + if [[ "${response}" =~ (.*)---spo-scripts---([0-9]*)* ]]; then + local responseJSON="${BASH_REMATCH[1]}" + local responseCode="${BASH_REMATCH[2]}" + else + echo -e "Query of the Koios-API via curl failed. Could not separate Content and ResponseCode."; exit 1; #curl query failed + fi + + #Check the responseCode + case ${responseCode} in + "200" ) ;; #all good, continue + * ) echo -e "HTTP Response code: ${responseCode}"; exit 1; #exit with a failure and the http response code + esac; + + jsonJSON=$(jq -r . <<< "${responseJSON}" 2> /dev/null) + if [ $? -ne 0 ]; then echo -e "Query via Koios-API (${koiosAPI}) failed, not a JSON response."; exit 1; fi; #reponse is not a json file + + #Filter for a given committeeHASH + if [[ "${committeeHASH}" != "" ]]; then + committeeHashArray=$(jq -r "[ .[0].members[] | select( .cc_cold_hex == \"${committeeHASH}\" or .cc_hot_hex == \"${committeeHASH}\") ]" 2> /dev/null <<< "${jsonJSON}"); + else + committeeHashArray=$(jq -r "[ .[0].members[] ]" 2> /dev/null <<< "${jsonJSON}"); + fi + + #Get the quorum values + local quorum_numerator; local quorum_denominator; local quorum; + { read quorum_numerator; read quorum_denominator; } <<< $(jq -r ".[0].quorum_numerator // \"null\", .[0].quorum_denominator // \"null\"" <<< "${jsonJSON}" 2> /dev/null) + if [[ "${quorum_numerator}" == "null" || "${quorum_denominator}" == "null" ]]; then echo -e "Query via Koios-API (${koiosAPI}) failed, missin quorum parameters."; exit 1; fi; + quorum=$(bc <<< "scale=2; ${quorum_numerator}/${quorum_denominator}"); + + #Generate the same output like the cli does. First step, generate the core structure. This also converts the quorum .xx value to 0.xx + jsonRet=$(jq -r <<< "{ \"epoch\": $(get_currentEpoch), \"threshold\": ${quorum} }") + + #convert all committee hash entries to the correct format depending on key/script hashes + committeeEntryCnt=$(jq -r "length" <<< ${committeeHashArray}) + for (( tmpCnt=0; tmpCnt<${committeeEntryCnt}; tmpCnt++ )) + do + + local cc_hot_hex; local cc_cold_hex; local expiration_epoch; local cc_hot_has_script; local cc_cold_has_script; local cc_status; + { read cc_hot_hex; read cc_cold_hex; read expiration_epoch; read cc_hot_has_script; read cc_cold_has_script; read cc_status; } <<< $(jq -r ".[${tmpCnt}].cc_hot_hex // \"null\", .[${tmpCnt}].cc_cold_hex // \"null\", .[${tmpCnt}].expiration_epoch // \"null\", \"\(.[${tmpCnt}].cc_hot_has_script)\" // \"null\", \"\(.[${tmpCnt}].cc_cold_has_script)\" // \"null\", \"\(.[${tmpCnt}].status)\" // \"null\"" <<< "${committeeHashArray}" 2> /dev/null) + + case ${cc_status} in + + "authorized") if [[ "${cc_hot_has_script}" == "true" ]]; then + hotCredAuthStatus="{ \"contents\": { \"scriptHash\": \"${cc_hot_hex}\" }, \"tag\": \"MemberAuthorized\" }"; + else + hotCredAuthStatus="{ \"contents\": { \"keyHash\": \"${cc_hot_hex}\" }, \"tag\": \"MemberAuthorized\" }"; + fi + ;; + + "not_authorized") hotCredAuthStatus="{ \"tag\": \"MemberNotAuthorized\" }" + ;; + + "resigned") hotCredAuthStatus="{ \"tag\": \"MemberResigned\" }" + ;; + + esac + + if [[ "${cc_cold_has_script}" == "true" ]]; then cc_cold_key="scriptHash-${cc_cold_hex}"; else cc_cold_key="keyHash-${cc_cold_hex}"; fi + + #Add this to the list of committee entries + jsonRet=$(jq -r ".committee.\"${cc_cold_key}\" += { \"expiration\": ${expiration_epoch}, \"hotCredsAuthStatus\": ${hotCredAuthStatus}, \"nextEpochChange\": null, \"status\": \"Active\" }" <<< "${jsonRet}" 2> /dev/null) + + done + + #return the composed json + printf "${jsonRet}" + + unset jsonRet response responseCode responseJSON addr error errorcnt tmpCnt quorum committeeEntryCnt cc_cold_key hotCredAuthStatus + +} +#------------------------------------------------------- + + + +#------------------------------------------------------- +#queryLight_actionState function +# +# makes an online query via koios API and returns and output like: cardano-cli conway query gov-state | jq -r ".proposals | to_entries[] | .value" +# +queryLight_actionState() { #for filtering, ${1} = govActionUTXO, ${2} = govActionIDX, + + local govActionUTXO="${1}" + local govActionIdx="${2}" + local voterID="${3}" + local errorcnt=0 + local error=-1 + while [[ ${errorcnt} -lt 5 && ${error} -ne 0 ]]; do #try a maximum of 5 times to request the information via koios API + error=0 + case "${voterID}" in + "drep"*|"cc_hot"*|"pool"*) #a voterID was given, so do a filtering directly via koios on the given bech voterID + response=$(curl -sL -m 30 -X GET -w "---spo-scripts---%{http_code}" "${koiosAPI}/voter_proposal_list?_voter_id=${voterID}&dropped_epoch=is.null" -H "${koiosAuthorizationHeader}" -H "Accept: application/json" -H "Content-Type: application/json" 2> /dev/null) + ;; + *) #no voterID was given, do a query for the complete proposal list + response=$(curl -sL -m 30 -X GET -w "---spo-scripts---%{http_code}" "${koiosAPI}/proposal_list?dropped_epoch=is.null" -H "${koiosAuthorizationHeader}" -H "Accept: application/json" -H "Content-Type: application/json" 2> /dev/null) + ;; + esac + if [ $? -ne 0 ]; then error=1; fi; + errorcnt=$(( ${errorcnt} + 1 )) + done + if [[ ${error} -ne 0 ]]; then echo -e "Query of the Koios-API via curl failed, tried 5 times."; exit 1; fi; #curl query failed + + #Split the response string into JSON content and the HTTP-ResponseCode + if [[ "${response}" =~ (.*)---spo-scripts---([0-9]*)* ]]; then + local responseJSON="${BASH_REMATCH[1]}" + local responseCode="${BASH_REMATCH[2]}" + else + echo -e "Query of the Koios-API via curl failed. Could not separate Content and ResponseCode."; exit 1; #curl query failed + fi + + #Check the responseCode + case ${responseCode} in + "200" ) ;; #all good, continue + * ) echo -e "HTTP Response code: ${responseCode}"; exit 1; #exit with a failure and the http response code + esac; + + jsonRet=$(jq -r . <<< "${responseJSON}" 2> /dev/null) + if [ $? -ne 0 ]; then echo -e "Query via Koios-API (${koiosAPI}) failed, not a JSON response."; exit 1; fi; #reponse is not a json file + + #If there is a given govActionUTXO+govActionIDX value, filter down the result directly to mimimize the number of koios requests + #Filter for a given Action-ID + if [[ ${govActionUTXO} != "" && ${govActionIdx} != "" ]]; then + jsonRet=$(jq -r "[ .[] | select(.proposal_tx_hash == \"${govActionUTXO}\" and .proposal_index == ${govActionIdx}) ]" 2> /dev/null <<< "${jsonRet}") + fi + + #Generate the same output like the cli does. First step, generate the core structure. Votes will get filled later on + jsonRet=$(jq "[ .[] | { \"actionId\": { \"govActionIx\": (.proposal_index), \"txId\": (.proposal_tx_hash) }, \"dRepVotes\": {}, \"committeeVotes\": {}, \"stakePoolVotes\": {}, \"expiresAfter\": (.expiration - 1), \"proposedIn\": (.proposed_epoch), \"proposalProcedure\": { \"anchor\": { \"dataHash\": (.meta_hash), \"url\": (.meta_url) }, \"deposit\": (.deposit), \"govAction\": { \"contents\": (.proposal_description.contents), \"tag\": (.proposal_description.tag) }, \"returnAddr\": (.return_address) } } ]" 2> /dev/null <<< ${jsonRet}) + + + #convert all return addresses into cli (credential-hash and network) format + actionStateEntryCnt=$(jq -r "length" <<< ${jsonRet}) + readarray -t actionDepositAddrArray <<< $(jq -r ".[].proposalProcedure.returnAddr" 2> /dev/null <<< "${jsonRet}") + for (( tmpCnt=0; tmpCnt<${actionStateEntryCnt}; tmpCnt++ )) + do + #convert the returnaddress into hash and network + tmpReturnAddr=${actionDepositAddrArray[${tmpCnt}]} + tmpReturnHash=$(${bech32_bin} <<< "${tmpReturnAddr}" 2> /dev/null) + tmpCredential="" + case "${tmpReturnAddr}" in + "stake1"*) tmpCredential="{ \"credential\": { \"keyHash\": \"${tmpReturnHash:2}\" } , \"network\": \"Mainnet\" }";; + "stake_test1"*) tmpCredential="{ \"credential\": { \"keyHash\": \"${tmpReturnHash:2}\" } , \"network\": \"Testnet\" }";; + ### scriptaddresses ??? + esac + + #replace it in the JSON + jsonRet=$(jq ".[${tmpCnt}].proposalProcedure.returnAddr = ${tmpCredential}" <<< "${jsonRet}" ) + + done + + #return the composed json +# printf "${jsonRet}" + echo -ne "${jsonRet}" + + unset jsonRet response responseCode responseJSON addr error errorcnt tmpReturnAddr tmpReturnHash tmpCredential actionStateEntryCnt tmpCnt + +} +#------------------------------------------------------- + + + +#------------------------------------------------------- +#queryLight_actionVotes function +# +# makes an online query via koios API and returns a JSON output that contains the { "dRepVotes": ..., "stakePoolVotes": ..., "committeeVotes": ... } +# this can than be merged into the JSON requested via the function queryLight_actionState +# +queryLight_actionVotes() { #for filtering, ${1} = govActionUTXO, ${2} = govActionIDX, + + local govActionUTXO="${1}" + local govActionIdx="${2}" + local errorcnt=0 + local error=-1 + + #CIP129 action-ID format + local govActionBech=$(convert_actionUTXO2Bech "${govActionUTXO}#${govActionIdx}") + + while [[ ${errorcnt} -lt 5 && ${error} -ne 0 ]]; do #try a maximum of 5 times to request the information via koios API + error=0 + response=$(curl -sL -m 30 -X GET -w "---spo-scripts---%{http_code}" "${koiosAPI}/proposal_votes?_proposal_id=${govActionBech}" -H "${koiosAuthorizationHeader}" -H "Accept: application/json" 2> /dev/null) + if [ $? -ne 0 ]; then error=1; fi; + errorcnt=$(( ${errorcnt} + 1 )) + done + if [[ ${error} -ne 0 ]]; then echo -e "Query of the Koios-API via curl failed, tried 5 times."; exit 1; fi; #curl query failed + + #Split the response string into JSON content and the HTTP-ResponseCode + if [[ "${response}" =~ (.*)---spo-scripts---([0-9]*)* ]]; then + local responseJSON="${BASH_REMATCH[1]}" + local responseCode="${BASH_REMATCH[2]}" + else + echo -e "Query of the Koios-API via curl failed. Could not separate Content and ResponseCode."; exit 1; #curl query failed + fi + + #Check the responseCode + case ${responseCode} in + "200" ) ;; #all good, continue + * ) echo -e "HTTP Response code: ${responseCode}"; exit 1; #exit with a failure and the http response code + esac; + + jsonRet=$(jq -r . <<< "${responseJSON}" 2> /dev/null) + if [ $? -ne 0 ]; then echo -e "Query via Koios-API (${koiosAPI}) failed, not a JSON response."; exit 1; fi; #reponse is not a json file + + + #convert the koios output into cli compatible format + jsonRet=$(jq -r "[([ .[] | select(.voter_role == \"DRep\") ] | { dRepVotes: (map( if .voter_has_script then { \"scriptHash-\(.voter_hex)\": .vote } else { \"keyHash-\(.voter_hex)\": .vote } end ) | add // {}) }), + ([ .[] | select(.voter_role == \"SPO\") ] | { stakePoolVotes: (map({ \"\(.voter_hex)\": .vote }) | add // {}) }), + ([ .[] | select(.voter_role == \"ConstitutionalCommittee\") ] | { committeeVotes: (map( if .voter_has_script then { \"scriptHash-\(.voter_hex)\": .vote } else { \"keyHash-\(.voter_hex)\": .vote } end ) | add // {}) })] | add" 2> /dev/null <<< "${jsonRet}") + + #return the composed json + printf "${jsonRet}" + + unset jsonRet response responseCode responseJSON error errorcnt + +} +#------------------------------------------------------- + + + + #------------------------------------------------------- #submitLight function # @@ -1502,7 +1882,7 @@ submitLight() { #${1} = path to txFile case ${responseCode} in "202" ) ;; #all good, continue "400" ) echo -e "HTTP Response code: ${responseCode} - Koios API reported back an error:\n${responseTxID}\nMaybe you have to wait for the next block - please retry later.\nIf you have issues further on, please report back, thx!"; exit 1;; #exit with a failure and the http response code - * ) echo -e "HTTP Response code: ${responseCode}"; exit 1; #exit with a failure and the http response code + * ) echo -e "HTTP Response code: ${responseCode} - ${responseTxID}"; exit 1; #exit with a failure and the http response code esac; local txID=${responseTxID//\"/} #remove any quote symbol @@ -2048,7 +2428,6 @@ do echo -e "\t \t\e[90mpayment via ${transactionFromAddr}\e[0m" ;; - * ) #Unknown Transaction Type !? echo -e "\e[90m\t[$((${tmpCnt}+1))]\t\e[35mUnknown transaction type\e[0m" ;; @@ -2232,10 +2611,7 @@ function showProcessAnimation() { local stopAnimation="false"; local idx=0; -#local animChar=("-" "\\" "|" "/"); -#local animChar=("⎺" "\\" "⎽" "/"); local animChar=("> " ">> " ">>> " " >>> " " >>>" " >>" " >" " "); -#local animChar=("> " " > " " > " " > " " >" " < " " < " " < "); trap terminate SIGINT terminate(){ stopAnimation="true"; } @@ -2275,7 +2651,8 @@ encrypt_skeyJSON() { local password="${2}" #check that the encryption/decryption tool gpg exists - if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > $(tty); exit 1; fi +# if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > $(tty); exit 1; fi + if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > ${termTTY}; exit 1; fi #check if the skeyJSON is already encrypted if [[ $(egrep "encrHex|Encrypted" <<< "${skeyJSON}" | wc -l) -ne 0 ]]; then echo "It is already encrypted!"; exit 1; fi @@ -2312,7 +2689,8 @@ decrypt_skeyJSON() { local password="${2}" #check that the encryption/decryption tool gpg exists - if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > $(tty); exit 1; fi +# if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > $(tty); exit 1; fi + if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > ${termTTY}; exit 1; fi #check if the skeyJSON is already decrypted if [[ $(egrep "encrHex|Encrypted" <<< "${skeyJSON}" | wc -l) -eq 0 ]]; then echo "It is already decrypted!"; exit 1; fi @@ -2356,10 +2734,12 @@ read_skeyFILE() { if [ ! -f "${skeyFILE}" ]; then echo -e "\e[35mGiven SKEY-File does not exist!\e[0m\n\n"; exit 1; fi #check if the skeyJSON is already decrypted, if so, just return the content - if [[ $(egrep "encrHex|Encrypted" < "${skeyFILE}" | wc -l) -eq 0 ]]; then echo -ne "\e[0mReading unencrypted file \e[32m${skeyFILE}\e[0m ... " > $(tty); cat "${skeyFILE}"; exit 0; fi +# if [[ $(egrep "encrHex|Encrypted" < "${skeyFILE}" | wc -l) -eq 0 ]]; then echo -ne "\e[0mReading unencrypted file \e[32m${skeyFILE}\e[0m ... " > $(tty); cat "${skeyFILE}"; exit 0; fi + if [[ $(egrep "encrHex|Encrypted" < "${skeyFILE}" | wc -l) -eq 0 ]]; then echo -ne "\e[0mReading unencrypted file \e[32m${skeyFILE}\e[0m ... " > ${termTTY}; cat "${skeyFILE}"; exit 0; fi #its encrypted, check that the encryption/decryption tool gpg exists - if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > $(tty); exit 1; fi +# if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > $(tty); exit 1; fi + if ! exists gpg; then echo -e "\n\n\e[33mYou need the little tool 'gnupg', its needed to encrypt/decrypt the data !\n\nInstall it on Ubuntu/Debian like:\n\e[97msudo apt update && sudo apt -y install gnupg\n\n\e[33mThx! :-)\e[0m\n" > ${termTTY}; exit 1; fi #main loop to repeat the decryption until we have a cborHex while [[ "${cborHex}" == "" ]]; do @@ -2371,7 +2751,8 @@ read_skeyFILE() { local password=$(ask_pass "\e[33mEnter the Password to decrypt '${skeyFILE}' (empty to abort)") if [[ ${password} == "" ]]; then echo -e "\e[35mAborted\e[0m\n\n"; exit 1; fi while [[ $(is_strong_password "${password}") != "true" ]]; do - echo -e "\n\e[35mThis is not a strong password, so it couldn't be the right one. Lets try it again...\e[0m\n" > $(tty) +# echo -e "\n\e[35mThis is not a strong password, so it couldn't be the right one. Lets try it again...\e[0m\n" > $(tty) + echo -e "\n\e[35mThis is not a strong password, so it couldn't be the right one. Lets try it again...\e[0m\n" > ${termTTY} local password=$(ask_pass "\e[33mEnter the Password to decrypt '${skeyFILE}' (empty to abort)") if [[ ${password} == "" ]]; then echo -e "\e[35mAborted\e[0m\n\n"; exit 1; fi done @@ -2394,7 +2775,8 @@ read_skeyFILE() { unset skeyJSON #not used after this line #decrypt - echo -ne "\r\033[K\e[0mDecrypting the file '\e[32m${skeyFILE}\e[0m' ${viaENV}... " > $(tty) +# echo -ne "\r\033[K\e[0mDecrypting the file '\e[32m${skeyFILE}\e[0m' ${viaENV}... " > $(tty) + echo -ne "\r\033[K\e[0mDecrypting the file '\e[32m${skeyFILE}\e[0m' ${viaENV}... " > ${termTTY} local cborHex=$(xxd -ps -r <<< ${skeyEncrHex} 2> /dev/null | gpg --decrypt --yes --batch --quiet --passphrase "${password}" --log-file /dev/null 2> /dev/null) unset skeyEncrHex #not used after this line unset password #not used after this line @@ -2413,6 +2795,3 @@ read_skeyFILE() { } #------------------------------------------------------- - - - diff --git a/cardano/testnet/01_queryAddress.sh b/cardano/testnet/01_queryAddress.sh index 52c4689..cab6a40 100755 --- a/cardano/testnet/01_queryAddress.sh +++ b/cardano/testnet/01_queryAddress.sh @@ -238,8 +238,6 @@ elif [[ ${typeOfAddr} == ${addrTypeStake} ]]; then #Staking Address esac -# jq -r . <<< ${rewardsJSON} - rewardsEntryCnt=$(jq -r 'length' <<< ${rewardsJSON}) if [[ ${rewardsEntryCnt} == 0 ]]; then echo -e "\e[35mStaking Address is not on the chain, register it first !\e[0m\n"; exit 1; @@ -336,6 +334,9 @@ elif [[ ${typeOfAddr} == ${addrTypeStake} ]]; then #Staking Address "scriptHash") drepDelegationID=$(${bech32_bin} "drep_script" <<< "${drepDelegationHASH##*-}" 2> /dev/null) echo -e " \t\e[0mVoting-Power of Staking Address is delegated to DRep-Script-ID(HASH): \e[32m${drepDelegationID}\e[0m (\e[94m${drepDelegationHASH##*-}\e[0m)\n"; ;; + "null") #not delegated + echo -e " \t\e[0mVoting-Power of Staking Address is not delegated to a DRep !\e[0m\n"; + ;; *) #unknown type echo -e " \t\e[0mVoting-Power of Staking Address is delegated to DRep-HASH: \e[32m${drepDelegationHASH}\e[0m\n"; ;; diff --git a/cardano/testnet/01_sendLovelaces.sh b/cardano/testnet/01_sendLovelaces.sh index 9fa1cb0..711e464 100755 --- a/cardano/testnet/01_sendLovelaces.sh +++ b/cardano/testnet/01_sendLovelaces.sh @@ -424,7 +424,7 @@ fi #Read ProtocolParameters case ${workMode} in - "online") protocolParametersJSON=$(${cardanocli} ${cliEra} query protocol-parameters | jq -r ".extraPraosEntropy+= null");; #onlinemode + "online") protocolParametersJSON=$(${cardanocli} ${cliEra} query protocol-parameters );; #onlinemode "light") protocolParametersJSON=${lightModeParametersJSON};; #lightmode "offline") protocolParametersJSON=$(jq ".protocol.parameters" <<< ${offlineJSON});; #offlinemode esac diff --git a/cardano/testnet/01_workOffline.sh b/cardano/testnet/01_workOffline.sh index 70a67a0..8e19d31 100755 --- a/cardano/testnet/01_workOffline.sh +++ b/cardano/testnet/01_workOffline.sh @@ -148,13 +148,13 @@ case ${action} in "online") #onlinemode #get the normal parameters protocolParametersJSON=$(${cardanocli} ${cliEra} query protocol-parameters ) - #get the previous actions ids for the various action types and the constitution state - prevActionIDsJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/null | jq -r ".nextRatifyState.nextEnactState.prevGovActionIds" 2> /dev/null) - if [[ ${prevActionIDsJSON} == "" ]]; then prevActionIDsJSON='{}'; fi - constitutionParametersJSON=$(${cardanocli} ${cliEra} query constitution 2> /dev/null | jq -r "." 2> /dev/null) - if [[ ${constitutionParametersJSON} == "" ]]; then constitutionParametersJSON='{}'; fi + + #Governance Stuff + governanceParametersJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/null | jq -r '{ committee : (.committee), constitution : (.constitution), prevActionIDs : (.nextRatifyState.nextEnactState.prevGovActionIds) }' 2> /dev/null) + if [[ "${governanceParametersJSON}" == "" ]]; then governanceParametersJSON="{}"; fi + #merge them together - protocolParametersJSON=$( jq --sort-keys ".constitution += ${constitutionParametersJSON} | .prevActionIDs += ${prevActionIDsJSON}" <<< ${protocolParametersJSON}) + protocolParametersJSON=$( jq --sort-keys ". += ${governanceParametersJSON}" <<< ${protocolParametersJSON}) ;; "light") #lightmode @@ -188,13 +188,13 @@ case ${action} in "online") #onlinemode #get the normal parameters protocolParametersJSON=$(${cardanocli} ${cliEra} query protocol-parameters ) - #get the previous actions ids for the various action types and the constitution state - prevActionIDsJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/null | jq -r ".nextRatifyState.nextEnactState.prevGovActionIds" 2> /dev/null) - if [[ ${prevActionIDsJSON} == "" ]]; then prevActionIDsJSON='{}'; fi - constitutionParametersJSON=$(${cardanocli} ${cliEra} query constitution 2> /dev/null | jq -r "." 2> /dev/null) - if [[ ${constitutionParametersJSON} == "" ]]; then constitutionParametersJSON='{}'; fi + + #Governance Stuff + governanceParametersJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/null | jq -r '{ committee : (.committee), constitution : (.constitution), prevActionIDs : (.nextRatifyState.nextEnactState.prevGovActionIds) }' 2> /dev/null) + if [[ "${governanceParametersJSON}" == "" ]]; then governanceParametersJSON="{}"; fi + #merge them together - protocolParametersJSON=$( jq --sort-keys ".constitution += ${constitutionParametersJSON} | .prevActionIDs += ${prevActionIDsJSON}" <<< ${protocolParametersJSON}) + protocolParametersJSON=$( jq --sort-keys ". += ${governanceParametersJSON}" <<< ${protocolParametersJSON}) ;; "light") #lightmode @@ -545,8 +545,8 @@ if [[ "${action}" == "add-drep" ]]; then drepID=$(cat ${drepName}.id) #we already checked that the file is present, so load the id from it -#do a short check if its a valid bech string -tmp=$(${bech32_bin} <<< ${drepID} 2> /dev/null) +#calculate the drep hash, and also do a bech validity check +drepHASH=$(${bech32_bin} 2> /dev/null <<< "${drepID}") if [ $? -ne 0 ]; then echo -e "\e[35mERROR - The content of '${drepName}.id' is not a valid DRep-Bech-ID.\e[0m\n"; exit 1; fi; echo -e "\e[0mChecking current Status about the DRep-ID:\e[32m ${drepID}\e[0m\n" @@ -554,21 +554,20 @@ if [ $? -ne 0 ]; then echo -e "\e[35mERROR - The content of '${drepName}.id' is #Get state data for the drepID. When in online mode of course from the node and the chain, in light mode via koios case ${workMode} in - "online") showProcessAnimation "Query DRep-ID Info: " & - drepStateJSON=$(${cardanocli} ${cliEra} query drep-state --drep-key-hash ${drepID} --include-stake 2> /dev/stdout ) - if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - drepStateJSON=$(jq -r ".[0] // []" <<< "${drepStateJSON}") #get rid of the outer array. if null, make an empty array - ;; -# "light") showProcessAnimation "Query DRep-ID-Info-LightMode: " & -# drepStateJSON=$(queryLight_drepInfo "${drepID}") -# if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; -# ;; + "online") showProcessAnimation "Query DRep-ID Info: " & + drepStateJSON=$(${cardanocli} ${cliEra} query drep-state --drep-key-hash ${drepHASH} --drep-script-hash ${drepHASH} --include-stake 2> /dev/stdout ) + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + drepStateJSON=$(jq -r ".[0] // []" <<< "${drepStateJSON}") #get rid of the outer array + ;; + "light") showProcessAnimation "Query DRep-ID-Info-LightMode: " & + drepStateJSON=$(queryLight_drepInfo "${drepID}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + drepStateJSON=$(jq -r ".[0] // []" <<< "${drepStateJSON}") #get rid of the outer array + ;; esac -# { read drepEntryCnt; read drepDepositAmount; read drepAnchorURL; read drepAnchorHASH; } <<< $(jq -r 'length, .[1].deposit, .[1].anchor.url // "empty", .[1].anchor.dataHash // "no hash"' <<< ${drepStateJSON}) - { read drepEntryCnt; read drepDepositAmount; read drepAnchorURL; diff --git a/cardano/testnet/03c_checkStakingAddrOnChain.sh b/cardano/testnet/03c_checkStakingAddrOnChain.sh index e04c112..f010616 100755 --- a/cardano/testnet/03c_checkStakingAddrOnChain.sh +++ b/cardano/testnet/03c_checkStakingAddrOnChain.sh @@ -132,6 +132,9 @@ if [[ ${typeOfAddr} == ${addrTypeStake} ]]; then #Staking Address "scriptHash") drepDelegationID=$(${bech32_bin} "drep_script" <<< "${drepDelegationHASH##*-}" 2> /dev/null) echo -e "\e[0mVoting-Power of Staking Address is delegated to DRep-Script-ID(HASH): \e[32m${drepDelegationID}\e[0m (\e[94m${drepDelegationHASH##*-}\e[0m)\n"; ;; + "null") #not delegated + echo -e "\e[0mVoting-Power of Staking Address is not delegated to a DRep !\e[0m\n"; + ;; *) drepDelegationID="" #unknown type echo -e "\e[0mVoting-Power of Staking Address is delegated to DRep-HASH: \e[32m${drepDelegationHASH}\e[0m\n"; ;; diff --git a/cardano/testnet/08b_deregStakingAddrCert.sh b/cardano/testnet/08b_deregStakingAddrCert.sh index a6438d2..5c09054 100755 --- a/cardano/testnet/08b_deregStakingAddrCert.sh +++ b/cardano/testnet/08b_deregStakingAddrCert.sh @@ -526,7 +526,7 @@ stakingName=$(basename ${stakeAddr} .staking) #contains the name before the .sta if [[ -f "${fromAddr}.hwsfile" && -f "${stakeAddr}.hwsfile" && "${paymentName}" == "${stakingName}" ]]; then #remove the tag(258) from the txBodyFile - sed -si 's/04d90102818308/04818308/g' "${txBodyFile}" +# sed -si 's/04d90102818308/04818308/g' "${txBodyFile}" echo -ne "\e[0mAutocorrect the TxBody for canonical order: " tmp=$(autocorrect_TxBodyFile "${txBodyFile}"); if [ $? -ne 0 ]; then echo -e "\e[35m${tmp}\e[0m\n\n"; exit 1; fi diff --git a/cardano/testnet/21b_regDRepCert.sh b/cardano/testnet/21b_regDRepCert.sh index 50ae8da..80f006d 100755 --- a/cardano/testnet/21b_regDRepCert.sh +++ b/cardano/testnet/21b_regDRepCert.sh @@ -194,9 +194,6 @@ echo case ${workMode} in "online") #onlinemode protocolParametersJSON=$(${cardanocli} ${cliEra} query protocol-parameters) -#not needed anymore since cli 8.24.0.0 -# governanceParametersJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/null | jq -r ".currentPParams") -# protocolParametersJSON=$( jq ". += ${governanceParametersJSON} " <<< ${protocolParametersJSON}) #embedding the governance parameters into the normal protocolParameters ;; "light") #lightmode @@ -227,7 +224,6 @@ checkError "$?"; if [ $? -ne 0 ]; then exit $?; fi #If in online/light mode, check the anchorURL if ${onlineMode}; then - #get Anchor-URL content and calculate the Anchor-Hash if [[ ${anchorURL} != "" ]]; then @@ -300,10 +296,10 @@ case ${workMode} in drepStateJSON=$(jq -r .[0] <<< "${drepStateJSON}") #get rid of the outer array ;; - "light") #showProcessAnimation "Query DRep-ID-Info-LightMode: " & - echo -e "\n\e[91mINFORMATION - This script does not support Light-Mode yet, waiting for Koios support!\n\e[0m"; exit; -# drepStateJSON=$(queryLight_drepInfo "${drepID}") -# if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + "light") showProcessAnimation "Query DRep-ID-Info-LightMode: " & + drepStateJSON=$(queryLight_drepInfo "${drepID}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + drepStateJSON=$(jq -r .[0] <<< "${drepStateJSON}") #get rid of the outer array ;; "offline") readOfflineFile; #Reads the offlinefile into the offlineJSON variable diff --git a/cardano/testnet/21c_checkDRepOnChain.sh b/cardano/testnet/21c_checkDRepOnChain.sh index 200d64e..29a7c1c 100755 --- a/cardano/testnet/21c_checkDRepOnChain.sh +++ b/cardano/testnet/21c_checkDRepOnChain.sh @@ -45,16 +45,41 @@ if [[ "${checkDRepID//[![:xdigit:]]}" == "${checkDRepID}" && ${#checkDRepID} -eq drepID=$(${bech32_bin} "drep" <<< "${checkDRepID}" 2> /dev/null) if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - Couldn't convert HEX-ID into Bech-ID.\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" - + echo -e "\e[0mRegular DRep-ID: \e[32m${drepID}\e[0m" + echo -e "\e[0m CIP129 DRep-ID: \e[33m$(convert_actionBech2CIP129 "${drepID}")\e[0m" elif [[ "${checkDRepID:0:5}" == "drep1" && ${#checkDRepID} -eq 56 ]]; then #parameter is most likely a bech32-drepid - echo -ne "\e[0mCheck if given Bech-ID\e[32m ${checkDRepID}\e[0m is valid ..." + echo -ne "\e[0mCheck if given Bech-ID is valid ..." + #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + tmp=$(${bech32_bin} 2> /dev/null <<< "${checkDRepID}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - \"${checkDRepID}\" is not a valid Bech32 DRep-ID.\e[0m"; exit 1; fi + echo -e "\e[32m OK\e[0m\n" + drepID=${checkDRepID} + echo -e "\e[0mRegular DRep-ID: \e[32m${drepID}\e[0m" + echo -e "\e[0m CIP129 DRep-ID: \e[33m$(convert_actionBech2CIP129 "${drepID}")\e[0m" + +elif [[ "${checkDRepID:0:5}" == "drep1" && ${#checkDRepID} -eq 58 ]]; then #parameter is most likely a CIP129 bech32-drepid + + echo -ne "\e[0mCheck if given CIP129 Bech-ID is valid ..." + #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + tmp=$(${bech32_bin} 2> /dev/null <<< "${checkDRepID}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - \"${checkDRepID}\" is not a valid Bech32 DRep-ID.\e[0m"; exit 1; fi + echo -e "\e[32m OK\e[0m\n" + drepID=${checkDRepID} + echo -e "\e[0m CIP129 DRep-ID: \e[33m${drepID}\e[0m" + echo -e "\e[0mRegular DRep-ID: \e[32m$(convert_actionCIP1292Bech "${drepID}")\e[0m" + +elif [[ "${checkDRepID:0:12}" == "drep_script1" && ${#checkDRepID} -eq 63 ]]; then #parameter is most likely a bech32-drep-script-id + + echo -ne "\e[0mCheck if given DRep-Script-ID is valid ..." #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID tmp=$(${bech32_bin} 2> /dev/null <<< "${checkDRepID}") #will have returncode 0 if the bech was valid if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - \"${checkDRepID}\" is not a valid Bech32 DRep-ID.\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" drepID=${checkDRepID} + echo -e "\e[0mRegular DRep-ID: \e[32m${drepID}\e[0m" + echo -e "\e[0m CIP129 DRep-ID: \e[33m$(convert_actionBech2CIP129 "${drepID}")\e[0m" elif [ -f "${checkDRepName}.drep.vkey" ]; then #parameter is a DRep verification key file @@ -63,6 +88,8 @@ elif [ -f "${checkDRepName}.drep.vkey" ]; then #parameter is a DRep verification drepID=$(${cardanocli} ${cliEra} governance drep id --drep-verification-key-file "${checkDRepName}.drep.vkey" --out-file /dev/stdout 2> /dev/null) if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - Could not generate the DRep-ID from \"${checkDRepName}.drep.vkey\"\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" + echo -e "\e[0mRegular DRep-ID: \e[32m${drepID}\e[0m" + echo -e "\e[0m CIP129 DRep-ID: \e[33m$(convert_actionBech2CIP129 "${drepID}")\e[0m" elif [ -f "${checkDRepName}.drep.id" ]; then #parameter is a DRep verification id file, containing a bech32 id @@ -70,19 +97,12 @@ elif [ -f "${checkDRepName}.drep.id" ]; then #parameter is a DRep verification i checkDRepID=$(cat "${checkDRepName}.drep.id" 2> /dev/null) if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - Could not read from file \"${checkDRepName}.drep.id\"\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" - #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + #lets do some further testing by converting the bech32 DRep-ID into a Hex-DRep-ID tmp=$(${bech32_bin} 2> /dev/null <<< "${checkDRepID}") #will have returncode 0 if the bech was valid if [ $? -ne 0 ]; then echo -e "\e[35mERROR - \"${checkDRepID}\" is not a valid Bech32 DRep-ID.\e[0m"; exit 1; fi drepID=${checkDRepID} - -elif [[ "${checkDRepID:0:12}" == "drep_script1" && ${#checkDRepID} -eq 63 ]]; then #parameter is most likely a bech32-drep-script-id - - echo -ne "\e[0mCheck if given DRep-Script-ID\e[32m ${checkDRepID}\e[0m is valid ..." - #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID - tmp=$(${bech32_bin} 2> /dev/null <<< "${checkDRepID}") #will have returncode 0 if the bech was valid - if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - \"${checkDRepID}\" is not a valid Bech32 DRep-ID.\e[0m"; exit 1; fi - echo -e "\e[32m OK\e[0m\n" - drepID=${checkDRepID} + echo -e "\e[0mRegular DRep-ID: \e[32m${drepID}\e[0m" + echo -e "\e[0m CIP129 DRep-ID: \e[33m$(convert_actionBech2CIP129 "${drepID}")\e[0m" else echo -e "\n\e[35mERROR - \"${checkDRepName}.drep.vkey/id\" does not exist, nor is \"${checkDRepID}\" a valid DRep-ID in Hex- or Bech-Format!\e[0m"; exit 1 @@ -90,10 +110,9 @@ fi #calculate the drep hash again, just for showing it to the user as an additional information -drepHASH=$(${bech32_bin} 2> /dev/null <<< "${drepID}") +drepHASH=$(${bech32_bin} 2> /dev/null <<< "${drepID}"); drepHASH=${drepHASH: -56} -echo -e "\e[0mChecking Information about the DRep-ID:\e[32m ${drepID}\e[0m" -echo -e "\e[0m DRep-HASH:\e[94m ${drepHASH}\e[0m\n" + echo -e "\e[0m DRep-HASH:\e[94m ${drepHASH}\e[0m\n" #Get state data for the drepID. When in online mode of course from the node and the chain, in light mode via koios case ${workMode} in @@ -101,13 +120,13 @@ case ${workMode} in "online") showProcessAnimation "Query DRep-ID Info: " & drepStateJSON=$(${cardanocli} ${cliEra} query drep-state --drep-key-hash ${drepHASH} --drep-script-hash ${drepHASH} --include-stake 2> /dev/stdout ) if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - drepStateJSON=$(jq -r .[0] <<< "${drepStateJSON}") #get rid of the outer array + drepStateJSON=$(jq -r ".[0] // []" <<< "${drepStateJSON}") #get rid of the outer array ;; - "light") #showProcessAnimation "Query DRep-ID-Info-LightMode: " & - #drepStateJSON=$(queryLight_drepInfo "${drepID}") - #if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - echo -e "\e[91mINFORMATION - There is currently not Light-Mode available for this action. Please use Online(Full)- or Offline-Mode!\e[0m\n"; exit; + "light") showProcessAnimation "Query DRep-ID-Info-LightMode: " & + drepStateJSON=$(queryLight_drepInfo "${drepID}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + drepStateJSON=$(jq -r ".[0] // []" <<< "${drepStateJSON}") #get rid of the outer array ;; "offline") readOfflineFile; #Reads the offlinefile into the offlineJSON variable @@ -117,8 +136,7 @@ case ${workMode} in esac -#jq -r . <<< ${drepStateJSON} - +currentEpoch=$(get_currentEpoch) { read drepEntryCnt; read drepDepositAmount; @@ -133,15 +151,17 @@ if [[ ${drepEntryCnt} == 0 ]]; then #not registered yet echo -e "\e[0mDRep-ID/HASH is\e[33m NOT registered on the chain\e[0m!\e[0m\n"; exit 1; -elif [[ ${drepExpireEpoch} -lt $(get_currentEpoch) ]]; then #activity expired +elif [[ ${drepExpireEpoch} -lt ${currentEpoch} ]]; then #activity expired echo -e "\e[0mDRep-ID/HASH is \e[32mregistered\e[0m but activity \e[91mexpired\e[0m on the chain!\n" echo -e "\e[0m Deposit-Amount:\e[32m ${drepDepositAmount}\e[0m lovelaces" echo -e "\e[0m Inactive-Epoch:\e[91m ${drepExpireEpoch}\e[0m" + echo -e "\e[0m Current-Epoch:\e[32m ${currentEpoch}\e[0m" else #normal registration and not expired echo -e "\e[0mDRep-ID/HASH is \e[32mregistered\e[0m on the chain!\n" echo -e "\e[0m Deposit-Amount:\e[32m ${drepDepositAmount}\e[0m lovelaces" echo -e "\e[0m Expire-Epoch:\e[32m ${drepExpireEpoch}\e[0m" + echo -e "\e[0m Current-Epoch:\e[32m ${currentEpoch}\e[0m" fi echo -e "\e[0m DRep-KeyType:\e[94m ${drepKeyType}\e[0m" @@ -196,7 +216,7 @@ if ${onlineMode}; then if [ "${contentHASH}" != "${drepAnchorHASH}" ]; then echo -e "\e[0m Anchor-STATUS:\e[35m HASH does not match! Online-HASH is \e[33m${contentHASH}\e[0m"; else - echo -e "\e[0m Anchor-STATUS:\e[32m Format and HASH is valid!\e[0m"; + echo -e "\e[0m Anchor-STATUS:\e[32m Content-HASH is valid! (No integrity check)\e[0m"; fi rm "${tmpAnchorContent}" #cleanup diff --git a/cardano/testnet/21d_retDRepCert.sh b/cardano/testnet/21d_retDRepCert.sh index 79be1b4..8839182 100755 --- a/cardano/testnet/21d_retDRepCert.sh +++ b/cardano/testnet/21d_retDRepCert.sh @@ -211,11 +211,11 @@ case ${workMode} in drepStateJSON=$(jq -r .[0] <<< "${drepStateJSON}") #get rid of the outer array ;; - "light") #showProcessAnimation "Query DRep-ID-Info-LightMode: " & - echo -e "\n\e[91mINFORMATION - This script does not support Light-Mode yet, waiting for Koios support!\n\e[0m"; exit; -# drepStateJSON=$(queryLight_drepInfo "${drepID}") -# if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - ;; + "light") showProcessAnimation "Query DRep-ID-Info-LightMode: " & + drepStateJSON=$(queryLight_drepInfo "${drepID}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + drepStateJSON=$(jq -r ".[0] // []" <<< "${drepStateJSON}") #get rid of the outer array + ;; "offline") readOfflineFile; #Reads the offlinefile into the offlineJSON variable drepStateJSON=$(jq -r ".drep.\"${drepID}\".drepStateJSON" <<< ${offlineJSON} 2> /dev/null) @@ -223,7 +223,7 @@ case ${workMode} in ;; esac -{ read drepEntryCnt; read drepDepositAmount; read drepAnchorURL; read drepAnchorHASH; } <<< $(jq -r 'length, .[1].deposit, .[1].anchor.url // "empty", .[1].anchor.dataHash // "no hash"' <<< ${drepStateJSON}) +{ read drepEntryCnt; read drepDepositAmount; read drepAnchorURL; read drepAnchorHASH; } <<< $(jq -r 'length, .[1].deposit // 0, .[1].anchor.url // "empty", .[1].anchor.dataHash // "no hash"' <<< ${drepStateJSON}) #Checking about the content if [[ ${drepEntryCnt} == 0 ]]; then #not registered yet diff --git a/cardano/testnet/22a_genVoteDelegCert.sh b/cardano/testnet/22a_genVoteDelegCert.sh index ce4113a..32cf2eb 100755 --- a/cardano/testnet/22a_genVoteDelegCert.sh +++ b/cardano/testnet/22a_genVoteDelegCert.sh @@ -61,6 +61,41 @@ elif [[ "${toDRepID:0:5}" == "drep1" && ${#toDRepID} -eq 56 ]]; then #parameter checkError "$?"; if [ $? -ne 0 ]; then file_lock ${delegateStakeAddr}.vote-deleg.cert; exit $?; fi file_lock ${delegateStakeAddr}.vote-deleg.cert +elif [[ "${toDRepID:0:12}" == "drep_script1" && ${#toDRepID} -eq 63 ]]; then #parameter is most likely a bech32-drepid-script + + #lets do some further testing by converting the beche32 DRep-id into a hex-DRep-id + toDRepHash=$(${bech32_bin} 2> /dev/null <<< "${toDRepID}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\e[35mERROR - \"${toDRepID}\" is not a valid bech32 DRep-ID for a ScriptHash.\e[0m"; exit 1; fi + + echo -e "\e[0mCreate a Vote-Delegation Certificate for Delegator\e[32m ${delegateStakeAddr}.staking.vkey\e[0m \nto the DRep with Bech32-ID(Script)\e[32m ${toDRepID}\e[0m" + echo + file_unlock ${delegateStakeAddr}.vote-deleg.cert + ${cardanocli} ${cliEra} stake-address vote-delegation-certificate --stake-verification-key-file ${delegateStakeAddr}.staking.vkey --drep-script-hash ${toDRepHash} --out-file ${delegateStakeAddr}.vote-deleg.cert + checkError "$?"; if [ $? -ne 0 ]; then file_lock ${delegateStakeAddr}.vote-deleg.cert; exit $?; fi + file_lock ${delegateStakeAddr}.vote-deleg.cert + +elif [[ "${toDRepID:0:5}" == "drep1" && ${#toDRepID} -eq 58 ]]; then #parameter is most likely a CIP129 bech32-drepid + + toDRepHash=$(${bech32_bin} 2> /dev/null <<< "${toDRepID}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\e[35mERROR - \"${toDRepID}\" is not a valid CIP129 Bech32 DRep-ID.\e[0m"; exit 1; fi + echo -e "\e[0mCreate a Vote-Delegation Certificate for Delegator\e[32m ${delegateStakeAddr}.staking.vkey\e[0m \nto the DRep with CIP129-Bech32-ID\e[32m ${toDRepID}\e[0m" + echo + #convert to standard format + toDRepID=$(convert_actionCIP1292Bech "${toDRepID}") + echo -e "\e[0mThe given CIP129 Bech-ID converted to regular is: \e[33m${toDRepID}\e[0m\n" + file_unlock ${delegateStakeAddr}.vote-deleg.cert + case "${toDRepID}" in + "drep1"*) #its a normal drep + ${cardanocli} ${cliEra} stake-address vote-delegation-certificate --stake-verification-key-file ${delegateStakeAddr}.staking.vkey --drep-key-hash ${toDRepID} --out-file ${delegateStakeAddr}.vote-deleg.cert + ;; + "drep_script1"*) #its a script drep + toDRepHash=${toDRepHash:2} #cut the first 2chars (1byte) + ${cardanocli} ${cliEra} stake-address vote-delegation-certificate --stake-verification-key-file ${delegateStakeAddr}.staking.vkey --drep-script-hash ${toDRepHash} --out-file ${delegateStakeAddr}.vote-deleg.cert + ;; + esac + checkError "$?"; if [ $? -ne 0 ]; then file_lock ${delegateStakeAddr}.vote-deleg.cert; exit $?; fi + file_lock ${delegateStakeAddr}.vote-deleg.cert + elif [ -f "${toDRepName}.drep.vkey" ]; then #parameter is a DRep verification key file echo -e "\e[0mCreate a Vote-Delegation Certificate for Delegator\e[32m ${delegateStakeAddr}.staking.vkey\e[0m \nto the DRep with the Key-File\e[32m ${toDRepName}.drep.vkey\e[0m" diff --git a/cardano/testnet/22b_regVoteDelegCert.sh b/cardano/testnet/22b_regVoteDelegCert.sh index c51d47a..2fa30b6 100755 --- a/cardano/testnet/22b_regVoteDelegCert.sh +++ b/cardano/testnet/22b_regVoteDelegCert.sh @@ -181,14 +181,27 @@ delegCBOR=$(jq -r ".cborHex" < "${delegName}.vote-deleg.cert" 2> /dev/null) case ${delegCBOR:68:4} in "8200") #delegating to a drep-id - delegDRepID=${delegCBOR: -56} #hex format + delegDRepHASH=${delegCBOR: -56} #hex format #get the bech DRepID - toDrepID=$(${bech32_bin} "drep" <<< ${delegDRepID} | tr -d '\n') + toDrepID=$(${bech32_bin} "drep" <<< ${delegDRepHASH} | tr -d '\n') checkError "$?"; if [ $? -ne 0 ]; then echo -e "\n\e[35mERROR - Could not convert the Hex-DRepID from the Delegation-Certificate into a Bech-DRepID.\e[0m\n"; exit 1; fi echo - echo -e "\e[0mDelegating Voting-Power of \e[32m${delegName}\e[0m to DRep with Hex-ID:\e[32m ${delegDRepID}\e[0m" + echo -e "\e[0mDelegating Voting-Power of \e[32m${delegName}\e[0m to DRep with Hash:\e[32m ${delegDRepHASH}\e[0m" echo - echo -e "\e[0mWhich resolves to the Bech-DRepID:\e[33m ${toDrepID}\e[0m" + echo -e "\e[0mWhich resolves to the Bech-DRepID:\e[32m ${toDrepID}\e[0m" + echo -e "\e[0m CIP129 Bech-DRepID:\e[33m $(convert_actionBech2CIP129 ${toDrepID})\e[0m" + ;; + + "8201") #delegating to a script drep-id + delegDRepHASH=${delegCBOR: -56} #hex format + #get the bech DRepID + toDrepID=$(${bech32_bin} "drep_script" <<< ${delegDRepHASH} | tr -d '\n') + checkError "$?"; if [ $? -ne 0 ]; then echo -e "\n\e[35mERROR - Could not convert the Hex-DRepID from the Delegation-Certificate into a Bech-DRepID.\e[0m\n"; exit 1; fi + echo + echo -e "\e[0mDelegating Voting-Power of \e[32m${delegName}\e[0m to DRep with Hash:\e[32m ${delegDRepHASH}\e[0m" + echo + echo -e "\e[0mWhich resolves to the Script-Bech-DRepID:\e[32m ${toDrepID}\e[0m" + echo -e "\e[0m CIP129 Script-Bech-DRepID:\e[33m $(convert_actionBech2CIP129 ${toDrepID})\e[0m" ;; "8102") #always-abstain @@ -209,7 +222,7 @@ esac #If in online/light mode, do a check if the DRep to delegate to is registered -if [[ ${onlineMode} == true && ${toDrepID} != "" ]]; then +if [[ ${onlineMode} == true && ${toDrepID} != "" && ${delegDRepHASH} != "" ]]; then echo echo -e "\e[0mChecking current ChainStatus about the DRep-ID:\e[32m ${toDrepID}\e[0m" @@ -222,15 +235,17 @@ if [[ ${onlineMode} == true && ${toDrepID} != "" ]]; then #check that the node is fully synced, otherwise the query would mabye return a false state if [[ $(get_currentSync) != "synced" ]]; then echo -e "\e[35mError - Node not fully synced or not running, please let your node sync to 100% first !\e[0m\n"; exit 1; fi showProcessAnimation "Query DRep-ID Info: " & - drepStateJSON=$(${cardanocli} ${cliEra} query drep-state --drep-key-hash ${toDrepID} 2> /dev/stdout ) +# drepStateJSON=$(${cardanocli} ${cliEra} query drep-state --drep-key-hash ${toDrepID} 2> /dev/stdout ) + drepStateJSON=$(${cardanocli} ${cliEra} query drep-state --drep-key-hash ${delegDRepHASH} --drep-script-hash ${delegDRepHASH} 2> /dev/stdout ) if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; #drepStateJSON=$(jq -rc . <<< "${drepStateJSON}") ;; -# "light") showProcessAnimation "Query-StakeAddress-Info-LightMode: " & -# rewardsJSON=$(queryLight_drepNameessInfo "${checkAddr}") -# if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${rewardsJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; -# ;; + "light") showProcessAnimation "Query DRep-ID-Info-LightMode: " & + drepStateJSON=$(queryLight_drepInfo "${toDrepID}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + #drepStateJSON=$(jq -r ".[0] // []" <<< "${drepStateJSON}") #get rid of the outer array + ;; esac @@ -284,10 +299,10 @@ if ${onlineMode}; then else #already registered #echo -e "Staking Address is registered on the chain, we continue ...\e[0m\n" - voteDelegationID=$(jq -r ".[0].voteDelegation // \"notSet\"" <<< ${rewardsJSON}) + drepDelegationHASH=$(jq -r ".[0].voteDelegation // \"notSet\"" <<< ${rewardsJSON}) #Show the current status of the voteDelegation - case ${voteDelegationID} in + case ${drepDelegationHASH} in "alwaysNoConfidence") #always-no-confidence echo -e "Voting-Power of Staking Address is currently set to: \e[94mALWAYS NO CONFIDENCE\e[0m"; @@ -303,9 +318,18 @@ if ${onlineMode}; then echo -e "\e[0mAccount's Voting-Power is \e[32mnot delegated to a DRep or set to a fixed status yet\e[0m - so lets change this :-)"; ;; - *) - #normal drep-id - echo -e "Voting-Power of Staking Address is currently delegated to DRepID: \e[94m${voteDelegationID}\e[0m"; + *) #normal drep-id or drep-script-id + case "${drepDelegationHASH%%-*}" in + "keyHash") drepDelegationID=$(${bech32_bin} "drep" <<< "${drepDelegationHASH##*-}" 2> /dev/null) + echo -e "\e[0mVoting-Power of Staking Address is delegated to DRepID(HASH): \e[32m${drepDelegationID}\e[0m (\e[94m${drepDelegationHASH##*-}\e[0m)\n"; + ;; + "scriptHash") drepDelegationID=$(${bech32_bin} "drep_script" <<< "${drepDelegationHASH##*-}" 2> /dev/null) + echo -e "\e[0mVoting-Power of Staking Address is delegated to DRep-Script-ID(HASH): \e[32m${drepDelegationID}\e[0m (\e[94m${drepDelegationHASH##*-}\e[0m)\n"; + ;; + *) drepDelegationID="" #unknown type + echo -e "\e[0mVoting-Power of Staking Address is delegated to DRep-HASH: \e[32m${drepDelegationHASH}\e[0m\n"; + ;; + esac ;; esac diff --git a/cardano/testnet/23c_regComAuthCert.sh b/cardano/testnet/23c_regComAuthCert.sh index 5f71e62..6ad7178 100755 --- a/cardano/testnet/23c_regComAuthCert.sh +++ b/cardano/testnet/23c_regComAuthCert.sh @@ -196,10 +196,13 @@ case ${workMode} in if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; ;; - "light") echo -e "\n\e[91mINFORMATION - This script does not support Light-Mode yet, waiting for Koios support!\n\e[0m"; exit; - ;; -# -# + + "light") + showProcessAnimation "Query Committee-State Info-LightMode: " & + committeeStateJSON=$(queryLight_committeeState "${commColdHash}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + ;; + "offline") echo -e "\n\e[91mINFORMATION - This script does not support Offline-Mode yet, waiting for Koios support!\n\e[0m"; exit; # readOfflineFile; #Reads the offlinefile into the offlineJSON variable # drepStateJSON=$(jq -r ".drep.\"${drepID}\".drepStateJSON" <<< ${offlineJSON} 2> /dev/null) @@ -607,7 +610,7 @@ if ask "\e[33mDoes this look good for you ?" N; then #onlinesubmit echo -ne "\e[0mSubmitting the transaction via the node... " ${cardanocli} ${cliEra} transaction submit --tx-file ${txFile} - if [ $? -ne 0 ]; then echo -e "\n\e[35mError - Make sure the pool is already registered.\n\e[0m\n"; exit $?; fi + if [ $? -ne 0 ]; then echo -e "\n\e[35mError - Cannot authorize this Committee-Cold-Hash.\n\e[0m\n"; exit $?; fi echo -e "\e[32mDONE\n" #Show the TxID diff --git a/cardano/testnet/23d_checkComOnChain.sh b/cardano/testnet/23d_checkComOnChain.sh index 30b68c4..caa515e 100755 --- a/cardano/testnet/23d_checkComOnChain.sh +++ b/cardano/testnet/23d_checkComOnChain.sh @@ -35,7 +35,7 @@ esac #Check can only be done in online mode if ${offlineMode}; then echo -e "\e[35mYou have to be in ONLINE or LIGHT mode to do this!\e[0m\n"; exit 1; fi -echo -e "\e[0mChecking CommitteeKey-Information on Chain - Resolving given Info into CommitteeKey-HASH:\n" +echo -e "\e[0mChecking CommitteeKey-Information on Chain - Resolving given Info into Committee-HASH:\n" #Check about the various input options: hex hash, bech id, .hash file, .cc-cold.hash file, .cc-hot.hash file, .vkey file, .cc-cold.vkey file, .cc-hot.vkey file if [[ "${checkCommitteeID//[![:xdigit:]]}" == "${checkCommitteeID}" && ${#checkCommitteeID} -eq 56 ]]; then #parameter is a committee-hash @@ -132,87 +132,45 @@ else fi -echo -e "\e[0mChecking Information about the CommitteeKey-HASH:\e[32m ${committeeHASH}\e[0m\n" +echo -e "\e[0mChecking Information about the Committee-HASH:\e[32m ${committeeHASH}\e[0m\n" # We don't make a difference if the given CommitteeKey-HASH is for a Cold-Key or for a Hot-Key, we query for both - +# For the CLI, we have to do the lookup in two steps, because CLI does not support to search for cold and hot keys at the same time # Get state data for the committeeHASH. When in online mode of course from the node and the chain, in light mode via koios - # COLD KEY CHECK case ${workMode} in "online") - #echo -e "\e[0mChecking Information about the Committee-COLD-HASH:\e[0m\n" showProcessAnimation "Query Committee-State Info: " & - committeeStateJSON=$(${cardanocli} ${cliEra} query committee-state --cold-verification-key-hash ${committeeHASH} 2> /dev/stdout ) + committeeStateJSON=$(${cardanocli} ${cliEra} query committee-state --cold-verification-key-hash ${committeeHASH} --cold-script-hash ${committeeHASH} 2> /dev/stdout ) if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + + #If there is no information in the result (length of the committee entrie is zero), try to query for the committeeHASH as a Hot-Key + if [[ $(jq -r ".committee | length" <<< ${committeeStateJSON}) -eq 0 ]]; then + showProcessAnimation "Query Committee-State Info: " & + committeeStateJSON=$(${cardanocli} ${cliEra} query committee-state --hot-key-hash ${committeeHASH} --hot-script-hash ${committeeHASH} 2> /dev/stdout ) + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + fi ;; - "light") echo -e "\n\e[91mINFORMATION - This script does not support Light-Mode yet, waiting for Koios support!\n\e[0m"; exit; + "light") + showProcessAnimation "Query Committee-State Info-LightMode: " & + committeeStateJSON=$(queryLight_committeeState "${committeeHASH}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; ;; -# -# "offline") readOfflineFile; #Reads the offlinefile into the offlineJSON variable -# drepStateJSON=$(jq -r ".drep.\"${drepID}\".drepStateJSON" <<< ${offlineJSON} 2> /dev/null) -# if [[ "${drepStateJSON}" == null ]]; then echo -e "\e[35mDRep-ID not included in the offline transferFile, please include it first online!\e[0m\n"; exit; fi -# ;; + *) ;; esac - -#If there is no information in the result (length of the committee entrie is zero), try to query for the committeeHASH as a Hot-Key -if [[ $(jq -r ".committee | length" <<< ${committeeStateJSON}) -eq 0 ]]; then - - # HOT KEY CHECK - case ${workMode} in - - "online") - #echo -e "\e[0mChecking Information about the Committee-HOT-HASH:\e[0m\n" - showProcessAnimation "Query Committee-State Info: " & - committeeStateJSON=$(${cardanocli} ${cliEra} query committee-state --hot-key-hash ${committeeHASH} 2> /dev/stdout ) - if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - ;; - - # "light") ;; - # - # "offline") readOfflineFile; #Reads the offlinefile into the offlineJSON variable - # drepStateJSON=$(jq -r ".drep.\"${drepID}\".drepStateJSON" <<< ${offlineJSON} 2> /dev/null) - # if [[ "${drepStateJSON}" == null ]]; then echo -e "\e[35mDRep-ID not included in the offline transferFile, please include it first online!\e[0m\n"; exit; fi - # ;; - *) ;; - - esac -fi - -#### DATA FORMAT -#{ -# "committee": { -# "keyHash-5f1b4429fe3bda963a7b70ab81135112a785afcf55ccd695b122e794": { -# "expiration": null, -# "hotCredsAuthStatus": { -# "contents": { -# "keyHash": "5aa349227e4068c85c03400396bcea13c7fd57d0ec78c604bc768fc5" -# }, -# "tag": "MemberAuthorized" -# }, -# "nextEpochChange": { -# "tag": "ToBeRemoved" -# }, -# "status": "Unrecognized" -# } -# }, -# "epoch": 240, -# "quorum": 0.0 -#} - comColdHashArray=(); readarray -t comColdHashArray <<< $(jq -r ".committee | keys_unsorted[]" <<< "${committeeStateJSON}" 2> /dev/null) comColdHashArrayCnt=$(jq -r ".committee | length" <<< "${committeeStateJSON}" 2> /dev/null) #If the amount of entries is zero, exit with a message that no information was found if [[ ${comColdHashArrayCnt} == 0 ]]; then echo -e "\e[0mFound: \e[33m0 entries\e[0m\n" - echo -e "\e[0mCommitteeKey HASH is \e[33mNOT\e[0m on the chain, no informations found!\n\e[0m"; + echo -e "\e[0mCommitteeKey/Script HASH is \e[33mNOT\e[0m on the chain, no informations found!\n\e[0m"; exit; fi @@ -229,7 +187,7 @@ do read comColdHotAuthTag; read comColdExpirationEpoch; read comColdStatus; - read comColdNextEpochChange; } <<< $(jq -r ".committee | length, ( .\"${comColdEntry}\" | .hotCredsAuthStatus.contents.keyHash // \"-\", .hotCredsAuthStatus.tag // \"-\", .expiration // \"-\", .status // \"-\", .nextEpochChange.tag // \"-\")" <<< ${committeeStateJSON}) + read comColdNextEpochChange; } <<< $(jq -r ".committee | length, ( .\"${comColdEntry}\" | .hotCredsAuthStatus.contents.keyHash // .hotCredsAuthStatus.contents.scriptHash // \"-\", .hotCredsAuthStatus.tag // \"-\", .expiration // \"-\", .status // \"-\", .nextEpochChange.tag // \"-\")" <<< ${committeeStateJSON}) comColdHash=${comColdEntry: -56} #last 56chars of the entry is the hash itself @@ -238,21 +196,21 @@ do case ${comColdHotAuthTag} in "MemberResigned") #Resigned - echo -e "\e[0m Committee-Cold-Key HASH: \e[33m${comColdHash} (RESIGNED) ";; + echo -e "\e[0m Committee-Cold-Key/Script HASH: \e[33m${comColdHash} (RESIGNED) ";; *) #Registered #Highlight the entry in green if it was the hash we initially searched for if [[ "${comColdHash}" == "${committeeHASH}" ]]; then activeColor="\e[32m"; else activeColor="\e[94m"; fi - echo -e "\e[0m Committee-Cold-Key HASH: ${activeColor}${comColdHash}";; + echo -e "\e[0m Committee-Cold-Key/Script HASH: ${activeColor}${comColdHash}";; esac #Highlight the entry in green if it was the hash we initially searched for if [[ "${comColdHotAuthHash}" == "${committeeHASH}" ]]; then activeColor="\e[32m"; else activeColor="\e[94m"; fi - echo -e "\e[0mAuthorizing-Hot-Key HASH: ${activeColor}${comColdHotAuthHash}\e[0m" - echo -e "\e[0m Current Status / Tag: \e[94m${comColdStatus} / ${comColdHotAuthTag}\e[0m" - echo -e "\e[0m Expiration Epoch: \e[94m${comColdExpirationEpoch}\e[0m" - echo -e "\e[0m Next Epoch Change: \e[94m${comColdNextEpochChange}\e[0m" + echo -e "\e[0mAuthorizing-Hot-Key/Script HASH: ${activeColor}${comColdHotAuthHash}\e[0m" + echo -e "\e[0m Current Status / Tag: \e[94m${comColdStatus} / ${comColdHotAuthTag}\e[0m" + echo -e "\e[0m Expiration Epoch: \e[94m${comColdExpirationEpoch}\e[0m" + echo -e "\e[0m Next Epoch Change: \e[94m${comColdNextEpochChange}\e[0m" echo echo diff --git a/cardano/testnet/23e_retComColdKeys.sh b/cardano/testnet/23e_retComColdKeys.sh index 63f7839..ad4d4cf 100755 --- a/cardano/testnet/23e_retComColdKeys.sh +++ b/cardano/testnet/23e_retComColdKeys.sh @@ -202,26 +202,28 @@ echo -e "\e[0mCommittee-Cold-Key HASH: \e[32m${comColdHash} \e[0m(\e[32m${comCol case ${workMode} in "online") - #echo -e "\e[0mChecking Information about the Committee-Cold-Key HASH:\e[0m\n" - showProcessAnimation "Query Committee-State Info: " & + showProcessAnimation "Query Committee-State Info: " & committeeStateJSON=$(${cardanocli} ${cliEra} query committee-state --cold-verification-key-hash ${comColdHash} 2> /dev/stdout ) if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; ;; - "light") echo -e "\n\e[91mINFORMATION - This script does not support Light-Mode yet, waiting for Koios support!\n\e[0m"; exit; - ;; -# -# + "light") + showProcessAnimation "Query Committee-State Info-LightMode: " & + committeeStateJSON=$(queryLight_committeeState "${comColdHash}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${committeeStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + ;; + "offline") echo -e "\n\e[91mINFORMATION - This script does not support Offline-Mode yet, waiting for Koios support!\n\e[0m"; exit; -# readOfflineFile; #Reads the offlinefile into the offlineJSON variable -# drepStateJSON=$(jq -r ".drep.\"${drepID}\".drepStateJSON" <<< ${offlineJSON} 2> /dev/null) -# if [[ "${drepStateJSON}" == null ]]; then echo -e "\e[35mDRep-ID not included in the offline transferFile, please include it first online!\e[0m\n"; exit; fi ;; *) ;; + esac +jq -r <<< ${committeeStateJSON} + + #### DATA FORMAT #{ # "committee": { @@ -245,7 +247,7 @@ esac read comColdHotAuthHash; read comColdExpirationEpoch; read comColdStatus; - read comColdNextEpochChange; } <<< $(jq -r ".committee | length, ( .\"keyHash-${comColdHash}\" | .hotCredsAuthStatus.contents.keyHash // \"-\", .expiration // \"-\", .status // \"-\", .nextEpochChange // \"-\")" <<< ${committeeStateJSON}) + read comColdNextEpochChange; } <<< $(jq -r ".committee | length, ( .\"keyHash-${comColdHash}\" | .hotCredsAuthStatus.contents.keyHash // \"-\", .expiration // \"-\", .status // \"-\", .nextEpochChange.tag // \"-\")" <<< ${committeeStateJSON}) #Checking about the content if [[ ${comColdEntryCnt} == 0 ]]; then #not registered yet diff --git a/cardano/testnet/24a_genVote.sh b/cardano/testnet/24a_genVote.sh index a64f11c..4cf60c9 100755 --- a/cardano/testnet/24a_genVote.sh +++ b/cardano/testnet/24a_genVote.sh @@ -39,6 +39,9 @@ EOF exit 1; fi +#Check can only be done in online mode +if ${offlineMode}; then echo -e "\e[35mYou have to be in ONLINE or LIGHT mode to do this! Signing and Submitting the Vote-Tx can of course be done later on in OFFLINE mode.\e[0m\n"; exit 1; fi + #exit with an information, that the script needs at least conway era case ${cliEra} in "babbage"|"alonzo"|"mary"|"allegra"|"shelley") @@ -53,7 +56,6 @@ voterName="$(dirname ${voterName})/$(basename $(basename $(basename $(basename $ voterFile="$(dirname ${1})/$(basename ${1} .vkey)"; voterFile=${voterFile/#.\//}; voterFile=${voterFile%\.}; govActionID="${2,,}"; - #Setting default variables anchorURL=""; anchorHASH=""; anchorPARAM=""; #Setting defaults @@ -88,7 +90,14 @@ for (( tmpCnt=2; tmpCnt<${paramCnt}; tmpCnt++ )) #Checks for needed files / parameters #Validate a correct Governance Action ID and split it up into the UTXO and index part -if [[ "${govActionID}" =~ ^([[:xdigit:]]{64}+#[[:digit:]]{1,})$ ]]; then +#Check if its a Governance Action-ID in Bech-Format +if [[ "${govActionID:0:11}" == "gov_action1" ]]; then #parameter is most likely a bech32-action-id + #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + govActionID=$(convert_actionBech2UTXO ${govActionID}) #converts the given action bech id (CIP-129) into standard UTXO#IDX format + if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${2,,}\" is not a valid Bech32 ACTION-ID.\e[0m"; exit 1; fi + govActionUTXO=${govActionID:0:64} + govActionIdx=$(( ${govActionID:65} + 0 )) #make sure to have single digits if provided like #00 #01 #02... +elif [[ "${govActionID}" =~ ^([[:xdigit:]]{64}+#[[:digit:]]{1,})$ ]]; then govActionUTXO=${govActionID:0:64} govActionIdx=$(( ${govActionID:65} + 0 )) #make sure to have single digits if provided like #00 #01 #02... else @@ -151,6 +160,10 @@ fi echo -e "\e[0mGenerating a Vote-File for ${voterType} PublicKey-File\e[32m ${voterVkeyFile}\e[0m" echo +#get the voterhash, this is used to display a previous voting answer if available +voterHash=$(jq -r ".cborHex" "${voterVkeyFile}" 2> /dev/null | cut -c 5-69 | xxd -r -ps | b2sum -l 224 -b | cut -d' ' -f 1 2> /dev/null) +if [[ ! "${voterHash,,}" =~ ^([[:xdigit:]]{56})$ ]]; then echo -e "\n\e[91mERROR - Could not generate Voter-Hash from VKEY-File '${voterVkeyFile}'\n\e[0m"; exit 1; fi + #If in online/light mode, check the anchorURL if ${onlineMode}; then @@ -228,71 +241,191 @@ echo case ${workMode} in "online") showProcessAnimation "Query Governance-Action Info: " & - actionStateJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/stdout) - if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${actionStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - actionEntry=$(jq -r ".proposals | to_entries[] | .value | select(.actionId.txId == \"${govActionUTXO}\" and .actionId.govActionIx == ${govActionIdx})" 2> /dev/null <<< "${actionStateJSON}") + govStateJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/stdout) + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${actionStateJSON}\e[0m\n"; exit 1; else stopProcessAnimation; fi; + actionStateJSON=$(jq -r ".proposals | to_entries[] | .value" 2> /dev/null <<< "${govStateJSON}") + if [ $? -ne 0 ]; then echo -e "\e[35mERROR - ${actionStateJSON}\e[0m\n"; exit 1; fi; + + #Filter for a given Action-ID + if [[ ${govActionUTXO} != "" && ${govActionIdx} != "" ]]; then + actionStateJSON=$(jq -r ". | select(.actionId.txId == \"${govActionUTXO}\" and .actionId.govActionIx == ${govActionIdx})" 2> /dev/null <<< "${actionStateJSON}") + if [[ "${actionStateJSON}" = "" ]]; then #action-id not on chain + echo -e "\e[0mThe provided Action-ID is\e[33m NOT present on the chain\e[0m!\e[0m\n"; + exit 1; + fi + fi + + #### Voting Power Stuff + #Get DRep Stake Distribution for quorum calculation later on + dRepStakeDistributionJSON=$(${cardanocli} ${cliEra} query drep-stake-distribution --all-dreps 2> /dev/stdout) + if [ $? -ne 0 ]; then echo -e "\e[35mERROR - ${dRepStakeDistributionJSON}\e[0m\n"; exit 1; fi; + + #Calculate the total dRep stake power (sum of all drep stake distribution entries without the alwaysAbstain and alwaysNoConfidence ones) + dRepPowerTotal=$(jq -r '[del(.[] | select(.[0] == "drep-alwaysAbstain" or .[0] == "drep-alwaysNoConfidence")) | .[][1]] | add' <<< "${dRepStakeDistributionJSON}" 2> /dev/null) + #Get the alwaysNoConfidence stake power (counts as a no-power in all actions, except the NoConfidence-Action, there it counts to the yes-power) + dRepPowerAlwaysNoConfidence=$(jq -r '(.[] | select(.[0] == "drep-alwaysNoConfidence") | .[1]) // 0' <<< "${dRepStakeDistributionJSON}" 2> /dev/null) + + #Get Pool Stake Distribution for quorum calculation later on - available with this command since cli 9.3.0.0 + poolStakeDistributionJSON=$(${cardanocli} ${cliEra} query spo-stake-distribution --all-spos 2> /dev/stdout) + if [ $? -ne 0 ]; then echo -e "\e[35mERROR - ${poolStakeDistributionJSON}\e[0m\n"; exit 1; fi; + + #Calculate the total pool stake power (sum of all stake that is delegated to pools) + poolPowerTotal=$(jq -r '[.[][1]] | add' <<< "${poolStakeDistributionJSON}" 2> /dev/null) + + #Get the current committee member count and voting threshold + { read committeePowerTotal; read committeePowerThreshold; } <<< $(jq -r '(.committee.members | length) // 0, (.committee.threshold) // 0' <<< ${govStateJSON} 2> /dev/null) + committeePowerThreshold=$(bc <<< "scale=2; 100.00 * ${committeePowerThreshold}") #scale it to 0.00-100.00% + + #Get the current protocolParameters for the dRep and pool voting thresholds + protocolParametersJSON=$(${cardanocli} ${cliEra} query protocol-parameters) ;; - "light") #showProcessAnimation "Query DRep-ID-Info-LightMode: " & - echo -e "\n\e[91mINFORMATION - This script does not support Light-Mode yet, waiting for Koios support!\n\e[0m"; exit; -# drepStateJSON=$(queryLight_drepInfo "${drepID}") -# if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - ;; -# - "offline") echo -e "\n\e[91mINFORMATION - This script does not support Offline-Mode yet, waiting for Koios support!\n\e[0m"; exit; -# readOfflineFile; #Reads the offlinefile into the offlineJSON variable -# drepStateJSON=$(jq -r ".drep.\"${drepID}\".drepStateJSON" <<< ${offlineJSON} 2> /dev/null) -# if [[ "${drepStateJSON}" == null ]]; then echo -e "\e[35mDRep-ID not included in the offline transferFile, please include it first online!\e[0m\n"; exit; fi - ;; -esac + "light") showProcessAnimation "Query Governance-Action Info-LightMode: " & + actionStateJSON=$(queryLight_actionState "${govActionUTXO}" "${govActionIdx}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${actionStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + #Filter for a given Action-ID was already done in the queryLight_actionState + #strip the outter array for now + actionStateJSON=$(jq -r ".[]" 2> /dev/null <<< "${actionStateJSON}") + + #Get the current protocolParameters for the dRep, pool and committee voting thresholds + protocolParametersJSON=${lightModeParametersJSON} #lightmode + + #Get the current committee member count and voting threshold + { read committeePowerTotal; read committeeThreshold; } <<< $(jq -r '(.committee.members | length) // 0, "\(.committee.threshold)" // 0' 2> /dev/null <<< "${protocolParametersJSON}") + committeeThresholdType=$(jq -r "type" <<< "${committeeThreshold}" 2> /dev/null) + case ${committeeThresholdType} in + "object") + { read numerator; read denominator; } <<< $(jq -r '.numerator // "-", .denominator // "-"' <<< "${committeeThreshold}") + committeePowerThreshold=$(bc <<< "scale=2; 100 * ${numerator} / ${denominator}") + ;; -case ${workMode} in + "number") + committeePowerThreshold=$(bc <<< "scale=2; 100 * ${committeeThreshold}") + ;; + esac + ;; - "online"|"light") +esac - #Checking about the action content - if [[ "${actionEntry}" = "" ]]; then #proposal not on chain - echo -e "\e[0mThe provided Action-ID is\e[33m NOT present on the chain\e[0m!\e[0m\n"; - exit 1; +#Convert the result(s) into an array and get the number of entries +actionStateJSON=$(jq --slurp <<< ${actionStateJSON}) +actionStateEntryCnt=$(jq -r "length" <<< ${actionStateJSON}) +if [[ ${actionStateEntryCnt} -eq 0 ]]; then echo -e "\e[91mNo matching votes found.\e[0m\n"; else echo -e "\e[0mFound: \e[32m${actionStateEntryCnt} entry/entries\e[0m\n"; fi + +#Show all found entries +for (( tmpCnt=0; tmpCnt<${actionStateEntryCnt}; tmpCnt++ )) +do + + #Get the indexed Entry + actionEntry=$(jq -r ".[${tmpCnt}]" <<< ${actionStateJSON}) + + #In Light-Mode, request the content of the individual votes now + if [[ ${workMode} == "light" ]]; then + { read actionUTXO; read actionIdx; } <<< $(jq -r '.actionId.txId // "-", .actionId.govActionIx // "-"' <<< ${actionEntry}) + showProcessAnimation "Query Action-Votes LightMode: " & + actionVotesJSON=$(queryLight_actionVotes "${actionUTXO}" "${actionIdx}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${actionVotesJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + #merge the requested content in the current actionEntry + actionEntry=$(jq -r ". += ${actionVotesJSON}" 2> /dev/null <<< "${actionEntry}") fi - #We have found an action, lets get the Tag and number of votes so far - { read actionTag; - read actionUTXO; - read actionIdx; - read actionContents; - read actionAnchorUrl; - read actionAnchorHash; - read actionProposedInEpoch; - read actionExpiresAfterEpoch; - read actionDepositReturnKeyType; - read actionDepositReturnHash; - read actionDepositReturnNetwork; - read actionDRepVoteYesCount; - read actionDRepVoteNoCount; - read actionDRepAbstainCount; - read actionPoolVoteYesCount; - read actionPoolVoteNoCount; - read actionPoolAbstainCount; - read actionCommitteeVoteYesCount; - read actionCommitteeVoteNoCount; - read actionCommitteeAbstainCount; + { read actionTag; read actionUTXO; read actionIdx; read actionContents; + read actionAnchorUrl; read actionAnchorHash; + read actionProposedInEpoch; read actionExpiresAfterEpoch; + read actionDepositReturnKeyType; read actionDepositReturnHash; read actionDepositReturnNetwork; + read actionDRepVoteYesCount; read actionDRepVoteNoCount; read actionDRepAbstainCount; + read actionPoolVoteYesCount; read actionPoolVoteNoCount; read actionPoolAbstainCount; + read actionCommitteeVoteYesCount; read actionCommitteeVoteNoCount; read actionCommitteeAbstainCount; } <<< $(jq -r '.proposalProcedure.govAction.tag // "-", .actionId.txId // "-", .actionId.govActionIx // "-", "\(.proposalProcedure.govAction.contents)" // "-", .proposalProcedure.anchor.url // "-", - .proposalProcedure.anchor.dataHash // "-", .proposedIn // "-", .expiresAfter // "-", (.proposalProcedure.returnAddr.credential|keys[0]) // "-", (.proposalProcedure.returnAddr.credential|flatten[0]) // "-", .proposalProcedure.returnAddr.network // "-", - (.dRepVotes | with_entries(select(.value == "VoteYes")) | length), - (.dRepVotes | with_entries(select(.value == "VoteNo")) | length), - (.dRepVotes | with_entries(select(.value == "Abstain")) | length), - (.stakePoolVotes | with_entries(select(.value == "VoteYes")) | length), - (.stakePoolVotes | with_entries(select(.value == "VoteNo")) | length), - (.stakePoolVotes | with_entries(select(.value == "Abstain")) | length), - (.committeeVotes | with_entries(select(.value == "VoteYes")) | length), - (.committeeVotes | with_entries(select(.value == "VoteNo")) | length), - (.committeeVotes | with_entries(select(.value == "Abstain")) | length)' <<< ${actionEntry}) + .proposalProcedure.anchor.dataHash // "-", .proposedIn // "-", .expiresAfter // "-", + (.proposalProcedure.returnAddr.credential|keys[0]) // "-", (.proposalProcedure.returnAddr.credential|flatten[0]) // "-", .proposalProcedure.returnAddr.network // "-", + (.dRepVotes | with_entries(select(.value | contains("Yes"))) | length), + (.dRepVotes | with_entries(select(.value | contains("No"))) | length), + (.dRepVotes | with_entries(select(.value | contains("Abstain"))) | length), + (.stakePoolVotes | with_entries(select(.value | contains("Yes"))) | length), + (.stakePoolVotes | with_entries(select(.value | contains("No"))) | length), + (.stakePoolVotes | with_entries(select(.value | contains("Abstain"))) | length), + (.committeeVotes | with_entries(select(.value | contains("Yes"))) | length), + (.committeeVotes | with_entries(select(.value | contains("No"))) | length), + (.committeeVotes | with_entries(select(.value | contains("Abstain"))) | length)' <<< ${actionEntry}) + + + case ${workMode} in + + "online") #Calculate the VotingPowers via cli + #Generate lists with the DRep hashes that are voted yes, no or abstain. Add a 'drep-' infront of each entry to mach up the syntax in the 'drep-stake-distribution' json + { read dRepHashYes; read dRepHashNo; read dRepHashAbstain; } <<< $(jq -r '"\(.dRepVotes | with_entries(select(.value | contains("Yes"))) | keys | ["drep-\(.[])"] )", + "\(.dRepVotes | with_entries(select(.value | contains("No"))) | keys | ["drep-\(.[])"])", + "\(.dRepVotes | with_entries(select(.value | contains("Abstain"))) | keys | ["drep-\(.[])"])"' <<< ${actionEntry} 2> /dev/null) + #Calculate the total power of the yes, no and abstain keys + { read dRepPowerYes; read dRepPowerNo; read dRepPowerAbstain;} <<< $(jq -r "([ .[] | select(.[0]==${dRepHashYes}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${dRepHashNo}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${dRepHashAbstain}[]) | .[1] ] | add) // 0" <<< "${dRepStakeDistributionJSON}" 2> /dev/null) + #Calculate the acceptance percentage for the DRep group + if [[ "${actionTag}" != "NoConfidence" ]]; then #normal percentage calculate if its not a NoConfidence + dRepPct=$(bc <<< "scale=2; 100.00 * ${dRepPowerYes} / ( ${dRepPowerTotal} + ${dRepPowerAlwaysNoConfidence} - ${dRepPowerAbstain} )") + else #in case of NoConfidence, the dRepPowerAlwaysNoConfidence counts towards the yes counts + dRepPct=$(bc <<< "scale=2; 100.00 * ( ${dRepPowerYes} + ${dRepPowerAlwaysNoConfidence} ) / ( ${dRepPowerTotal} - ${dRepPowerAbstain} )") + fi + #Generate lists with the pool hashes that are voted yes, no or abstain. + { read poolHashYes; read poolHashNo; read poolHashAbstain; } <<< $(jq -r '"\(.stakePoolVotes | with_entries(select(.value | contains("Yes"))) | keys )", + "\(.stakePoolVotes | with_entries(select(.value | contains("No"))) | keys)", + "\(.stakePoolVotes | with_entries(select(.value | contains("Abstain"))) | keys)"' <<< ${actionEntry} 2> /dev/null) + #Calculate the total power of the yes, no and abstain keys + { read poolPowerYes; read poolPowerNo; read poolPowerAbstain;} <<< $(jq -r "([ .[] | select(.[0]==${poolHashYes}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${poolHashNo}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${poolHashAbstain}[]) | .[1] ] | add) // 0" <<< "${poolStakeDistributionJSON}" 2> /dev/null) + #Calculate the acceptance percentage for the Pool group + poolPct=$(bc <<< "scale=2; ( 100.00 * ${poolPowerYes} ) / ( ${poolPowerTotal} - ${poolPowerAbstain} )") + + #### DEBUG OUTPUT +# echo -e "*** DREPS ***" +# echo -e "YES-Hash:\n${dRepHashYes}\nNO-Hash:\n${dRepHashNo}\nABSTAIN-Hash:\n${dRepHashAbstain}\n" +# echo -e "YES-Power: ${dRepPowerYes}\nNO-Power: ${dRepPowerNo}\nABSTAIN-Power: ${dRepPowerAbstain}\n" +# echo -e "DREP-Total-Power: ${dRepPowerTotal}" +# echo -e "DREP-AlwaysAbstain-Power: ${dRepPowerAlwaysAbstain}\n" +# echo -e "DREP-AlwaysNoConfidence-Power: ${dRepPowerAlwaysNoConfidence}\n" +# echo -e "DREP-Vote-PCT = 100 * ${dRepPowerYes} / ( ${dRepPowerTotal} + ${dRepPowerAlwaysNoConfidence} - ${dRepPowerAbstain} )" +# echo -e "DREP-Vote-PCT = ${dRepPct}%\n" +# echo -e "*** POOLS ***" +# echo -e "YES-Hash:\n${poolHashYes}\nNO-Hash:\n${poolHashNo}\nABSTAIN-Hash:\n${poolHashAbstain}\n" +# echo -e "YES-Power: ${poolPowerYes}\nNO-Power: ${poolPowerNo}\nABSTAIN-Power: ${poolPowerAbstain}\n" +# echo -e "POOL-Total-Power: ${poolPowerTotal}\n" +# echo -e "POOL-Vote-PCT = 100 * ${poolPowerYes} / ( ${poolPowerTotal} - ${poolPowerAbstain} )" +# echo -e "POOL-Vote-PCT = ${poolPct}%\n" +# echo -e "*** COMMITTEE ***" +# echo -e "COMMITTEE-Total-Power: ${committeePowerTotal}" +# echo -e "COMMITTEE-Threshold: ${committeePowerThreshold}" +# echo -e "COMMITTEE-Vote-PCT = ${committeePct}%\n" + ;; + + "light") #Get the VotingPowers/Percentage via koios + echo -e "\e[33mLive voting percentage calculation not yet available in Light-Mode!\e[0m"; + dRepPct=0 + poolPct=0 + ;; + esac + + #Calculate the acceptance percentage for the committee + if [[ $((${committeePowerTotal}-${actionCommitteeAbstainCount})) -eq 0 ]]; then + committeePct=0; + else + committeePct=$(bc <<< "scale=2; ( 100.00 * ${actionCommitteeVoteYesCount} ) / ( ${committeePowerTotal} - ${actionCommitteeAbstainCount} )"); + fi + #Setup variables + totalAccept=""; totalAcceptIcon=""; + dRepAcceptIcon=""; poolAcceptIcon=""; committeeAcceptIcon=""; + dRepPowerThreshold="N/A"; poolPowerThreshold="N/A"; #N/A -> not available + + echo + echo -e "\e[36m--- Entry $((${tmpCnt}+1)) of ${actionStateEntryCnt} --- Action-ID ${actionUTXO}#${actionIdx}\e[0m" + echo + echo -e "Action-Bech: \e[32m$(convert_actionUTXO2Bech "${actionUTXO}#${actionIdx}")\e[0m" echo - echo -e "\e[0mAction-Type: \e[32m${actionTag}\e[0m \tProposed in Epoch: \e[32m${actionProposedInEpoch}\e[0m \tExpires after Epoch: \e[32m${actionExpiresAfterEpoch}\e[0m" + echo -e "\e[0mAction-Type: \e[32m${actionTag}\e[0m \tProposed in Epoch: \e[32m${actionProposedInEpoch}\e[0m \tExpires after Epoch: \e[32m${actionExpiresAfterEpoch}\e[0m" echo #Show the Anchor-URL(HASH) if available @@ -320,15 +453,42 @@ case ${workMode} in esac - #DO A NICE OUTPUT OF THE DIFFERENT CONTENTS +### threshold parameters +# "dRepVotingThresholds": { +# "committeeNoConfidence": 0.65, +# "committeeNormal": 0.67, +# "hardForkInitiation": 0.6, +# "motionNoConfidence": 0.67, +# "ppEconomicGroup": 0.67, +# "ppGovGroup": 0.75, +# "ppNetworkGroup": 0.67, +# "ppTechnicalGroup": 0.67, +# "treasuryWithdrawal": 0.67, +# "updateToConstitution": 0.75 +# }, +# "poolVotingThresholds": { +# "committeeNoConfidence": 0.65, +# "committeeNormal": 0.65, +# "hardForkInitiation": 0.51, +# "motionNoConfidence": 0.6, +# "ppSecurityGroup": 0.6 +# }, + + + #DO A NICE OUTPUT OF THE DIFFERENT CONTENTS & DO THE RIGHT CALCULATIONS FOR THE ACCEPTANCE case "${actionTag}" in - "InfoAction") #show the proposed major/minor version to fork to + "InfoAction") #This is just an InfoAction #Show referencing Action-Id if avaiable { read prevActionUTXO; read prevActionIDX; } <<< $(jq -r '.txId // "-", .govActionIx // "-"' 2> /dev/null <<< ${actionContents}) if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi echo -e "\e[0mAction-Content:\e[36m Information\e[0m" - echo -e "\e[0m";; + echo -e "\e[0m" + + dRepAcceptIcon="N/A"; poolAcceptIcon="N/A"; + totalAccept="N/A"; + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; "HardForkInitiation") #show the proposed major/minor version to fork to @@ -344,7 +504,17 @@ case ${workMode} in if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi echo -e "\e[0mAction-Content:\e[36m Do a Hardfork\e[0m\n" echo -e "\e[0mFork to\e[32m Protocol-Version \e[0m► \e[94m${forkMajorVer}.${forkMinorVer}\e[0m" - echo -e "\e[0m";; + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; read poolPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.hardForkInitiation // 0, .poolVotingThresholds.hardForkInitiation // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; + "ParameterChange") #show the proposed parameterchanges # [ @@ -368,7 +538,64 @@ case ${workMode} in echo -e "\e[0mAction-Content:\e[36m Change protocol parameters\n\e[0m" changeParameterRender=$(jq -r 'to_entries[] | "\\e[0mChange parameter\\e[32m \(.key) \\e[0m► \\e[94m\(.value)\\e[0m"' <<< ${changeParameters} 2> /dev/null) echo -e "${changeParameterRender}" - echo -e "\e[0m";; + echo -e "\e[0m" + + dRepPowerThreshold="0"; #start with a zero threshold, we are searching the max value in the next steps + + parameterSecurityGroup="false" + + #Calculate acceptance depending on the security group a parameter belongs to: Get the right threshold, make it a nice percentage number, check if threshold is reached + case "${changeParameters}" in + + #SECURITY GROUP - pools must vote on it + *"maxBlockBodySize"*|*"maxTxSize"*|*"maxBlockHeaderSize"*|*"maxValueSize"*|*"maxBlockExecutionUnits"*|*"txFeePerByte"*|*"txFeeFixed"*|*"utxoCostPerByte"*|*"govActionDeposit"*|*"minFeeRefScriptCostPerByte"*) + { read poolPowerThreshold; } <<< $(jq -r '.poolVotingThresholds.ppSecurityGroup // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + echo -e "A parameter from the \e[32mSECURITY\e[0m group is present ► \e[94mStakePools must vote\e[0m" + parameterSecurityGroup="true" + ;;& #also check next condition + + #NETWORK GROUP + *"maxBlockBodySize"*|*"maxTxSize"*|*"maxBlockHeaderSize"*|*"maxValueSize"*|*"maxTxExecutionUnits"*|*"maxBlockExecutionUnits"*|*"maxCollateralInputs"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppNetworkGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mNETWORK\e[0m group is present" + ;;& #also check next condition + + #ECONOMIC GROUP + *"txFeePerByte"*|*"txFeeFixed"*|*"stakeAddressDeposit"*|*"stakePoolDeposit"*|*"monetaryExpansion"*|*"treasuryCut"*|*"minPoolCost"*|*"utxoCostPerByte"*|*"executionUnitPrices"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppEconomicGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mECONOMIC\e[0m group is present" + ;;& #also check next condition + + #TECHNICAL GROUP + *"poolPledgeInfluence"*|*"poolRetireMaxEpoch"*|*"stakePoolTargetNum"*|*"costModels"*|*"collateralPercentage"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppTechnicalGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mTECHNICAL\e[0m group is present" + ;;& #also check next condition + + #GOVERNANCE GROUP + *"govActionLifetime"*|*"govActionDeposit"*|*"dRepDeposit"*|*"dRepActivity"*|*"committeeMinSize"*|*"committeeMaxTermLength"*|*"VotingThresholds"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppGovGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mGOVERNANCE\e[0m group is present" + ;; + + esac + + #Throw an error if for some reason, the dRepPowerThreshold is still at zero or empty + if [[ "${dRepPowerThreshold}" == "0" || "${dRepPowerThreshold}" == "" ]]; then echo -e "\e[35mERROR - Something went wrong finding the dRepPowerThreshold.\n\e[0m"; exit 1; fi + + echo + + #Now lets use the choosen threshold (highest of all involved groups) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + + #committee can vote on all parameters + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + + ;; + "NewConstitution") #show the proposed infos/anchor for a new constition # [ @@ -383,12 +610,22 @@ case ${workMode} in # } # } # ] - { read prevActionUTXO; read prevActionIDX; read anchorHash; read anchorURL; } <<< $(jq -r '.[0].txId // "-", .[0].govActionIx // "-", .[1].anchor.dataHash // "-", .[1].anchor.url // "-"' 2> /dev/null <<< ${actionContents}) + { read prevActionUTXO; read prevActionIDX; read anchorHash; read anchorURL; read scriptHash; } <<< $(jq -r '.[0].txId // "-", .[0].govActionIx // "-", .[1].anchor.dataHash // "-", .[1].anchor.url // "-", .[1].script // "-"' 2> /dev/null <<< ${actionContents}) if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi echo -e "\e[0mAction-Content:\e[36m Change to a new Constitution\e[0m\n" echo -e "\e[0mSet new\e[32m Constitution-URL \e[0m► \e[94m${anchorURL}\e[0m" echo -e "\e[0mSet new\e[32m Constitution-Hash \e[0m► \e[94m${anchorHash}\e[0m" - echo -e "\e[0m";; + echo -e "\e[0mSet new\e[32m Guardrails-Script-Hash \e[0m► \e[94m${scriptHash}\e[0m" + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.updateToConstitution // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolAcceptIcon=""; #pools not allowed to vote on this + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; + "UpdateCommittee") #show the proposed infos for a committeeupdate # [ @@ -429,7 +666,32 @@ case ${workMode} in remHashesRender=$(jq -r '.[1][] // [] | to_entries[] | "\\e[0mRemove\\e[32m \(.key) \\e[0m◄ \\e[91m\(.value)\\e[0m"' <<< ${actionContents} 2> /dev/null) echo -e "${addHashesRender}" echo -e "${remHashesRender}" - echo -e "\e[0m";; + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; read poolPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.committeeNormal // 0, .poolVotingThresholds.committeeNormal // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then dRepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + committeeAcceptIcon=""; #committee not allowed to vote on this + ;; + + "NoConfidence") #This is just a NoConfidence action + #Show referencing Action-Id if avaiable + { read prevActionUTXO; read prevActionIDX; } <<< $(jq -r '.txId // "-", .govActionIx // "-"' 2> /dev/null <<< ${actionContents}) + if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi + echo -e "\e[0mAction-Content:\e[36m No Confidence in the Committee\e[0m" + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; read poolPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.committeeNoConfidence // 0, .poolVotingThresholds.committeeNoConfidence // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + committeeAcceptIcon=""; #committee not allowed to vote on this + ;; "TreasuryWithdrawals") #show the treasury withdrawals address and amount #[ @@ -446,10 +708,11 @@ case ${workMode} in # ], # null #] - { read withdrawalsAmount; read withdrawalsKeyType; read withdrawalsHash; read withdrawalsNetwork; } <<< $( jq -r '.[0][0][1], (.[0][0][0].credential|keys[0]) // "-", (.[0][0][0].credential|flatten[0]) // "-", .[0][0][0].network // "-"' 2> /dev/null <<< ${actionContents}) + { read withdrawalsAmount; read withdrawalsKeyType; read withdrawalsHash; read withdrawalsNetwork; } <<< $( jq -r '.[0][0][1] // "0", (.[0][0][0].credential|keys[0]) // "-", (.[0][0][0].credential|flatten[0]) // "-", .[0][0][0].network // "-"' 2> /dev/null <<< ${actionContents}) echo -e "\e[0mAction-Content:\e[36m Withdrawal funds from the treasury\n\e[0m" case "${withdrawalsNetwork,,}${withdrawalsKeyType,,}" in + *"scripthash") echo -e "\e[0mWithdrawal to\e[32m ScriptHash \e[0m► \e[94m${withdrawalsHash}\e[0m" ;; @@ -463,41 +726,80 @@ case ${workMode} in echo -e "\e[0mWithdrawal to\e[32m StakeAddr \e[0m► \e[94m${withdrawalsAddr}\e[0m" ;; - *) echo -e "\n\e[35mERROR - Unknown network type ${actionDepositReturnNetwork} for the Withdrawal KeyHash !\n\e[0m"; exit 1; + "") echo -e "\e[0mWithdrawal \e[32mdirectly\e[0m to the \e[94mDeposit-Return-Address\n\e[0m" + ;; + + *) echo -e "\n\e[35mERROR - Unknown network type ${withdrawalsNetwork} for the Withdrawal KeyHash !\n\e[0m"; exit 1; ;; esac echo -e "\e[0mWithdrawal the\e[32m Amount \e[0m► \e[94m$(convertToADA ${withdrawalsAmount}) ADA / ${withdrawalsAmount} lovelaces\e[0m" - echo -e "\e[0m";; + echo -e "\e[0m" - esac + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.treasuryWithdrawal // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolAcceptIcon=""; #pools not allowed to vote on this + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; - echo -e "\e[0mCurrent Votes\tYes\tNo\tAbstain" - echo -e "\e[0m---------------------------------------" - echo -e "\e[94m DReps\t\e[32m${actionDRepVoteYesCount}\t\e[91m${actionDRepVoteNoCount}\t\e[33m${actionDRepAbstainCount}\e[0m" - echo -e "\e[94m StakePools\t\e[32m${actionPoolVoteYesCount}\t\e[91m${actionPoolVoteNoCount}\t\e[33m${actionPoolAbstainCount}\e[0m" - echo -e "\e[94m Committee\t\e[32m${actionCommitteeVoteYesCount}\t\e[91m${actionCommitteeVoteNoCount}\t\e[33m${actionCommitteeAbstainCount}\e[0m" - echo - #Check if the used voterType is allowed to do a vote on the actionTag - case "${voterType}_${actionTag}" in - "Committee-Hot_NoConfidence"|"Committee-Hot_UpdateCommittee"|"Pool_NewConstitution"|"Pool_ParameterChange"|"Pool_TreasuryWithdrawals") - echo -e "\n\e[91mSORRY - This voterType '${voterType}' is not allowed to vote on an action of type '${actionTag}'!\n\e[0m"; exit 1;; esac - ;; #online|light + printf "\e[97mCurrent Votes\e[90m │ \e[0mYes\e[90m │ \e[0mNo\e[90m │ \e[0mAbstain\e[90m │ \e[0mThreshold\e[90m │ \e[97mLive-Pct\e[90m │ \e[97mAccept\e[0m\n" + printf "\e[90m──────────────┼─────────┼────────┼─────────┼───────────┼──────────┼────────\e[0m\n" + if [[ "${dRepAcceptIcon}" != "" ]]; then + printf "\e[94m%13s\e[90m │ \e[32m%7s\e[90m │ \e[91m%6s\e[90m │ \e[33m%7s\e[90m │ \e[0m%7s %%\e[90m │ \e[97m%6s %%\e[90m │ %b \e[0m\n" "DReps" "${actionDRepVoteYesCount}" "${actionDRepVoteNoCount}" "${actionDRepAbstainCount}" "${dRepPowerThreshold}" "${dRepPct}" "${dRepAcceptIcon}" + else + printf "\e[90m%13s\e[90m │ \e[90m%7s\e[90m │ \e[90m%6s\e[90m │ \e[90m%7s\e[90m │ \e[90m%7s %%\e[90m │ \e[90m%6s %%\e[90m │ %b \e[0m\n" "DReps" "-" "-" "-" "-" "-" "" + fi + if [[ "${poolAcceptIcon}" != "" ]]; then + printf "\e[94m%13s\e[90m │ \e[32m%7s\e[90m │ \e[91m%6s\e[90m │ \e[33m%7s\e[90m │ \e[0m%7s %%\e[90m │ \e[97m%6s %%\e[90m │ %b \e[0m\n" "StakePools" "${actionPoolVoteYesCount}" "${actionPoolVoteNoCount}" "${actionPoolAbstainCount}" "${poolPowerThreshold}" "${poolPct}" "${poolAcceptIcon}" + else + printf "\e[90m%13s\e[90m │ \e[90m%7s\e[90m │ \e[90m%6s\e[90m │ \e[90m%7s\e[90m │ \e[90m%7s %%\e[90m │ \e[90m%6s %%\e[90m │ %b \e[0m\n" "StakePools" "-" "-" "-" "-" "-" "" + fi + if [[ "${committeeAcceptIcon}" != "" ]]; then + printf "\e[94m%13s\e[90m │ \e[32m%7s\e[90m │ \e[91m%6s\e[90m │ \e[33m%7s\e[90m │ \e[0m%7s %%\e[90m │ \e[97m%6s %%\e[90m │ %b \e[0m\n" "Committee" "${actionCommitteeVoteYesCount}" "${actionCommitteeVoteNoCount}" "${actionCommitteeAbstainCount}" "${committeePowerThreshold}" "${committeePct}" "${committeeAcceptIcon}" + else + printf "\e[90m%13s\e[90m │ \e[90m%7s\e[90m │ \e[90m%6s\e[90m │ \e[90m%7s\e[90m │ \e[90m%7s %%\e[90m │ \e[90m%6s %%\e[90m │ %b \e[0m\n" "Committee" "-" "-" "-" "-" "-" "" + fi + printf "\e[90m──────────────┴─────────┴────────┴─────────┴───────────┴──────────┼────────\e[0m\n" + case "${totalAccept}" in + *"N/A"*) totalAcceptIcon="N/A";; + *"NO"*) totalAcceptIcon="\e[91m❌";; + *) totalAcceptIcon="\e[92m✅";; + esac + printf "\e[97m%65s\e[90m │ %b \e[0m\n" "Full approval of the proposal" "${totalAcceptIcon}" +# printf "\e[90m──────────────────────────────────────────────────────────────────┴────────\e[0m\n" + + echo + + #If there is a voterHash, get the voting answer for it + if [[ "${voterHash}" != "" ]]; then + voteAnswer=$(jq -r ".dRepVotes[\"keyHash-${voterHash}\"] // .committeeVotes[\"keyHash-${voterHash}\"] // .dRepVotes[\"scriptHash-${voterHash}\"] // .committeeVotes[\"scriptHash-${voterHash}\"] // .stakePoolVotes[\"${voterHash}\"]" 2> /dev/null <<< "${actionEntry}") + #In case its included in the answers, show the current on chain status + case "${voteAnswer}" in + *"Yes"*) echo -e "\e[97mYou've already voted on this Action-ID before, your on chain ${voterType}-Voter answer is: \e[102m\e[30m YES \e[0m\n";; + *"No"*) echo -e "\e[97mYou've already voted on this Action-ID before, your on chain ${voterType}-Voter answer is: \e[101m\e[30m NO \e[0m\n";; + *"Abstain"*) echo -e "\e[97mYou've already voted on this Action-ID before, your on chain ${voterType}-Voter answer is: \e[43m\e[30m ABSTAIN \e[0m\n";; + esac + fi + +done + +#Check if the used voterType is allowed to do a vote on the actionTag +case "${voterType}_${actionTag}" in + + "Committee-Hot_NoConfidence"|"Committee-Hot_UpdateCommittee"|"Pool_NewConstitution"|"Pool_TreasuryWithdrawals") + echo -e "\n\e[91mSORRY - This voterType '${voterType}' is not allowed to vote on an action of type '${actionTag}'!\n\e[0m"; exit 1 + ;; + "Pool_ParameterChange") + if [[ "${parameterSecurityGroup}" == "false" ]]; then echo -e "\n\e[91mSORRY - This proposal does not contain a parameter of the SecurityGroup, so voterType '${voterType}' is not allowed to vote!\n\e[0m"; exit 1; fi + ;; esac -#echo -e "\e[0mYour voting decision on that Action-ID?\e[32m" -#select choice in "Yes" "No" "Abstain" "Cancel"; do -# case ${choice} in -# "Yes" ) actionVote="Yes"; break;; -# "No" ) actionVote="No"; break;; -# "Abstain" ) actionVote="Abstain"; break;; -# * ) echo -e "\e[0m"; exit;; -# esac -#done #Get the voting answer from the user while true; do diff --git a/cardano/testnet/24b_regVote.sh b/cardano/testnet/24b_regVote.sh index d86c1d7..1104260 100755 --- a/cardano/testnet/24b_regVote.sh +++ b/cardano/testnet/24b_regVote.sh @@ -214,7 +214,7 @@ for (( tmpCnt=2; tmpCnt<${paramCnt}; tmpCnt++ )) #Get action-id voteActionUTXO=${voteActionID:0:64} voteActionIdx=${voteActionID:65} - echo -e "\e[0m Action-Tx-ID: \e[94m${voteActionUTXO}\n\e[0m Action-Index: \e[94m${voteActionIdx}\e[0m" + echo -e "\e[0m Action-Tx-ID: \e[94m${voteActionUTXO}\n\e[0m Action-Index: \e[94m${voteActionIdx}\n\e[0m Action-Bech: \e[94m$(convert_actionUTXO2Bech "${voteActionUTXO}#${voteActionIdx}")\e[0m" #Get action-url and hash if [[ "${voteActionAnchorURL}" != "-" && "${voteActionAnchorHASH}" != "-" ]]; then @@ -336,7 +336,12 @@ checkError "$?"; if [ $? -ne 0 ]; then exit $?; fi #Do a check if we are at least in conway-era (protocol major 9 and above) protocolVersionMajor=$(jq -r ".protocolVersion.major // -1" <<< ${protocolParametersJSON}) if [[ ${protocolVersionMajor} -lt 9 ]]; then - echo -e "\n\e[35mERROR - The current era on the chain does not support submitting governance votes. Needs conway-era and above!\n\e[0m"; exit 1; fi + echo -e "\n\e[35mERROR - The current era on the chain does not support submitting governance votes. Needs conway-era and above!\n\e[0m"; exit 1; +elif [[ ${protocolVersionMajor} -eq 9 ]] && [[ ${voterType} == "DRep" ]]; then + echo -e "\n\e[35mSORRY - We are currently in conway bootstrap-phase with protocol version 9. During this period, DReps are not allowed to do any votes!\n\e[0m"; exit 1; +fi + + #get live values currentTip=$(get_currentTip); checkError "$?"; diff --git a/cardano/testnet/24c_queryVote.sh b/cardano/testnet/24c_queryVote.sh index 40a2eb4..e308e61 100755 --- a/cardano/testnet/24c_queryVote.sh +++ b/cardano/testnet/24c_queryVote.sh @@ -75,6 +75,7 @@ if ${offlineMode}; then echo -e "\e[35mYou have to be in ONLINE or LIGHT mode to #Default Variables govActionID=""; voterHash=""; voterType=""; returnHash=""; +voterID="-" #voterID="-" -> disabled currently for light mode #Parameter Count is 1 or more @@ -93,12 +94,25 @@ for (( tmpCnt=0; tmpCnt<${paramCnt}; tmpCnt++ )) echo -e "\e[0mUsing Governance Action-ID:\e[32m ${govActionID}\e[0m\n" govActionUTXO=${govActionID:0:64} govActionIdx=$(( ${govActionID:65} + 0 )) #make sure to have single digits if provided like #00 #01 #02... + voterID="" + + #Check if its a Governance Action-ID in Bech-Format + elif [[ "${paramValue:0:11}" == "gov_action1" ]]; then #parameter is most likely a bech32-action-id + echo -ne "\e[0mCheck if given Action Bech-ID\e[32m ${paramValue}\e[0m is valid ..." + #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + govActionID=$(convert_actionBech2UTXO ${paramValue}) #converts the given action bech id (CIP-129) into standard UTXO#IDX format + if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid Bech32 ACTION-ID.\e[0m"; exit 1; fi + echo -e "\e[32m OK\e[0m\n" + echo -e "\e[0mUsing Governance Action-ID:\e[32m ${govActionID}\e[0m\n" + govActionUTXO=${govActionID:0:64} + govActionIdx=$(( ${govActionID:65} + 0 )) #make sure to have single digits if provided like #00 #01 #02... + voterID="" #Check if its a Voter-Hash. Could be a DRep, CC-Hot or Pool-Hash. We don't know. elif [[ "${paramValue,,}" =~ ^([[:xdigit:]]{56})$ ]]; then if [[ "${voterHash}" != "" ]]; then echo -e "\n\e[91mERROR - Only one Voter-Hash is allowed as parameter!\e[0m\n"; exit 1; fi voterHash="${paramValue,,}" - voterType="Hash" + voterType="Hash"; #Check if its a DRep-ID in Bech-Format elif [[ "${paramValue:0:5}" == "drep1" && ${#paramValue} -eq 56 ]]; then #parameter is most likely a bech32-drep-id @@ -107,7 +121,26 @@ for (( tmpCnt=0; tmpCnt<${paramCnt}; tmpCnt++ )) voterHash=$(${bech32_bin} 2> /dev/null <<< "${paramValue,,}") #will have returncode 0 if the bech was valid if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid Bech32 DRep-ID.\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" - voterType="DRep" + voterType="DRep"; voterID=${paramValue,,} + + #Check if its a DRep-ID in Bech-Format + elif [[ "${paramValue:0:12}" == "drep_script1" && ${#paramValue} -eq 63 ]]; then #parameter is most likely a bech32-drep-id + echo -ne "\e[0mCheck if given DRep-Script Bech-ID\e[32m ${paramValue}\e[0m is valid ..." + #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + voterHash=$(${bech32_bin} 2> /dev/null <<< "${paramValue,,}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid Bech32 DRep-Script-ID.\e[0m"; exit 1; fi + echo -e "\e[32m OK\e[0m\n" + voterType="DRep"; voterID=${paramValue,,} + + #Check if its a DRep-ID in CIP129-Bech-Format + elif [[ "${paramValue:0:5}" == "drep1" && ${#paramValue} -eq 58 ]]; then #parameter is most likely a CIP129 bech32-drep-id + echo -ne "\e[0mCheck if given CIP129 DRep Bech-ID\e[32m ${paramValue}\e[0m is valid ..." + #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + voterHash=$(${bech32_bin} 2> /dev/null <<< "${paramValue,,}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid CIP129 Bech32 DRep-ID.\e[0m"; exit 1; fi + echo -e "\e[32m OK\e[0m\n" + voterHash=${voterHash: -56} + voterType="DRep"; voterID=${paramValue,,} #Check if its a DRep-ID file with a Bech-ID elif [[ -f "${paramValue}.drep.id" ]]; then #parameter is a DRep id file, containing a bech32 id @@ -119,16 +152,35 @@ for (( tmpCnt=0; tmpCnt<${paramCnt}; tmpCnt++ )) voterHash=$(${bech32_bin} 2> /dev/null <<< "${drepID}") #will have returncode 0 if the bech was valid if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${drepID}\" is not a valid Bech32 DRep-ID.\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" - voterType="DRep" + voterType="DRep"; voterID=${drepID} #Check if its a Committee-Hot-ID in Bech-Format elif [[ "${paramValue:0:7}" == "cc_hot1" && ${#paramValue} -eq 58 ]]; then #parameter is most likely a bech32-committee-hot-id echo -ne "\e[0mCheck if given Committee-Hot Bech-ID\e[32m ${paramValue}\e[0m is valid ..." - #lets do some further testing by converting the bech32 DRep-id into a Hex-DRep-ID + #lets do some further testing by converting the bech32 Committee-id into a Hex-Committee-ID voterHash=$(${bech32_bin} 2> /dev/null <<< "${paramValue,,}") #will have returncode 0 if the bech was valid if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid Bech32 Committee-Hot-ID.\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" - voterType="Committee-Hot" + voterType="Committee-Hot"; voterID=${paramValue,,} + + #Check if its a Committee-Hot-ID in Bech-Format + elif [[ "${paramValue:0:14}" == "cc_hot_script1" && ${#paramValue} -eq 65 ]]; then #parameter is most likely a bech32-committee-hot-id + echo -ne "\e[0mCheck if given Committee-Script-Hot Bech-ID\e[32m ${paramValue}\e[0m is valid ..." + #lets do some further testing by converting the bech32 Committee-id into a Hex-Committee-ID + voterHash=$(${bech32_bin} 2> /dev/null <<< "${paramValue,,}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid Bech32 Committee-Script-Hot-ID.\e[0m"; exit 1; fi + echo -e "\e[32m OK\e[0m\n" + voterType="Committee-Hot"; voterID=${paramValue,,} + + #Check if its a Committee-Hot-ID in CIP129-Bech-Format + elif [[ "${paramValue:0:7}" == "cc_hot1" && ${#paramValue} -eq 60 ]]; then #parameter is most likely a CIP129 bech32-committee-hot-id + echo -ne "\e[0mCheck if given CIP129 Committee-Hot Bech-ID\e[32m ${paramValue}\e[0m is valid ..." + #lets do some further testing by converting the bech32 Committee-id into a Hex-Committee-ID + voterHash=$(${bech32_bin} 2> /dev/null <<< "${paramValue,,}") #will have returncode 0 if the bech was valid + if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid CIP129 Bech32 Committee-Hot-ID.\e[0m"; exit 1; fi + echo -e "\e[32m OK\e[0m\n" + voterHash=${voterHash: -56} + voterType="Committee-Hot"; voterID=${paramValue,,} #Check if its a Committee-Hot-Hash File elif [[ -f "${paramValue}.cc-hot.hash" ]]; then #parameter is a Committee Hot hash file, containing the hash id @@ -137,7 +189,7 @@ for (( tmpCnt=0; tmpCnt<${paramCnt}; tmpCnt++ )) if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - Could not read from file \"${paramValue}.cc-hot.hash\"\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" if [[ ! "${voterHash,,}" =~ ^([[:xdigit:]]{56})$ ]]; then echo -e "\n\e[91mERROR - Content of Committee-Hot-Hash File '${paramValue}.cc-hot.hash' is not a valid Voter-Hash!\n\e[0m"; exit 1; fi - voterType="Committee-Hot" + voterType="Committee-Hot"; #Check if its a Pool-ID in Bech-Format elif [[ "${paramValue:0:5}" == "pool1" && ${#paramValue} -eq 56 ]]; then #parameter is most likely a bech32-pool-id @@ -146,7 +198,7 @@ for (( tmpCnt=0; tmpCnt<${paramCnt}; tmpCnt++ )) voterHash=$(${bech32_bin} 2> /dev/null <<< "${paramValue,,}") #will have returncode 0 if the bech was valid if [ $? -ne 0 ]; then echo -e "\n\n\e[91mERROR - \"${paramValue}\" is not a valid Bech32 Pool-ID.\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" - voterType="Pool" + voterType="Pool"; voterID=${paramValue,,} #Check if its a Pool-ID File with the hex Pool-ID elif [[ -f "${paramValue}.pool.id" ]]; then #parameter is a Pool-ID file, containing the hash id in hex format @@ -155,7 +207,7 @@ for (( tmpCnt=0; tmpCnt<${paramCnt}; tmpCnt++ )) if [ $? -ne 0 ]; then echo -e "\n\n\e[35mERROR - Could not read from file \"${paramValue}.pool.id\"\e[0m"; exit 1; fi echo -e "\e[32m OK\e[0m\n" if [[ ! "${voterHash,,}" =~ ^([[:xdigit:]]{56})$ ]]; then echo -e "\n\e[91mERROR - Content of Pool-ID File '${paramValue}.pool.id' is not a valid Voter-Hash!\n\e[0m"; exit 1; fi - voterType="Pool" + voterType="Pool"; #Check VKEY Files for all three types elif [[ -f "${paramValue}.vkey" ]]; then #parameter was the first part of a vkey file @@ -228,7 +280,7 @@ for (( tmpCnt=0; tmpCnt<${paramCnt}; tmpCnt++ )) #Exit the check if the keyword 'all' was used -> don't filter on a action-id or voter-hash or action-type elif [[ "${paramValue,,}" == "all" ]]; then - voterType=""; voterHash=""; govActionID=""; govActionUTXO=""; govActionIdx=""; govActionType=""; + voterType=""; voterHash=""; govActionID=""; govActionUTXO=""; govActionIdx=""; govActionType=""; voterID=""; break; #Unknown parameter @@ -246,30 +298,87 @@ if [[ "${voterHash}" != "" ]]; then echo -e "\e[0mVoter-Type is\e[32m ${voterTyp case ${workMode} in "online") showProcessAnimation "Query Governance-Action Info: " & - actionStateJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/stdout) + govStateJSON=$(${cardanocli} ${cliEra} query gov-state 2> /dev/stdout) if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${actionStateJSON}\e[0m\n"; exit 1; else stopProcessAnimation; fi; - actionStateJSON=$(jq -r ".proposals | to_entries[] | .value" 2> /dev/null <<< "${actionStateJSON}") + actionStateJSON=$(jq -r ".proposals | to_entries[] | .value" 2> /dev/null <<< "${govStateJSON}") if [ $? -ne 0 ]; then echo -e "\e[35mERROR - ${actionStateJSON}\e[0m\n"; exit 1; fi; + + #Filter for a given Action-ID + if [[ ${govActionUTXO} != "" && ${govActionIdx} != "" ]]; then + actionStateJSON=$(jq -r ". | select(.actionId.txId == \"${govActionUTXO}\" and .actionId.govActionIx == ${govActionIdx})" 2> /dev/null <<< "${actionStateJSON}") + if [[ "${actionStateJSON}" = "" ]]; then #action-id not on chain + echo -e "\e[0mThe provided Action-ID is\e[33m NOT present on the chain\e[0m!\e[0m\n"; + exit 1; + fi + fi + + #Filter for a voterHash in online-mode + if [[ "${voterHash}" != "" ]]; then + actionStateJSON=$(jq -r ". | select( (.committeeVotes, .dRepVotes, .stakePoolVotes) | keys[] | contains(\"${voterHash}\"))" 2> /dev/null <<< "${actionStateJSON}"); + fi + + #### Voting Power Stuff + #Get DRep Stake Distribution for quorum calculation later on + dRepStakeDistributionJSON=$(${cardanocli} ${cliEra} query drep-stake-distribution --all-dreps 2> /dev/stdout) + if [ $? -ne 0 ]; then echo -e "\e[35mERROR - ${dRepStakeDistributionJSON}\e[0m\n"; exit 1; fi; + + #Calculate the total dRep stake power (sum of all drep stake distribution entries without the alwaysAbstain and alwaysNoConfidence ones) + dRepPowerTotal=$(jq -r '[del(.[] | select(.[0] == "drep-alwaysAbstain" or .[0] == "drep-alwaysNoConfidence")) | .[][1]] | add' <<< "${dRepStakeDistributionJSON}" 2> /dev/null) + #Get the alwaysNoConfidence stake power (counts as a no-power in all actions, except the NoConfidence-Action, there it counts to the yes-power) + dRepPowerAlwaysNoConfidence=$(jq -r '(.[] | select(.[0] == "drep-alwaysNoConfidence") | .[1]) // 0' <<< "${dRepStakeDistributionJSON}" 2> /dev/null) + + #Get Pool Stake Distribution for quorum calculation later on - available with this command since cli 9.3.0.0 + poolStakeDistributionJSON=$(${cardanocli} ${cliEra} query spo-stake-distribution --all-spos 2> /dev/stdout) + if [ $? -ne 0 ]; then echo -e "\e[35mERROR - ${poolStakeDistributionJSON}\e[0m\n"; exit 1; fi; + + #Calculate the total pool stake power (sum of all stake that is delegated to pools) + poolPowerTotal=$(jq -r '[.[][1]] | add' <<< "${poolStakeDistributionJSON}" 2> /dev/null) + + #Get the current committee member count and voting threshold + { read committeePowerTotal; read committeePowerThreshold; } <<< $(jq -r '(.committee.members | length) // 0, (.committee.threshold) // 0' <<< ${govStateJSON} 2> /dev/null) + committeePowerThreshold=$(bc <<< "scale=2; 100.00 * ${committeePowerThreshold}") #scale it to 0.00-100.00% + + #Get the current protocolParameters for the dRep and pool voting thresholds + protocolParametersJSON=$(${cardanocli} ${cliEra} query protocol-parameters) ;; - "light") #showProcessAnimation "Query Governance-Action Info-LightMode: " & -# drepStateJSON=$(queryLight_drepInfo "${drepID}") -# if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${drepStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; - echo -e "\n\e[91mINFORMATION - This script does not support Light-Mode yet, waiting for Koios support!\n\e[0m"; exit; + + "light") #Check the voterID and voterType and generate filter is possible + case "${voterID}${voterType}" in + "-DRep") voterID=$(${bech32_bin} "drep" <<< "${voterHash}" 2> /dev/null);; + "-Committee-Hot") voterID=$(${bech32_bin} "cc_hot" <<< "${voterHash}" 2> /dev/null);; + "-Pool") voterID=$(${bech32_bin} "pool" <<< "${voterHash}" 2> /dev/null);; + "-Hash") echo -e "\e[35mSORRY - Filter by HASH is not supported yet in light-mode.\e[0m\n"; exit 1;; + "-") voterID="";; #fall back to disable filtering, show all + esac + + showProcessAnimation "Query Governance-Action Info-LightMode: " & + actionStateJSON=$(queryLight_actionState "${govActionUTXO}" "${govActionIdx}" "${voterID}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${actionStateJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + #Filter for a given Action-ID and/or Voter was already done in the queryLight_actionState + #strip the outter array for now + actionStateJSON=$(jq -r ".[]" 2> /dev/null <<< "${actionStateJSON}") + + #Get the current protocolParameters for the dRep, pool and committee voting thresholds + protocolParametersJSON=${lightModeParametersJSON} #lightmode + + #Get the current committee member count and voting threshold + { read committeePowerTotal; read committeeThreshold; } <<< $(jq -r '(.committee.members | length) // 0, "\(.committee.threshold)" // 0' 2> /dev/null <<< "${protocolParametersJSON}") + committeeThresholdType=$(jq -r "type" <<< "${committeeThreshold}" 2> /dev/null) + case ${committeeThresholdType} in + "object") + { read numerator; read denominator; } <<< $(jq -r '.numerator // "-", .denominator // "-"' <<< "${committeeThreshold}") + committeePowerThreshold=$(bc <<< "scale=2; 100 * ${numerator} / ${denominator}") + ;; + + "number") + committeePowerThreshold=$(bc <<< "scale=2; 100 * ${committeeThreshold}") + ;; + esac ;; -# esac -#Filter for a given Action-ID -if [[ ${govActionUTXO} != "" && ${govActionIdx} != "" ]]; then - actionStateJSON=$(jq -r ". | select(.actionId.txId == \"${govActionUTXO}\" and .actionId.govActionIx == ${govActionIdx})" 2> /dev/null <<< "${actionStateJSON}") - if [[ "${actionStateJSON}" = "" ]]; then #action-id not on chain - echo -e "\e[0mThe provided Action-ID is\e[33m NOT present on the chain\e[0m!\e[0m\n"; - exit 1; - fi -fi - #Filter for a given Action-Type if [[ ${govActionType} != "" ]]; then @@ -284,23 +393,22 @@ if [[ ${returnHash} != "" ]]; then fi +#OLD METHOD #Filter for a given voterHash -> voterType set -case "${voterType}" in - - "DRep") #Filter for a DRep keyHash entry - actionStateJSON=$(jq -r ". | select(.dRepVotes[\"keyHash-${voterHash}\"] != null)" 2> /dev/null <<< "${actionStateJSON}");; - - "Committee-Hot") #Filter for a Committee-Hot keyHash entry - actionStateJSON=$(jq -r ". | select(.committeeVotes[\"keyHash-${voterHash}\"] != null)" 2> /dev/null <<< "${actionStateJSON}");; - - "Pool") #Filter for a Pool Hash entry - actionStateJSON=$(jq -r ". | select(.stakePoolVotes[\"${voterHash}\"] != null)" 2> /dev/null <<< "${actionStateJSON}");; - - "Hash") #Filter just for a hash, can be a DRep, Committee or Pool Hash - actionStateJSON=$(jq -r ". | select( (.dRepVotes[\"keyHash-${voterHash}\"] != null) or (.committeeVotes[\"keyHash-${voterHash}\"] != null) or (.stakePoolVotes[\"${voterHash}\"] != null) )" 2> /dev/null <<< "${actionStateJSON}");; - -esac - +#case "${voterType}" in +# +# "DRep") #Filter for a DRep keyHash entry +# actionStateJSON=$(jq -r ". | select(.dRepVotes[\"keyHash-${voterHash}\"] != null)" 2> /dev/null <<< "${actionStateJSON}");; +# +# "Committee-Hot") #Filter for a Committee-Hot keyHash entry +# actionStateJSON=$(jq -r ". | select(.committeeVotes[\"keyHash-${voterHash}\"] != null)" 2> /dev/null <<< "${actionStateJSON}");; +# +# "Pool") #Filter for a Pool Hash entry +# actionStateJSON=$(jq -r ". | select(.stakePoolVotes[\"${voterHash}\"] != null)" 2> /dev/null <<< "${actionStateJSON}");; +# +# "Hash") #Filter just for a hash, can be a DRep, Committee or Pool Hash +# actionStateJSON=$(jq -r ". | select( (.dRepVotes[\"keyHash-${voterHash}\"] != null) or (.committeeVotes[\"keyHash-${voterHash}\"] != null) or (.stakePoolVotes[\"${voterHash}\"] != null) )" 2> /dev/null <<< "${actionStateJSON}");; +#esac #Convert the result(s) into an array and get the number of entries @@ -308,6 +416,7 @@ actionStateJSON=$(jq --slurp <<< ${actionStateJSON}) actionStateEntryCnt=$(jq -r "length" <<< ${actionStateJSON}) if [[ ${actionStateEntryCnt} -eq 0 ]]; then echo -e "\e[91mNo matching votes found.\e[0m\n"; else echo -e "\e[0mFound: \e[32m${actionStateEntryCnt} entry/entries\e[0m\n"; fi + #jq -r . <<< ${actionStateJSON} #Example Format @@ -349,43 +458,108 @@ do #Get the indexed Entry actionEntry=$(jq -r ".[${tmpCnt}]" <<< ${actionStateJSON}) + #In Light-Mode, request the content of the individual votes now + if [[ ${workMode} == "light" ]]; then + { read actionUTXO; read actionIdx; } <<< $(jq -r '.actionId.txId // "-", .actionId.govActionIx // "-"' <<< ${actionEntry}) + showProcessAnimation "Query Action-Votes LightMode: " & + actionVotesJSON=$(queryLight_actionVotes "${actionUTXO}" "${actionIdx}") + if [ $? -ne 0 ]; then stopProcessAnimation; echo -e "\e[35mERROR - ${actionVotesJSON}\e[0m\n"; exit $?; else stopProcessAnimation; fi; + #merge the requested content in the current actionEntry + actionEntry=$(jq -r ". += ${actionVotesJSON}" 2> /dev/null <<< "${actionEntry}") + fi + #We have found an action, lets get the Tag and number of votes so far - { read actionTag; - read actionUTXO; - read actionIdx; - read actionContents; - read actionAnchorUrl; - read actionAnchorHash; - read actionProposedInEpoch; - read actionExpiresAfterEpoch; - read actionDepositReturnKeyType; - read actionDepositReturnHash; - read actionDepositReturnNetwork; - read actionDRepVoteYesCount; - read actionDRepVoteNoCount; - read actionDRepAbstainCount; - read actionPoolVoteYesCount; - read actionPoolVoteNoCount; - read actionPoolAbstainCount; - read actionCommitteeVoteYesCount; - read actionCommitteeVoteNoCount; - read actionCommitteeAbstainCount; + { read actionTag; read actionUTXO; read actionIdx; read actionContents; + read actionAnchorUrl; read actionAnchorHash; + read actionProposedInEpoch; read actionExpiresAfterEpoch; + read actionDepositReturnKeyType; read actionDepositReturnHash; read actionDepositReturnNetwork; + read actionDRepVoteYesCount; read actionDRepVoteNoCount; read actionDRepAbstainCount; + read actionPoolVoteYesCount; read actionPoolVoteNoCount; read actionPoolAbstainCount; + read actionCommitteeVoteYesCount; read actionCommitteeVoteNoCount; read actionCommitteeAbstainCount; } <<< $(jq -r '.proposalProcedure.govAction.tag // "-", .actionId.txId // "-", .actionId.govActionIx // "-", "\(.proposalProcedure.govAction.contents)" // "-", .proposalProcedure.anchor.url // "-", - .proposalProcedure.anchor.dataHash // "-", .proposedIn // "-", .expiresAfter // "-", (.proposalProcedure.returnAddr.credential|keys[0]) // "-", (.proposalProcedure.returnAddr.credential|flatten[0]) // "-", .proposalProcedure.returnAddr.network // "-", - (.dRepVotes | with_entries(select(.value == "VoteYes")) | length), - (.dRepVotes | with_entries(select(.value == "VoteNo")) | length), - (.dRepVotes | with_entries(select(.value == "Abstain")) | length), - (.stakePoolVotes | with_entries(select(.value == "VoteYes")) | length), - (.stakePoolVotes | with_entries(select(.value == "VoteNo")) | length), - (.stakePoolVotes | with_entries(select(.value == "Abstain")) | length), - (.committeeVotes | with_entries(select(.value == "VoteYes")) | length), - (.committeeVotes | with_entries(select(.value == "VoteNo")) | length), - (.committeeVotes | with_entries(select(.value == "Abstain")) | length)' <<< ${actionEntry}) + .proposalProcedure.anchor.dataHash // "-", .proposedIn // "-", .expiresAfter // "-", + (.proposalProcedure.returnAddr.credential|keys[0]) // "-", (.proposalProcedure.returnAddr.credential|flatten[0]) // "-", .proposalProcedure.returnAddr.network // "-", + (.dRepVotes | with_entries(select(.value | contains("Yes"))) | length), + (.dRepVotes | with_entries(select(.value | contains("No"))) | length), + (.dRepVotes | with_entries(select(.value | contains("Abstain"))) | length), + (.stakePoolVotes | with_entries(select(.value | contains("Yes"))) | length), + (.stakePoolVotes | with_entries(select(.value | contains("No"))) | length), + (.stakePoolVotes | with_entries(select(.value | contains("Abstain"))) | length), + (.committeeVotes | with_entries(select(.value | contains("Yes"))) | length), + (.committeeVotes | with_entries(select(.value | contains("No"))) | length), + (.committeeVotes | with_entries(select(.value | contains("Abstain"))) | length)' <<< ${actionEntry}) + + + case ${workMode} in + + "online") #Calculate the VotingPowers via cli + #Generate lists with the DRep hashes that are voted yes, no or abstain. Add a 'drep-' infront of each entry to mach up the syntax in the 'drep-stake-distribution' json + { read dRepHashYes; read dRepHashNo; read dRepHashAbstain; } <<< $(jq -r '"\(.dRepVotes | with_entries(select(.value | contains("Yes"))) | keys | ["drep-\(.[])"] )", + "\(.dRepVotes | with_entries(select(.value | contains("No"))) | keys | ["drep-\(.[])"])", + "\(.dRepVotes | with_entries(select(.value | contains("Abstain"))) | keys | ["drep-\(.[])"])"' <<< ${actionEntry} 2> /dev/null) + #Calculate the total power of the yes, no and abstain keys + { read dRepPowerYes; read dRepPowerNo; read dRepPowerAbstain;} <<< $(jq -r "([ .[] | select(.[0]==${dRepHashYes}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${dRepHashNo}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${dRepHashAbstain}[]) | .[1] ] | add) // 0" <<< "${dRepStakeDistributionJSON}" 2> /dev/null) + #Calculate the acceptance percentage for the DRep group + if [[ "${actionTag}" != "NoConfidence" ]]; then #normal percentage calculate if its not a NoConfidence + dRepPct=$(bc <<< "scale=2; 100.00 * ${dRepPowerYes} / ( ${dRepPowerTotal} + ${dRepPowerAlwaysNoConfidence} - ${dRepPowerAbstain} )") + else #in case of NoConfidence, the dRepPowerAlwaysNoConfidence counts towards the yes counts + dRepPct=$(bc <<< "scale=2; 100.00 * ( ${dRepPowerYes} + ${dRepPowerAlwaysNoConfidence} ) / ( ${dRepPowerTotal} - ${dRepPowerAbstain} )") + fi + #Generate lists with the pool hashes that are voted yes, no or abstain. + { read poolHashYes; read poolHashNo; read poolHashAbstain; } <<< $(jq -r '"\(.stakePoolVotes | with_entries(select(.value | contains("Yes"))) | keys )", + "\(.stakePoolVotes | with_entries(select(.value | contains("No"))) | keys)", + "\(.stakePoolVotes | with_entries(select(.value | contains("Abstain"))) | keys)"' <<< ${actionEntry} 2> /dev/null) + #Calculate the total power of the yes, no and abstain keys + { read poolPowerYes; read poolPowerNo; read poolPowerAbstain;} <<< $(jq -r "([ .[] | select(.[0]==${poolHashYes}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${poolHashNo}[]) | .[1] ] | add) // 0, + ([ .[] | select(.[0]==${poolHashAbstain}[]) | .[1] ] | add) // 0" <<< "${poolStakeDistributionJSON}" 2> /dev/null) + #Calculate the acceptance percentage for the Pool group + poolPct=$(bc <<< "scale=2; ( 100.00 * ${poolPowerYes} ) / ( ${poolPowerTotal} - ${poolPowerAbstain} )") + + #### DEBUG OUTPUT +# echo -e "*** DREPS ***" +# echo -e "YES-Hash:\n${dRepHashYes}\nNO-Hash:\n${dRepHashNo}\nABSTAIN-Hash:\n${dRepHashAbstain}\n" +# echo -e "YES-Power: ${dRepPowerYes}\nNO-Power: ${dRepPowerNo}\nABSTAIN-Power: ${dRepPowerAbstain}\n" +# echo -e "DREP-Total-Power: ${dRepPowerTotal}" +# echo -e "DREP-AlwaysAbstain-Power: ${dRepPowerAlwaysAbstain}\n" +# echo -e "DREP-AlwaysNoConfidence-Power: ${dRepPowerAlwaysNoConfidence}\n" +# echo -e "DREP-Vote-PCT = 100 * ${dRepPowerYes} / ( ${dRepPowerTotal} + ${dRepPowerAlwaysNoConfidence} - ${dRepPowerAbstain} )" +# echo -e "DREP-Vote-PCT = ${dRepPct}%\n" +# echo -e "*** POOLS ***" +# echo -e "YES-Hash:\n${poolHashYes}\nNO-Hash:\n${poolHashNo}\nABSTAIN-Hash:\n${poolHashAbstain}\n" +# echo -e "YES-Power: ${poolPowerYes}\nNO-Power: ${poolPowerNo}\nABSTAIN-Power: ${poolPowerAbstain}\n" +# echo -e "POOL-Total-Power: ${poolPowerTotal}\n" +# echo -e "POOL-Vote-PCT = 100 * ${poolPowerYes} / ( ${poolPowerTotal} - ${poolPowerAbstain} )" +# echo -e "POOL-Vote-PCT = ${poolPct}%\n" +# echo -e "*** COMMITTEE ***" +# echo -e "COMMITTEE-Total-Power: ${committeePowerTotal}" +# echo -e "COMMITTEE-Threshold: ${committeePowerThreshold}" +# echo -e "COMMITTEE-Vote-PCT = ${committeePct}%\n" + ;; + + "light") #Get the VotingPowers/Percentage via koios + echo -e "\e[33mLive voting percentage calculation not yet available in Light-Mode!\e[0m"; + dRepPct=0 + poolPct=0 + ;; + esac + + #Calculate the acceptance percentage for the committee + if [[ $((${committeePowerTotal}-${actionCommitteeAbstainCount})) -eq 0 ]]; then committeePct=0; else committeePct=$(bc <<< "scale=2; ( 100.00 * ${actionCommitteeVoteYesCount} ) / ( ${committeePowerTotal} - ${actionCommitteeAbstainCount} )"); fi + + #Setup variables + totalAccept=""; totalAcceptIcon=""; + dRepAcceptIcon=""; poolAcceptIcon=""; committeeAcceptIcon=""; + dRepPowerThreshold="N/A"; poolPowerThreshold="N/A"; #N/A -> not available echo echo -e "\e[36m--- Entry $((${tmpCnt}+1)) of ${actionStateEntryCnt} --- Action-ID ${actionUTXO}#${actionIdx}\e[0m" echo - echo -e "\e[0mAction-Type: \e[32m${actionTag}\e[0m \tProposed in Epoch: \e[32m${actionProposedInEpoch}\e[0m \tExpires after Epoch: \e[32m${actionExpiresAfterEpoch}\e[0m" + echo -e "Action-Bech: \e[32m$(convert_actionUTXO2Bech "${actionUTXO}#${actionIdx}")\e[0m" + echo + echo -e "Action-Type: \e[32m${actionTag}\e[0m \tProposed in Epoch: \e[32m${actionProposedInEpoch}\e[0m \tExpires after Epoch: \e[32m${actionExpiresAfterEpoch}\e[0m" echo #Show the Anchor-URL(HASH) if available @@ -413,15 +587,42 @@ do esac - #DO A NICE OUTPUT OF THE DIFFERENT CONTENTS +### threshold parameters +# "dRepVotingThresholds": { +# "committeeNoConfidence": 0.65, +# "committeeNormal": 0.67, +# "hardForkInitiation": 0.6, +# "motionNoConfidence": 0.67, +# "ppEconomicGroup": 0.67, +# "ppGovGroup": 0.75, +# "ppNetworkGroup": 0.67, +# "ppTechnicalGroup": 0.67, +# "treasuryWithdrawal": 0.67, +# "updateToConstitution": 0.75 +# }, +# "poolVotingThresholds": { +# "committeeNoConfidence": 0.65, +# "committeeNormal": 0.65, +# "hardForkInitiation": 0.51, +# "motionNoConfidence": 0.6, +# "ppSecurityGroup": 0.6 +# }, + + + #DO A NICE OUTPUT OF THE DIFFERENT CONTENTS & DO THE RIGHT CALCULATIONS FOR THE ACCEPTANCE case "${actionTag}" in - "InfoAction") #show the proposed major/minor version to fork to + "InfoAction") #This is just an InfoAction #Show referencing Action-Id if avaiable { read prevActionUTXO; read prevActionIDX; } <<< $(jq -r '.txId // "-", .govActionIx // "-"' 2> /dev/null <<< ${actionContents}) if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi echo -e "\e[0mAction-Content:\e[36m Information\e[0m" - echo -e "\e[0m";; + echo -e "\e[0m" + + dRepAcceptIcon="N/A"; poolAcceptIcon="N/A"; + totalAccept="N/A"; + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; "HardForkInitiation") #show the proposed major/minor version to fork to @@ -437,7 +638,17 @@ do if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi echo -e "\e[0mAction-Content:\e[36m Do a Hardfork\e[0m\n" echo -e "\e[0mFork to\e[32m Protocol-Version \e[0m► \e[94m${forkMajorVer}.${forkMinorVer}\e[0m" - echo -e "\e[0m";; + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; read poolPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.hardForkInitiation // 0, .poolVotingThresholds.hardForkInitiation // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; + "ParameterChange") #show the proposed parameterchanges # [ @@ -461,7 +672,61 @@ do echo -e "\e[0mAction-Content:\e[36m Change protocol parameters\n\e[0m" changeParameterRender=$(jq -r 'to_entries[] | "\\e[0mChange parameter\\e[32m \(.key) \\e[0m► \\e[94m\(.value)\\e[0m"' <<< ${changeParameters} 2> /dev/null) echo -e "${changeParameterRender}" - echo -e "\e[0m";; + echo -e "\e[0m" + + dRepPowerThreshold="0"; #start with a zero threshold, we are searching the max value in the next steps + + #Calculate acceptance depending on the security group a parameter belongs to: Get the right threshold, make it a nice percentage number, check if threshold is reached + case "${changeParameters}" in + + #SECURITY GROUP - pools must vote on it + *"maxBlockBodySize"*|*"maxTxSize"*|*"maxBlockHeaderSize"*|*"maxValueSize"*|*"maxBlockExecutionUnits"*|*"txFeePerByte"*|*"txFeeFixed"*|*"utxoCostPerByte"*|*"govActionDeposit"*|*"minFeeRefScriptCostPerByte"*) + { read poolPowerThreshold; } <<< $(jq -r '.poolVotingThresholds.ppSecurityGroup // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + echo -e "A parameter from the \e[32mSECURITY\e[0m group is present ► \e[94mStakePools must vote\e[0m" + ;;& #also check next condition + + #NETWORK GROUP + *"maxBlockBodySize"*|*"maxTxSize"*|*"maxBlockHeaderSize"*|*"maxValueSize"*|*"maxTxExecutionUnits"*|*"maxBlockExecutionUnits"*|*"maxCollateralInputs"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppNetworkGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mNETWORK\e[0m group is present" + ;;& #also check next condition + + #ECONOMIC GROUP + *"txFeePerByte"*|*"txFeeFixed"*|*"stakeAddressDeposit"*|*"stakePoolDeposit"*|*"monetaryExpansion"*|*"treasuryCut"*|*"minPoolCost"*|*"utxoCostPerByte"*|*"executionUnitPrices"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppEconomicGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mECONOMIC\e[0m group is present" + ;;& #also check next condition + + #TECHNICAL GROUP + *"poolPledgeInfluence"*|*"poolRetireMaxEpoch"*|*"stakePoolTargetNum"*|*"costModels"*|*"collateralPercentage"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppTechnicalGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mTECHNICAL\e[0m group is present" + ;;& #also check next condition + + #GOVERNANCE GROUP + *"govActionLifetime"*|*"govActionDeposit"*|*"dRepDeposit"*|*"dRepActivity"*|*"committeeMinSize"*|*"committeeMaxTermLength"*|*"VotingThresholds"*) + dRepPowerThreshold=$(jq -r "[ ${dRepPowerThreshold}, .dRepVotingThresholds.ppGovGroup // 0 ] | max" <<< "${protocolParametersJSON}" 2> /dev/null) + echo -e "A parameter from the \e[32mGOVERNANCE\e[0m group is present" + ;; + + esac + + #Throw an error if for some reason, the dRepPowerThreshold is still at zero or empty + if [[ "${dRepPowerThreshold}" == "0" || "${dRepPowerThreshold}" == "" ]]; then echo -e "\e[35mERROR - Something went wrong finding the dRepPowerThreshold.\n\e[0m"; exit 1; fi + + echo + + #Now lets use the choosen threshold (highest of all involved groups) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + + #committee can vote on all parameters + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + + ;; + "NewConstitution") #show the proposed infos/anchor for a new constition # [ @@ -476,12 +741,22 @@ do # } # } # ] - { read prevActionUTXO; read prevActionIDX; read anchorHash; read anchorURL; } <<< $(jq -r '.[0].txId // "-", .[0].govActionIx // "-", .[1].anchor.dataHash // "-", .[1].anchor.url // "-"' 2> /dev/null <<< ${actionContents}) + { read prevActionUTXO; read prevActionIDX; read anchorHash; read anchorURL; read scriptHash; } <<< $(jq -r '.[0].txId // "-", .[0].govActionIx // "-", .[1].anchor.dataHash // "-", .[1].anchor.url // "-", .[1].script // "-"' 2> /dev/null <<< ${actionContents}) if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi echo -e "\e[0mAction-Content:\e[36m Change to a new Constitution\e[0m\n" echo -e "\e[0mSet new\e[32m Constitution-URL \e[0m► \e[94m${anchorURL}\e[0m" echo -e "\e[0mSet new\e[32m Constitution-Hash \e[0m► \e[94m${anchorHash}\e[0m" - echo -e "\e[0m";; + echo -e "\e[0mSet new\e[32m Guardrails-Script-Hash \e[0m► \e[94m${scriptHash}\e[0m" + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.updateToConstitution // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolAcceptIcon=""; #pools not allowed to vote on this + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; + "UpdateCommittee") #show the proposed infos for a committeeupdate # [ @@ -522,7 +797,32 @@ do remHashesRender=$(jq -r '.[1][] // [] | to_entries[] | "\\e[0mRemove\\e[32m \(.key) \\e[0m◄ \\e[91m\(.value)\\e[0m"' <<< ${actionContents} 2> /dev/null) echo -e "${addHashesRender}" echo -e "${remHashesRender}" - echo -e "\e[0m";; + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; read poolPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.committeeNormal // 0, .poolVotingThresholds.committeeNormal // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then dRepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + committeeAcceptIcon=""; #committee not allowed to vote on this + ;; + + "NoConfidence") #This is just a NoConfidence action + #Show referencing Action-Id if avaiable + { read prevActionUTXO; read prevActionIDX; } <<< $(jq -r '.txId // "-", .govActionIx // "-"' 2> /dev/null <<< ${actionContents}) + if [[ ${#prevActionUTXO} -gt 1 ]]; then echo -e "Reference-Action-ID: \e[32m${prevActionUTXO}#${prevActionIDX}\e[0m\n"; fi + echo -e "\e[0mAction-Content:\e[36m No Confidence in the Committee\e[0m" + echo -e "\e[0m" + + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; read poolPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.committeeNoConfidence // 0, .poolVotingThresholds.committeeNoConfidence // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolPowerThreshold=$(bc <<< "scale=2; 100.00 * ${poolPowerThreshold}") + if [[ $(bc <<< "${poolPct} > ${poolPowerThreshold}") -eq 1 ]]; then poolAcceptIcon="\e[92m✅"; else poolAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + committeeAcceptIcon=""; #committee not allowed to vote on this + ;; "TreasuryWithdrawals") #show the treasury withdrawals address and amount #[ @@ -539,10 +839,11 @@ do # ], # null #] - { read withdrawalsAmount; read withdrawalsKeyType; read withdrawalsHash; read withdrawalsNetwork; } <<< $( jq -r '.[0][0][1], (.[0][0][0].credential|keys[0]) // "-", (.[0][0][0].credential|flatten[0]) // "-", .[0][0][0].network // "-"' 2> /dev/null <<< ${actionContents}) + { read withdrawalsAmount; read withdrawalsKeyType; read withdrawalsHash; read withdrawalsNetwork; } <<< $( jq -r '.[0][0][1] // "0", (.[0][0][0].credential|keys[0]) // "-", (.[0][0][0].credential|flatten[0]) // "-", .[0][0][0].network // "-"' 2> /dev/null <<< ${actionContents}) echo -e "\e[0mAction-Content:\e[36m Withdrawal funds from the treasury\n\e[0m" case "${withdrawalsNetwork,,}${withdrawalsKeyType,,}" in + *"scripthash") echo -e "\e[0mWithdrawal to\e[32m ScriptHash \e[0m► \e[94m${withdrawalsHash}\e[0m" ;; @@ -556,36 +857,65 @@ do echo -e "\e[0mWithdrawal to\e[32m StakeAddr \e[0m► \e[94m${withdrawalsAddr}\e[0m" ;; - *) echo -e "\n\e[35mERROR - Unknown network type ${actionDepositReturnNetwork} for the Withdrawal KeyHash !\n\e[0m"; exit 1; + "") echo -e "\e[0mWithdrawal \e[32mdirectly\e[0m to the \e[94mDeposit-Return-Address\n\e[0m" + ;; + + *) echo -e "\n\e[35mERROR - Unknown network type ${withdrawalsNetwork} for the Withdrawal KeyHash !\n\e[0m"; exit 1; ;; esac echo -e "\e[0mWithdrawal the\e[32m Amount \e[0m► \e[94m$(convertToADA ${withdrawalsAmount}) ADA / ${withdrawalsAmount} lovelaces\e[0m" - echo -e "\e[0m";; + echo -e "\e[0m" - esac + #Calculate acceptance: Get the right threshold, make it a nice percentage number, check if threshold is reached + { read dRepPowerThreshold; } <<< $(jq -r '.dRepVotingThresholds.treasuryWithdrawal // 0' <<< "${protocolParametersJSON}" 2> /dev/null) + dRepPowerThreshold=$(bc <<< "scale=2; 100.00 * ${dRepPowerThreshold}") + if [[ $(bc <<< "${dRepPct} > ${dRepPowerThreshold}") -eq 1 ]]; then drepAcceptIcon="\e[92m✅"; else dRepAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + poolAcceptIcon=""; #pools not allowed to vote on this + if [[ $(bc <<< "${committeePct} > ${committeePowerThreshold}") -eq 1 ]]; then committeeAcceptIcon="\e[92m✅"; else committeeAcceptIcon="\e[91m❌"; totalAccept+="NO"; fi + ;; - echo -e "\e[0mCurrent Votes\tYes\tNo\tAbstain" - echo -e "\e[0m---------------------------------------" - echo -e "\e[94m DReps\t\e[32m${actionDRepVoteYesCount}\t\e[91m${actionDRepVoteNoCount}\t\e[33m${actionDRepAbstainCount}\e[0m" - echo -e "\e[94m StakePools\t\e[32m${actionPoolVoteYesCount}\t\e[91m${actionPoolVoteNoCount}\t\e[33m${actionPoolAbstainCount}\e[0m" - echo -e "\e[94m Committee\t\e[32m${actionCommitteeVoteYesCount}\t\e[91m${actionCommitteeVoteNoCount}\t\e[33m${actionCommitteeAbstainCount}\e[0m" - echo - #### TODO : Stake calculation for the current voting state + esac #If there is a voterHash, get the voting answer for it if [[ "${voterHash}" != "" ]]; then voteAnswer=$(jq -r ".dRepVotes[\"keyHash-${voterHash}\"] // .committeeVotes[\"keyHash-${voterHash}\"] // .dRepVotes[\"scriptHash-${voterHash}\"] // .committeeVotes[\"scriptHash-${voterHash}\"] // .stakePoolVotes[\"${voterHash}\"]" 2> /dev/null <<< "${actionEntry}") echo -ne "\e[97mVoting-Answer of the selected ${voterType}-Voter is: " case "${voteAnswer}" in - "VoteYes") echo -e "\e[102m\e[30m YES \e[0m\n";; - "VoteNo") echo -e "\e[101m\e[30m NO \e[0m\n";; - "Abstain") echo -e "\e[43m\e[30m ABSTAIN \e[0m\n";; + *"Yes"*) echo -e "\e[102m\e[30m YES \e[0m\n";; + *"No"*) echo -e "\e[101m\e[30m NO \e[0m\n";; + *"Abstain"*) echo -e "\e[43m\e[30m ABSTAIN \e[0m\n";; esac fi - echo + printf "\e[97mCurrent Votes\e[90m │ \e[0mYes\e[90m │ \e[0mNo\e[90m │ \e[0mAbstain\e[90m │ \e[0mThreshold\e[90m │ \e[97mLive-Pct\e[90m │ \e[97mAccept\e[0m\n" + printf "\e[90m──────────────┼─────────┼────────┼─────────┼───────────┼──────────┼────────\e[0m\n" + if [[ "${dRepAcceptIcon}" != "" ]]; then + printf "\e[94m%13s\e[90m │ \e[32m%7s\e[90m │ \e[91m%6s\e[90m │ \e[33m%7s\e[90m │ \e[0m%7s %%\e[90m │ \e[97m%6s %%\e[90m │ %b \e[0m\n" "DReps" "${actionDRepVoteYesCount}" "${actionDRepVoteNoCount}" "${actionDRepAbstainCount}" "${dRepPowerThreshold}" "${dRepPct}" "${dRepAcceptIcon}" + else + printf "\e[90m%13s\e[90m │ \e[90m%7s\e[90m │ \e[90m%6s\e[90m │ \e[90m%7s\e[90m │ \e[90m%7s %%\e[90m │ \e[90m%6s %%\e[90m │ %b \e[0m\n" "DReps" "-" "-" "-" "-" "-" "" + fi + if [[ "${poolAcceptIcon}" != "" ]]; then + printf "\e[94m%13s\e[90m │ \e[32m%7s\e[90m │ \e[91m%6s\e[90m │ \e[33m%7s\e[90m │ \e[0m%7s %%\e[90m │ \e[97m%6s %%\e[90m │ %b \e[0m\n" "StakePools" "${actionPoolVoteYesCount}" "${actionPoolVoteNoCount}" "${actionPoolAbstainCount}" "${poolPowerThreshold}" "${poolPct}" "${poolAcceptIcon}" + else + printf "\e[90m%13s\e[90m │ \e[90m%7s\e[90m │ \e[90m%6s\e[90m │ \e[90m%7s\e[90m │ \e[90m%7s %%\e[90m │ \e[90m%6s %%\e[90m │ %b \e[0m\n" "StakePools" "-" "-" "-" "-" "-" "" + fi + if [[ "${committeeAcceptIcon}" != "" ]]; then + printf "\e[94m%13s\e[90m │ \e[32m%7s\e[90m │ \e[91m%6s\e[90m │ \e[33m%7s\e[90m │ \e[0m%7s %%\e[90m │ \e[97m%6s %%\e[90m │ %b \e[0m\n" "Committee" "${actionCommitteeVoteYesCount}" "${actionCommitteeVoteNoCount}" "${actionCommitteeAbstainCount}" "${committeePowerThreshold}" "${committeePct}" "${committeeAcceptIcon}" + else + printf "\e[90m%13s\e[90m │ \e[90m%7s\e[90m │ \e[90m%6s\e[90m │ \e[90m%7s\e[90m │ \e[90m%7s %%\e[90m │ \e[90m%6s %%\e[90m │ %b \e[0m\n" "Committee" "-" "-" "-" "-" "-" "" + fi + printf "\e[90m──────────────┴─────────┴────────┴─────────┴───────────┴──────────┼────────\e[0m\n" + case "${totalAccept}" in + *"N/A"*) totalAcceptIcon="N/A";; + *"NO"*) totalAcceptIcon="\e[91m❌";; + *) totalAcceptIcon="\e[92m✅";; + esac + printf "\e[97m%65s\e[90m │ %b \e[0m\n" "Full approval of the proposal" "${totalAcceptIcon}" +# printf "\e[90m──────────────────────────────────────────────────────────────────┴────────\e[0m\n" + + echo done diff --git a/cardano/testnet/25b_regAction.sh b/cardano/testnet/25b_regAction.sh index 7451caa..63b30b4 100755 --- a/cardano/testnet/25b_regAction.sh +++ b/cardano/testnet/25b_regAction.sh @@ -797,7 +797,7 @@ if [ "${ENV_SKIP_PROMPT}" == "YES" ] || ask "\n\e[33mDoes this look good for you done echo - if [[ "${transactionExplorer}" != "" ]]; then echo -e "\e[0mYour Action-ID is: \e[32m${transactionExplorer}/${txID}\n\e[0m"; fi + if [[ "${transactionExplorer}" != "" ]]; then echo -e "\e[0mTracking: \e[32m${transactionExplorer}/${txID}\n\e[0m"; fi ;;