From c11deddd96816caf08983abc6ed2244d6dbdc97d Mon Sep 17 00:00:00 2001 From: mxaddict Date: Thu, 10 Mar 2022 06:38:05 +0800 Subject: [PATCH] Add nft index (#936) * Added new -nftindex param * Added missing declaration * Added missing declaration * Updated ALLINDEX * Updated some whitespace in rpcwallet * Added nftindex flags to blocktree logic * Added txdb functions for nft index * Added nft index update calls to Connect/Disconnect block * Added nftunspendindex.h * Added nftunspendindex.h * Updates based on alex's input * Added new DB_NFTUNSPENTINDEX char to txdb.h * Fixed some references * Moved a line down that I think should not be on the end of the previous line * Removed tokenId from CNftUnspendIndexValue * Updated nftunspentindex to serialize the blockHeight as big indian * Changed blockHeight to uint32_t * Updated nftunspentindex calls to not have the tokenId in value param * Added a Read func for nftunspentindex * Removed tokenId from CNftUnspentIndexValue * Ops * Added -ntfindex=1 to stressor for tokens * Revert "Ops" This reverts commit ce50c307002c5dad63c78fce854abb7af6ae4d46. * Revert "Removed tokenId from CNftUnspentIndexValue" This reverts commit c517ba4f5d435bbcaec0d7c5205a417c57bcf497. * Added UTXO data to listtokens * Replaced tokenId uint256 with tokenId TokenId instead * Replaced tokenId uint256 with tokenId TokenId instead * Replaced tokenId uint256 with tokenId TokenId instead * Fixed a crash * Updated the way the token is loaded for adding to the index * Removed redundant checks * Fixed reading method for nft index * Changed some logic and stored the index in a more efficient way * Removed a commit that is no longer needed * Added (with_utxo) to gettoken rpc command * Added new viewstate functions for token utxos * Added new viewstate functions for token utxos * Updated the nftindex logic * Updated TokenUtxoModified logic * Updated TokenUtxoValue * Updated TokenUtxoValue * Renamed DB_NFTUNSPENTINDEX to DB_TOKEN_UTXO * Updated BatchWrite logic for CStateView flush * Updated BatchWrite logic for txdb batchwrite * Fixed a crash and added some logging * Removed a log * Fix gettoken * Check for null entries in TokenUtxoValues * Replaced TokenId with unint256 from SerializeHash(TokenId) * Added more logging for now * Removed some logging * Updated some logging * Updated some logging * Add a call to view.HaveTokenUtxos to reload the cache with old values before running view.AddTokenUtxo * Fixed rpcwallet listtokens and gettoken to use latest non-null entry from GetTokenUtxos * Moved nftindex call to navcoind * Made rpc function changes discussed with alex, added getnft and listnft functions * Updated stressor_token * Updates to the stressor for tokens/nfts * Updates to the stressor for tokens/nfts * Updates to the stressor for tokens/nfts --- contrib/stressor_token.sh | 1833 +++++++++++++++++++------------------ src/Makefile.am | 1 + src/coins.cpp | 122 ++- src/coins.h | 45 +- src/ctokens/tokenid.h | 4 + src/ctokens/tokenutxos.h | 114 +++ src/init.cpp | 9 +- src/main.cpp | 71 +- src/main.h | 3 + src/rpc/client.cpp | 5 + src/txdb.cpp | 54 +- src/txdb.h | 4 +- src/wallet/rpcwallet.cpp | 285 ++++-- 13 files changed, 1568 insertions(+), 982 deletions(-) create mode 100644 src/ctokens/tokenutxos.h diff --git a/contrib/stressor_token.sh b/contrib/stressor_token.sh index 9fba2c45a..e72fd0684 100755 --- a/contrib/stressor_token.sh +++ b/contrib/stressor_token.sh @@ -88,743 +88,775 @@ bool_network_split=1 #######################################################################################Functions function initialize_network { - array_p2p_port=() - array_rpc_port=() - array_data=() - array_user=() - array_pwd=() + array_p2p_port=() + array_rpc_port=() + array_data=() + array_user=() + array_pwd=() } function initialize_node { - array_p2p_port[$1]=$( bc <<< "10000+$1" ) - array_rpc_port[$1]=$( bc <<< "30000+$1" ) - data_=$(mktemp -d) - array_data[$1]=$data_ - array_user[$1]=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) - array_pwd[$1]=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) - echo "-datadir=${array_data[$1]} -rpcport=${array_rpc_port[$1]}" - start_node $1 - array_active_nodes[$1]=$1 - array_all_nodes[$1]=$1 + array_p2p_port[$1]=$( bc <<< "10000+$1" ) + array_rpc_port[$1]=$( bc <<< "30000+$1" ) + data_=$(mktemp -d) + array_data[$1]=$data_ + array_user[$1]=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) + array_pwd[$1]=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) + echo "-datadir=${array_data[$1]} -rpcport=${array_rpc_port[$1]}" + start_node $1 + array_active_nodes[$1]=$1 + array_all_nodes[$1]=$1 } function remove_node { - unset array_active_nodes[$1] - unset array_all_nodes[$1] - unset array_data[$1] - unset array_user[$1] - unset array_pwd[$1] - unset array_p2p_port[$1] - unset array_rpc_port[$1] - unset array_stopped_nodes[$1] + unset array_active_nodes[$1] + unset array_all_nodes[$1] + unset array_data[$1] + unset array_user[$1] + unset array_pwd[$1] + unset array_p2p_port[$1] + unset array_rpc_port[$1] + unset array_stopped_nodes[$1] } trap ctrl_c INT function ctrl_c() { - terminate 1 + terminate 1 } function copy_array { - declare -n array=$1 - unset $2 - for i in ${!array[@]}; - do - eval "$2[$i]=\"${array[$i]}\"" - done + declare -n array=$1 + unset $2 + for i in ${!array[@]}; + do + eval "$2[$i]=\"${array[$i]}\"" + done } function nav_cli { - $navpath/navcoin-cli -datadir=${array_data[$1]} -rpcport=${array_rpc_port[$1]} -devnet $2 $3 $4 $5 $6 2> /dev/null + $navpath/navcoin-cli -datadir=${array_data[$1]} -rpcport=${array_rpc_port[$1]} -devnet $2 $3 $4 $5 $6 2> /dev/null } function terminate { - if [ "$1" == 1 ]; - then - fail_logs_D=$(echo "/tmp/stresser_fail_logs_$RANDOM") - mkdir $fail_logs_D - for i in $(seq 0 1 $( bc <<< "$node_count-1") ); - do - folder_name=$(echo "${array_data[$i]}" | cut -c 10-) - nav_cli $i listproposals > ${array_data[$i]}/devnet/listproposals.out - nav_cli $i listconsultations > ${array_data[$i]}/devnet/listconsultations.out - cp ${array_data[$i]}/devnet/debug.log $fail_logs_D/debug-out-node$i-$folder_name - cp ${array_data[$i]}/devnet/listproposals.out $fail_logs_D/listproposals.out-node$i-$folder_name - cp ${array_data[$i]}/devnet/listconsultations.out $fail_logs_D/listconsultations.out-node$i-$folder_name - done - echo debug.log files, listconsultations.out, listproposals.out files are copied to $fail_logs_D - fi - for i in ${array_active_nodes[@]}; - do - nav_cli $i stop - done - echo "Stopping all nodes..." - sleep 30 - echo Done - killall -9 navcoind - exit $1 + if [ "$1" == 1 ]; + then + fail_logs_D=$(echo "/tmp/stresser_fail_logs_$RANDOM") + mkdir $fail_logs_D + for i in $(seq 0 1 $( bc <<< "$node_count-1") ); + do + folder_name=$(echo "${array_data[$i]}" | cut -c 10-) + nav_cli $i listproposals > ${array_data[$i]}/devnet/listproposals.out + nav_cli $i listconsultations > ${array_data[$i]}/devnet/listconsultations.out + cp ${array_data[$i]}/devnet/debug.log $fail_logs_D/debug-out-node$i-$folder_name + cp ${array_data[$i]}/devnet/listproposals.out $fail_logs_D/listproposals.out-node$i-$folder_name + cp ${array_data[$i]}/devnet/listconsultations.out $fail_logs_D/listconsultations.out-node$i-$folder_name + done + echo debug.log files, listconsultations.out, listproposals.out files are copied to $fail_logs_D + fi + for i in ${array_active_nodes[@]}; + do + nav_cli $i stop + done + echo "Stopping all nodes..." + sleep 30 + echo Done + killall -9 navcoind + exit $1 } function connect_nodes { - nav_cli "$1" "addnode 127.0.0.1:$( bc <<< 10000+$2 ) add" - nav_cli "$1" "addnode 127.0.0.1:$( bc <<< 10000+$2 ) onetry" + nav_cli "$1" "addnode 127.0.0.1:$( bc <<< 10000+$2 ) add" + nav_cli "$1" "addnode 127.0.0.1:$( bc <<< 10000+$2 ) onetry" } function disconnect_nodes { - nav_cli "$1" "addnode 127.0.0.1:$( bc <<< 10000+$2 ) remove" - nav_cli "$1" "disconnectnode 127.0.0.1:$( bc <<< 10000+$2 )" + nav_cli "$1" "addnode 127.0.0.1:$( bc <<< 10000+$2 ) remove" + nav_cli "$1" "disconnectnode 127.0.0.1:$( bc <<< 10000+$2 )" } function select_random_nodes { - if [ "$2" == 1 ]; - then - array_random_nodes=($(shuf -i 1-$( bc <<< "$node_count-1" ) -n $1)) - else - array_random_nodes=($(shuf -i 0-$( bc <<< "$node_count-1" ) -n $1)) - fi + if [ "$2" == 1 ]; + then + array_random_nodes=($(shuf -i 1-$( bc <<< "$node_count-1" ) -n $1)) + else + array_random_nodes=($(shuf -i 0-$( bc <<< "$node_count-1" ) -n $1)) + fi } function shuffle_array { - local array=("$@") - shuffled_array=($(shuf -e ${array[@]})) + local array=("$@") + shuffled_array=($(shuf -e ${array[@]})) } #Establish topology using set variable $network_topology function connect_network { - local local_array=("$@") - if [ "${#local_array[@]}" -lt 1 ]; - then - return - fi - for i in ${!local_array[@]}; - do - local connection_pair=(${local_array[$i]}) - out=$(connect_nodes ${connection_pair[0]} ${connection_pair[1]}) - done - sleep 1 - check_connection "${local_array[@]}" + local local_array=("$@") + if [ "${#local_array[@]}" -lt 1 ]; + then + return + fi + for i in ${!local_array[@]}; + do + local connection_pair=(${local_array[$i]}) + out=$(connect_nodes ${connection_pair[0]} ${connection_pair[1]}) + done + sleep 1 + check_connection "${local_array[@]}" } function disconnect_network { - local local_array=("$@") - for i in ${!local_array[@]}; - do - local connection_pair=(${local_array[$i]}) - out=$(disconnect_nodes ${connection_pair[0]} ${connection_pair[1]}) - done - sleep 5 + local local_array=("$@") + for i in ${!local_array[@]}; + do + local connection_pair=(${local_array[$i]}) + out=$(disconnect_nodes ${connection_pair[0]} ${connection_pair[1]}) + done + sleep 5 } #Checks if topology is correctly connected function check_connection { - local array=("$@") - check_connection_done=0 - for i in ${!array[@]}; - do - local connection_pair=(${array[$i]}) - check=$(nav_cli ${connection_pair[0]} getpeerinfo | grep "\"addr\": \"127.0.0.1:$( bc <<< "10000+${connection_pair[1]}" )\"") - if [ -z "$check" ]; - then - echo "Node ${connection_pair[0]} failed to connect to node ${connection_pair[1]}" - echo " " - echo "Trying to reconnect to all nodes...please wait" - sleep 5 - connect_network "${array[@]}" - break - fi - done - if [ "$check_connection_done" == 0 ]; - then - echo "All nodes are connected!" - check_connection_done=1 - fi + local array=("$@") + check_connection_done=0 + for i in ${!array[@]}; + do + local connection_pair=(${array[$i]}) + check=$(nav_cli ${connection_pair[0]} getpeerinfo | grep "\"addr\": \"127.0.0.1:$( bc <<< "10000+${connection_pair[1]}" )\"") + if [ -z "$check" ]; + then + echo "Node ${connection_pair[0]} failed to connect to node ${connection_pair[1]}" + echo " " + echo "Trying to reconnect to all nodes...please wait" + sleep 5 + connect_network "${array[@]}" + break + fi + done + if [ "$check_connection_done" == 0 ]; + then + echo "All nodes are connected!" + check_connection_done=1 + fi } function create_random_network_topology { - unset array_topology_node_pairs - local local_array_create_random_network_topology=("$@") - if [ "${#local_array_create_random_network_topology[@]}" -eq 1 ]; - then - return - fi - array_network_connection_pool=() - for j in $(seq 0 1 $( bc <<< "${#local_array_create_random_network_topology[@]}-2" )); - do - for k in $(seq $( bc <<< "$j+1" ) 1 $( bc <<< "${#local_array_create_random_network_topology[@]}-1" )); - do - array_network_connection_pool+=("${local_array_create_random_network_topology[$j]} ${local_array_create_random_network_topology[$k]}") - done - done - if [ "${#local_array_create_random_network_topology[@]}" -lt 100 ]; - then - network_density_upper_bound=$( echo $(echo "e(1.4*l(${#local_array_create_random_network_topology[@]}-1))" | bc -l)/1 | bc ) - else - network_density_upper_bound=$( echo "${#local_array_create_random_network_topology[@]}*8" | bc ) - fi - #network_denstiy is used to create a somewhat loosely connect network topology - network_density_lower_bound=$( echo "${#local_array_create_random_network_topology[@]} - 1" | bc ) - network_density=$( shuf -i $network_density_lower_bound-$network_density_upper_bound -n 1 ) - array_topology_index=($(shuf -i 0-$( bc <<< "${#array_network_connection_pool[@]} - 1") -n $network_density)) - for i in $(seq 0 1 $( bc <<< "$network_density-1" )); - do - array_topology_node_pairs[$i]=${array_network_connection_pool[${array_topology_index[$i]}]} - done - - check_network_connected array_topology_node_pairs - - while [ "${#array_connected_nodes[@]}" -lt "${#local_array_create_random_network_topology[@]}" ]; + unset array_topology_node_pairs + local local_array_create_random_network_topology=("$@") + if [ "${#local_array_create_random_network_topology[@]}" -eq 1 ]; + then + return + fi + array_network_connection_pool=() + for j in $(seq 0 1 $( bc <<< "${#local_array_create_random_network_topology[@]}-2" )); + do + for k in $(seq $( bc <<< "$j+1" ) 1 $( bc <<< "${#local_array_create_random_network_topology[@]}-1" )); do - create_random_network_topology "${local_array_create_random_network_topology[@]}" + array_network_connection_pool+=("${local_array_create_random_network_topology[$j]} ${local_array_create_random_network_topology[$k]}") done + done + if [ "${#local_array_create_random_network_topology[@]}" -lt 100 ]; + then + network_density_upper_bound=$( echo $(echo "e(1.4*l(${#local_array_create_random_network_topology[@]}-1))" | bc -l)/1 | bc ) + else + network_density_upper_bound=$( echo "${#local_array_create_random_network_topology[@]}*8" | bc ) + fi + #network_denstiy is used to create a somewhat loosely connect network topology + network_density_lower_bound=$( echo "${#local_array_create_random_network_topology[@]} - 1" | bc ) + network_density=$( shuf -i $network_density_lower_bound-$network_density_upper_bound -n 1 ) + array_topology_index=($(shuf -i 0-$( bc <<< "${#array_network_connection_pool[@]} - 1") -n $network_density)) + for i in $(seq 0 1 $( bc <<< "$network_density-1" )); + do + array_topology_node_pairs[$i]=${array_network_connection_pool[${array_topology_index[$i]}]} + done + + check_network_connected array_topology_node_pairs + + while [ "${#array_connected_nodes[@]}" -lt "${#local_array_create_random_network_topology[@]}" ]; + do + create_random_network_topology "${local_array_create_random_network_topology[@]}" + done } function check_network_connected { - unset array_to_be_checked_connected - copy_array $1 array_to_be_checked_connected - connection_progression_old=0 - connection_progression_new=2 - unset array_connected_nodes - for i in ${array_to_be_checked_connected[@]}; - do - if [ -n $i ]; - then - array_connected_nodes=($i) - break - fi - done - while [ "$connection_progression_old" != "$connection_progression_new" ]; - do - connection_progression_old=$connection_progression_new - for i in ${!array_to_be_checked_connected[@]}; - do - array_connection=(${array_to_be_checked_connected[$i]}) - for j in ${!array_connected_nodes[@]}; - do - within_network=0 - if [ "${array_connection[0]}" == "${array_connected_nodes[$j]}" ]; - then - for k in $(seq 0 1 $( bc <<< " ${#array_connected_nodes[@]}-1" )); - do - if [ "${array_connection[1]}" == "${array_connected_nodes[$k]}" ]; - then - within_network=1 - fi - done - if [ "$within_network" == 0 ]; - then - array_connected_nodes+=("${array_connection[1]}") - array_to_be_checked_connected[$i]="" - ((connection_progression_new++)) - fi - elif [ "${array_connection[1]}" == "${array_connected_nodes[$j]}" ]; - then - for k in $(seq 0 1 $( bc <<< " ${#array_connected_nodes[@]}-1" )); - do - if [ "${array_connection[0]}" == "${array_connected_nodes[$k]}" ]; - then - within_network=1 - fi - done - if [ "$within_network" == 0 ]; - then - array_connected_nodes+=("${array_connection[0]}") - array_to_be_checked_connected[$i]="" - ((connection_progression_new++)) - fi - fi - done - done - done + unset array_to_be_checked_connected + copy_array $1 array_to_be_checked_connected + connection_progression_old=0 + connection_progression_new=2 + unset array_connected_nodes + for i in ${array_to_be_checked_connected[@]}; + do + if [ -n $i ]; + then + array_connected_nodes=($i) + break + fi + done + while [ "$connection_progression_old" != "$connection_progression_new" ]; + do + connection_progression_old=$connection_progression_new + for i in ${!array_to_be_checked_connected[@]}; + do + array_connection=(${array_to_be_checked_connected[$i]}) + for j in ${!array_connected_nodes[@]}; + do + within_network=0 + if [ "${array_connection[0]}" == "${array_connected_nodes[$j]}" ]; + then + for k in $(seq 0 1 $( bc <<< " ${#array_connected_nodes[@]}-1" )); + do + if [ "${array_connection[1]}" == "${array_connected_nodes[$k]}" ]; + then + within_network=1 + fi + done + if [ "$within_network" == 0 ]; + then + array_connected_nodes+=("${array_connection[1]}") + array_to_be_checked_connected[$i]="" + ((connection_progression_new++)) + fi + elif [ "${array_connection[1]}" == "${array_connected_nodes[$j]}" ]; + then + for k in $(seq 0 1 $( bc <<< " ${#array_connected_nodes[@]}-1" )); + do + if [ "${array_connection[0]}" == "${array_connected_nodes[$k]}" ]; + then + within_network=1 + fi + done + if [ "$within_network" == 0 ]; + then + array_connected_nodes+=("${array_connection[0]}") + array_to_be_checked_connected[$i]="" + ((connection_progression_new++)) + fi + fi + done + done + done } function stop_selected_nodes { - if [ "$attempts" == 10 ]; - then - echo Cannot find suitable nodes to stop - copy_array array_topology_node_pairs_backcup array_topology_node_pairs - copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped - return - fi - echo "Stopping nodes ${array_nodes_to_stop[@]}" - for i in ${array_nodes_to_stop[@]}; - do - stop_node $i - done - - sleep 5 - for i in ${array_nodes_to_stop[@]}; - do - unset array_active_nodes[$i] - if [ -n ${array_stressing_nodes[$i]} ]; - then - array_stopped_stressing_nodes[$i]=${array_stressing_nodes[$i]} - unset array_stressing_nodes[$i] - fi - array_stopped_nodes[$i]=$i - done -# echo Currently active nodes are: ${array_active_nodes[@]} inactive ones are: ${array_stopped_nodes[@]} -# echo array topology node pairs is ${array_topology_node_pairs[@]} -# echo array topology node pairs stopped is ${array_topology_node_pairs_stopped[@]} with size of ${#array_topology_node_pairs_stopped[@]} + if [ "$attempts" == 10 ]; + then + echo Cannot find suitable nodes to stop + copy_array array_topology_node_pairs_backcup array_topology_node_pairs + copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped + return + fi + echo "Stopping nodes ${array_nodes_to_stop[@]}" + for i in ${array_nodes_to_stop[@]}; + do + stop_node $i + done + + sleep 5 + for i in ${array_nodes_to_stop[@]}; + do + unset array_active_nodes[$i] + if [ -n ${array_stressing_nodes[$i]} ]; + then + array_stopped_stressing_nodes[$i]=${array_stressing_nodes[$i]} + unset array_stressing_nodes[$i] + fi + array_stopped_nodes[$i]=$i + done + # echo Currently active nodes are: ${array_active_nodes[@]} inactive ones are: ${array_stopped_nodes[@]} + # echo array topology node pairs is ${array_topology_node_pairs[@]} + # echo array topology node pairs stopped is ${array_topology_node_pairs_stopped[@]} with size of ${#array_topology_node_pairs_stopped[@]} } function select_random_nodes_to_stop { - attempts=0 - copy_array array_topology_node_pairs array_topology_node_pairs_backcup - copy_array array_topology_node_pairs_stopped array_topology_node_pairs_stopped_backup - echo "Trying to find nodes to stop while avoiding network splitting..." - unset array_connected_nodes - while [ "${#array_connected_nodes[@]}" -lt "$( bc <<< "$node_count-$1" )" ] && [ "$attempts" -lt 10 ]; + attempts=0 + copy_array array_topology_node_pairs array_topology_node_pairs_backcup + copy_array array_topology_node_pairs_stopped array_topology_node_pairs_stopped_backup + echo "Trying to find nodes to stop while avoiding network splitting..." + unset array_connected_nodes + while [ "${#array_connected_nodes[@]}" -lt "$( bc <<< "$node_count-$1" )" ] && [ "$attempts" -lt 10 ]; + do + unset array_topology_node_pairs + copy_array array_topology_node_pairs_backcup array_topology_node_pairs + copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped + ((attempts++)) + shuffle_array "${array_active_nodes[@]}" + unset array_nodes_to_stop + for i in $(seq 0 1 $( bc <<< "$1 - 1" )); do - unset array_topology_node_pairs - copy_array array_topology_node_pairs_backcup array_topology_node_pairs - copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped - ((attempts++)) - shuffle_array "${array_active_nodes[@]}" - unset array_nodes_to_stop - for i in $(seq 0 1 $( bc <<< "$1 - 1" )); - do - array_nodes_to_stop[${shuffled_array[$i]}]=${shuffled_array[$i]} - done - check_stopping_nodes_cause_network_split "${array_nodes_to_stop[@]}" + array_nodes_to_stop[${shuffled_array[$i]}]=${shuffled_array[$i]} done + check_stopping_nodes_cause_network_split "${array_nodes_to_stop[@]}" + done } function check_stopping_nodes_cause_network_split { - local local_array=("$@") - for i in ${!array_topology_node_pairs[@]}; - do - local node_pair=(${array_topology_node_pairs[$i]}) - for j in ${local_array[@]} - do - if [ "${node_pair[0]}" == "$j" ] || [ "${node_pair[1]}" == "$j" ]; - then - array_topology_node_pairs_stopped[$i]=${array_topology_node_pairs[$i]} - unset array_topology_node_pairs[$i] - node_pair=() - fi - done - done - check_network_connected array_topology_node_pairs + local local_array=("$@") + for i in ${!array_topology_node_pairs[@]}; + do + local node_pair=(${array_topology_node_pairs[$i]}) + for j in ${local_array[@]} + do + if [ "${node_pair[0]}" == "$j" ] || [ "${node_pair[1]}" == "$j" ]; + then + array_topology_node_pairs_stopped[$i]=${array_topology_node_pairs[$i]} + unset array_topology_node_pairs[$i] + node_pair=() + fi + done + done + check_network_connected array_topology_node_pairs } function start_random_stopped_nodes { - local local_array=() - if [ ${#array_stopped_nodes[@]} == 0 ]; - then - echo "All nodes are already active." - else - for i in ${!array_stopped_nodes[@]}; - do - dice=$( bc <<< "$RANDOM % 2" ) - if [ "$dice" -eq 1 ]; - then - local_array[$i]=$i - start_stopped_node $i - else - echo skipping starting node $i - fi - done - echo Waiting 30 seconds for navcoind... - sleep 30 - for j in ${!local_array[@]}; - do - connect_node_to_network $j - echo "Nodes $j balance: $(nav_cli $j getbalance) tNAV" - done - fi - echo Currently active nodes are: ${array_active_nodes[@]} inactive ones are: ${array_stopped_nodes[@]} + local local_array=() + if [ ${#array_stopped_nodes[@]} == 0 ]; + then + echo "All nodes are already active." + else + for i in ${!array_stopped_nodes[@]}; + do + dice=$( bc <<< "$RANDOM % 2" ) + if [ "$dice" -eq 1 ]; + then + local_array[$i]=$i + start_stopped_node $i + else + echo skipping starting node $i + fi + done + echo Waiting 30 seconds for navcoind... + sleep 30 + for j in ${!local_array[@]}; + do + connect_node_to_network $j + echo "Nodes $j balance: $(nav_cli $j getbalance) tNAV" + done + fi + echo Currently active nodes are: ${array_active_nodes[@]} inactive ones are: ${array_stopped_nodes[@]} } function start_stopped_node { - echo Starting node $1... - start_node $1 - unset array_stopped_nodes[$1] - array_active_nodes[$1]=$1 - if [ -n ${array_stopped_stressing_nodes[$1]} ]; - then - array_stressing_nodes[$1]=${array_stopped_stressing_nodes[$1]} - unset array_stopped_stressing_nodes[$1] - fi + echo Starting node $1... + start_node $1 + unset array_stopped_nodes[$1] + array_active_nodes[$1]=$1 + if [ -n ${array_stopped_stressing_nodes[$1]} ]; + then + array_stressing_nodes[$1]=${array_stopped_stressing_nodes[$1]} + unset array_stopped_stressing_nodes[$1] + fi } function connect_node_to_network { - echo Connecting node $1 to the network... - for i in ${!array_topology_node_pairs_stopped[@]}; - do - local node_connection=(${array_topology_node_pairs_stopped[$i]}) - if [ "$1" == "${node_connection[0]}" ] || [ "$1" == "${node_connection[1]}" ]; - then - local match=0 - for k in ${array_stopped_nodes[@]}; - do - if [ "$k" == "${node_connection[0]}" ] || [ "$k" == "${node_connection[1]}" ]; - then - match=1 - fi - done - if [ "$match" == 0 ] && [ -z ${array_topology_node_pairs[$i]} ]; - then - array_topology_node_pairs[$i]=${array_topology_node_pairs_stopped[$i]} - unset array_topology_node_pairs_stopped[$i] - fi - fi - done - connect_network "${array_topology_node_pairs[@]}" + echo Connecting node $1 to the network... + for i in ${!array_topology_node_pairs_stopped[@]}; + do + local node_connection=(${array_topology_node_pairs_stopped[$i]}) + if [ "$1" == "${node_connection[0]}" ] || [ "$1" == "${node_connection[1]}" ]; + then + local match=0 + for k in ${array_stopped_nodes[@]}; + do + if [ "$k" == "${node_connection[0]}" ] || [ "$k" == "${node_connection[1]}" ]; + then + match=1 + fi + done + if [ "$match" == 0 ] && [ -z ${array_topology_node_pairs[$i]} ]; + then + array_topology_node_pairs[$i]=${array_topology_node_pairs_stopped[$i]} + unset array_topology_node_pairs_stopped[$i] + fi + fi + done + connect_network "${array_topology_node_pairs[@]}" } function wait_until_sync { - sleep 5 - local local_array=("$@") - local local_array_best_hash=() - wait_until_sync_done=0 - for i in ${local_array[@]}; - do - local_array_best_hash[$i]=$(nav_cli $i getbestblockhash) -# echo best block hash of node $i: -# echo "$(nav_cli $i getbestblockhash)" - done - if [ $(printf "%s\n" "${local_array_best_hash[@]}" | LC_CTYPE=C sort -z -u | uniq | grep -n -c .) -gt 1 ]; - then - echo best block hash mismatch!!! -# echo array topoloy node pairs is "${array_topology_node_pairs[@]}" -# echo array topology node pairs stopped is "${array_topology_node_pairs_stopped[@]}" - sleep 1 - shuffle_array "${local_array[@]}" - node=${shuffled_array[0]} - $(nav_cli $node "generate 1") - echo now on blcok $(nav_cli $node "getblockcount") - if [ "$network_split_started" == 1 ]; - then - for wusi in $(seq 0 1 $( bc <<< "$network_count-1" )); - do - echo Syncing network $wusi... - eval "connect_network \"\${array_topology_node_pairs_network$nc[@]}\"" - done - else - connect_network "${array_topology_node_pairs[@]}" - fi - sleep 2 - wait_until_sync "${local_array[@]}" - fi - if [ "$wait_until_sync_done" == 0 ]; - then - echo Best block hashes matched! - wait_until_sync_done=1 - fi + sleep 5 + local local_array=("$@") + local local_array_best_hash=() + wait_until_sync_done=0 + for i in ${local_array[@]}; + do + local_array_best_hash[$i]=$(nav_cli $i getbestblockhash) + echo best block hash/height of node $i: $(nav_cli $i getblockcount) $(nav_cli $i getbestblockhash) + done + if [ $(printf "%s\n" "${local_array_best_hash[@]}" | LC_CTYPE=C sort -z -u | uniq | grep -n -c .) -gt 1 ]; + then + echo best block hash mismatch!!! + echo array topoloy node pairs is "${array_topology_node_pairs[@]}" + echo array topology node pairs stopped is "${array_topology_node_pairs_stopped[@]}" + sleep 1 + shuffle_array "${local_array[@]}" + node=${shuffled_array[0]} + $(nav_cli $node "generate 1") + echo now on block $(nav_cli $node "getblockcount") + if [ "$network_split_started" == 1 ]; + then + for wusi in $(seq 0 1 $( bc <<< "$network_count-1" )); + do + echo Syncing network $wusi... + eval "connect_network \"\${array_topology_node_pairs_network$nc[@]}\"" + done + else + connect_network "${array_topology_node_pairs[@]}" + fi + sleep 2 + wait_until_sync "${local_array[@]}" + fi + if [ "$wait_until_sync_done" == 0 ]; + then + echo Best block hashes matched! + wait_until_sync_done=1 + fi } function random_transactions { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 75 ]; + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 75 ]; + then + transaction_amount=$( bc <<< "$RANDOM%1000" ) + shuffle_array "${array_stressing_nodes[@]}" + node_send=${shuffled_array[0]} + node_receive=${shuffled_array[1]} + if [ $dice -lt 30 ]; then - transaction_amount=$( bc <<< "$RANDOM%1000" ) - shuffle_array "${array_stressing_nodes[@]}" - node_send=${shuffled_array[0]} - node_receive=${shuffled_array[1]} - if [ $dice -lt 30 ]; - then - out=$(nav_cli ${array_stressing_nodes[$node_send]} sendtoaddress ${array_address[$node_receive]} $transaction_amount) - else - out=$(nav_cli ${array_stressing_nodes[$node_send]} privatesendtoaddress ${array_address[$node_receive]} $transaction_amount) - fi - fi + out=$(nav_cli ${array_stressing_nodes[$node_send]} sendtoaddress ${array_address[$node_receive]} $transaction_amount) + else + out=$(nav_cli ${array_stressing_nodes[$node_send]} privatesendtoaddress ${array_address[$node_receive]} $transaction_amount) + fi + fi } function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; } function dice_create_token { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 20 ]; - then - shuffle_array "${array_stressing_nodes[@]}" - supply=$(openssl rand 4 | od -DAn) - node=${shuffled_array[0]} - random_name=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) - random_symbol=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 3) - out=$(nav_cli $node createtoken $random_name $random_symbol $supply) - if [ ! -z $out ] - then - echo 'create token success!' - fi - fi + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 20 ]; + then + shuffle_array "${array_stressing_nodes[@]}" + supply=$(openssl rand 4 | od -DAn) + node=${shuffled_array[0]} + random_name=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) + random_symbol=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 3) + out=$(nav_cli $node createtoken $random_name $random_symbol $supply) + if [ ! -z $out ] + then + echo 'create token success!' + fi + fi } function dice_mint_token { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 70 ]; - then - shuffle_array "${array_stressing_nodes[@]}" - node=${shuffled_array[0]} - node_receive=${shuffled_array[1]} - tokens=($(nav_cli $node listtokens | jq '.[] | select(.version==0) | .id' | tr "\n" " " | tr -d '"')) - for t in ${tokens[@]} - do - amount=$(openssl rand 4 | od -DAn) - out=$(nav_cli $node minttoken $t ${array_private_address[$node_receive]} $amount) - if [ ! -z $out ] - then - echo 'mint token success!' - fi - done - fi + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 70 ]; + then + shuffle_array "${array_stressing_nodes[@]}" + node=${shuffled_array[0]} + node_receive=${shuffled_array[1]} + tokens=($(nav_cli $node listtokens | jq '.[] | select(.version==0) | .id' | tr "\n" " " | tr -d '"')) + for t in ${tokens[@]} + do + amount=$(openssl rand 4 | od -DAn) + out=$(nav_cli $node minttoken $t ${array_private_address[$node_receive]} $amount) + if [ ! -z $out ] + then + echo 'mint token success!' + fi + done + fi } function dice_send_token { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 80 ]; - then - tokens=($(nav_cli $node listtokens | jq '.[] | select(.version==0) | .id' | tr "\n" " " | tr -d '"')) - for n in ${array_stressing_nodes[@]} - do - for t in ${tokens[@]} - do - shuffle_array "${array_stressing_nodes[@]}" - node_receive=${shuffled_array[0]} - amount=$(openssl rand 4 | od -DAn) - out=$(nav_cli $n sendtoken $t ${array_private_address[$node_receive]} $amount) - if [ ! -z $out ] - then - echo 'send token success!' - fi - done - done - fi + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 80 ]; + then + tokens=($(nav_cli $node listtokens | jq '.[] | select(.version==0) | .id' | tr "\n" " " | tr -d '"')) + for n in ${array_stressing_nodes[@]} + do + for t in ${tokens[@]} + do + shuffle_array "${array_stressing_nodes[@]}" + node_receive=${shuffled_array[0]} + amount=$(openssl rand 4 | od -DAn) + out=$(nav_cli $n sendtoken $t ${array_private_address[$node_receive]} $amount) + if [ ! -z $out ] + then + echo 'send token success!' + fi + done + done + fi } function dice_burn_token { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 50 ]; - then - tokens=($(nav_cli $node listtokens | jq '.[] | select(.version==0) | .id' | tr "\n" " " | tr -d '"')) - for n in ${array_stressing_nodes[@]} - do - for t in ${tokens[@]} - do - amount=$(openssl rand 4 | od -DAn) - out=$(nav_cli $n burntoken $t $amount) - if [ ! -z $out ] - then - echo 'burn token success!' - fi - done - done - fi + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 50 ]; + then + tokens=($(nav_cli $node listtokens | jq '.[] | select(.version==0) | .id' | tr "\n" " " | tr -d '"')) + for n in ${array_stressing_nodes[@]} + do + for t in ${tokens[@]} + do + amount=$(openssl rand 4 | od -DAn) + out=$(nav_cli $n burntoken $t $amount) + if [ ! -z $out ] + then + echo 'burn token success!' + fi + done + done + fi } function dice_create_nft { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 20 ]; - then - shuffle_array "${array_stressing_nodes[@]}" - supply=$RANDOM - node=${shuffled_array[0]} - random_name=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) - random_scheme=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 100) - out=$(nav_cli $node createnft $random_name $random_scheme $supply) - if [ ! -z $out ] - then - echo 'create nft success!' - fi - fi + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 20 ]; + then + shuffle_array "${array_stressing_nodes[@]}" + supply=$RANDOM + node=${shuffled_array[0]} + random_name=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 10) + random_scheme=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 100) + out=$(nav_cli $node createnft $random_name $random_scheme $supply) + if [ ! -z $out ] + then + echo 'create nft success!' + fi + fi } function dice_mint_nft { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 70 ]; - then - shuffle_array "${array_stressing_nodes[@]}" - node=${shuffled_array[0]} - node_receive=${shuffled_array[1]} - nfts=($(nav_cli $node listtokens | jq '.[] | select(.version==1) | .id' | tr "\n" " " | tr -d '"')) - random_scheme=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 100) - for n in ${nfts[@]} - do - nft_id=$(nav_cli $node listtokens | jq ".[] | select(.id==\"$n\") | .current_supply" | tr -d '"') -# echo 'nft_id = ' $nft_id - out=$(nav_cli $node mintnft $n $nft_id ${array_private_address[$node_receive]} $random_scheme) - if [ ! -z $out ] - then - echo 'mint nft success!' - fi - done - fi + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 70 ]; + then + shuffle_array "${array_stressing_nodes[@]}" + node=${shuffled_array[0]} + node_receive=${shuffled_array[1]} + nfts=($(nav_cli $node listnfts | jq '.[] | select(.version==1) | .id' | tr "\n" " " | tr -d '"')) + random_scheme=$(env LC_CTYPE=C tr -dc "a-zA-Z0-9-_\$\?" < /dev/urandom | head -c 100) + for n in ${nfts[@]} + do + nft_id=$(nav_cli $node listnfts | jq ".[] | select(.id==\"$n\") | .current_supply" | tr -d '"') + # echo 'nft_id = ' $nft_id + out=$(nav_cli $node mintnft $n $nft_id ${array_private_address[$node_receive]} $random_scheme) + if [ ! -z $out ] + then + echo 'mint nft success!' + fi + done + fi } function dice_send_nft { - dice=$(bc <<< "$RANDOM % 100") - if [ $dice -lt 90 ]; - then - nfts=($(nav_cli $node listtokens | jq '.[] | select(.version==1) | .id' | tr "\n" " " | tr -d '"')) - for n in ${array_stressing_nodes[@]} - do - for t in ${nfts[@]} - do - shuffle_array "${array_stressing_nodes[@]}" - node_receive=${shuffled_array[0]} - nftid=$(nav_cli $node listtokens | jq ".[] | select(.id==\"$t\") | .nfts | .[] | select(.balance==\"1\") | .index" | shuf -n 1) -# echo 'nftid= ' $nftid - out=$(nav_cli $n sendnft $t $nftid ${array_private_address[$node_receive]}) - if [ ! -z $out ] - then - echo 'send nft success!' - fi - done - done - fi - + dice=$(bc <<< "$RANDOM % 100") + if [ $dice -lt 90 ]; + then + nfts=($(nav_cli $node listnfts | jq '.[] | select(.version==1) | .id' | tr "\n" " " | tr -d '"')) + for n in ${array_stressing_nodes[@]} + do + for t in ${nfts[@]} + do + shuffle_array "${array_stressing_nodes[@]}" + node_receive=${shuffled_array[0]} + nftid=$(nav_cli $node listnfts | jq ".[] | select(.id==\"$t\") | .nfts | .[] | select(.balance==\"1\") | .index" | shuf -n 1) + #echo 'nftid= ' $nftid + out=$(nav_cli $n sendnft $t $nftid ${array_private_address[$node_receive]}) + if [ ! -z $out ] + then + echo 'send nft success!' + fi + done + done + fi } function check_token_hash { - sleep 1 - local local_array=("$@") - local local_array_tokenhash=() - for i in ${local_array[@]}; - do - local_array_tokenhash[$i]=$(nav_cli $i "listtokens" | jq -c '[.[]|del(.balance)|del(.nfts)]'|shasum|awk '{print $1}') - done -# echo 'token hashes = ' ${local_array_tokenhash[@]} - if [ $(printf "%s\n" "${local_array_tokenhash[@]}" | LC_CTYPE=C sort -z -u | uniq | grep -n -c .) -gt 1 ]; + check_token_hash_token + check_token_hash_nft +} + +function check_token_hash_token { + sleep 1 + local local_array=("$@") + local local_array_tokenhash=() + for i in ${local_array[@]}; + do + local_array_tokenhash[$i]=$(nav_cli $i "listtokens" | jq -c '[.[]|del(.balance)|del(.nfts)|del(.is_mine)]'|shasum|awk '{print $1}') + done + echo 'token hashes = ' ${local_array_tokenhash[@]} + if [ $(printf "%s\n" "${local_array_tokenhash[@]}" | LC_CTYPE=C sort -z -u | uniq | grep -n -c .) -gt 1 ]; + then + if [ "$bool_assert_state_mismatch" != 1 ]; then - if [ "$bool_assert_state_mismatch" != 1 ]; - then - echo TOKEN HASH MISMATCH! Syncing again to make sure best block hashes match. - bool_assert_state_mismatch=1 - wait_until_sync "${local_array[@]}" - check_token_hash "${local_array[@]}" - else - echo TOKEN HASH MISMATCH! - for i in ${!local_array_tokenhash[@]}; - do - echo the hashes of nodes $i are ${local_array_tokenhash[$i]} - done - terminate 1 - fi + echo TOKEN HASH MISMATCH! Syncing again to make sure best block hashes match. + bool_assert_state_mismatch=1 + wait_until_sync "${local_array[@]}" + check_token_hash_token "${local_array[@]}" + else + echo TOKEN HASH MISMATCH! + for i in ${!local_array_tokenhash[@]}; + do + echo the hashes of node $i are ${local_array_tokenhash[$i]} + done + terminate 1 fi - echo Token hash check OK! - bool_assert_state_mismatch=0 + fi + echo Token hash check OK! + bool_assert_state_mismatch=0 } +function check_token_hash_nft { + sleep 1 + local local_array=("$@") + local local_array_tokenhash=() + for i in ${local_array[@]}; + do + local_array_tokenhash[$i]=$(nav_cli $i "listnfts" | jq -c '[.[]|del(.balance)|del(.nfts)|del(.is_mine)]'|shasum|awk '{print $1}') + done + echo 'nft hashes = ' ${local_array_tokenhash[@]} + if [ $(printf "%s\n" "${local_array_tokenhash[@]}" | LC_CTYPE=C sort -z -u | uniq | grep -n -c .) -gt 1 ]; + then + if [ "$bool_assert_state_mismatch" != 1 ]; + then + echo NFT HASH MISMATCH! Syncing again to make sure best block hashes match. + bool_assert_state_mismatch=1 + wait_until_sync "${local_array[@]}" + check_token_hash_nft "${local_array[@]}" + else + echo NFT HASH MISMATCH! + for i in ${!local_array_tokenhash[@]}; + do + echo the hashes of node $i are ${local_array_tokenhash[$i]} + done + terminate 1 + fi + fi + echo NFT hash check OK! + bool_assert_state_mismatch=0 +} function check_consensus_parameters { - consensusparameter_tmp=($(nav_cli $1 getconsensusparameters | tr -d "[],\n")) - if [ "${#consensusparameter_tmp[@]}" -gt 10 ]; - then - copy_array consensusparameter_tmp consensusparameter_new - fi - for i in $(seq 0 1 $( bc <<< "${#consensusparameter_new[@]}-1" )); - do - if [ "${consensusparameter_new[$i]}" != "${consensusparameter_old[$i]}" ]; - then - echo Consensus Parameter ${consensus_parameter_name[$i]} changed from ${consensusparameter_old[$i]} to ${consensusparameter_new[$i]}!!! - fi - done - consensusparameter_old=("${consensusparameter_new[@]}") + consensusparameter_tmp=($(nav_cli $1 getconsensusparameters | tr -d "[],\n")) + if [ "${#consensusparameter_tmp[@]}" -gt 10 ]; + then + copy_array consensusparameter_tmp consensusparameter_new + fi + for i in $(seq 0 1 $( bc <<< "${#consensusparameter_new[@]}-1" )); + do + if [ "${consensusparameter_new[$i]}" != "${consensusparameter_old[$i]}" ]; + then + echo Consensus Parameter ${consensus_parameter_name[$i]} changed from ${consensusparameter_old[$i]} to ${consensusparameter_new[$i]}!!! + fi + done + consensusparameter_old=("${consensusparameter_new[@]}") } function stress { - for i in ${array_stressing_nodes[@]}; - do - out=$(nav_cli $i "staking true") - done - time=$(bc <<< $(date +%s)+$1) - while [ $time -gt $(date +%s) ] - do - dice_create_token - dice_mint_token - dice_send_token - dice_burn_token - dice_create_nft - dice_mint_nft - dice_send_nft - if [ "$bool_random_tx" == 1 ]; - then - random_transactions - fi - check_cycle - sleep $stress_sleep_time - done + for i in ${array_stressing_nodes[@]}; + do + out=$(nav_cli $i "staking true") + done + time=$(bc <<< $(date +%s)+$1) + while [ $time -gt $(date +%s) ] + do + dice_create_token + dice_mint_token + dice_send_token + dice_burn_token + dice_create_nft + dice_mint_nft + dice_send_nft + if [ "$bool_random_tx" == 1 ]; + then + random_transactions + fi + check_cycle + sleep $stress_sleep_time + done } function check_node { - blocks=`nav_cli $2 getinfo|jq .blocks` - block_increment=${#array_verifychain_nodes[@]} - for i in $(seq $1 $block_increment $blocks); - do - verifyoutput=`nav_cli $2 verifychain 4 $i` - if [[ "$verifyoutput" == "false" ]]; - then - verifyoutput+=`echo ' - ' && echo failed at $(grep 'ERROR: VerifyDB()' ${array_data[$2]}/devnet/debug.log |tail -1|sed 's/.*block at \(\d*\)/\1/')` - terminate 1 - fi - echo Node $2 - Rewinding to \ - $(bc <<< $blocks-$i) \ - - reconnecting up to $blocks \ - - verifychain 4 $i -\> $verifyoutput; - done - verifyoutput=`nav_cli $2 verifychain 4 0` - echo "Node $2 - Doing verifychain 4 0 -> $verifyoutput" - echo "node $2 finished" -} + blocks=`nav_cli $2 getinfo|jq .blocks` + block_increment=${#array_verifychain_nodes[@]} + for i in $(seq $1 $block_increment $blocks); + do + verifyoutput=`nav_cli $2 verifychain 4 $i` + if [[ "$verifyoutput" == "false" ]]; + then + verifyoutput+=`echo ' - ' && echo failed at $(grep 'ERROR: VerifyDB()' ${array_data[$2]}/devnet/debug.log |tail -1|sed 's/.*block at \(\d*\)/\1/')` + terminate 1 + fi + echo Node $2 - Rewinding to \ + $(bc <<< $blocks-$i) \ + - reconnecting up to $blocks \ + - verifychain 4 $i -\> $verifyoutput; + done + verifyoutput=`nav_cli $2 verifychain 4 0` + echo "Node $2 - Doing verifychain 4 0 -> $verifyoutput" + echo "node $2 finished" + } function check_cycle { - shuffle_array "${array_stressing_nodes[@]}" - node=${shuffled_array[0]} - blocks=$(nav_cli $node getinfo|jq .blocks) - if [ "$( bc <<< "$blocks - $last_cycle_end_block_height" )" -gt "$voting_cycle_length" ]; - then - last_cycle_end_block_height=$(echo "$last_cycle_end_block_height+$voting_cycle_length" | bc ) - voting_cycle_length=${consensusparameter_new[0]} - ((this_cycle++)) - fi + shuffle_array "${array_stressing_nodes[@]}" + node=${shuffled_array[0]} + blocks=$(nav_cli $node getinfo|jq .blocks) + if [ "$( bc <<< "$blocks - $last_cycle_end_block_height" )" -gt "$voting_cycle_length" ]; + then + last_cycle_end_block_height=$(echo "$last_cycle_end_block_height+$voting_cycle_length" | bc ) + voting_cycle_length=${consensusparameter_new[0]} + ((this_cycle++)) + fi } function random_verifychain_check { - echo "Performing random verifycahin check..." - - echo "Selecting random verifychain nodes..." - shuffle_array "${array_active_nodes[@]}" - if [ "${#array_active_nodes[@]}" -lt 7 ]; - then - for i in $(seq 0 1 $( bc <<< "${#array_active_nodes[@]} - 1" )); - do - array_verifychain_nodes[$i]=${shuffled_array[$i]} - done - else - for i in $(seq 0 1 7 ); - do - array_verifychain_nodes[$i]=${shuffled_array[$i]} - done - fi - echo '' - echo Running verifychain test with node ${array_verifychain_nodes[@]}... - echo '' - - starting_block=0 - for i in ${!array_verifychain_nodes[@]}; - do - ((starting_block++)) - check_node $starting_block ${array_verifychain_nodes[$i]} & - done - wait + echo "Performing random verifycahin check..." + + echo "Selecting random verifychain nodes..." + shuffle_array "${array_active_nodes[@]}" + if [ "${#array_active_nodes[@]}" -lt 7 ]; + then + for i in $(seq 0 1 $( bc <<< "${#array_active_nodes[@]} - 1" )); + do + array_verifychain_nodes[$i]=${shuffled_array[$i]} + done + else + for i in $(seq 0 1 7 ); + do + array_verifychain_nodes[$i]=${shuffled_array[$i]} + done + fi + echo '' + echo Running verifychain test with node ${array_verifychain_nodes[@]}... + echo '' + + starting_block=0 + for i in ${!array_verifychain_nodes[@]}; + do + ((starting_block++)) + check_node $starting_block ${array_verifychain_nodes[$i]} & + done + wait } function start_node { - $(echo $navpath)/navcoind -datadir=${array_data[$1]} -port=${array_p2p_port[$1]} -rpcport=${array_rpc_port[$1]} -devnet -daemon -debug=dao -debug=statehash -ntpminmeasures=0 -dandelion=0 -disablesafemode -staking=0 2> /dev/null -# gdb -batch -ex "run" -ex "bt" --args $(echo $navpath)/navcoind -datadir=${array_data[$1]} -port=${array_p2p_port[$1]} -rpcport=${array_rpc_port[$1]} -devnet -debug=dao -debug=statehash -ntpminmeasures=0 -dandelion=0 -disablesafemode -staking=0 > out.gdb & + $(echo $navpath)/navcoind -datadir=${array_data[$1]} -port=${array_p2p_port[$1]} -rpcport=${array_rpc_port[$1]} -devnet -daemon -nftindex -debug=dao -debug=statehash -ntpminmeasures=-1 -dandelion=0 -disablesafemode -staking=0 2> /dev/null + # gdb -batch -ex "run" -ex "bt" --args $(echo $navpath)/navcoind -datadir=${array_data[$1]} -port=${array_p2p_port[$1]} -rpcport=${array_rpc_port[$1]} -devnet -debug=dao -debug=statehash -ntpminmeasures=-1 -dandelion=0 -disablesafemode -staking=0 > out.gdb & } function stop_node { - out=$(nav_cli $1 stop) + out=$(nav_cli $1 stop) } ##########################################################Script body @@ -833,7 +865,7 @@ initialize_network for i in $(seq 0 1 $( bc <<< "$node_count-1" )); do - initialize_node $i + initialize_node $i done echo '' @@ -844,53 +876,53 @@ sleep 30 if [ "$node_count" == 1 ]; then - array_stressing_nodes=0 - array_verifychain_nodes=0 - stressing_node_count=1 + array_stressing_nodes=0 + array_verifychain_nodes=0 + stressing_node_count=1 else - if [ -z $array_topology ]; - then - echo "Creating random network topology..." - create_random_network_topology "${array_active_nodes[@]}" - echo "Created topology that has ${#array_topology_node_pairs[@]} connections out of max connections ${#array_network_connection_pool[@]} in the network" - echo "Network topology: ${array_topology_node_pairs[@]}" - else - echo "Using speficyfied network topology ${array_topology_node_pairs[@]}" - fi - - - echo "running script with $stressing_node_count stressing nodes in a network of $node_count nodes" - - if [ -z $array_stressing_nodes ]; - then - echo "Selecting random stressing nodes..." - shuffle_array "${array_active_nodes[@]}" - for i in $(seq 0 1 $( bc <<< "$stressing_node_count-1" )); - do - array_stressing_nodes[${shuffled_array[$i]}]=${shuffled_array[$i]} - done - else - echo "Using specified stressing nodes" - fi - - echo "array_stressing_nodes = ${array_stressing_nodes[@]}" - - if [ -z $array_verifychain_nodes ]; - then - echo "Selecting random verifychain nodes..." - shuffle_array "${array_active_nodes[@]}" - for i in $(seq 0 1 7); - do - array_verifychain_nodes[$i]=${shuffled_array[$i]} - done - else - echo "Using specified verifychain nodes" - fi - - echo "array_verifychain_nodes = ${array_verifychain_nodes[@]}" - - - connect_network "${array_topology_node_pairs[@]}" + if [ -z $array_topology ]; + then + echo "Creating random network topology..." + create_random_network_topology "${array_active_nodes[@]}" + echo "Created topology that has ${#array_topology_node_pairs[@]} connections out of max connections ${#array_network_connection_pool[@]} in the network" + echo "Network topology: ${array_topology_node_pairs[@]}" + else + echo "Using speficyfied network topology ${array_topology_node_pairs[@]}" + fi + + + echo "running script with $stressing_node_count stressing nodes in a network of $node_count nodes" + + if [ -z $array_stressing_nodes ]; + then + echo "Selecting random stressing nodes..." + shuffle_array "${array_active_nodes[@]}" + for i in $(seq 0 1 $( bc <<< "$stressing_node_count-1" )); + do + array_stressing_nodes[${shuffled_array[$i]}]=${shuffled_array[$i]} + done + else + echo "Using specified stressing nodes" + fi + + echo "array_stressing_nodes = ${array_stressing_nodes[@]}" + + if [ -z $array_verifychain_nodes ]; + then + echo "Selecting random verifychain nodes..." + shuffle_array "${array_active_nodes[@]}" + for i in $(seq 0 1 7); + do + array_verifychain_nodes[$i]=${shuffled_array[$i]} + done + else + echo "Using specified verifychain nodes" + fi + + echo "array_verifychain_nodes = ${array_verifychain_nodes[@]}" + + + connect_network "${array_topology_node_pairs[@]}" fi out=$(nav_cli 0 "generate 10") @@ -899,20 +931,20 @@ out=$(nav_cli 0 "generate 10") array_address=() for n in ${!array_stressing_nodes[@]}; do - array_address[$n]=$(nav_cli ${array_stressing_nodes[$n]} getnewaddress) + array_address[$n]=$(nav_cli ${array_stressing_nodes[$n]} getnewaddress) done for i in {1..10}; do - for j in ${array_address[@]}; - do - out=$(nav_cli 0 "sendtoaddress $j 10000") - done - out=$(nav_cli 0 "generate 1") + for j in ${array_address[@]}; + do + out=$(nav_cli 0 "sendtoaddress $j 10000") + done + out=$(nav_cli 0 "generate 1") done sleep 1 for n in ${array_stressing_nodes[@]}; do - echo "Node $n balance: $(nav_cli $n getbalance) NAV" + echo "Node $n balance: $(nav_cli $n getbalance) NAV" done @@ -921,8 +953,8 @@ blocks=$(nav_cli 0 getblockcount) echo 'blocks=' $blocks while [ $blocks -lt 300 ]; do - out=$(nav_cli 0 "generate 10") - blocks=$(nav_cli 0 getblockcount) + out=$(nav_cli 0 "generate 10") + blocks=$(nav_cli 0 getblockcount) done echo 'Distributing xNAV to nodes...' @@ -930,20 +962,20 @@ echo 'Distributing xNAV to nodes...' array_private_address=() for n in ${!array_stressing_nodes[@]}; do - array_private_address[$n]=$(nav_cli ${array_stressing_nodes[$n]} getnewprivateaddress) + array_private_address[$n]=$(nav_cli ${array_stressing_nodes[$n]} getnewprivateaddress) done -for i in {1..10}; +for i in {1..$node_count}; do - for j in ${array_private_address[@]}; - do - out=$(nav_cli 0 "sendtoaddress $j 10000") - done - out=$(nav_cli 0 "generate 1") + for j in ${array_private_address[@]}; + do + out=$(nav_cli 0 "sendtoaddress $j 10000") + done + out=$(nav_cli 0 "generate 1") done sleep 30 for n in ${array_stressing_nodes[@]}; do - echo "Node $n balance: $(nav_cli $n getwalletinfo | jq '.private_balance') xNAV" + echo "Node $n balance: $(nav_cli $n getwalletinfo | jq '.private_balance') xNAV" done out=$(nav_cli 0 "generate 10") @@ -959,7 +991,7 @@ consensus_parameter_count=$(nav_cli 0 getconsensusparameters | jq length) echo 'consensus parameter count =' $consensus_parameter_count for i in $(seq 0 1 $( bc <<< "$consensus_parameter_count - 1" )); do - eval "consensus_parameter_name[\$i]=\$(nav_cli 0 \"getconsensusparameters true\" | jq -r '.[] | select(.id==$i) | .desc')" + eval "consensus_parameter_name[\$i]=\$(nav_cli 0 \"getconsensusparameters true\" | jq -r '.[] | select(.id==$i) | .desc')" done consensusparameter_old=($(nav_cli 0 getconsensusparameters | tr -d "[],\n")) consensusparameter_original=("${consensusparameter_old[@]}") @@ -976,239 +1008,239 @@ check_consensus_parameters $node node_count_to_stop=$(echo "sqrt($node_count)" | bc ) while [ $wait_until_cycle -gt $this_cycle ]; do + echo '' + echo '' + echo Stressing for $seconds_round seconds... + + stress $seconds_round + + echo Waiting until nodes are synced... + wait_until_sync "${array_active_nodes[@]}" - echo '' - echo '' - echo Stressing for $seconds_round seconds... - - stress $seconds_round - - echo Waiting until nodes are synced... - - wait_until_sync "${array_active_nodes[@]}" - - for i in ${array_stressing_nodes[@]}; - do - out=$(nav_cli $i "staking false") - echo "Node $i balance: $(nav_cli $i getwalletinfo | jq '.private_balance') xNAV" - done - - echo Checking token hashes match... - - check_token_hash "${array_active_nodes[@]}" - shuffle_array "${array_stressing_nodes[@]}" - node=${shuffled_array[0]} - check_consensus_parameters $node - - if [ "$node_count" == 1 ]; - then - out=$(nav_cli 0 "generate 5") - fi - - if [ "$bool_random_verifychain_check" == 1 ]; - then - dice=$( bc <<< "$RANDOM % 60" ) - if [ "$dice" == 1 ]; - then - random_verifychain_check - fi - fi - if [ "$bool_stopstart_nodes" == 1 ]; - then - dice=$( bc <<< "$RANDOM % 3" ) - node_count_to_stop_this_round=$( bc <<< "$RANDOM % $node_count_to_stop + 1" ) - if [ "${#array_active_nodes[@]}" -gt "$( bc <<< "$node_count_to_stop_this_round + 2" )" ]; - then - if [ "$dice" != 1 ]; - then - select_random_nodes_to_stop $node_count_to_stop_this_round - stop_selected_nodes - fi - fi - if [ "$dice" == 1 ]; - then - start_random_stopped_nodes - fi - fi - if [ "$bool_random_new_topology" == 1 ]; - then - dice=$( bc <<< "$RANDOM % 10" ) - if [ "$dice" == 1 ]; - then - - attempts=0 - copy_array array_topology_node_pairs array_topology_node_pairs_backcup - copy_array array_topology_node_pairs_stopped array_topology_node_pairs_stopped_backup - unset array_connected_nodes - echo "Trying to create a new network topology while avoiding network splitting..." - while [ "${#array_connected_nodes[@]}" -lt "${#array_active_nodes[@]}" ] && [ "$attempts" -lt 10 ]; - do - copy_array array_topology_node_pairs_backup array_topology_node_pairs - copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped - create_random_network_topology ${array_all_nodes[@]} - unset array_topology_node_pairs_stopped - check_stopping_nodes_cause_network_split "${array_stopped_nodes[@]}" - ((attempts++)) - done - if [ "$attempts" == 10 ]; - then - copy_array array_topology_node_pairs_backup array_topology_node_pairs - copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped - else - disconnect_network "${array_topology_node_pairs_backup[@]}" - connect_network "${array_topology_node_pairs[@]}" - echo "Created topology that has ${#array_topology_node_pairs[@]} connections out of max connections ${#array_network_connection_pool[@]} in the network" - fi - echo array topoloy node pairs is "${array_topology_node_pairs[@]}" with index "${!array_topology_node_pairs[@]}" - echo array topology node pairs stopped is "${array_topology_node_pairs_stopped[@]}" with index "${!array_topology_node_pairs_stopped[@]}" - fi - fi - - if [ "$bool_sync_new_node" == 1 ]; - then - dice=$( bc <<< "$RANDOM % 10" ) - if [ "$dice" == 0 ]; - then - echo Initializing a new node to test syncing from genesis... - shuffle_array "${array_active_nodes[@]}" - node=${shuffled_array[0]} - ((node_count++)) - array_topology_node_pairs+=("$node_count $node") - #echo "array topology node pairs is now ${array_topology_node_pairs[@]}" - initialize_node $node_count - echo Waiting 30 sec for navcoind... - sleep 30 - connect_network "${array_topology_node_pairs[@]}" - sleep 30 - wait_until_sync "${array_active_nodes[@]}" - check_token_hash "${array_active_nodes[@]}" - echo Removing test sync node... - stop_node $node_count - sleep 30 - remove_node $node_count - for i in ${!array_topology_node_pairs[@]}; - do - unset connection - connection=(${array_topology_node_pairs[$i]}) - if [ "$node_count" == "${connection[0]}" ] || [ "$node_count" == "${connection[1]}" ]; - then - unset array_topology_node_pairs[$i] - fi - done - ((node_count--)) - fi - fi - cycles_left=$(bc <<< "$wait_until_cycle - $this_cycle") - previous_block=$current_block - current_block=$blocks - shuffle_array "${array_stressing_nodes[@]}" - node=${shuffled_array[0]} - echo Current block: $current_block Current cycle: $this_cycle - $cycles_left cycle\(s\) left to finish. - echo Tokens and NFTs created: $(nav_cli $node listtokens | jq ".[].name" | wc -l) - echo Active nodes: ${array_active_nodes[@]} Inactive nodes: ${array_stopped_nodes[@]} -# for j in $(seq 0 1 $( bc <<< "$node_count-1")); -# do -# echo Reorganizations Node $j: $(prev=0;for i in $(grep height= ${array_data[$j]}/devnet/debug.log|sed -n -e '/height\='$previous_block'/,$p'|sed -n 's/.*height\=\([0-9]*\) .*/\1/p'); do if [ $i -lt $prev ]; then echo $i; fi; prev=$i; done) -# done + for i in ${array_stressing_nodes[@]}; + do + out=$(nav_cli $i "staking false") + echo "Node $i balance: $(nav_cli $i getwalletinfo | jq '.private_balance') xNAV" + done + + echo Checking token hashes match... + + check_token_hash "${array_active_nodes[@]}" + shuffle_array "${array_stressing_nodes[@]}" + node=${shuffled_array[0]} + check_consensus_parameters $node + + if [ "$node_count" == 1 ]; + then + out=$(nav_cli 0 "generate 5") + fi + + if [ "$bool_random_verifychain_check" == 1 ]; + then + dice=$( bc <<< "$RANDOM % 60" ) + if [ "$dice" == 1 ]; + then + random_verifychain_check + fi + fi + if [ "$bool_stopstart_nodes" == 1 ]; + then + dice=$( bc <<< "$RANDOM % 3" ) + node_count_to_stop_this_round=$( bc <<< "$RANDOM % $node_count_to_stop + 1" ) + if [ "${#array_active_nodes[@]}" -gt "$( bc <<< "$node_count_to_stop_this_round + 2" )" ]; + then + if [ "$dice" != 1 ]; + then + select_random_nodes_to_stop $node_count_to_stop_this_round + stop_selected_nodes + fi + fi + if [ "$dice" == 1 ]; + then + start_random_stopped_nodes + fi + fi + if [ "$bool_random_new_topology" == 1 ]; + then + dice=$( bc <<< "$RANDOM % 10" ) + if [ "$dice" == 1 ]; + then + + attempts=0 + copy_array array_topology_node_pairs array_topology_node_pairs_backcup + copy_array array_topology_node_pairs_stopped array_topology_node_pairs_stopped_backup + unset array_connected_nodes + echo "Trying to create a new network topology while avoiding network splitting..." + while [ "${#array_connected_nodes[@]}" -lt "${#array_active_nodes[@]}" ] && [ "$attempts" -lt 10 ]; + do + copy_array array_topology_node_pairs_backup array_topology_node_pairs + copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped + create_random_network_topology ${array_all_nodes[@]} + unset array_topology_node_pairs_stopped + check_stopping_nodes_cause_network_split "${array_stopped_nodes[@]}" + ((attempts++)) + done + if [ "$attempts" == 10 ]; + then + copy_array array_topology_node_pairs_backup array_topology_node_pairs + copy_array array_topology_node_pairs_stopped_backup array_topology_node_pairs_stopped + else + disconnect_network "${array_topology_node_pairs_backup[@]}" + connect_network "${array_topology_node_pairs[@]}" + echo "Created topology that has ${#array_topology_node_pairs[@]} connections out of max connections ${#array_network_connection_pool[@]} in the network" + fi + echo array topoloy node pairs is "${array_topology_node_pairs[@]}" with index "${!array_topology_node_pairs[@]}" + echo array topology node pairs stopped is "${array_topology_node_pairs_stopped[@]}" with index "${!array_topology_node_pairs_stopped[@]}" + fi + fi + + if [ "$bool_sync_new_node" == 1 ]; + then + dice=$( bc <<< "$RANDOM % 10" ) + if [ "$dice" == 0 ]; + then + echo Initializing a new node to test syncing from genesis... + shuffle_array "${array_active_nodes[@]}" + node=${shuffled_array[0]} + ((node_count++)) + array_topology_node_pairs+=("$node_count $node") + #echo "array topology node pairs is now ${array_topology_node_pairs[@]}" + initialize_node $node_count + echo Waiting 30 sec for navcoind... + sleep 30 + connect_network "${array_topology_node_pairs[@]}" + sleep 30 + wait_until_sync "${array_active_nodes[@]}" + check_token_hash "${array_active_nodes[@]}" + echo Removing test sync node... + stop_node $node_count + sleep 30 + remove_node $node_count + for i in ${!array_topology_node_pairs[@]}; + do + unset connection + connection=(${array_topology_node_pairs[$i]}) + if [ "$node_count" == "${connection[0]}" ] || [ "$node_count" == "${connection[1]}" ]; + then + unset array_topology_node_pairs[$i] + fi + done + ((node_count--)) + fi + fi + cycles_left=$(bc <<< "$wait_until_cycle - $this_cycle") + previous_block=$current_block + current_block=$blocks + shuffle_array "${array_stressing_nodes[@]}" + node=${shuffled_array[0]} + echo Current block: $current_block Current cycle: $this_cycle - $cycles_left cycle\(s\) left to finish. + echo Tokens created: $(nav_cli $node listtokens | jq ".[].name" | wc -l) + echo NFTs created: $(nav_cli $node listnfts | jq ".[].name" | wc -l) + echo Active nodes: ${array_active_nodes[@]} Inactive nodes: ${array_stopped_nodes[@]} + # for j in $(seq 0 1 $( bc <<< "$node_count-1")); + # do + # echo Reorganizations Node $j: $(prev=0;for i in $(grep height= ${array_data[$j]}/devnet/debug.log|sed -n -e '/height\='$previous_block'/,$p'|sed -n 's/.*height\=\([0-9]*\) .*/\1/p'); do if [ $i -lt $prev ]; then echo $i; fi; prev=$i; done) + # done done ##############################Network Splitting if [ "$bool_network_split" == 1 ]; then - echo Stopping and then starting all nodes... - for i in ${array_active_nodes[@]}; - do - echo Stopping node $i... - stop_node $i - done - sleep 10 - for i in ${array_all_nodes[@]}; - do - echo Starting node $i... - start_node $i - done - echo Waiting 30 sec for navcoind... - sleep 30 - echo Splitting the network into $network_count sub networks... - network_split_started=1 - counter=0 - for i in ${array_stressing_nodes[@]}; - do - if [ "$counter" == "$network_count" ]; - then - counter=0 - fi - eval "array_stressing_nodes_network$counter[$i]=$i" - eval "array_all_nodes_network$counter[$i]=$i" - ((counter++)) - done - counter=0 - for i in ${array_all_nodes[@]}; - do - if [ "$counter" == "$network_count" ]; - then - counter=0 - fi - match=0 - for j in ${array_stressing_nodes[@]}; - do - if [ "$j" == "$i" ]; - then - match=1 - fi - done - if [ "$match" != 1 ]; - then - eval "array_all_nodes_network$counter[$i]=$i" - fi - ((counter++)) - done - copy_array array_topology_node_pairs array_topology_node_pairs_backup - for nc in $(seq 0 1 $( bc <<< "$network_count-1" )); - do - eval "echo stressing nodes in network $nc: \${array_stressing_nodes_network$nc[@]}" - eval "echo nodes in network $nc: \${array_all_nodes_network$nc[@]} index: \${!array_all_nodes_network$nc[@]}" - eval "create_random_network_topology \"\${array_all_nodes_network$nc[@]}\"" - copy_array array_topology_node_pairs array_topology_node_pairs_network$nc - eval "echo topology of network$nc is: \"\${array_topology_node_pairs_network$nc[@]}\"" - eval "connect_network \"\${array_topology_node_pairs_network$nc[@]}\"" - done - wait_until_cycle=$(bc <<< "$this_cycle + $cycles_network_split") - while [ $wait_until_cycle -gt $this_cycle ]; do - echo '' - echo Stressing for $seconds_round seconds... - stress $seconds_round - for nc in $(seq 0 1 $( bc <<< "$network_count - 1" )); - do - echo Waiting until nodes are synced... - eval "wait_until_sync \"\${array_all_nodes_network$nc[@]}\"" - echo Checking token hashes match... - eval "check_token_hash \"\${array_all_nodes_network$nc[@]}\"" - eval "shuffle_array \"\${array_all_nodes_network$nc[@]}\"" - node=${shuffled_array[0]} - check_consensus_parameters $node - blocks=$(nav_cli $node getinfo|jq .blocks) - echo Tokens and NFTs created: $(nav_cli $node listtokens | jq ".[].name" | wc -l) - done - cycles_left=$(bc <<< "$wait_until_cycle - $this_cycle") - previous_block=$current_block - current_block=$blocks - shuffle_array "${array_stressing_nodes[@]}" - node=${shuffled_array[0]} - echo Current block: $current_block - echo Current cycle: $this_cycle - $cycles_left cycle\(s\) left to finish. - for i in ${array_stressing_nodes[@]}; - do - out=$(nav_cli $i "staking false") - echo "Node $i balance: $(nav_cli $i getwalletinfo | jq '.private_balance') xNAV" - done - done - - create_random_network_topology "${array_all_nodes[@]}" - connect_network "${array_topology_node_pairs[@]}" - wait_until_sync "${array_all_nodes[@]}" - check_token_hash "${array_all_nodes[@]}" + echo Stopping and then starting all nodes... + for i in ${array_active_nodes[@]}; + do + echo Stopping node $i... + stop_node $i + done + sleep 10 + for i in ${array_all_nodes[@]}; + do + echo Starting node $i... + start_node $i + done + echo Waiting 30 sec for navcoind... + sleep 30 + echo Splitting the network into $network_count sub networks... + network_split_started=1 + counter=0 + for i in ${array_stressing_nodes[@]}; + do + if [ "$counter" == "$network_count" ]; + then + counter=0 + fi + eval "array_stressing_nodes_network$counter[$i]=$i" + eval "array_all_nodes_network$counter[$i]=$i" + ((counter++)) + done + counter=0 + for i in ${array_all_nodes[@]}; + do + if [ "$counter" == "$network_count" ]; + then + counter=0 + fi + match=0 + for j in ${array_stressing_nodes[@]}; + do + if [ "$j" == "$i" ]; + then + match=1 + fi + done + if [ "$match" != 1 ]; + then + eval "array_all_nodes_network$counter[$i]=$i" + fi + ((counter++)) + done + copy_array array_topology_node_pairs array_topology_node_pairs_backup + for nc in $(seq 0 1 $( bc <<< "$network_count-1" )); + do + eval "echo stressing nodes in network $nc: \${array_stressing_nodes_network$nc[@]}" + eval "echo nodes in network $nc: \${array_all_nodes_network$nc[@]} index: \${!array_all_nodes_network$nc[@]}" + eval "create_random_network_topology \"\${array_all_nodes_network$nc[@]}\"" + copy_array array_topology_node_pairs array_topology_node_pairs_network$nc + eval "echo topology of network$nc is: \"\${array_topology_node_pairs_network$nc[@]}\"" + eval "connect_network \"\${array_topology_node_pairs_network$nc[@]}\"" + done + wait_until_cycle=$(bc <<< "$this_cycle + $cycles_network_split") + while [ $wait_until_cycle -gt $this_cycle ]; do + echo '' + echo Stressing for $seconds_round seconds... + stress $seconds_round + for nc in $(seq 0 1 $( bc <<< "$network_count - 1" )); + do + echo Waiting until nodes are synced... + eval "wait_until_sync \"\${array_all_nodes_network$nc[@]}\"" + echo Checking token hashes match... + eval "check_token_hash \"\${array_all_nodes_network$nc[@]}\"" + eval "shuffle_array \"\${array_all_nodes_network$nc[@]}\"" + node=${shuffled_array[0]} + check_consensus_parameters $node + blocks=$(nav_cli $node getinfo|jq .blocks) + echo Tokens created: $(nav_cli $node listtokens | jq ".[].name" | wc -l) + echo NFTs created: $(nav_cli $node listnfts | jq ".[].name" | wc -l) + done + cycles_left=$(bc <<< "$wait_until_cycle - $this_cycle") + previous_block=$current_block + current_block=$blocks + shuffle_array "${array_stressing_nodes[@]}" + node=${shuffled_array[0]} + echo Current block: $current_block + echo Current cycle: $this_cycle - $cycles_left cycle\(s\) left to finish. + for i in ${array_stressing_nodes[@]}; + do + out=$(nav_cli $i "staking false") + echo "Node $i balance: $(nav_cli $i getwalletinfo | jq '.private_balance') xNAV" + done + done + + create_random_network_topology "${array_all_nodes[@]}" + connect_network "${array_topology_node_pairs[@]}" + wait_until_sync "${array_all_nodes[@]}" + check_token_hash "${array_all_nodes[@]}" fi ############################# @@ -1220,23 +1252,24 @@ echo '' echo Stopping stresser. Waiting until $extra_blocks blocks are staked for i in ${array_stressing_nodes[@]}; do - out=$(nav_cli $i "staking true") + out=$(nav_cli $i "staking true") done while [ $blocks -lt $wait_until ]; do - sleep 30 - blocks=$(nav_cli $node getinfo|jq .blocks) - echo $(bc <<< "$wait_until-$blocks") blocks left... + sleep 30 + blocks=$(nav_cli $node getinfo|jq .blocks) + echo $(bc <<< "$wait_until-$blocks") blocks left... done for i in ${array_stressing_nodes[@]}; do - out=$(nav_cli $i "staking false") + out=$(nav_cli $i "staking false") done echo '' echo Waiting until all nodes are synced wait_until_sync "${array_active_nodes[@]}" echo Ok! Block: $(nav_cli $node getinfo|jq .blocks) Cycle: $(bc <<< $(nav_cli $node getinfo|jq .blocks)/$voting_cycle_length). -echo Tokens and NFTs created: $(nav_cli $node listtokens | jq ".[].name" | wc -l) +echo Tokens created: $(nav_cli $node listtokens | jq ".[].name" | wc -l) +echo NFTs created: $(nav_cli $node listnfts | jq ".[].name" | wc -l) echo '' echo Running verifychain test with node ${verifychain_nodes[@]}... @@ -1245,15 +1278,15 @@ echo '' starting_block=0 for i in ${!array_verifychain_nodes[@]}; do - ((starting_block++)) - check_node $starting_block ${array_verifychain_nodes[$i]} & + ((starting_block++)) + check_node $starting_block ${array_verifychain_nodes[$i]} & done wait for n in ${array_stressing_nodes[@]}; do - echo "Node $n balance: $(nav_cli $n getbalance) tNAV" + echo "Node $n balance: $(nav_cli $n getbalance) tNAV" done diff --git a/src/Makefile.am b/src/Makefile.am index 2c9a61b91..62340f459 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -333,6 +333,7 @@ libnavcoin_consensus_a_SOURCES = \ consensus/programs.h \ consensus/program_actions.h \ ctokens/ctokens.h \ + ctokens/tokenutxos.h \ ctokens/tokenid.h \ dotnav/namedata.h \ dotnav/namerecord.h \ diff --git a/src/coins.cpp b/src/coins.cpp index 33d6526ff..6c0848144 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -53,6 +53,7 @@ bool CStateView::GetConsultation(const uint256 &cid, CConsultation& consultation bool CStateView::GetConsultationAnswer(const uint256 &cid, CConsultationAnswer& answer) const { return false; } bool CStateView::GetConsensusParameter(const int &pid, CConsensusParameter& cparameter) const { return false; } bool CStateView::GetToken(const uint256 &id, TokenInfo& token) const { return false; } +bool CStateView::GetTokenUtxos(const uint256 &id, TokenUtxoValues &tokenUtxos) { return false; }; bool CStateView::GetNameRecord(const uint256 &id, NameRecordValue& height) const { return false; } bool CStateView::GetNameData(const uint256 &id, NameDataValues& data) { return false; } bool CStateView::HaveCoins(const uint256 &txid) const { return false; } @@ -63,6 +64,7 @@ bool CStateView::HaveConsultation(const uint256 &cid) const { return false; } bool CStateView::HaveConsultationAnswer(const uint256 &cid) const { return false; } bool CStateView::HaveConsensusParameter(const int &pid) const { return false; } bool CStateView::HaveToken(const uint256 &id) const { return false; } +bool CStateView::HaveTokenUtxos(const uint256 &id) const { return false; } bool CStateView::HaveNameRecord(const uint256 &id) const { return false; } bool CStateView::HaveNameData(const uint256 &id) const { return false; } bool CStateView::GetAllProposals(CProposalMap& map) { return false; } @@ -78,7 +80,7 @@ uint256 CStateView::GetBestBlock() const { return uint256(); } bool CStateView::BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap &mapVotes, CConsultationMap& mapConsultations, CConsultationAnswerMap& mapAnswers, - CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, + CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, TokenUtxoMap& mapTokenUtxos, NameRecordMap& mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlock, const int& nCacheExcludeVotes) { return false; } CStateViewCursor *CStateView::Cursor() const { return 0; } @@ -92,6 +94,7 @@ bool CStateViewBacked::GetConsultation(const uint256 &cid, CConsultation &consul bool CStateViewBacked::GetConsultationAnswer(const uint256 &cid, CConsultationAnswer &answer) const { return base->GetConsultationAnswer(cid, answer); } bool CStateViewBacked::GetConsensusParameter(const int &pid, CConsensusParameter& cparameter) const { return base->GetConsensusParameter(pid, cparameter); } bool CStateViewBacked::GetToken(const uint256 &id, TokenInfo& token) const { return base->GetToken(id, token); } +bool CStateViewBacked::GetTokenUtxos(const uint256 &id, TokenUtxoValues &tokenUtxos) { return base->GetTokenUtxos(id, tokenUtxos); } bool CStateViewBacked::GetNameRecord(const uint256 &id, NameRecordValue& height) const { return base->GetNameRecord(id, height); } bool CStateViewBacked::GetNameData(const uint256 &id, NameDataValues& data) { return base->GetNameData(id, data); } bool CStateViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); } @@ -102,6 +105,7 @@ bool CStateViewBacked::HaveConsultation(const uint256 &cid) const { return base- bool CStateViewBacked::HaveConsultationAnswer(const uint256 &cid) const { return base->HaveConsultationAnswer(cid); } bool CStateViewBacked::HaveConsensusParameter(const int &pid) const { return base->HaveConsensusParameter(pid); } bool CStateViewBacked::HaveToken(const uint256 &id) const { return base->HaveToken(id); } +bool CStateViewBacked::HaveTokenUtxos(const uint256 &id) const { return base->HaveTokenUtxos(id); } bool CStateViewBacked::HaveNameRecord(const uint256 &id) const { return base->HaveNameRecord(id); } bool CStateViewBacked::HaveNameData(const uint256 &id) const { return base->HaveNameData(id); } int CStateViewBacked::GetExcludeVotes() const { return base->GetExcludeVotes(); } @@ -119,10 +123,10 @@ void CStateViewBacked::SetBackend(CStateView &viewIn) { base = &viewIn; } bool CStateViewBacked::BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap &mapVotes, CConsultationMap &mapConsultations, CConsultationAnswerMap &mapAnswers, - CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, + CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, TokenUtxoMap& mapTokenUtxos, NameRecordMap& mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlock, const int &nCacheExcludeVotes) { - return base->BatchWrite(mapCoins, mapProposals, mapPaymentRequests, mapVotes, mapConsultations, mapAnswers, mapConsensus, mapTokens, mapNameRecords, mapNameData, hashBlock, nCacheExcludeVotes); + return base->BatchWrite(mapCoins, mapProposals, mapPaymentRequests, mapVotes, mapConsultations, mapAnswers, mapConsensus, mapTokens, mapTokenUtxos, mapNameRecords, mapNameData, hashBlock, nCacheExcludeVotes); } CStateViewCursor *CStateViewBacked::Cursor() const { return base->Cursor(); } @@ -278,6 +282,23 @@ TokenMap::const_iterator CStateViewCache::FetchToken(const uint256 &id) const { return ret; } +TokenUtxoMap::const_iterator CStateViewCache::FetchTokenUtxos(const uint256 &id) const { + TokenUtxoMap::iterator it = cacheTokenUtxos.find(id); + + if (it != cacheTokenUtxos.end() && it->second.size() > 0) + return it; + + TokenUtxoValues tmp; + + if (!base->GetTokenUtxos(id, tmp) || tmp.size() == 0) + return cacheTokenUtxos.end(); + + TokenUtxoMap::iterator ret = cacheTokenUtxos.insert(std::make_pair(id, TokenUtxoValues())).first; + tmp.swap(ret->second); + + return ret; +} + NameRecordMap::const_iterator CStateViewCache::FetchNameRecord(const uint256 &id) const { NameRecordMap::iterator it = cacheNameRecords.find(id); @@ -389,6 +410,15 @@ bool CStateViewCache::GetToken(const uint256 &id, TokenInfo &token) const { return false; } +bool CStateViewCache::GetTokenUtxos(const uint256 &id, TokenUtxoValues &tokenUtxos) { + TokenUtxoMap::const_iterator it = FetchTokenUtxos(id); + if (it != cacheTokenUtxos.end() && it->second.size() > 0) { + tokenUtxos = it->second; + return true; + } + return false; +} + bool CStateViewCache::GetNameRecord(const uint256 &id, NameRecordValue &height) const { NameRecordMap::const_iterator it = FetchNameRecord(id); if (it != cacheNameRecords.end() && !it->second.IsNull()) { @@ -616,6 +646,17 @@ TokenModifier CStateViewCache::ModifyToken(const uint256 &id, int nHeight) { return TokenModifier(*this, ret.first, nHeight); } +TokenUtxosModifier CStateViewCache::ModifyTokenUtxos(const uint256 &id, int blockHeight) { + assert(!hasModifier); + std::pair ret = cacheTokenUtxos.insert(std::make_pair(id, TokenUtxoValues())); + if (ret.second) { + if (!base->GetTokenUtxos(id, ret.first->second)) { + ret.first->second.clear(); + } + } + return TokenUtxosModifier(*this, ret.first, blockHeight); +} + NameRecordModifier CStateViewCache::ModifyNameRecord(const uint256 &id, int nHeight) { assert(!hasModifier); std::pair ret = cacheNameRecords.insert(std::make_pair(id, 0)); @@ -773,6 +814,21 @@ bool CStateViewCache::AddToken(const Token& token) const { return true; } +bool CStateViewCache::AddTokenUtxo(const uint256 &id, const TokenUtxoEntry& utxo) const { + if (cacheTokenUtxos.count(id)) { + cacheTokenUtxos[id].erase( + std::remove_if(cacheTokenUtxos[id].begin(), cacheTokenUtxos[id].end(), + [&utxo](const TokenUtxoEntry & o) { return o.first == utxo.first && o.second.IsNull(); }), + cacheTokenUtxos[id].end()); + cacheTokenUtxos[id].push_back(utxo); + } else { + cacheTokenUtxos.insert(std::make_pair(id, TokenUtxoValues())); + cacheTokenUtxos[id].push_back(utxo); + } + + return true; +} + bool CStateViewCache::AddNameRecord(const NameRecord& namerecord) const { if (HaveNameRecord(namerecord.first)) return false; @@ -826,6 +882,28 @@ bool CStateViewCache::RemoveToken(const uint256 &id) const { return true; } +bool CStateViewCache::RemoveTokenUtxo(const TokenUtxoKey &key) const { + if (!HaveTokenUtxos(key.id)) + return false; + + if (cacheTokenUtxos.count(key.id)) + { + TokenUtxoValues temp; + + for (auto& it: cacheTokenUtxos[key.id]) { + if (it.first == key.blockHeight) { + temp.push_back(TokenUtxoEntry(key.blockHeight, TokenUtxoValue())); + } else { + temp.push_back(it); + } + } + + cacheTokenUtxos[key.id] = temp; + } + + return true; +} + bool CStateViewCache::RemoveNameRecord(const uint256 &id) const { if (!HaveNameRecord(id)) return false; @@ -960,6 +1038,11 @@ bool CStateViewCache::HaveToken(const uint256 &id) const { return (it != cacheTokens.end() && !it->second.IsNull()); } +bool CStateViewCache::HaveTokenUtxos(const uint256 &id) const { + TokenUtxoMap::const_iterator it = FetchTokenUtxos(id); + return (it != cacheTokenUtxos.end() && it->second.size()); +} + bool CStateViewCache::HaveNameRecord(const uint256 &id) const { NameRecordMap::const_iterator it = FetchNameRecord(id); return (it != cacheNameRecords.end() && !it->second.IsNull()); @@ -1016,7 +1099,7 @@ void CStateViewCache::SetBestBlock(const uint256 &hashBlockIn) { bool CStateViewCache::BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap& mapVotes, CConsultationMap& mapConsultations, CConsultationAnswerMap& mapAnswers, - CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, NameRecordMap& mapNameRecords, + CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, TokenUtxoMap& mapTokenUtxos, NameRecordMap& mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlockIn, const int &nCacheExcludeVotesIn) { assert(!hasModifier); assert(!hasModifierConsensus); @@ -1121,6 +1204,13 @@ bool CStateViewCache::BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals mapTokens.erase(itOld); } + for (TokenUtxoMap::iterator it = mapTokenUtxos.begin(); it != mapTokenUtxos.end();) { + TokenUtxoValues& entry = cacheTokenUtxos[it->first]; + entry.swap(it->second); + TokenUtxoMap::iterator itOld = it++; + mapTokenUtxos.erase(itOld); + } + for (NameRecordMap::iterator it = mapNameRecords.begin(); it != mapNameRecords.end();) { NameRecordValue& entry = cacheNameRecords[it->first]; entry.swap(it->second); @@ -1141,7 +1231,7 @@ bool CStateViewCache::BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals } bool CStateViewCache::Flush() { - bool fOk = base->BatchWrite(cacheCoins, cacheProposals, cachePaymentRequests, cacheVotes, cacheConsultations, cacheAnswers, cacheConsensus, cacheTokens, cacheNameRecords, cacheNameData, hashBlock, nCacheExcludeVotes); + bool fOk = base->BatchWrite(cacheCoins, cacheProposals, cachePaymentRequests, cacheVotes, cacheConsultations, cacheAnswers, cacheConsensus, cacheTokens, cacheTokenUtxos, cacheNameRecords, cacheNameData, hashBlock, nCacheExcludeVotes); cacheCoins.clear(); cacheProposals.clear(); cachePaymentRequests.clear(); @@ -1150,6 +1240,7 @@ bool CStateViewCache::Flush() { cacheAnswers.clear(); cacheConsensus.clear(); cacheTokens.clear(); + cacheTokenUtxos.clear(); cacheNameRecords.clear(); cacheNameData.clear(); cachedCoinsUsage = 0; @@ -1397,6 +1488,27 @@ TokenModifier::~TokenModifier() } } +TokenUtxosModifier::TokenUtxosModifier(CStateViewCache& cache_, TokenUtxoMap::iterator it_, int blockHeight_) : cache(cache_), it(it_), blockHeight(blockHeight_) { + assert(!cache.hasModifier); + cache.hasModifier = true; + prev = it->second; +} + +TokenUtxosModifier::~TokenUtxosModifier() +{ + assert(cache.hasModifier); + cache.hasModifier = false; + + if (it->second.size() == 0) { + cache.cacheTokenUtxos[it->first].clear(); + } + + if (!(prev == it->second)) + { + LogPrint("token", "%s: Modified %stoken utxo %s\n", __func__, blockHeight > 0 ? strprintf("at block height %d ", blockHeight) : "", it->first.ToString()); + } +} + NameRecordModifier::NameRecordModifier(CStateViewCache& cache_, NameRecordMap::iterator it_, int height_) : cache(cache_), it(it_), height(height_) { assert(!cache.hasModifier); cache.hasModifier = true; diff --git a/src/coins.h b/src/coins.h index df5df0fcd..77bad3110 100644 --- a/src/coins.h +++ b/src/coins.h @@ -13,10 +13,11 @@ #include #include -#include "consensus/dao.h" -#include "ctokens/ctokens.h" -#include "dotnav/namerecord.h" -#include "dotnav/namedata.h" +#include +#include +#include +#include +#include #include #include @@ -387,6 +388,9 @@ class CStateView virtual bool GetAllTokens(TokenMap& map); virtual bool HaveToken(const uint256 &id) const; + virtual bool GetTokenUtxos(const uint256 &id, TokenUtxoValues &tokenUtxos); + virtual bool HaveTokenUtxos(const uint256 &id) const; + virtual bool GetNameRecord(const uint256 &id, NameRecordValue& height) const; virtual bool GetAllNameRecords(NameRecordMap& map); virtual bool HaveNameRecord(const uint256 &id) const; @@ -405,7 +409,7 @@ class CStateView virtual bool BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap &mapVotes, CConsultationMap &mapConsultations, CConsultationAnswerMap &mapAnswers, - CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, + CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, TokenUtxoMap& mapTokenUtxos, NameRecordMap& mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlock, const int &nCacheExcludeVotes); @@ -448,6 +452,9 @@ class CStateViewBacked : public CStateView bool GetAllTokens(TokenMap& map); bool HaveToken(const uint256 &id) const; + bool GetTokenUtxos(const uint256 &id, TokenUtxoValues &tokenUtxos); + bool HaveTokenUtxos(const uint256 &id) const; + bool GetNameRecord(const uint256 &id, NameRecordValue& height) const; bool GetAllNameRecords(NameRecordMap& map); bool HaveNameRecord(const uint256 &id) const; @@ -463,7 +470,7 @@ class CStateViewBacked : public CStateView bool BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap &mapVotes, CConsultationMap &mapConsultations, CConsultationAnswerMap &mapAnswers, - CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, + CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, TokenUtxoMap& mapTokenUtxos, NameRecordMap& mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlock, const int &nCacheExcludeVotes); CStateViewCursor *Cursor() const; @@ -589,6 +596,22 @@ class TokenModifier friend class CStateViewCache; }; +class TokenUtxosModifier +{ +private: + CStateViewCache& cache; + TokenUtxoMap::iterator it; + TokenUtxosModifier(CStateViewCache& cache_, TokenUtxoMap::iterator it_, int blockHeight=0); + TokenUtxoValues prev; + int blockHeight; + +public: + TokenUtxoValues* operator->() { return &it->second; } + TokenUtxoValues& operator*() { return it->second; } + ~TokenUtxosModifier(); + friend class CStateViewCache; +}; + class NameRecordModifier { private: @@ -658,6 +681,7 @@ class CStateViewCache : public CStateViewBacked mutable CConsultationAnswerMap cacheAnswers; mutable CConsensusParameterMap cacheConsensus; mutable TokenMap cacheTokens; + mutable TokenUtxoMap cacheTokenUtxos; mutable NameRecordMap cacheNameRecords; mutable NameDataMap cacheNameData; mutable int nCacheExcludeVotes; @@ -679,6 +703,7 @@ class CStateViewCache : public CStateViewBacked bool HaveConsultationAnswer(const uint256 &cid) const; bool HaveConsensusParameter(const int& pid) const; bool HaveToken(const uint256& id) const; + bool HaveTokenUtxos(const uint256 &id) const; bool HaveNameRecord(const uint256& id) const; bool HaveNameData(const uint256& id) const; bool GetProposal(const uint256 &txid, CProposal &proposal) const; @@ -688,6 +713,7 @@ class CStateViewCache : public CStateViewBacked bool GetConsultationAnswer(const uint256 &cid, CConsultationAnswer& answer) const; bool GetConsensusParameter(const int& pid, CConsensusParameter& cparameter) const; bool GetToken(const uint256& pid, TokenInfo& token) const; + bool GetTokenUtxos(const uint256 &id, TokenUtxoValues &tokenUtxos); bool GetNameRecord(const uint256& pid, NameRecordValue& height) const; bool GetNameData(const uint256& pid, NameDataValues& data); bool GetAllProposals(CProposalMap& map); @@ -703,7 +729,7 @@ class CStateViewCache : public CStateViewBacked bool BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap &mapVotes, CConsultationMap &mapConsultations, CConsultationAnswerMap &mapAnswers, - CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, + CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, TokenUtxoMap& mapTokenUtxos, NameRecordMap& mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlockIn, const int &nCacheExcludeVotes); bool AddProposal(const CProposal& proposal) const; @@ -711,6 +737,7 @@ class CStateViewCache : public CStateViewBacked bool AddCachedVoter(const CVoteMapKey &voter, CVoteMapValue& vote) const; bool AddConsultation(const CConsultation& consultation) const; bool AddToken(const Token& token) const; + bool AddTokenUtxo(const uint256 &id, const TokenUtxoEntry& utxo) const; bool AddNameRecord(const NameRecord& record) const; bool AddNameData(const uint256& id, const NameDataEntry& record) const; bool AddConsultationAnswer(const CConsultationAnswer& answer); @@ -718,6 +745,7 @@ class CStateViewCache : public CStateViewBacked bool RemovePaymentRequest(const uint256 &prid) const; bool RemoveCachedVoter(const CVoteMapKey &voter) const; bool RemoveToken(const uint256 &pid) const; + bool RemoveTokenUtxo(const TokenUtxoKey &key) const; bool RemoveNameRecord(const uint256 &pid) const; bool RemoveNameData(const NameDataKey &id) const; bool RemoveConsultation(const uint256 &cid); @@ -756,6 +784,7 @@ class CStateViewCache : public CStateViewBacked CConsultationAnswerModifier ModifyConsultationAnswer(const uint256 &cid, int nHeight = 0); CConsensusParameterModifier ModifyConsensusParameter(const int &pid, int nHeight = 0); TokenModifier ModifyToken(const uint256 &id, int nHeight = 0); + TokenUtxosModifier ModifyTokenUtxos(const uint256 &id, int blockHeight = 0); NameRecordModifier ModifyNameRecord(const uint256 &id, int nHeight = 0); NameDataModifier ModifyNameData(const uint256& id, int nHeight = 0); @@ -819,6 +848,7 @@ class CStateViewCache : public CStateViewBacked friend class CConsultationAnswerModifier; friend class CConsensusParameterModifier; friend class TokenModifier; + friend class TokenUtxosModifier; friend class NameRecordModifier; friend class NameDataModifier; @@ -832,6 +862,7 @@ class CStateViewCache : public CStateViewBacked CConsultationAnswerMap::const_iterator FetchConsultationAnswer(const uint256 &cid) const; CConsensusParameterMap::const_iterator FetchConsensusParameter(const int &pid) const; TokenMap::const_iterator FetchToken(const uint256 &id) const; + TokenUtxoMap::const_iterator FetchTokenUtxos(const uint256 &id) const; NameRecordMap::const_iterator FetchNameRecord(const uint256 &id) const; NameDataMap::const_iterator FetchNameData(const uint256 &id) const; diff --git a/src/ctokens/tokenid.h b/src/ctokens/tokenid.h index 2fa2350a9..975363f51 100644 --- a/src/ctokens/tokenid.h +++ b/src/ctokens/tokenid.h @@ -17,6 +17,10 @@ class TokenId TokenId(const uint256& t = uint256(), const uint64_t& i = -1) : token(t), subid(i){} + void SetNull() { token = uint256(); subid = -1; } + + bool IsNull() const { return token == uint256() && subid == -1; } + friend bool operator==(const TokenId& a, const TokenId& b) { return a.token == b.token && a.subid == b.subid; } friend bool operator<(const TokenId& a, const TokenId& b) { diff --git a/src/ctokens/tokenutxos.h b/src/ctokens/tokenutxos.h new file mode 100644 index 000000000..77a517c9a --- /dev/null +++ b/src/ctokens/tokenutxos.h @@ -0,0 +1,114 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef NAVCOIN_TOKENUTXOS_H +#define NAVCOIN_TOKENUTXOS_H + +#include + +struct TokenUtxoKey { + uint256 id; + uint64_t blockHeight; + + size_t GetSerializeSize(int nType, int nVersion) const { + return 32 + 8; + } + template + void Serialize(Stream& s, int nType, int nVersion) const { + id.Serialize(s, nType, nVersion); + ser_writedata32be(s, blockHeight); + } + template + void Unserialize(Stream& s, int nType, int nVersion) { + id.Unserialize(s, nType, nVersion); + blockHeight = ser_readdata32be(s); + } + + TokenUtxoKey(uint256 t, int h) { + id = t; + blockHeight = h; + } + + TokenUtxoKey() { + SetNull(); + } + + void SetNull() { + id.SetNull(); + blockHeight = 0; + } + + bool IsNull() const { + return id.IsNull(); + } + + bool operator==(const TokenUtxoKey& other) const { + return (id == other.id && blockHeight == other.blockHeight); + } + + bool operator<(const TokenUtxoKey& b) const { + if (id == b.id) { + return blockHeight < b.blockHeight; + } else { + return id < b.id; + } + } + + void swap(TokenUtxoKey &to) { + std::swap(to.id, id); + std::swap(to.blockHeight, blockHeight); + } +}; + +struct TokenUtxoValue { + uint256 hash; + std::vector spendingKey; + uint32_t n; + + TokenUtxoValue(uint256 _hash, std::vector _spendingKey, uint32_t _n) { + hash = _hash; + spendingKey = _spendingKey; + n = _n; + } + + TokenUtxoValue() { + SetNull(); + } + + void SetNull() { + hash.SetNull(); + spendingKey.clear(); + n = (uint32_t) -1; + } + + bool IsNull() const { + return hash.IsNull(); + } + + bool operator==(const TokenUtxoValue& other) const { + return (hash == other.hash && spendingKey == other.spendingKey && n == other.n); + } + + void swap(TokenUtxoValue &to) { + std::swap(to.hash, hash); + std::swap(to.spendingKey, spendingKey); + std::swap(to.n, n); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(hash); + READWRITE(spendingKey); + READWRITE(n); + } +}; + +typedef std::pair TokenUtxoEntry; +typedef std::vector TokenUtxoValues; +typedef std::map TokenUtxoMap; + +#endif // NAVCOIN_TOKENUTXOS_H diff --git a/src/init.cpp b/src/init.cpp index 63611c227..8ed2f530f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -425,8 +425,9 @@ std::string HelpMessage(HelpMessageMode mode) #ifndef WIN32 strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); #endif - strUsage += HelpMessageOpt("-allindex", strprintf(_("Maintain all indexes supported (default: %u)"), false)); + strUsage += HelpMessageOpt("-allindex", strprintf(_("Maintain all indexes supported (default: %u)"), DEFAULT_ALLINDEX)); strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX)); + strUsage += HelpMessageOpt("-nftindex", strprintf(_("Maintain an index of ntf data, used by the nft related rpc calls (default: %u)"), DEFAULT_NFTINDEX)); strUsage += HelpMessageOpt("-addressindex", strprintf(_("Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u)"), DEFAULT_ADDRESSINDEX)); strUsage += HelpMessageOpt("-timestampindex", strprintf(_("Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u)"), DEFAULT_TIMESTAMPINDEX)); @@ -1629,6 +1630,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler, const std break; } + // Check for changed -nftindex state + if (fNftIndex != GetBoolArg("-nftindex", DEFAULT_NFTINDEX)) { + strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -nftindex"); + break; + } + // Check for changed -addressindex state if (fAddressIndex != GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX)) { strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -addressindex"); diff --git a/src/main.cpp b/src/main.cpp index 99e5db517..d49021415 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -78,6 +78,7 @@ bool fImporting = false; bool fReindex = false; bool fVerifyChain = false; bool fTxIndex = false; +bool fNftIndex = false; bool fAddressIndex = false; bool fTimestampIndex = false; bool fSpentIndex = false; @@ -2871,6 +2872,7 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI std::vector > addressHistory; std::map addressHistoryMap; std::vector > addressUnspentIndex; + std::vector > tokenUtxoIndex; std::vector > spentIndex; bool fCFund = IsCommunityFundEnabled(pindex->pprev, Params().GetConsensus()); @@ -2924,8 +2926,7 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI { return state.DoS(100, false, REJECT_INVALID, "error-program-vdata"); } - if (GetConsensusParameter(Consensus::CONSENSUS_PARAMS_CONFIDENTIAL_TOKENS_ENABLED, view)) - { + if (GetConsensusParameter(Consensus::CONSENSUS_PARAMS_CONFIDENTIAL_TOKENS_ENABLED, view)) { if (program.action == MINT) { auto tokenId = SerializeHash(program.kParameters[0]); @@ -2982,6 +2983,7 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI token->canMint = true; } } + if (fDotNav) { if (program.action == REGISTER_NAME) { @@ -2997,6 +2999,24 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI return state.DoS(100, false, REJECT_INVALID, "error-program-vdata"); } } + + if (fNftIndex) { + auto tokenId = txout.tokenId.token; + + if (view.HaveToken(tokenId)) + { + TokenModifier token = view.ModifyToken(tokenId); + + auto tokenIdHash = SerializeHash(txout.tokenId); + + // Check if we have an nft + if (token->nVersion == 1) { + if (view.HaveTokenUtxos(tokenIdHash)) + if (!view.RemoveTokenUtxo(TokenUtxoKey(tokenIdHash, pindex->nHeight))) + return AbortNode(state, "Failed to remove token utxo from index"); + } + } + } } if (fAddressIndex) @@ -4033,6 +4053,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > addressHistory; std::map addressHistoryMap; std::vector > addressUnspentIndex; + std::vector > tokenUtxoIndex; std::vector > spentIndex; CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : nullptr); @@ -4747,8 +4768,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin bool fDotNav = IsDotNavEnabled(pindex->pprev, Params().GetConsensus()); - for(const CTxOut& vout: tx.vout) + + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut& vout = tx.vout[i]; + if(vout.IsCommunityFundContribution()) { fContribution=true; @@ -4868,7 +4892,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, false, REJECT_INVALID, strprintf("wrong-salt-name:%s", program.sParameters[0])); if (pindex->nHeight-recordvalue.height < 6) - return state.DoS(100, false, REJECT_INVALID, strprintf("6-block-maturity-not-reached:%s", program.sParameters[0])); NameDataValues data; + return state.DoS(100, false, REJECT_INVALID, strprintf("6-block-maturity-not-reached:%s", program.sParameters[0])); + + NameDataValues data; if (view.GetNameData(DotNav::GetHashName(program.sParameters[0]), data)) { @@ -4974,6 +5000,30 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, false, REJECT_INVALID, "error-program-vdata"); } } + + if (fNftIndex) { + auto tokenId = vout.tokenId.token; + + if (view.HaveToken(tokenId)) + { + TokenModifier token = view.ModifyToken(tokenId); + + auto tokenIdHash = SerializeHash(vout.tokenId); + + // Check if we have an nft + if (token->nVersion == 1) { + auto op = COutPoint(tx.GetHash(), i); + + if (view.HaveTokenUtxos(tokenIdHash)) + LogPrint("token", "%s: We have an old utxo set for token"); + else + LogPrint("token", "%s: Fresh utxo set for token"); + + if (!view.AddTokenUtxo(tokenIdHash, std::make_pair(pindex->nHeight, TokenUtxoValue(op.hash, vout.spendingKey, op.n)))) + return AbortNode(state, "Failed to add token utxo to index"); + } + } + } } if(fCFund || fDAOConsultations) @@ -7298,6 +7348,10 @@ bool static LoadBlockIndexDB() pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + // Check whether we have an nft index + pblocktree->ReadFlag("nftindex", fNftIndex); + LogPrintf("%s: nft index %s\n", __func__, fNftIndex ? "enabled" : "disabled"); + // Check whether we have an address index pblocktree->ReadFlag("addressindex", fAddressIndex); LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled"); @@ -7721,20 +7775,25 @@ bool InitBlockIndex(const CChainParams& chainparams) // Load the flag values | use DEFAULT_* values if not set fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX); + fNftIndex = GetBoolArg("-nftindex", DEFAULT_NFTINDEX); fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX); fTimestampIndex = GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX); fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX); // Check if we want all indexes - if (GetBoolArg("-allindex", false)) + if (GetBoolArg("-allindex", DEFAULT_ALLINDEX)) { - fTxIndex = fAddressIndex = fTimestampIndex = fSpentIndex = true; + fTxIndex = fNftIndex = fAddressIndex = fTimestampIndex = fSpentIndex = true; } // Use the provided setting for -txindex in the new database pblocktree->WriteFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); + // Use the provided setting for -nftindex in the new database + pblocktree->WriteFlag("nftindex", fNftIndex); + LogPrintf("%s: nft index %s\n", __func__, fNftIndex ? "enabled" : "disabled"); + // Use the provided setting for -addressindex in the new database pblocktree->WriteFlag("addressindex", fAddressIndex); LogPrintf("%s: address index %s\n", __func__, fAddressIndex ? "enabled" : "disabled"); diff --git a/src/main.h b/src/main.h index 370aef5d0..094392880 100644 --- a/src/main.h +++ b/src/main.h @@ -145,7 +145,9 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; /** Default for -permitbaremultisig */ static const bool DEFAULT_PERMIT_BAREMULTISIG = true; static const bool DEFAULT_CHECKPOINTS_ENABLED = true; +static const bool DEFAULT_ALLINDEX = false; static const bool DEFAULT_TXINDEX = false; +static const bool DEFAULT_NFTINDEX = false; static const bool DEFAULT_ADDRESSINDEX = false; static const bool DEFAULT_TIMESTAMPINDEX = false; static const bool DEFAULT_SPENTINDEX = false; @@ -203,6 +205,7 @@ extern bool fReindex; extern bool fVerifyChain; extern int nScriptCheckThreads; extern bool fTxIndex; +extern bool fNftIndex; extern bool fAddressIndex; extern bool fSpentIndex; extern bool fTimestampIndex; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 65a91916a..03b3a46b0 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -93,6 +93,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getreceivedbyaddress", 1 }, { "getspentinfo", 0}, { "getstakerscript", 1 }, + { "gettoken", 1 }, + { "getnft", 1 }, + { "getnft", 2 }, { "gettransaction", 1 }, { "gettxout", 1 }, { "gettxout", 2 }, @@ -116,6 +119,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listsinceblock", 1 }, { "listsinceblock", 2 }, { "listtokens", 0 }, + { "listnfts", 0 }, + { "listnfts", 1 }, { "listtransactions", 1 }, { "listtransactions", 2 }, { "listtransactions", 3 }, diff --git a/src/txdb.cpp b/src/txdb.cpp index 35bf1e2dd..e8633b113 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -40,6 +40,7 @@ static const char DB_LAST_BLOCK = 'l'; static const char DB_EXCLUDE_VOTES = 'X'; static const char DB_TOKENS = 'T'; +static const char DB_TOKEN_UTXO = 'Z'; static const char DB_NAME_RECORDS = 'n'; static const char DB_NAME_DATA = 'N'; @@ -67,10 +68,44 @@ bool CStateViewDB::GetToken(const uint256 &id, TokenInfo &token) const { return db.Read(std::make_pair(DB_TOKENS, id), token); } +bool CStateViewDB::GetTokenUtxos(const uint256 &id, TokenUtxoValues &vect) { + vect.clear(); + + boost::scoped_ptr pcursor(db.NewIterator()); + + pcursor->Seek(std::make_pair(DB_TOKEN_UTXO, uint256())); + + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_TOKEN_UTXO) { + if (key.second.id == id) { + TokenUtxoValue data; + if (pcursor->GetValue(data)) { + vect.push_back(std::make_pair(key.second.blockHeight, data)); + pcursor->Next(); + } else { + return error("GetTokenUtxos() : failed to read value"); + } + } else { + pcursor->Next(); + } + } else { + break; + } + } + + return true; +} + bool CStateViewDB::HaveToken(const uint256 &id) const { return db.Exists(std::make_pair(DB_TOKENS, id)); } +bool CStateViewDB::HaveTokenUtxos(const uint256 &id) const { + return db.Exists(std::make_pair(DB_TOKEN_UTXO, id)); +} + bool CStateViewDB::GetNameRecord(const uint256 &id, NameRecordValue &height) const { return db.Read(std::make_pair(DB_NAME_RECORDS, id), height); } @@ -353,7 +388,7 @@ bool CStateViewDB::BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap &mapVotes, CConsultationMap &mapConsultations, CConsultationAnswerMap &mapAnswers, CConsensusParameterMap &mapConsensus, - TokenMap &mapTokens, NameRecordMap &mapNameRecords, + TokenMap &mapTokens, TokenUtxoMap &mapTokenUtxos, NameRecordMap &mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlock, const int &nExcludeVotes) { @@ -458,6 +493,23 @@ bool CStateViewDB::BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, mapTokens.erase(itOld); } + for (TokenUtxoMap::iterator it = mapTokenUtxos.begin(); it != mapTokenUtxos.end();) { + if (it->second.size() == 0) { + batch.Erase(std::make_pair(DB_TOKEN_UTXO, it->first)); + } else { + for (auto &it2: it->second) { + if (it2.second.IsNull()) + { + batch.Erase(std::make_pair(DB_TOKEN_UTXO, TokenUtxoKey(it->first, it2.first))); + } else { + batch.Write(std::make_pair(DB_TOKEN_UTXO, TokenUtxoKey(it->first, it2.first)), it2.second); + } + } + } + TokenUtxoMap::iterator itOld = it++; + mapTokenUtxos.erase(itOld); + } + for (NameRecordMap::iterator it = mapNameRecords.begin(); it != mapNameRecords.end();) { if (it->second.IsNull()) { batch.Erase(std::make_pair(DB_NAME_RECORDS, it->first)); diff --git a/src/txdb.h b/src/txdb.h index c84528c5c..25624ac18 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -113,7 +113,9 @@ class CStateViewDB : public CStateView bool GetConsensusParameter(const int &pid, CConsensusParameter& cparameter) const; bool HaveConsensusParameter(const int &pid) const; bool GetToken(const uint256 &id, TokenInfo &token) const; + bool GetTokenUtxos(const uint256 &id, TokenUtxoValues &vect); bool HaveToken(const uint256 &id) const; + bool HaveTokenUtxos(const uint256 &id) const; bool GetNameRecord(const uint256 &id, NameRecordValue &height) const; bool HaveNameRecord(const uint256 &id) const; @@ -125,7 +127,7 @@ class CStateViewDB : public CStateView bool BatchWrite(CCoinsMap &mapCoins, CProposalMap &mapProposals, CPaymentRequestMap &mapPaymentRequests, CVoteMap &mapVotes, CConsultationMap &mapConsultations, CConsultationAnswerMap &mapAnswers, - CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, + CConsensusParameterMap& mapConsensus, TokenMap& mapTokens, TokenUtxoMap &mapTokenUtxos, NameRecordMap& mapNameRecords, NameDataMap& mapNameData, const uint256 &hashBlock, const int &nExcludeVotes); bool GetAllProposals(CProposalMap& map); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8a5dafa6f..d9e748369 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4116,30 +4116,30 @@ UniValue gettransaction(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" - " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"blockhash\" : \"hash\", (string) The block hash\n" - " \"blockindex\" : xx, (numeric) The index of the transaction in the block that includes it\n" - " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" - " \"txid\" : \"transactionid\", (string) The transaction id.\n" - " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" - " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" - " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" - " may be unknown for unconfirmed transactions not in the mempool\n" - " \"details\" : [\n" - " {\n" - " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" - " \"address\" : \"navcoinaddress\", (string) The navcoin address involved in the transaction\n" - " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" - " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" - " \"vout\" : n, (numeric) the vout value\n" - " }\n" - " ,...\n" - " ],\n" - " \"hex\" : \"data\" (string) Raw data for transaction\n" - "}\n" - - "\nExamples:\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"blockhash\" : \"hash\", (string) The block hash\n" + " \"blockindex\" : xx, (numeric) The index of the transaction in the block that includes it\n" + " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"txid\" : \"transactionid\", (string) The transaction id.\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" + " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" + " may be unknown for unconfirmed transactions not in the mempool\n" + " \"details\" : [\n" + " {\n" + " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" + " \"address\" : \"navcoinaddress\", (string) The navcoin address involved in the transaction\n" + " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" + " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"vout\" : n, (numeric) the vout value\n" + " }\n" + " ,...\n" + " ],\n" + " \"hex\" : \"data\" (string) Raw data for transaction\n" + "}\n" + + "\nExamples:\n" + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") @@ -5989,9 +5989,14 @@ UniValue listtokens(const UniValue& params, bool fHelp) if (fHelp) throw std::runtime_error( "listtokens (mine)\n" - "\nList the confidential tokens. Set mine to true to show only tokens you own.\n" + "\nList the confidential tokens.\n" + + "\nArguments:\n" + "1. mine (boolean, optional, default=false) Show only owned tokens\n" + "\nExamples:\n" + HelpExampleCli("listtokens", "") + + HelpExampleCli("listtokens", "true") ); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6010,7 +6015,9 @@ UniValue listtokens(const UniValue& params, bool fHelp) if (!view.GetToken(it->first, token)) continue; - int64_t balance = 0; + // Check for regular tokens + if (it->second.nVersion != 0) + continue; UniValue o(UniValue::VOBJ); o.pushKV("version", it->second.nVersion); @@ -6020,26 +6027,118 @@ UniValue listtokens(const UniValue& params, bool fHelp) o.pushKV(it->second.nVersion == 0 ? "token_code" : "scheme", it->second.sDesc); o.pushKV("current_supply", it->second.nVersion == 0 ? FormatMoney(it->second.currentSupply) : std::to_string(it->second.mapMetadata.size())); o.pushKV("max_supply", it->second.nVersion == 0 ? FormatMoney(it->second.totalSupply) : std::to_string(it->second.totalSupply)); - if (it->second.nVersion == 0) + o.pushKV("balance", FormatMoney(pwalletMain->GetPrivateBalance(TokenId(it->first, -1)))); + + // Is this token ours? + bool fTokenIsMine = false; + + blsctKey pk; + if (!pwalletMain->GetBLSCTTokenKey(it->second.key, pk)) { - balance += pwalletMain->GetPrivateBalance(TokenId(it->first, -1)); - o.pushKV("balance", FormatMoney(balance)); + blsctKey sk; + + if (!pwalletMain->GetBLSCTSpendKey(sk)) + throw JSONRPCError(RPC_TYPE_ERROR, "Wallet not available"); + + pk = sk.PrivateChildHash(SerializeHash("nft/"+it->second.sName+it->second.sDesc)); + + pwalletMain->AddBLSCTTokenKey(pk); } - else if (it->second.nVersion == 1) - { - UniValue a(UniValue::VARR); - for (auto& it_: it->second.mapMetadata) { - UniValue n(UniValue::VOBJ); - n.pushKV("index", it_.first); - n.pushKV("metadata", it_.second); - int64_t tempBalance = pwalletMain->GetPrivateBalance(TokenId(it->first, it_.first)); - n.pushKV("balance", std::to_string(tempBalance)); - balance += tempBalance; - a.push_back(n); + + if (pk.GetG1Element() == it->second.key) + fTokenIsMine = true; + + o.pushKV("is_mine", fTokenIsMine); + if (!fMine || (fMine && fTokenIsMine)) + ret.push_back(o); + } + } + return ret; +} + +UniValue listnfts(const UniValue& params, bool fHelp) +{ + if (fHelp) + throw std::runtime_error( + "listnfts (mine) (with_utxo)\n" + "\nList the confidential tokens.\n" + + "\nArguments:\n" + "1. mine (boolean, optional, default=false) Show only owned tokens\n" + "2. with_utxo (boolean, optional, default=false) Show last utxo for nfts\n" + + "\nExamples:\n" + + HelpExampleCli("listnfts", "") + + HelpExampleCli("listnfts", "true") + + HelpExampleCli("listnfts", "true true") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + bool fMine = params[0].getBool(); + bool fWithUtxo = params.size() > 1 ? params[1].getBool() : false; + + UniValue ret(UniValue::VARR); + TokenMap mapTokens; + CStateViewCache view(pcoinsTip); + + if(view.GetAllTokens(mapTokens)) + { + for (TokenMap::iterator it = mapTokens.begin(); it != mapTokens.end(); it++) + { + TokenInfo token; + if (!view.GetToken(it->first, token)) + continue; + + // Check for nft + if (it->second.nVersion != 1) + continue; + + UniValue o(UniValue::VOBJ); + o.pushKV("version", it->second.nVersion); + o.pushKV("id", it->first.ToString()); + o.pushKV("pubkey", HexStr(it->second.key.Serialize())); + o.pushKV("name", it->second.sName); + o.pushKV(it->second.nVersion == 0 ? "token_code" : "scheme", it->second.sDesc); + o.pushKV("current_supply", it->second.nVersion == 0 ? FormatMoney(it->second.currentSupply) : std::to_string(it->second.mapMetadata.size())); + o.pushKV("max_supply", it->second.nVersion == 0 ? FormatMoney(it->second.totalSupply) : std::to_string(it->second.totalSupply)); + + UniValue a(UniValue::VARR); + + for (auto& it_: it->second.mapMetadata) { + UniValue n(UniValue::VOBJ); + n.pushKV("index", it_.first); + n.pushKV("metadata", it_.second); + n.pushKV("balance", pwalletMain->GetPrivateBalance(TokenId(it->first, it_.first))); + + TokenUtxoValues utxos; + if (fWithUtxo && view.GetTokenUtxos(SerializeHash(TokenId(it->first, it_.first)), utxos)) { + if (utxos.size() > 0) { + TokenUtxoValue txout; + auto i = utxos.end(); + while (i != utxos.begin()) + { + --i; + if (!i->second.IsNull()) { + txout = i->second; + break; + } + } + if (!txout.IsNull()) { + UniValue utxo(UniValue::VOBJ); + utxo.pushKV("n", std::to_string(txout.n)); + utxo.pushKV("hash", txout.hash.ToString()); + utxo.pushKV("spendingKey", HexStr(txout.spendingKey)); + n.pushKV("utxo", utxo); + } + } } - o.pushKV("nfts", a); + + a.push_back(n); } + o.pushKV("nfts", a); + // Is this token ours? bool fTokenIsMine = false; @@ -6064,6 +6163,7 @@ UniValue listtokens(const UniValue& params, bool fHelp) ret.push_back(o); } } + return ret; } @@ -6071,15 +6171,16 @@ UniValue gettoken(const UniValue& params, bool fHelp) { if (fHelp) throw std::runtime_error( - "gettoken hash\n" + "gettoken hash (with_utxo)\n" "\nShows information about a token.\n" + HelpExampleCli("gettoken", "90fc7410164a466b78096967ec948fcc13142b0f5fb4397462304c517840d74f") + + HelpExampleCli("gettoken", "90fc7410164a466b78096967ec948fcc13142b0f5fb4397462304c517840d74f true") ); LOCK(cs_main); - bool fMine = params[0].getBool(); + bool fWithUtxo = params.size() > 1 ? params[1].getBool() : false; UniValue ret(UniValue::VOBJ); @@ -6089,7 +6190,8 @@ UniValue gettoken(const UniValue& params, bool fHelp) if (!view.GetToken(uint256S(params[0].get_str()), token)) return ret; - int64_t balance = 0; + if (token.nVersion != 0) + return ret; ret.pushKV("version", token.nVersion); ret.pushKV("id", params[0].get_str()); @@ -6098,26 +6200,85 @@ UniValue gettoken(const UniValue& params, bool fHelp) ret.pushKV(token.nVersion == 0 ? "token_code" : "scheme", token.sDesc); ret.pushKV("current_supply", token.nVersion == 0 ? FormatMoney(token.currentSupply) : std::to_string(token.mapMetadata.size())); ret.pushKV("max_supply", token.nVersion == 0 ? FormatMoney(token.totalSupply) : std::to_string(token.totalSupply)); - if (token.nVersion == 0) - { - balance += pwalletMain->GetPrivateBalance(TokenId(uint256S(params[0].get_str()), -1)); - ret.pushKV("balance", FormatMoney(balance)); - } - else if (token.nVersion == 1) - { - UniValue a(UniValue::VARR); - for (auto& it_: token.mapMetadata) { - UniValue n(UniValue::VOBJ); - n.pushKV("index", it_.first); - n.pushKV("metadata", it_.second); - int64_t tempBalance = pwalletMain->GetPrivateBalance(TokenId(uint256S(params[0].get_str()), it_.first)); - n.pushKV("balance", std::to_string(tempBalance)); - balance += tempBalance; - a.push_back(n); + ret.pushKV("balance", FormatMoney(pwalletMain->GetPrivateBalance(TokenId(uint256S(params[0].get_str()), -1)))); + + return ret; +} + +UniValue getnft(const UniValue& params, bool fHelp) +{ + if (fHelp) + throw std::runtime_error( + "getnft hash (subid) (with_utxo)\n" + "\nShows information about a token.\n" + + + HelpExampleCli("getnft", "90fc7410164a466b78096967ec948fcc13142b0f5fb4397462304c517840d74f") + + HelpExampleCli("getnft", "90fc7410164a466b78096967ec948fcc13142b0f5fb4397462304c517840d74f 1") + + HelpExampleCli("getnft", "90fc7410164a466b78096967ec948fcc13142b0f5fb4397462304c517840d74f 1 true") + ); + + LOCK(cs_main); + + UniValue ret(UniValue::VOBJ); + + CStateViewCache view(pcoinsTip); + + TokenInfo token; + if (!view.GetToken(uint256S(params[0].get_str()), token)) + return ret; + + if (token.nVersion != 1) + return ret; + + int64_t nSubid = params.size() > 1 ? params[1].get_int() : -1; + + bool fWithUtxo = params.size() > 2 ? params[2].getBool() : false; + + ret.pushKV("version", token.nVersion); + ret.pushKV("id", params[0].get_str()); + ret.pushKV("pubkey", HexStr(token.key.Serialize())); + ret.pushKV("name", token.sName); + ret.pushKV(token.nVersion == 0 ? "token_code" : "scheme", token.sDesc); + ret.pushKV("current_supply", token.nVersion == 0 ? FormatMoney(token.currentSupply) : std::to_string(token.mapMetadata.size())); + ret.pushKV("max_supply", token.nVersion == 0 ? FormatMoney(token.totalSupply) : std::to_string(token.totalSupply)); + UniValue a(UniValue::VARR); + for (auto& it_: token.mapMetadata) { + if (nSubid > -1 && it_.first != nSubid) + continue; + + UniValue n(UniValue::VOBJ); + n.pushKV("index", it_.first); + n.pushKV("metadata", it_.second); + n.pushKV("balance", pwalletMain->GetPrivateBalance(TokenId(uint256S(params[0].get_str()), it_.first))); + + TokenUtxoValues utxos; + if (fWithUtxo && view.GetTokenUtxos(SerializeHash(TokenId(uint256S(params[0].get_str()), it_.first)), utxos)) { + if (utxos.size() > 0) { + TokenUtxoValue txout; + auto i = utxos.end(); + while (i != utxos.begin()) + { + --i; + if (!i->second.IsNull()) { + txout = i->second; + break; + } + } + if (!txout.IsNull()) { + UniValue utxo(UniValue::VOBJ); + utxo.pushKV("n", std::to_string(txout.n)); + utxo.pushKV("hash", txout.hash.ToString()); + utxo.pushKV("spendingKey", HexStr(txout.spendingKey)); + n.pushKV("utxo", utxo); + } + } } - ret.pushKV("nfts", a); + + a.push_back(n); } + ret.pushKV("nfts", a); + return ret; } @@ -6165,7 +6326,9 @@ static const CRPCCommand commands[] = { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true }, { "wallet", "listprivateaddresses", &listprivateaddresses, true }, { "wallet", "listtokens", &listtokens, true }, + { "wallet", "listnfts", &listnfts, true }, { "wallet", "gettoken", &gettoken, true }, + { "wallet", "getnft", &getnft, true }, { "wallet", "getbalance", &getbalance, false }, { "wallet", "getnewaddress", &getnewaddress, true }, { "wallet", "getcoldstakingaddress", &getcoldstakingaddress, true },