From 64b78d2b31a4e3f8cf662632377ebc400f4d263d Mon Sep 17 00:00:00 2001 From: Dakoda Greaves Date: Fri, 19 Jan 2024 20:06:01 -0800 Subject: [PATCH] wallet: clear memory leaks from radio doge functions -total redesign of dogecoin_unregister_watch_address_with_node to alleviate calls to concat -populate wallet->filename on dogecoin_wallet_new otherwise it's populated by name and freed -added replace_last_after_delim which searchs for a match and replaces with replacement -added remove_substr which returns a string with substr removed -changed wallet cmd to sanity -sanity cmd test now iterates through multiple addresses -free waddr and buf and call dogecoin_wallet_next_addr on edge case where we delete our last address -refactor/fix spvtool.py, fetch.py, .gitignore dummy -clear spvnode wallet sanity cmd of memleaks -fix wallet sanity check on windows -added file_copy function to utils -check if file exists before performance of file operations -free remaining waddr during sanity check -run wallet sanity check in spvtool.py test and verify wallet balance -run spvtool.py only on returncode match from previous subprocess completion -add Dockerfile for easier implementation -add to x86_64-linux-dbg in ci.yml -adapt to use -l for zero prompts during automated testing --- .github/workflows/ci.yml | 2 + .gitignore | 1 + include/dogecoin/utils.h | 5 +- include/dogecoin/wallet.h | 2 +- rpctest/Dockerfile | 17 ++++ rpctest/fetch.py | 8 +- rpctest/path.sh | 7 -- rpctest/spvtool.py | 51 +++++++--- src/cli/spvnode.c | 96 +++++++++++++++---- src/utils.c | 66 +++++++++++-- src/wallet.c | 191 ++++++++++++++++++++------------------ 11 files changed, 303 insertions(+), 143 deletions(-) create mode 100644 rpctest/Dockerfile delete mode 100755 rpctest/path.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b80b7fe4..78b50a733 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -245,6 +245,8 @@ jobs: "x86_64-linux-dbg") make check -j"$(getconf _NPROCESSORS_ONLN)" V=1 python3 tooltests.py + sudo ./rpctest/fetch.py --host x86_64-linux-gnu + sudo rm /usr/local/bin/dogecoind ;; "x86_64-linux-openenclave") make check -j"$(getconf _NPROCESSORS_ONLN)" V=1 diff --git a/.gitignore b/.gitignore index 23c15c09a..7a4ea647a 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,7 @@ dist/ contrib/gitian/ contrib/gitian/* example +dummy # CMake files build/* diff --git a/include/dogecoin/utils.h b/include/dogecoin/utils.h index 72006c1a9..da0adeed5 100644 --- a/include/dogecoin/utils.h +++ b/include/dogecoin/utils.h @@ -81,19 +81,20 @@ LIBDOGECOIN_API void prepend(char* s, const char* t); LIBDOGECOIN_API void append(char* s, char* t); LIBDOGECOIN_API char* concat(char* prefix, char* suffix); LIBDOGECOIN_API void slice(const char *str, char *result, size_t start, size_t end); +LIBDOGECOIN_API void replace_last_after_delim(const char *str, char* delim, char* replacement); LIBDOGECOIN_API void text_to_hex(char* in, char* out); LIBDOGECOIN_API const char* get_build(); LIBDOGECOIN_API char* getpass(const char *prompt); LIBDOGECOIN_API void dogecoin_str_reverse(char s[]); LIBDOGECOIN_API void dogecoin_uitoa(int n, char s[]); LIBDOGECOIN_API bool dogecoin_network_enabled(); - +LIBDOGECOIN_API int integer_length(int x); +LIBDOGECOIN_API int file_copy (char src [], char dest []); unsigned int base64_int(unsigned int ch); unsigned int base64_encoded_size(unsigned int in_size); unsigned int base64_decoded_size(unsigned int in_size); unsigned int base64_encode(const unsigned char* in, unsigned int in_len, unsigned char* out); unsigned int base64_decode(const unsigned char* in, unsigned int in_len, unsigned char* out); -int integer_length(int x); #define _SEARCH_PRIVATE #ifdef _SEARCH_PRIVATE diff --git a/include/dogecoin/wallet.h b/include/dogecoin/wallet.h index b49cf836c..65d2461e7 100644 --- a/include/dogecoin/wallet.h +++ b/include/dogecoin/wallet.h @@ -71,7 +71,7 @@ DISABLE_WARNING_POP /** single key/value record */ typedef struct dogecoin_wallet_ { - const char* filename; + const char filename[311]; // max path length FILE *dbfile; dogecoin_hdnode* masterkey; uint32_t next_childindex; //cached next child index diff --git a/rpctest/Dockerfile b/rpctest/Dockerfile new file mode 100644 index 000000000..7b6a0bb4e --- /dev/null +++ b/rpctest/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:jammy AS build + +# configure the shell before the first RUN +SHELL ["/bin/bash", "-ex", "-o", "pipefail", "-c"] + +WORKDIR /home/root + +COPY ./rpctest ./rpctest +COPY spvnode . + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + python3 \ + python3-requests \ + libevent-dev + +RUN ./rpctest/fetch.py --host x86_64-linux-gnu diff --git a/rpctest/fetch.py b/rpctest/fetch.py index 226d66d39..69e2c0607 100755 --- a/rpctest/fetch.py +++ b/rpctest/fetch.py @@ -98,13 +98,13 @@ for f in deps_path: src = "dogecoin-1.14.6/bin/" + f src_path = os.path.join(os.getcwd(), src) - dst_path = os.path.join(os.getcwd(), "dogecoind") - shutil.move(src_path, dst_path) + if os.path.isdir('/usr/local/bin'): + dst_path = os.path.join('/usr/local/bin', f) + shutil.move(src_path, dst_path) -subprocess.run([os.path.join(os.getcwd(), "rpctest/path.sh")]) subprocess.run([os.path.join(os.getcwd(), "rpctest/spvtool.py")]) -rmlist = ['./dogecoin-*', 'dummy', 'dogecoind', '*.tar.gz', '*.zip', '*.asc'] +rmlist = ['./dogecoin-*', '*.dmg', '*.tar.gz', '*.zip', '*.asc'] for path in rmlist: for name in glob.glob(path): if os.path.isdir(name): diff --git a/rpctest/path.sh b/rpctest/path.sh deleted file mode 100755 index 6db3c6321..000000000 --- a/rpctest/path.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -target_path=$(grep `pwd` ~/.bashrc) -if [[ -z $target_path ]]; then - echo "export PATH=\$PATH:$(pwd)" >> ~/.bashrc - source ~/.bashrc -fi diff --git a/rpctest/spvtool.py b/rpctest/spvtool.py index dcdcd63fa..a9f7d7d5c 100755 --- a/rpctest/spvtool.py +++ b/rpctest/spvtool.py @@ -23,9 +23,34 @@ def setup_network(self, split=False): cur_time = int(time.time())- 100*600 for i in range(100): self.nodes[0].setmocktime(cur_time + 600) + min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee'] + # This test is not meant to test fee estimation and we'd like + # to be sure all txs are sent at a consistent desired feerate + for node in self.nodes: + node.settxfee(min_relay_tx_fee) + + # if the fee's positive delta is higher than this value tests will fail, + # neg. delta always fail the tests. + # The size of the signature of every input may be at most 2 bytes larger + # than a minimum sized signature. + + # = 2 bytes * minRelayTxFeePerByte + feeTolerance = 2 * min_relay_tx_fee/1000 + + self.nodes[0].generate(121) + self.sync_all() + + # ensure that setting changePosition in fundraw with an exact match is handled properly + rawmatch = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress():500000}) + rawmatch = self.nodes[0].fundrawtransaction(rawmatch, {"changePosition":1, "subtractFeeFromOutputs":[0]}) + assert_equal(rawmatch["changepos"], -1) + + self.nodes[0].sendtoaddress("mggFqzCUQmWWnh9vaoyT4BwKen7EbqhBmY", 1.5) + self.nodes[0].sendtoaddress("mrvi2kJiHJGb3fSyHVmRa19Pt1xwanxuEF", 1.0) + self.nodes[0].sendtoaddress("mmzGnpWs4VnwLvMoyRqbmf2GKbHTZks3bm", 5.0) + self.nodes[0].generate(1) cur_time += 600 - self.nodes[0].setmocktime(cur_time + 1600) def execute_and_get_response(self, cmd): @@ -47,22 +72,24 @@ def run_test (self): max_size = 1000 log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16) log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) - + address = "mggFqzCUQmWWnh9vaoyT4BwKen7EbqhBmY mrvi2kJiHJGb3fSyHVmRa19Pt1xwanxuEF mmzGnpWs4VnwLvMoyRqbmf2GKbHTZks3bm" #sync with no headers database (-f 0) and debug (-d) only against localhost - cmd = "./spvnode --regtest -f 0 -d -i 127.0.0.1:"+str(p2p_port(0))+" scan" + cmd = "./spvnode --regtest -l -f 0 -d -i 127.0.0.1:"+str(p2p_port(0))+" -a '" + address + "' scan" data = self.execute_and_get_response(cmd) - assert("Sync completed, at height 100" in data) - + assert("Sync completed, at height 11725" in data) + cmd = "./spvnode --regtest -a " + address + " sanity" + data = self.execute_and_get_response(cmd) + assert("total: 6.00000000" in data) # do the same with a headers db - try: - os.remove("headers.db") - except OSError: - pass - cmd = "./spvnode --regtest -d -i 127.0.0.1:"+str(p2p_port(0))+" scan" + cmd = "./spvnode --regtest -l -d -i 127.0.0.1:"+str(p2p_port(0))+" -a '" + address + "' scan" + data = self.execute_and_get_response(cmd) + assert("Sync completed, at height 11725" in data) + cmd = "./spvnode --regtest -a " + address + " sanity" data = self.execute_and_get_response(cmd) - assert("Sync completed, at height 100" in data) + assert("total: 6.00000000" in data) try: - os.remove("headers.db") + os.remove("regtest_headers.db") + os.remove("regtest_wallet.db") except OSError: pass diff --git a/src/cli/spvnode.c b/src/cli/spvnode.c index cd4065ec5..eb2cc34d2 100644 --- a/src/cli/spvnode.c +++ b/src/cli/spvnode.c @@ -488,29 +488,93 @@ int main(int argc, char* argv[]) { #endif } dogecoin_ecc_stop(); - } else if (strcmp(data, "wallet") == 0) { + } else if (strcmp(data, "sanity") == 0) { #if WITH_WALLET dogecoin_ecc_start(); if (address != NULL) { - int res = dogecoin_register_watch_address_with_node(address); - printf("registered: %d %s\n", res, address); - uint64_t amount = dogecoin_get_balance(address); - if (amount > 0) { - printf("amount: %s\n", dogecoin_get_balance_str(address)); - unsigned int utxo_count = dogecoin_get_utxos_length(address); - if (utxo_count) { - printf("utxo count: %d\n", utxo_count); - unsigned int i = 1; - for (; i <= utxo_count; i++) { - printf("txid: %s\n", dogecoin_get_utxo_txid_str(address, i)); - printf("vout: %d\n", dogecoin_get_utxo_vout(address, i)); - printf("amount: %s\n", dogecoin_get_utxo_amount(address, i)); + char delim[] = " "; + // copy address into a new string, strtok modifies the string + char* address_copy = strdup(address); + + // backup existing default wallet file prior to radio doge functions test + const dogecoin_chainparams *params = chain_from_b58_prefix(address_copy); + dogecoin_wallet *tmp = dogecoin_wallet_new(params); + int result; + FILE *file; + if ((file = fopen(tmp->filename, "r"))) + { + fclose(file); +#ifdef WIN32 + #include + result = CopyFile((char*)tmp->filename, "tmp.bin", true); + if (result == 1) result = 0; +#else + result = file_copy((char *)tmp->filename, "tmp.bin"); +#endif + if (result != 0) { + printf( "could not copy '%s' %d\n", tmp->filename, result ); + } else { + printf( "File '%s' copied to 'tmp.bin'\n", tmp->filename); + } + } + + char *ptr; + char* temp_address_copy = address_copy; + + while((ptr = strtok_r(temp_address_copy, delim, &temp_address_copy))) { + int res = dogecoin_register_watch_address_with_node(ptr); + printf("registered: %d %s\n", res, ptr); + uint64_t amount = dogecoin_get_balance(ptr); + if (amount > 0) { + char* amount_str = dogecoin_get_balance_str(ptr); + printf("total: %s\n", amount_str); + unsigned int utxo_count = dogecoin_get_utxos_length(ptr); + if (utxo_count) { + printf("utxo count: %d\n", utxo_count); + unsigned int i = 1; + for (; i <= utxo_count; i++) { + printf("txid: %s\n", dogecoin_get_utxo_txid_str(ptr, i)); + printf("vout: %d\n", dogecoin_get_utxo_vout(ptr, i)); + char* utxo_amount_str = dogecoin_get_utxo_amount(ptr, i); + printf("amount: %s\n", utxo_amount_str); + dogecoin_free(utxo_amount_str); + } } + dogecoin_free(amount_str); + } + res = dogecoin_unregister_watch_address_with_node(ptr); + printf("unregistered: %s\n", res ? "true" : "false"); + } + + if ((file = fopen("tmp.bin", "r"))) { + fclose(file); +#ifdef WIN32 + #include + char *tmp_filename = _strdup((char *)tmp->filename); + char *filename = _strdup((char *)tmp->filename); + replace_last_after_delim(filename, "\\", "tmp.bin"); + LPVOID message; + result = DeleteFile(tmp->filename); + if (!result) { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, NULL); + printf("ERROR: %s\n", (char *)message); + } + result = rename(filename, tmp->filename); + dogecoin_free(filename); + dogecoin_free(tmp_filename); +#else + result = rename("tmp.bin", tmp->filename); +#endif + if( result != 0 ) { + printf( "could not copy 'tmp.bin' %d\n", result ); + } else { + printf( "File 'tmp.bin' copied to '%s'\n", tmp->filename); } } - res = dogecoin_unregister_watch_address_with_node(address); - printf("unregistered: %s\n", res ? "true" : "false"); + dogecoin_wallet_free(tmp); + dogecoin_free(address_copy); } + dogecoin_ecc_stop(); #endif } else { diff --git a/src/utils.c b/src/utils.c index afdb5a91d..d0cb450d6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -620,6 +620,31 @@ void slice(const char *str, char *result, size_t start, size_t end) strncpy(result, str + start, end - start); } +void remove_substr(char *string, char *sub) { + char *match; + int len = strlen(sub); + while ((match = strstr(string, sub))) { + *match = '\0'; + strcat(string, match+len); + } +} + +void replace_last_after_delim(const char *str, char* delim, char* replacement) { + char* tmp = strdup((char*)str); + char* new = tmp; + char *strptr = strtok(new, delim); + char* last = NULL; + while (strptr != NULL) { + last = strptr; + strptr = strtok(NULL, delim); + } + if (last) { + remove_substr((char*)str, last); + append((char*)str, replacement); + } + dogecoin_free(tmp); +} + /** * @brief function to convert ascii text to hexadecimal string * @@ -768,6 +793,38 @@ bool dogecoin_network_enabled() { #endif } +int integer_length(int x) { + int count = 0; + while (x > 0) { + x /= 10; + count++; + } + return count > 0 ? count : 1; +} + +int file_copy(char src [], char dest []) +{ + int c; + FILE *stream_read; + FILE *stream_write; + + stream_read = fopen (src, "r"); + if (stream_read == NULL) + return -1; + stream_write = fopen (dest, "w"); //create and write to file + if (stream_write == NULL) + { + fclose (stream_read); + return -2; + } + while ((c = fgetc(stream_read)) != EOF) + fputc (c, stream_write); + fclose (stream_read); + fclose (stream_write); + + return 0; +} + unsigned char base64_char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; unsigned int base64_int(unsigned int ch) { @@ -869,12 +926,3 @@ unsigned int base64_decode(const unsigned char* in, unsigned int in_len, unsigne return k; } - -int integer_length(int x) { - int count = 0; - while (x > 0) { - x /= 10; - count++; - } - return count > 0 ? count : 1; -} diff --git a/src/wallet.c b/src/wallet.c index 93e4d5058..ed49eb54b 100644 --- a/src/wallet.c +++ b/src/wallet.c @@ -347,11 +347,32 @@ void dogecoin_wallet_output_free(dogecoin_output* output) WALLET CORE FUNCTIONS ========================================================== */ +void set_wallet_filename(dogecoin_wallet* wallet, const dogecoin_chainparams *params) { + char path[256]; + memcpy_safe(path, getcwd(path, 256), 256); + char* delim = NULL; +#ifdef WIN32 + delim = "\\"; +#else + delim = "/"; +#endif + char* file_suffix = "_wallet.db"; + size_t path_size = strlen(path); + size_t delim_size = strlen(delim); + size_t chain_size = strlen(params->chainname); + size_t file_size = strlen(file_suffix); + memcpy_safe((char*)wallet->filename, path, path_size); + memcpy_safe((char*)wallet->filename + path_size, delim, delim_size); + memcpy_safe((char*)wallet->filename + path_size + delim_size, params->chainname, chain_size); + memcpy_safe((char*)wallet->filename + path_size + delim_size + chain_size, file_suffix, file_size); +} + dogecoin_wallet* dogecoin_wallet_new(const dogecoin_chainparams *params) { dogecoin_wallet* wallet = dogecoin_calloc(1, sizeof(*wallet)); wallet->masterkey = NULL; wallet->chain = params; + set_wallet_filename(wallet, params); wallet->hdkeys_rbtree = 0; wallet->utxos = utxos; wallet->unspent_rbtree = 0; @@ -389,9 +410,8 @@ dogecoin_wallet* dogecoin_wallet_init(const dogecoin_chainparams* chain, const c res = dogecoin_wallet_load(wallet, name, &error, &created, prompt); } else { - // prefix chain to wallet file name: - walletfile = concat(wallet_prefix, wallet_suffix); - res = dogecoin_wallet_load(wallet, walletfile, &error, &created, prompt); + // use prefixed chain to wallet file name: + res = dogecoin_wallet_load(wallet, wallet->filename, &error, &created, prompt); } dogecoin_free(walletfile); if (!res) { @@ -569,13 +589,8 @@ void print_utxos(dogecoin_wallet* wallet) { } koinu_to_coins_str(wallet_total_u64, wallet_total); printf("Spent Balance: %s\n", wallet_total); - } - if (HASH_COUNT(utxos) > 0) { - char wallet_total[21]; dogecoin_mem_zero(wallet_total, 21); - uint64_t wallet_total_u64 = 0; - dogecoin_utxo* utxo; - dogecoin_utxo* tmp; + wallet_total_u64 = 0; HASH_ITER(hh, utxos, utxo, tmp) { if (!is_spent(utxo)) { printf("%s\n", "----------------------"); @@ -656,10 +671,8 @@ void dogecoin_wallet_scrape_utxos(dogecoin_wallet* wallet, dogecoin_wtx* wtx) { dogecoin_utxo* spent_utxo; dogecoin_utxo* spent_tmp; HASH_ITER(hh, utxos, spent_utxo, spent_tmp) { - if (is_spent(spent_utxo)) { - if (memcmp((const uint8_t*)spent_utxo->txid, (const uint8_t*)utxo->txid, 32) == 0 && spent_utxo->vout == utxo->vout) { - n++; - } + if (is_spent(spent_utxo) && memcmp((const uint8_t*)spent_utxo->txid, (const uint8_t*)utxo->txid, 32) == 0 && spent_utxo->vout == utxo->vout) { + n++; } } if (n == 0) { @@ -773,7 +786,7 @@ dogecoin_bool dogecoin_wallet_create(dogecoin_wallet* wallet, const char* file_p // open wallet file if not already open if (!wallet->dbfile) { - wallet->filename = file_path; + memcpy_safe((char*)wallet->filename, file_path, strlen(file_path)); wallet->dbfile = fopen(file_path, "a+b"); } @@ -900,7 +913,6 @@ dogecoin_bool dogecoin_wallet_load(dogecoin_wallet* wallet, const char* file_pat struct stat buffer; *created = true; - if (stat(file_path, &buffer) == 0) { *created = false; // Set created to false as file already exists @@ -1501,13 +1513,7 @@ void dogecoin_wallet_check_transaction(void *ctx, dogecoin_tx *tx, unsigned int dogecoin_wallet* dogecoin_wallet_read(char* address) { dogecoin_chainparams* chain = (dogecoin_chainparams*)chain_from_b58_prefix(address); - // prefix chain to wallet file name: - char* wallet_suffix = "_wallet.db"; - char* wallet_prefix = (char*)chain->chainname; - char* walletfile = concat(wallet_prefix, wallet_suffix); - dogecoin_wallet* wallet = dogecoin_wallet_init(chain, address, walletfile, 0, 0, false, false, -1, false, false); - wallet->filename = concat(wallet_prefix, wallet_suffix); - dogecoin_free(walletfile); + dogecoin_wallet* wallet = dogecoin_wallet_init(chain, address, NULL, 0, 0, false, false, -1, false, false); return wallet; } @@ -1528,6 +1534,8 @@ int dogecoin_register_watch_address_with_node(char* address) { dogecoin_free(address_copy); return false; } + dogecoin_wallet_addr_free(waddr); + dogecoin_wallet_free(wallet); } dogecoin_free(address_copy); } else return false; @@ -1538,8 +1546,7 @@ int dogecoin_unregister_watch_address_with_node(char* address) { if (address != NULL) { char delim[] = " "; // copy address into a new string, strtok modifies the string - char* address_copy = strdup(address); - char* temp_address_copy = address_copy; + char* temp_address_copy = address; char *ptr; while((ptr = strtok_r(temp_address_copy, delim, &temp_address_copy))) { @@ -1548,23 +1555,17 @@ int dogecoin_unregister_watch_address_with_node(char* address) { dogecoin_bool created; // set up new wallet to store everything except our soon to be unregistered watch address: dogecoin_wallet* wallet_new = dogecoin_wallet_new(wallet->chain); - char path[256]; - memcpy_safe(path, getcwd(path, 256), 256); + char* file_delim = NULL; #ifdef WIN32 - char win_delim[] = "\\"; - char* oldname = concat(path, concat(win_delim, "temp.bin")); - char* newname = concat(path, concat(win_delim, (char*)wallet->filename)); + file_delim = "\\"; #else - char unix_delim[] = "/"; - char* oldname = concat(path, concat(unix_delim, "temp.bin")); - char* newname = concat(path, concat(unix_delim, (char*)wallet->filename)); + file_delim = "/"; #endif - dogecoin_wallet_load(wallet_new, oldname, &error, &created, false); - wallet_new->filename = oldname; + replace_last_after_delim((char*)wallet_new->filename, file_delim, "temp.bin"); + dogecoin_wallet_load(wallet_new, wallet_new->filename, &error, &created, false); dogecoin_wallet_addr* waddr_check = dogecoin_wallet_addr_new(); // convert address to 20 byte script hash: dogecoin_p2pkh_address_to_wallet_pubkeyhash(ptr, waddr_check, wallet); - dogecoin_wallet_addr* waddr; // serialize address prior to search: cstring* record = cstr_new_sz(256); dogecoin_wallet_addr_serialize(record, wallet->chain, waddr_check); @@ -1624,7 +1625,7 @@ int dogecoin_unregister_watch_address_with_node(char* address) { dogecoin_wallet_set_master_key_copy(wallet_new, wallet->masterkey); } } else if (rectype == WALLET_DB_REC_TYPE_ADDR) { - waddr = dogecoin_wallet_addr_new(); + dogecoin_wallet_addr* waddr = dogecoin_wallet_addr_new(); size_t addr_len = 20+1+4+1; unsigned char* buf = dogecoin_uchar_vla(addr_len); struct const_buffer cbuf = {buf, addr_len}; @@ -1637,6 +1638,7 @@ int dogecoin_unregister_watch_address_with_node(char* address) { dogecoin_p2pkh_addr_from_hash160(waddr->pubkeyhash, wallet->chain, p2pkh_check, P2PKHLEN); if (memcmp(record->str, buf, record->len)==0) { found = 1; + dogecoin_wallet_addr_free(waddr); } else { const char* addr_match = find_needle(ptr, strlen(ptr), p2pkh_check, P2PKHLEN); if (!addr_match) { @@ -1663,9 +1665,11 @@ int dogecoin_unregister_watch_address_with_node(char* address) { dogecoin_p2pkh_addr_from_hash160(addr_check->pubkeyhash, wallet->chain, p2pkh_check, P2PKHLEN); const char* match = find_needle(address, strlen(address), p2pkh_check, strlen(p2pkh_check)); if (!match) { + dogecoin_free(buf); goto copy; } } + dogecoin_free(buf); copy: dogecoin_wallet_scrape_utxos(wallet_new, wtx); dogecoin_wallet_add_wtx_move(wallet_new, wtx); // hands memory management over to the binary tree @@ -1673,42 +1677,45 @@ int dogecoin_unregister_watch_address_with_node(char* address) { fseek(wallet->dbfile, reclen, SEEK_CUR); } } + + if (!wallet_new->waddr_vector->len) { + dogecoin_wallet_next_addr(wallet_new); + } + cstr_free(record, true); dogecoin_wallet_flush(wallet); - dogecoin_wallet_free(wallet); dogecoin_wallet_flush(wallet_new); - dogecoin_wallet_free(wallet_new); if (found) { /* Attempt to rename file: */ #ifdef WIN32 #include _fcloseall(); LPVOID message; - int result = DeleteFile(newname); + int result = DeleteFile(wallet->filename); if (!result) { error = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, NULL); printf("ERROR: %s\n", message); } - result = rename( oldname, newname ); + result = rename( wallet_new->filename, wallet->filename ); #else - int result = rename( oldname, newname ); + int result = rename( wallet_new->filename, wallet->filename ); #endif if( result != 0 ) - printf( "Could not rename '%s' %d\n", oldname, result ); + printf( "Could not rename '%s' %d\n", wallet_new->filename, result ); else - printf( "File '%s' renamed to '%s' for %s\n", oldname, newname, ptr ); + printf( "File '%s' renamed to '%s' for %s\n", wallet_new->filename, wallet->filename, ptr ); } else { #ifndef WIN32 - int res = remove(oldname); + int res = remove(wallet_new->filename); if (!res) { printf("remove failed!\n"); return false; } #endif } - dogecoin_free(oldname); - dogecoin_free(newname); + dogecoin_wallet_free(wallet); + dogecoin_wallet_free(wallet_new); } } else return false; return true; @@ -1718,9 +1725,9 @@ int dogecoin_get_utxo_vector(char* address, vector* utxo_vec) { if (!address) return false; dogecoin_wallet* wallet = dogecoin_wallet_read(address); if (HASH_COUNT(utxos) > 0) { - unsigned int i; - for (i = 0; i < HASH_COUNT(utxos); i++) { - dogecoin_utxo* utxo = find_dogecoin_utxo(i + 1); + dogecoin_utxo* utxo; + dogecoin_utxo* tmp; + HASH_ITER(hh, utxos, utxo, tmp) { if (strncmp(utxo->address, address, strlen(utxo->address))==0 && !is_spent(utxo)) { vector_add(utxo_vec, utxo); } @@ -1737,10 +1744,11 @@ unsigned int dogecoin_get_utxos_length(char* address) { if (!address) return false; dogecoin_wallet* wallet = dogecoin_wallet_read(address); unsigned int utxos_total = 0; - vector* utxos = vector_new(1, free); - if (!dogecoin_get_utxo_vector(address, utxos)) return false; - utxos_total = utxos->len; - vector_free(utxos, true); + dogecoin_utxo* utxo; + dogecoin_utxo* tmp; + HASH_ITER(hh, utxos, utxo, tmp) { + if (!is_spent(utxo) && strcmp(utxo->address, address) == 0) utxos_total++; + } dogecoin_wallet_free(wallet); return utxos_total; } @@ -1785,17 +1793,19 @@ uint8_t* dogecoin_get_utxos(char* address) { char* dogecoin_get_utxo_txid_str(char* address, unsigned int index) { if (!address || !index) return false; dogecoin_wallet* wallet = dogecoin_wallet_read(address); - vector* utxos = vector_new(1, free); - if (!dogecoin_get_utxo_vector(address, utxos)) return false; char* txid = NULL; - unsigned int i; - for (i = 0; i < utxos->len; i++) { - dogecoin_utxo* utxo = vector_idx(utxos, i); - if (i==index - 1) { - txid = utils_uint8_to_hex((const uint8_t*)utxo->txid, DOGECOIN_HASH_LENGTH); + unsigned int i = 0; + dogecoin_utxo* utxo; + dogecoin_utxo* tmp; + HASH_ITER(hh, utxos, utxo, tmp) { + if (strcmp(address, utxo->address) == 0 && !is_spent(utxo)) { + if (i == index - 1) { + txid = to_string(utxo->txid); + break; + } + i++; } } - vector_free(utxos, true); dogecoin_wallet_free(wallet); return txid; } @@ -1811,17 +1821,19 @@ uint8_t* dogecoin_get_utxo_txid(char* address, unsigned int index) { int dogecoin_get_utxo_vout(char* address, unsigned int index) { if (!address || !index) return false; dogecoin_wallet* wallet = dogecoin_wallet_read(address); - vector* utxos = vector_new(1, free); - if (!dogecoin_get_utxo_vector(address, utxos)) return false; int vout = 0; - unsigned int i; - for (i = 0; i < utxos->len; i++) { - dogecoin_utxo* utxo = vector_idx(utxos, i); - if (i==index - 1) { - vout = utxo->vout; + unsigned int i = 0; + dogecoin_utxo* utxo; + dogecoin_utxo* tmp; + HASH_ITER(hh, utxos, utxo, tmp) { + if (strcmp(address, utxo->address) == 0 && !is_spent(utxo)) { + if (i == index - 1) { + vout = utxo->vout; + break; + } + i++; } } - vector_free(utxos, true); dogecoin_wallet_free(wallet); return vout; } @@ -1829,17 +1841,19 @@ int dogecoin_get_utxo_vout(char* address, unsigned int index) { char* dogecoin_get_utxo_amount(char* address, unsigned int index) { if (!address || !index) return false; dogecoin_wallet* wallet = dogecoin_wallet_read(address); - vector* utxos = vector_new(1, free); - if (!dogecoin_get_utxo_vector(address, utxos)) return false; - char* amount = dogecoin_char_vla(21); - unsigned int i; - for (i = 0; i < utxos->len; i++) { - dogecoin_utxo* utxo = vector_idx(utxos, i); - if (i==index - 1) { - strcpy(amount, utxo->amount); + unsigned int i = 0; + dogecoin_utxo* utxo; + dogecoin_utxo* tmp; + HASH_ITER(hh, utxos, utxo, tmp) { + if (strcmp(address, utxo->address) == 0 && !is_spent(utxo)) { + if (i == index - 1) { + break; + } + i++; } } - vector_free(utxos, true); + char* amount = (char*)dogecoin_calloc(1, 21); + memcpy_safe(amount, utxo->amount, 21); dogecoin_wallet_free(wallet); return amount; } @@ -1847,23 +1861,16 @@ char* dogecoin_get_utxo_amount(char* address, unsigned int index) { uint64_t dogecoin_get_balance(char* address) { if (!address) return false; dogecoin_wallet* wallet = dogecoin_wallet_read(address); - vector* utxos = vector_new(1, free); - if (!dogecoin_get_utxo_vector(address, utxos)) { - dogecoin_wallet_free(wallet); - vector_free(utxos, true); - return false; - } uint64_t wallet_total_u64 = 0; - if (utxos->len) { - vector* addrs = vector_new(1, free); - dogecoin_wallet_get_addresses(wallet, addrs); - unsigned int i; - for (i = 0; i < utxos->len; i++) { - dogecoin_utxo* utxo = vector_idx(utxos, i); - wallet_total_u64 += coins_to_koinu_str(utxo->amount); + if (HASH_COUNT(utxos)) { + dogecoin_utxo* utxo; + dogecoin_utxo* tmp; + HASH_ITER(hh, utxos, utxo, tmp) { + if (!is_spent(utxo) && strcmp(address, utxo->address) == 0) { + wallet_total_u64 += coins_to_koinu_str(utxo->amount); + } } } - vector_free(utxos, true); dogecoin_wallet_free(wallet); return wallet_total_u64; }