Skip to content

Commit

Permalink
2 votes per tx
Browse files Browse the repository at this point in the history
  • Loading branch information
fmaste committed Nov 12, 2024
1 parent fd36191 commit 90ec864
Showing 1 changed file with 135 additions and 108 deletions.
243 changes: 135 additions & 108 deletions nix/workbench/service/voting.nix
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ let
# Sleeps.
# Used when splitting funds to wait for them to arrive, a pretty high number!
# Any UTxO is considered a success in this phase.
wait_any_utxo_tries = 30;
wait_any_utxo_sleep = 10; # 5 minutes in 10s steps.
wait_any_utxo_tries = 36;
wait_any_utxo_sleep = 10; # 6 minutes in 10s steps.
# Used when sending funds if they need to be batched, it waits for the
# expected UTxO to arrive to the change-address. More than this is a failure!
wait_utxo_id_tries = 24;
Expand All @@ -101,6 +101,7 @@ let
wait_proposals_count_tries = 150;
wait_proposals_count_sleep = 10; # 25 minutes in 10s steps.

votes_per_tx = 2;
# The most important one. To calculate and achieve a predictable TPS.
# For reference:
### A local 2-node cluster with no tx-generator, max TPS was:
Expand All @@ -119,7 +120,7 @@ let
create_proposals = true;
build_vote = true; use_build_raw = true;
sign_vote = true;
submit_vote = false;
submit_vote = true;

in ''
Expand Down Expand Up @@ -1023,7 +1024,7 @@ function governance_create_withdrawal {
function vote_tps_throttle() {
# Function arguments.
local node_str=$1 # node name / folder to find the socket to use.
local votes_count=$2 # Actual number of total votes already submitted.
local txs_count=$2 # Actual number of total txs already submitted.
local filepath_first=../"''${node_str}"/first_vote_time
local filepath_last=../"''${node_str}"/last_vote_time
Expand All @@ -1046,17 +1047,17 @@ function vote_tps_throttle() {
--argjson start_time "''${start_time}" \
--argjson last_time "''${last_time}" \
--argjson current_time "''${current_time}" \
--argjson votes_count "''${votes_count}" \
--argjson txs_count "''${txs_count}" \
'
if $votes_count < 3
if $txs_count < 3
then 0
else (
($current_time - $start_time ) as $cluster_seconds
| ($votes_count / $cluster_seconds) as $current_tps
| ($txs_count / $cluster_seconds) as $current_tps
| if $current_tps < ${toString desired_producer_tps}
then 0
else (
(($votes_count + 1) / ${toString desired_producer_tps}) as $next_cluster_seconds
(($txs_count + 1) / ${toString desired_producer_tps}) as $next_cluster_seconds
| ($start_time + $next_cluster_seconds - $current_time)
)
end
Expand Down Expand Up @@ -1097,17 +1098,40 @@ function governance_vote_all {
)"
proposal_tx_ids_array=$(echo "''${txIdsJSON}" | ${jq}/bin/jq --raw-output '. | join (" ")')
# Keep a TXs counter for TPS calculation.
local txs_count=0 # Actual number of total txs already submitted.
# To calculate DReps assigned to this node.
local drep_step
drep_step=$(( node_i * ${toString dreps_per_producer} ))
# Cycle proposals.
local prop_i=0
local votes_count=0 # Keep a counter for TPS calculation.
for proposal_tx_id in ''${proposal_tx_ids_array[*]}
do
prop_i=$(( prop_i + 1 )) # Prop key to use.
governance_vote_proposal "''${node_str}" "''${node_i}" "''${prop_i}" "''${proposal_tx_id}" "''${votes_count}"
# Prop key to use.
prop_i=$(( prop_i + 1 ))
# Dreps to use (a voting transaction can carry more than 1 vote).
local dreps_array=()
for i in {1..${toString dreps_per_producer}}
do
local actual_drep
actual_drep="$((drep_step + i))"
dreps_array+=("''${actual_drep}")
if test "''${#dreps_array[@]}" -ge ${toString votes_per_tx}
then
vote_tps_throttle "''${node_str}" "''${txs_count}"
governance_vote_proposal \
"''${node_str}" \
"''${node_i}" \
"''${prop_i}" \
"''${proposal_tx_id}" \
"''${dreps_array[@]}"
txs_count=$(( txs_count + 1 ))
dreps_array=()
fi
done
local proposal_flag="../''${node_str}/proposal.''${proposal_tx_id}.voted"
${coreutils}/bin/touch "''${proposal_flag}"
# Separate counter, which proposal in which epoch, etc will change!
votes_count=$(( votes_count + ${toString dreps_per_producer} ))
done
}
Expand All @@ -1118,113 +1142,116 @@ function governance_vote_all {
function governance_vote_proposal {
# Function arguments.
local node_str=$1 # node name / folder to find the socket to use.
local node_i=$2 # This "i" is part of the node name ("node-i").
local prop_i=$3 # Proposal key/address to use.
local proposal_tx_id=$4
local votes_count=$5 # Actual number of total votes already submitted.
local node_str=''${1}; shift # node name / folder to find the socket to use.
local node_i=''${1}; shift # This "i" is part of the node name ("node-i").
local prop_i=''${1}; shift
local proposal_tx_id=''${1}; shift # Proposal key/address to use.
local dreps_array=("$@") # DReps to use in this voting transaction.
# Only defined in functions that use it.
local socket_path
socket_path="$(get_socket_path "''${node_str}")"
local drep_step actual_drep
drep_step=$((node_i * ${toString dreps_per_producer}))
for i in {1..${toString dreps_per_producer}} # for drepDir in ../genesis/cache-entry/drep-keys/drep*
do
actual_drep="$((drep_step + i))"
${coreutils}/bin/echo "governance_vote_proposal: ''${proposal_tx_id} - ''${node_str} - ''${actual_drep}"
${coreutils}/bin/echo "governance_vote_proposal: ''${proposal_tx_id} - ''${node_str} - ''${dreps_array[0]}-''${dreps_array[-1]}"
local funds_addr funds_tx funds_value
local vote_file_params_array=()
local signing_key_file_params_array=()
for drep_i in ''${dreps_array[*]}
do
local node_drep_skey node_drep_addr
node_drep_skey="$(create_node_prop_drep_key_files "''${node_str}" "''${node_i}" "''${prop_i}" "''${actual_drep}")".skey
node_drep_addr="$(build_node_prop_drep_address "''${node_str}" "''${node_i}" "''${prop_i}" "''${actual_drep}")"
# Funds address.
# The input is calculated from the last transaction submitted.
# No waiting! But, if last submitted transaction fails (function
# `governance_funds_producer` or `governance_vote_proposal` in current
# workflow), everything else fails.
local funds_tx
funds_tx="$(get_address_utxo_expected_id "''${node_str}" "''${node_drep_addr}")"
# A next UTxO must be cached by `funds_from_to` when splitting the funds,
# we don't check the response to be sure there is an expected UTxO to be
# really sure we are not querying the node unnecessarily.
vote_tps_throttle "''${node_str}" "''${votes_count}"
votes_count=$(( votes_count + 1 ))
# Voting with DRep keys:
local tx_filename=../"''${node_str}"/proposal."''${proposal_tx_id}"."''${actual_drep}"
node_drep_skey="$(create_node_prop_drep_key_files "''${node_str}" "''${node_i}" "''${prop_i}" "''${drep_i}")".skey
node_drep_addr="$(build_node_prop_drep_address "''${node_str}" "''${node_i}" "''${prop_i}" "''${drep_i}")"
# UTxO are created for 1 vote per transaction so all runs have the same
# number of UTxOs. We grab the funds from the first address/UTxO.
if test -z "''${funds_tx-}"
then
# Funds address.
funds_addr="''${node_drep_addr}"
# The input is calculated from the last transaction submitted.
# No waiting! But, if last submitted transaction fails (function
# `governance_funds_producer` or `governance_vote_proposal` in current
# workflow), everything else fails.
funds_tx="$(get_address_utxo_expected_id "''${node_str}" "''${node_drep_addr}")"
# A next UTxO must be cached by `funds_from_to` when splitting the funds,
# we don't check the response to be sure there is an expected UTxO to be
# really sure we are not querying the node unnecessarily.
funds_value="$(get_address_utxo_expected_value "''${node_str}" "''${node_drep_addr}")"
# Need to be used twice to sign
signing_key_file_params_array+=("--signing-key-file ''${node_drep_skey}")
fi
local vote_filename=../"''${node_str}"/proposal."''${proposal_tx_id}"."''${drep_i}"
${cardano-cli}/bin/cardano-cli conway governance vote create \
--yes \
--governance-action-tx-id "''${proposal_tx_id}" \
--governance-action-index "0" \
--drep-verification-key-file ../genesis/cache-entry/drep-keys/drep"''${actual_drep}"/drep.vkey \
--out-file "''${tx_filename}".action
# Build the transaction.
${if build_vote
then (
if use_build_raw
then ''
local funds_value change
funds_value="$(get_address_utxo_expected_value "''${node_str}" "''${node_drep_addr}")"
change=$(( $funds_value - 250000 ))
${cardano-cli}/bin/cardano-cli conway transaction build-raw \
--tx-in "''${funds_tx}" \
--fee 250000 \
--tx-out "''${node_drep_addr}"+"''${change}" \
--vote-file "''${tx_filename}".action \
--out-file "''${tx_filename}".raw
''
else ''
${cardano-cli}/bin/cardano-cli conway transaction build \
--testnet-magic ${toString testnetMagic} \
--socket-path "''${socket_path}" \
--tx-in "''${funds_tx}" \
--change-address "''${node_drep_addr}" \
--witness-override 2 \
--vote-file "''${tx_filename}".action \
--out-file "''${tx_filename}".raw \
> /dev/null
''
)
else ''
${coreutils}/bin/echo "transaction build off!"
''
}
${if build_vote && sign_vote
then ''
# Sign it with the DRep key:
${cardano-cli}/bin/cardano-cli conway transaction sign \
--testnet-magic ${toString testnetMagic} \
--signing-key-file ../genesis/cache-entry/drep-keys/drep"''${actual_drep}"/drep.skey \
--signing-key-file "''${node_drep_skey}" \
--tx-body-file "''${tx_filename}".raw \
--out-file "''${tx_filename}".signed
''
else ''
${coreutils}/bin/echo "transaction sign off!"
''
}
${if build_vote && sign_vote && submit_vote
then ''
# Submit the transaction:
${cardano-cli}/bin/cardano-cli conway transaction submit \
--testnet-magic ${toString testnetMagic} \
--socket-path "''${socket_path}" \
--tx-file "''${tx_filename}".signed \
>/dev/null
''
else ''
${coreutils}/bin/echo "transaction submit off!"
''
}
${coreutils}/bin/touch "''${tx_filename}".voted
# No `store_address_utxo_expected`, all UTxOs are created before voting!
--drep-verification-key-file ../genesis/cache-entry/drep-keys/drep"''${drep_i}"/drep.vkey \
--out-file "''${vote_filename}".action
vote_file_params_array+=("--vote-file ''${vote_filename}.action")
signing_key_file_params_array+=("--signing-key-file ../genesis/cache-entry/drep-keys/drep''${drep_i}/drep.skey")
done
local tx_filename=../"''${node_str}"/proposal."''${proposal_tx_id}"."''${dreps_array[0]}"-"''${dreps_array[-1]}"
# Build the transaction.
${if build_vote
then (
if use_build_raw
then ''
local change
change=$(( $funds_value - 250000 ))
${cardano-cli}/bin/cardano-cli conway transaction build-raw \
--tx-in "''${funds_tx}" \
--fee 250000 \
--tx-out "''${funds_addr}"+"''${change}" \
''${vote_file_params_array[@]} \
--out-file "''${tx_filename}".raw
''
else ''
${cardano-cli}/bin/cardano-cli conway transaction build \
--testnet-magic ${toString testnetMagic} \
--socket-path "''${socket_path}" \
--tx-in "''${funds_tx}" \
--change-address "''${funds_addr}" \
--witness-override "$(( 1 + ''${#dreps_array[@]} ))" \
''${vote_file_params_array[@]} \
--out-file "''${tx_filename}".raw \
> /dev/null
''
)
else ''
${coreutils}/bin/echo "transaction build off!"
''
}
${if build_vote && sign_vote
then ''
# Sign it with the DRep key:
${cardano-cli}/bin/cardano-cli conway transaction sign \
--testnet-magic ${toString testnetMagic} \
''${signing_key_file_params_array[@]} \
--tx-body-file "''${tx_filename}".raw \
--out-file "''${tx_filename}".signed
''
else ''
${coreutils}/bin/echo "transaction sign off!"
''
}
${if build_vote && sign_vote && submit_vote
then ''
# Submit the transaction:
${cardano-cli}/bin/cardano-cli conway transaction submit \
--testnet-magic ${toString testnetMagic} \
--socket-path "''${socket_path}" \
--tx-file "''${tx_filename}".signed \
>/dev/null
''
else ''
${coreutils}/bin/echo "transaction submit off!"
''
}
${coreutils}/bin/touch "''${tx_filename}".voted
# No `store_address_utxo_expected`, all UTxOs are created before voting!
}
################################################################################
Expand Down

0 comments on commit 90ec864

Please sign in to comment.