From ee232be9e131ee4d1cd62a87b7ca462db794b219 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Tue, 2 Nov 2021 14:16:49 +0530 Subject: [PATCH] Cluster script improvements and lib upgrades. (#73) --- bootstrap-contract/hotpocket_contract.h | 111 +++++++++++++++++++++--- installer/sashimono-install.sh | 10 +-- installer/sashimono-uninstall.sh | 2 +- mb-xrpl/package-lock.json | 6 +- mb-xrpl/package.json | 2 +- test/vm-cluster/cluster.sh | 34 +++++++- test/vm-cluster/evcluster.js | 8 +- test/vm-cluster/package-lock.json | 12 +-- test/vm-cluster/package.json | 2 +- 9 files changed, 154 insertions(+), 33 deletions(-) diff --git a/bootstrap-contract/hotpocket_contract.h b/bootstrap-contract/hotpocket_contract.h index 74dcbd7..c47fdb3 100644 --- a/bootstrap-contract/hotpocket_contract.h +++ b/bootstrap-contract/hotpocket_contract.h @@ -1,6 +1,8 @@ #ifndef __HOTPOCKET_CONTRACT_LIB_C__ #define __HOTPOCKET_CONTRACT_LIB_C__ +// Hot Pocket contract library version 0.5.0 + #include #include #include @@ -21,8 +23,9 @@ const char *__HP_PATCH_FILE_PATH = "../patch.cfg"; // Public constants. #define HP_NPL_MSG_MAX_SIZE __HP_SEQPKT_MAX_SIZE -#define HP_KEY_SIZE 66 // Hex pubkey size. (64 char key + 2 chars for key type prfix) -#define HP_HASH_SIZE 64 // Hex hash size. +#define HP_KEY_SIZE 66 // Hex pubkey size. (64 char key + 2 chars for key type prfix) +#define HP_HASH_SIZE 64 // Hex hash size. +#define HP_CONTRACT_ID_SIZE 36 // Contract Id UUIDv4 string length. const char *HP_POST_EXEC_SCRIPT_NAME = "post_exec.sh"; #define __HP_ASSIGN_STRING(dest, elem) \ @@ -168,9 +171,10 @@ struct hp_contract_context { bool readonly; uint64_t timestamp; - char pubkey[HP_KEY_SIZE + 1]; // +1 for null char. - uint64_t lcl_seq_no; // lcl sequence no. - char lcl_hash[HP_HASH_SIZE + 1]; // +1 for null char. + char contract_id[HP_CONTRACT_ID_SIZE + 1]; // +1 for null char. + char pubkey[HP_KEY_SIZE + 1]; // +1 for null char. + uint64_t lcl_seq_no; // lcl sequence no. + char lcl_hash[HP_HASH_SIZE + 1]; // +1 for null char. struct hp_users_collection users; struct hp_unl_collection unl; }; @@ -195,6 +199,7 @@ int hp_writev_npl_msg(const struct iovec *bufs, const int buf_count); int hp_read_npl_msg(void *msg_buf, char *pubkey_buf, const int timeout); struct hp_config *hp_get_config(); int hp_update_config(const struct hp_config *config); +int hp_update_peers(const char *add_peers[], const size_t add_peers_count, const char *remove_peers[], const size_t remove_peers_count); void hp_set_config_string(char **config_str, const char *value, const size_t value_size); void hp_set_config_unl(struct hp_config *config, const struct hp_unl_node *new_unl, const size_t new_unl_count); void hp_free_config(struct hp_config *config); @@ -204,6 +209,8 @@ int __hp_write_control_msg(const void *buf, const uint32_t len); void __hp_populate_patch_from_json_object(struct hp_config *config, const struct json_object_s *object); int __hp_write_to_patch_file(const int fd, const struct hp_config *config); struct hp_config *__hp_read_from_patch_file(const int fd); +size_t __hp_get_json_string_array_encoded_len(const char *elems[], const size_t count); +int __hp_encode_json_string_array(char *buf, const char *elems[], const size_t count); static struct __hp_contract __hpc = {}; @@ -276,9 +283,10 @@ int hp_deinit_contract() __HP_FREE(cctx); // Send termination control message. - __hp_write_control_msg("{\"type\":\"contract_end\"}", 23); + const int ret = __hp_write_control_msg("{\"type\":\"contract_end\"}", 23); + close(__hpc.control_fd); - return 0; + return ret; } const struct hp_contract_context *hp_get_context() @@ -355,7 +363,7 @@ int hp_write_npl_msg(const void *buf, const uint32_t len) { if (len > HP_NPL_MSG_MAX_SIZE) { - fprintf(stderr, "NPL message exceeds max length %d.", HP_NPL_MSG_MAX_SIZE); + fprintf(stderr, "NPL message exceeds max length %d.\n", HP_NPL_MSG_MAX_SIZE); return -1; } @@ -370,7 +378,7 @@ int hp_writev_npl_msg(const struct iovec *bufs, const int buf_count) if (len > HP_NPL_MSG_MAX_SIZE) { - fprintf(stderr, "NPL message exceeds max length %d.", HP_NPL_MSG_MAX_SIZE); + fprintf(stderr, "NPL message exceeds max length %d.\n", HP_NPL_MSG_MAX_SIZE); return -1; } @@ -583,6 +591,83 @@ void hp_free_config(struct hp_config *config) __HP_FREE(config); } +/** + * Updates the known-peers this node must attempt connections to. + * @param add_peers Array of strings containing peers to be added. Each string must be in the format of ":". + * @param add_peers_count No. of peers to be added. + * @param remove_peers Array of strings containing peers to be removed. Each string must be in the format of ":". + * @param remove_peers_count No. of peers to be removed. + */ +int hp_update_peers(const char *add_peers[], const size_t add_peers_count, const char *remove_peers[], const size_t remove_peers_count) +{ + const size_t add_json_len = __hp_get_json_string_array_encoded_len(add_peers, add_peers_count); + char add_json[add_json_len]; + if (__hp_encode_json_string_array(add_json, add_peers, add_peers_count) == -1) + { + fprintf(stderr, "Error when encoding peer update changeset 'add'.\n"); + return -1; + } + + const size_t remove_json_len = __hp_get_json_string_array_encoded_len(remove_peers, remove_peers_count); + char remove_json[remove_json_len]; + if (__hp_encode_json_string_array(remove_json, remove_peers, remove_peers_count) == -1) + { + fprintf(stderr, "Error when encoding peer update changeset 'remove'.\n"); + return -1; + } + + const size_t msg_len = 47 + (add_json_len - 1) + (remove_json_len - 1); + char msg[msg_len]; + sprintf(msg, "{\"type\":\"peer_changeset\",\"add\":[%s],\"remove\":[%s]}", add_json, remove_json); + + if (__hp_write_control_msg(msg, msg_len - 1) == -1) + return -1; + + return 0; +} + +/** + * Returns the null-terminated string length required to encode as a json string array without enclosing brackets. + * @param elems Array of strings. + * @param count No. of strings. + */ +size_t __hp_get_json_string_array_encoded_len(const char *elems[], const size_t count) +{ + size_t len = 1; // +1 for null terminator. + for (size_t i = 0; i < count; i++) + { + len += (strlen(elems[i]) + 2); // Quoted string. + if (i < count - 1) + len += 1; // Comma + } + + return len; +} + +/** + * Formats a string array in JSON notation without enclosing brackets. + * @param buf Buffer to populate the encoded output. + * @param elems Array of strings. + * @param count No. of strings. + */ +int __hp_encode_json_string_array(char *buf, const char *elems[], const size_t count) +{ + size_t pos = 0; + for (size_t i = 0; i < count; i++) + { + const char *elem = elems[i]; + buf[pos++] = '\"'; + strcpy((buf + pos), elem); + pos += strlen(elem); + buf[pos++] = '\"'; + + if (i < count - 1) + buf[pos++] = ','; + } + buf[pos] = '\0'; + return 0; +} + /** * Read the values from the existing patch file. * @param fd File discriptor of the patch.cfg file. @@ -846,7 +931,11 @@ void __hp_parse_args_json(const struct json_object_s *object) { const struct json_string_s *k = elem->name; - if (strcmp(k->string, "pubkey") == 0) + if (strcmp(k->string, "contract_id") == 0) + { + __HP_ASSIGN_STRING(cctx->contract_id, elem); + } + else if (strcmp(k->string, "pubkey") == 0) { __HP_ASSIGN_STRING(cctx->pubkey, elem); } @@ -957,7 +1046,7 @@ int __hp_write_control_msg(const void *buf, const uint32_t len) { if (len > __HP_SEQPKT_MAX_SIZE) { - fprintf(stderr, "Control message exceeds max length %d.", __HP_SEQPKT_MAX_SIZE); + fprintf(stderr, "Control message exceeds max length %d.\n", __HP_SEQPKT_MAX_SIZE); return -1; } diff --git a/installer/sashimono-install.sh b/installer/sashimono-install.sh index 77e9098..32ac12b 100755 --- a/installer/sashimono-install.sh +++ b/installer/sashimono-install.sh @@ -13,7 +13,7 @@ mb_xrpl_conf=$mb_xrpl_data/mb-xrpl.cfg sashimono_service="sashimono-agent" cgcreate_service="sashimono-cgcreate" mb_xrpl_service="sashimono-mb-xrpl" -hook_address="rK4qxNScYAz6rw7rv3tbktMn4Qr2VBLCM9" +hook_address="rpTA2MtFV7L6hLzjSBYc1FBgYqTs6APVdz" group="sashimonousers" admin_group="sashiadmin" cgroupsuffix="-cg" @@ -81,10 +81,8 @@ cp "$script_dir"/sashi $user_bin # Check whether docker installation dir is still empty. [ -z "$(ls -A $docker_bin 2>/dev/null)" ] && echo "Rootless Docker installation failed." && rollback -# This will be commented and self ip will be hardcoded since the interface differs from machine to machine. -# This needs to be fixed later. -# selfip=$(ip -4 a l ens3 | awk '/inet/ {print $2}' | cut -d/ -f1) -selfip="127.0.0.1" +# Detect self host address +selfip=$(hostname -I) # Install private docker registry. # (Disabled until secure registry configuration) @@ -148,7 +146,7 @@ if [ "$quiet"=="-q" ]; then # (This is done for testing purposes during development) xrpl_faucet_url="https://hooks-testnet.xrpl-labs.com/newcreds" - hook_secret="snVCiKRox58HokST4YBEETw6w57oW" + hook_secret="spAhiYQMb71CyEHU4zAA5Q2PSiUkN" func_url="https://func-hotpocket.azurewebsites.net/api/evrfaucet?code=pPUyV1q838ryrihA5NVlobVXj8ZGgn9HsQjGGjl6Vhgxlfha4/xCgQ==" # Generate new fauset account. diff --git a/installer/sashimono-uninstall.sh b/installer/sashimono-uninstall.sh index 7e85e47..ff34c79 100755 --- a/installer/sashimono-uninstall.sh +++ b/installer/sashimono-uninstall.sh @@ -35,7 +35,7 @@ if [ "$quiet" == "-q" ]; then # We only perform this for our testing setup during development. echo "Cleaning up host XRP account..." - hook_address="rK4qxNScYAz6rw7rv3tbktMn4Qr2VBLCM9" + hook_address="rpTA2MtFV7L6hLzjSBYc1FBgYqTs6APVdz" func_url="https://func-hotpocket.azurewebsites.net/api/evrfaucet?code=pPUyV1q838ryrihA5NVlobVXj8ZGgn9HsQjGGjl6Vhgxlfha4/xCgQ==" mb_xrpl_conf=$mb_xrpl_data/mb-xrpl.cfg diff --git a/mb-xrpl/package-lock.json b/mb-xrpl/package-lock.json index 2c0b149..5da848b 100644 --- a/mb-xrpl/package-lock.json +++ b/mb-xrpl/package-lock.json @@ -490,9 +490,9 @@ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "evernode-js-client": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.2.2.tgz", - "integrity": "sha512-NX8gNJfOkYNFJfWyKOmRS8psf+Dr32KglLBT0Vh6vUyIfHDIEyclbhoVAqFb6ea/PIpVwKN274pWof8jux+RDw==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.2.5.tgz", + "integrity": "sha512-L0noX3uopHA4NGD5bMEiogqLZglxPaBknbwBwWWjWCAg7Vtf29CzArehd4aGEPPaBAzZnj6WmJ1L9qLYq4RVeg==", "requires": { "eccrypto": "1.1.6", "ripple-address-codec": "4.1.3", diff --git a/mb-xrpl/package.json b/mb-xrpl/package.json index 8a9a108..8dc3a95 100644 --- a/mb-xrpl/package.json +++ b/mb-xrpl/package.json @@ -4,7 +4,7 @@ "build": "ncc build mb-xrpl.js --minify -o dist" }, "dependencies": { - "evernode-js-client": "0.2.2", + "evernode-js-client": "0.2.5", "sqlite3": "5.0.2" } } diff --git a/test/vm-cluster/cluster.sh b/test/vm-cluster/cluster.sh index 278df65..effe02f 100755 --- a/test/vm-cluster/cluster.sh +++ b/test/vm-cluster/cluster.sh @@ -42,14 +42,14 @@ PRINTFORMAT="Node %2s: %s\n" mode=$1 if [ "$mode" == "select" ] || [ "$mode" == "reconfig" ] || [ "$mode" == "lcl" ] || [ "$mode" == "get-unl" ] || [ "$mode" == "docker-pull" ] || - [ "$mode" == "create" ] || [ "$mode" == "createall" ] || [ "$mode" == "start" ] || [ "$mode" == "stop" ] || [ "$mode" == "destroy" ] || + [ "$mode" == "create" ] || [ "$mode" == "createall" ] || [ "$mode" == "start" ] || [ "$mode" == "stop" ] || [ "$mode" == "destroy" ] || [ "$mode" == "destroy-all" ] || [ "$mode" == "ssh" ] || [ "$mode" == "sshu" ] || [ "$mode" == "attach" ] || [ "$mode" == "ip" ] || [ "$mode" == "updatecfg" ] || [ "$mode" == "statefile" ] || [ "$mode" == "umount" ] || [ "$mode" == "backup" ] || [ "$mode" == "restore" ] || [ "$mode" == "syncwith" ]; then echo "mode: $mode" else echo "Invalid command." echo " Expected: select | reconfig [N] [R] | lcl [N] | get-unl | docker-pull [N] | create [N] | createall | start [N] | stop [N] |" - echo " destroy [N] | ssh or | sshu | attach | ip [N] | updatecfg [N] | statefile [N] | umount [N] | backup | restore [N] | syncwith " + echo " destroy [N] | destroy-all [N] | ssh or | sshu | attach | ip [N] | updatecfg [N] | statefile [N] | umount [N] | backup | restore [N] | syncwith " echo " [N]: Optional node no. : Required node no. [R]: 'R' If sashimono needed to reinstall." exit 1 fi @@ -590,6 +590,36 @@ if [ $mode == "destroy" ]; then exit 0 fi +if [ $mode == "destroy-all" ]; then + # Destroy all instances of given host. + function destroyallinstances() { + hostaddr=${hostaddrs[$1]} + nodeno=$(expr $1 + 1) + + while : + do + containername=$(sshskp $sshuser@$hostaddr sashi list | tail +3 | head -1 | awk '{ print $1 }') + if [ "$containername" != "" ]; then + echo "Node$nodeno. Destroying $containername..." + result=$(sshskp $sshuser@$hostaddr sashi destroy -n $containername) + echo "Node$nodeno. $containername: $result" + else + break + fi + done + } + + if [ $nodeid = -1 ]; then + for i in "${!hostaddrs[@]}"; do + destroyallinstances $i & + done + wait + else + destroyallinstances $nodeid + fi + exit 0 +fi + if [ $mode = "ssh" ]; then if [ $nodeid = -1 ]; then if [ -n "$2" ]; then diff --git a/test/vm-cluster/evcluster.js b/test/vm-cluster/evcluster.js index 635a068..6c6999a 100644 --- a/test/vm-cluster/evcluster.js +++ b/test/vm-cluster/evcluster.js @@ -264,7 +264,11 @@ async function main() { }); const mode = args[0]; - if (mode === "create") { + if (mode === "init") { + await createEvernodeConnections(); + await initHosts(); + } + else if (mode === "create") { if (args.length === 1) { await createInstancesSequentially(); } @@ -280,7 +284,7 @@ async function main() { console.log("Specify peer port for 'createall'."); } else { - console.log("Specifiy args: create | createall ") + console.log("Specifiy args: init | create | createall ") } if (rippleAPI) diff --git a/test/vm-cluster/package-lock.json b/test/vm-cluster/package-lock.json index 19b2630..6bae1be 100644 --- a/test/vm-cluster/package-lock.json +++ b/test/vm-cluster/package-lock.json @@ -9,9 +9,9 @@ "integrity": "sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ==" }, "@types/node": { - "version": "16.11.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.2.tgz", - "integrity": "sha512-w34LtBB0OkDTs19FQHXy4Ig/TOXI4zqvXS2Kk1PAsRKZ0I+nik7LlMYxckW0tSNGtvWmzB+mrCTbuEjuB9DVsw==" + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==" }, "@types/ws": { "version": "7.4.7", @@ -285,9 +285,9 @@ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "evernode-js-client": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.2.1.tgz", - "integrity": "sha512-iKe0iRxiqUQdPkRZ4ZzwgZ/mALdu7cW9rbOuL88fu3aLDNc451uGasfokrx9jxSFYVK4Uif0oHQkefg+oVetSg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.2.5.tgz", + "integrity": "sha512-L0noX3uopHA4NGD5bMEiogqLZglxPaBknbwBwWWjWCAg7Vtf29CzArehd4aGEPPaBAzZnj6WmJ1L9qLYq4RVeg==", "requires": { "eccrypto": "1.1.6", "ripple-address-codec": "4.1.3", diff --git a/test/vm-cluster/package.json b/test/vm-cluster/package.json index 24824f3..d7cb658 100644 --- a/test/vm-cluster/package.json +++ b/test/vm-cluster/package.json @@ -2,7 +2,7 @@ "name": "evcluster", "type": "module", "dependencies": { - "evernode-js-client": "0.2.2", + "evernode-js-client": "0.2.5", "node-fetch": "3.0.0" } }