aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/depends.yml2
-rw-r--r--README.md4
-rw-r--r--contrib/depends/packages/unbound.mk9
-rw-r--r--contrib/depends/patches/unbound/disable-glibc-reallocarray.patch14
-rw-r--r--contrib/gitian/README.md3
-rwxr-xr-xcontrib/gitian/gitian-build.py6
-rw-r--r--external/boost/archive/portable_binary_archive.hpp4
-rw-r--r--external/db_drivers/liblmdb/mdb.c58
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp40
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h1
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp4
-rw-r--r--src/device/device_ledger.cpp12
-rw-r--r--src/rpc/core_rpc_server.cpp50
-rw-r--r--src/simplewallet/simplewallet.cpp24
-rw-r--r--src/simplewallet/simplewallet.h2
-rw-r--r--src/wallet/api/wallet.cpp44
-rw-r--r--src/wallet/api/wallet.h1
-rw-r--r--src/wallet/api/wallet2_api.h7
-rw-r--r--src/wallet/wallet2.cpp92
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--utils/fish/README.md2
-rw-r--r--utils/fish/monero-wallet-cli.fish61
-rw-r--r--utils/fish/monero-wallet-rpc.fish65
-rw-r--r--utils/fish/monerod.fish113
24 files changed, 450 insertions, 170 deletions
diff --git a/.github/workflows/depends.yml b/.github/workflows/depends.yml
index f20bf949f..32df7dc42 100644
--- a/.github/workflows/depends.yml
+++ b/.github/workflows/depends.yml
@@ -17,7 +17,7 @@ env:
ccache --set-config=compression=true
jobs:
- build-macos:
+ build-cross:
runs-on: ubuntu-18.04
env:
CCACHE_TEMPDIR: /tmp/.ccache-temp
diff --git a/README.md b/README.md
index b3422280d..81db2478a 100644
--- a/README.md
+++ b/README.md
@@ -138,6 +138,8 @@ Dates are provided in the format YYYY-MM-DD.
| 1978433 | 2019-11-30 | v12 | v0.15.0.0 | v0.16.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs
| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.3.2 | New CLSAG transaction format
| 2210720 | 2020-10-18 | v14 | v0.17.1.1 | v0.17.3.2 | forbid old MLSAG transaction format
+| 2668888 | 2022-07-16 | v15 | v0.18.0.0 | v0.18.0.0 | ringsize = 16, bulletproofs+, view tags, adjusted dynamic block weight algorithm
+| 2669608 | 2022-07-17 | v16 | v0.18.0.0 | v0.18.0.0 | forbid old v14 transaction format
| XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX |
X's indicate that these details have not been determined as of commit date.
@@ -579,7 +581,7 @@ You can also cross-compile static binaries on Linux for Windows and macOS with t
* ```make depends target=aarch64-linux-android``` for 64bit android binaries
-The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names.
+The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. The `depends` system has been tested on Ubuntu 18.04 and 20.04.
Using `depends` might also be easier to compile Monero on Windows than using MSYS. Activate Windows Subsystem for Linux (WSL) with a distro (for example Ubuntu), install the apt build-essentials and follow the `depends` steps as depicted above.
diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk
index a85c47e4e..9336524f3 100644
--- a/contrib/depends/packages/unbound.mk
+++ b/contrib/depends/packages/unbound.mk
@@ -4,6 +4,8 @@ $(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f
$(package)_dependencies=openssl expat ldns
+$(package)_patches=disable-glibc-reallocarray.patch
+
define $(package)_set_vars
$(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix) --with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only
@@ -12,8 +14,13 @@ define $(package)_set_vars
$(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread"
endef
+define $(package)_preprocess_cmds
+ patch -p1 < $($(package)_patch_dir)/disable-glibc-reallocarray.patch &&\
+ autoconf
+endef
+
define $(package)_config_cmds
- $($(package)_autoconf)
+ $($(package)_autoconf) ac_cv_func_getentropy=no
endef
define $(package)_build_cmds
diff --git a/contrib/depends/patches/unbound/disable-glibc-reallocarray.patch b/contrib/depends/patches/unbound/disable-glibc-reallocarray.patch
new file mode 100644
index 000000000..d66a821ad
--- /dev/null
+++ b/contrib/depends/patches/unbound/disable-glibc-reallocarray.patch
@@ -0,0 +1,14 @@
+diff --git a/configure.ac b/configure.ac
+index 5c7da197..e2b25288 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1702,6 +1702,9 @@ AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT
+ #ifndef _OPENBSD_SOURCE
+ #define _OPENBSD_SOURCE 1
+ #endif
++#ifdef __linux__
++# error reallocarray() is currently disabled on Linux to support glibc < 2.26
++#endif
+ #include <stdlib.h>
+ int main(void) {
+ void* p = reallocarray(NULL, 10, 100);
diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md
index 9852b07ba..24cf26fa3 100644
--- a/contrib/gitian/README.md
+++ b/contrib/gitian/README.md
@@ -133,10 +133,11 @@ Common setup part:
su - gitianuser
GH_USER=YOUR_GITHUB_USER_NAME
-VERSION=v0.17.2.0
+VERSION=v0.17.3.2
```
Where `GH_USER` is your GitHub user name and `VERSION` is the version tag you want to build.
+The `gitian-build.py`'s `--setup` switch will also refresh the environment of any stale files and submodules.
Setup for LXC:
diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py
index 859c2c645..859f460a7 100755
--- a/contrib/gitian/gitian-build.py
+++ b/contrib/gitian/gitian-build.py
@@ -31,8 +31,10 @@ def setup():
subprocess.check_call(['git', 'checkout', 'c0f77ca018cb5332bfd595e0aff0468f77542c23'])
os.makedirs('inputs', exist_ok=True)
os.chdir('inputs')
- if not os.path.isdir('monero'):
- subprocess.check_call(['git', 'clone', args.url, 'monero'])
+ if os.path.isdir('monero'):
+ # Remove the potentially stale monero dir. Otherwise you might face submodule mismatches.
+ subprocess.check_call(['rm', 'monero', '-fR'])
+ subprocess.check_call(['git', 'clone', args.url, 'monero'])
os.chdir('..')
make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64']
if args.docker:
diff --git a/external/boost/archive/portable_binary_archive.hpp b/external/boost/archive/portable_binary_archive.hpp
index cdbd38aad..b1d6ae73e 100644
--- a/external/boost/archive/portable_binary_archive.hpp
+++ b/external/boost/archive/portable_binary_archive.hpp
@@ -44,12 +44,16 @@ reverse_bytes(signed char size, char *address){
char * first = address;
char * last = first + size - 1;
for(;first < last;++first, --last){
+#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow="
+#endif
char x = *last;
*last = *first;
*first = x;
+#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
+#endif
}
}
diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c
index 6314d5775..bf60c7013 100644
--- a/external/db_drivers/liblmdb/mdb.c
+++ b/external/db_drivers/liblmdb/mdb.c
@@ -1467,6 +1467,8 @@ struct MDB_env {
#endif
/** Failed to update the meta page. Probably an I/O error. */
#define MDB_FATAL_ERROR 0x80000000U
+ /** using a raw block device */
+#define MDB_RAWPART 0x40000000U
/** Some fields are initialized. */
#define MDB_ENV_ACTIVE 0x20000000U
/** me_txkey is set */
@@ -4038,6 +4040,8 @@ fail:
return rc;
}
+static int ESECT mdb_env_map(MDB_env *env, void *addr);
+
/** Read the environment parameters of a DB environment before
* mapping it into memory.
* @param[in] env the environment handle
@@ -4054,6 +4058,31 @@ mdb_env_read_header(MDB_env *env, int prev, MDB_meta *meta)
int i, rc, off;
enum { Size = sizeof(pbuf) };
+ if (env->me_flags & MDB_RAWPART) {
+#define VM_ALIGN 0x200000
+ env->me_mapsize += VM_ALIGN-1;
+ env->me_mapsize &= ~(VM_ALIGN-1);
+ env->me_psize = env->me_os_psize;
+ rc = mdb_env_map(env, NULL);
+ if (rc) {
+ DPRINTF(("mdb_env_map: %s", mdb_strerror(rc)));
+ return rc;
+ }
+ p = (MDB_page *)env->me_map;
+ for (i=0; i<NUM_METAS; i++) {
+ if (!F_ISSET(p->mp_flags, P_META))
+ return ENOENT;
+ if (env->me_metas[i]->mm_magic != MDB_MAGIC)
+ return MDB_INVALID;
+ if (env->me_metas[i]->mm_version != MDB_DATA_VERSION)
+ return MDB_VERSION_MISMATCH;
+ if (i == 0 || env->me_metas[i]->mm_txnid > meta->mm_txnid)
+ *meta = *env->me_metas[i];
+ p = (MDB_page *)((char *)p + env->me_psize);
+ }
+ return 0;
+ }
+
/* We don't know the page size yet, so use a minimum value.
* Read both meta pages so we can use the latest one.
*/
@@ -4081,6 +4110,8 @@ mdb_env_read_header(MDB_env *env, int prev, MDB_meta *meta)
p = (MDB_page *)&pbuf;
if (!F_ISSET(p->mp_flags, P_META)) {
+ if (env->me_flags & MDB_RAWPART)
+ return ENOENT;
DPRINTF(("page %"Yu" not a meta page", p->mp_pgno));
return MDB_INVALID;
}
@@ -4148,6 +4179,18 @@ mdb_env_init_meta(MDB_env *env, MDB_meta *meta)
psize = env->me_psize;
+ if ((env->me_flags & (MDB_RAWPART|MDB_WRITEMAP)) == (MDB_RAWPART|MDB_WRITEMAP)) {
+ p = (MDB_page *)env->me_map;
+ p->mp_pgno = 0;
+ p->mp_flags = P_META;
+ *(MDB_meta *)METADATA(p) = *meta;
+ q = (MDB_page *)((char *)p + psize);
+ q->mp_pgno = 1;
+ q->mp_flags = P_META;
+ *(MDB_meta *)METADATA(q) = *meta;
+ return 0;
+ }
+
p = calloc(NUM_METAS, psize);
if (!p)
return ENOMEM;
@@ -4410,7 +4453,7 @@ mdb_env_map(MDB_env *env, void *addr)
int prot = PROT_READ;
if (flags & MDB_WRITEMAP) {
prot |= PROT_WRITE;
- if (ftruncate(env->me_fd, env->me_mapsize) < 0)
+ if (!(flags & MDB_RAWPART) && ftruncate(env->me_fd, env->me_mapsize) < 0)
return ErrCode();
}
env->me_map = mmap(addr, env->me_mapsize, prot, MAP_SHARED,
@@ -5449,6 +5492,17 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode
goto leave;
#endif
#endif
+#ifndef _WIN32
+ {
+ struct stat st;
+ flags &= ~MDB_RAWPART;
+ if (!stat(path, &st) && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) {
+ flags |= MDB_RAWPART | MDB_NOSUBDIR;
+ if (!env->me_mapsize)
+ env->me_mapsize = DEFAULT_MAPSIZE;
+ }
+ }
+#endif
flags |= MDB_ENV_ACTIVE; /* tell mdb_env_close0() to clean up */
if (flags & MDB_RDONLY) {
@@ -7668,7 +7722,7 @@ more:
offset *= 4; /* space for 4 more */
break;
}
- /* FALLTHRU: Big enough MDB_DUPFIXED sub-page */
+ /* FALLTHRU *//* Big enough MDB_DUPFIXED sub-page */
case MDB_CURRENT:
fp->mp_flags |= P_DIRTY;
COPY_PGNO(fp->mp_pgno, mp->mp_pgno);
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index e2ac9df0b..db7fa6c7c 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -25,13 +25,6 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef _WIN32
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#endif
-
#include "db_lmdb.h"
#include <boost/filesystem.hpp>
@@ -1303,26 +1296,6 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB()
m_hardfork = nullptr;
}
-void BlockchainLMDB::check_mmap_support()
-{
-#ifndef _WIN32
- const boost::filesystem::path mmap_test_file = m_folder / boost::filesystem::unique_path();
- int mmap_test_fd = ::open(mmap_test_file.string().c_str(), O_RDWR | O_CREAT, 0600);
- if (mmap_test_fd < 0)
- throw0(DB_ERROR((std::string("Failed to check for mmap support: open failed: ") + strerror(errno)).c_str()));
- epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([mmap_test_fd, &mmap_test_file]() {
- ::close(mmap_test_fd);
- boost::filesystem::remove(mmap_test_file.string());
- });
- if (write(mmap_test_fd, "mmaptest", 8) != 8)
- throw0(DB_ERROR((std::string("Failed to check for mmap support: write failed: ") + strerror(errno)).c_str()));
- void *mmap_res = mmap(NULL, 8, PROT_READ, MAP_SHARED, mmap_test_fd, 0);
- if (mmap_res == MAP_FAILED)
- throw0(DB_ERROR("This filesystem does not support mmap: use --data-dir to place the blockchain on a filesystem which does"));
- munmap(mmap_res, 8);
-#endif
-}
-
void BlockchainLMDB::open(const std::string& filename, const int db_flags)
{
int result;
@@ -1334,14 +1307,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
throw0(DB_OPEN_FAILURE("Attempted to open db, but it's already open"));
boost::filesystem::path direc(filename);
- if (boost::filesystem::exists(direc))
- {
- if (!boost::filesystem::is_directory(direc))
- throw0(DB_OPEN_FAILURE("LMDB needs a directory path, but a file was passed"));
- }
- else
- {
- if (!boost::filesystem::create_directories(direc))
+ if (!boost::filesystem::exists(direc) &&
+ !boost::filesystem::create_directories(direc)) {
throw0(DB_OPEN_FAILURE(std::string("Failed to create directory ").append(filename).c_str()));
}
@@ -1364,9 +1331,6 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
m_folder = filename;
- try { check_mmap_support(); }
- catch(...) { MERROR("Failed to check for mmap support, proceeding"); }
-
#ifdef __OpenBSD__
if ((mdb_flags & MDB_WRITEMAP) == 0) {
MCLOG_RED(el::Level::Info, "global", "Running on OpenBSD: forcing WRITEMAP");
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 20edab2e9..bdae44948 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -359,7 +359,6 @@ public:
static int compare_string(const MDB_val *a, const MDB_val *b);
private:
- void check_mmap_support();
void do_resize(uint64_t size_increase=0);
bool need_resize(uint64_t threshold_size=0) const;
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index f101f10c5..388013f96 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -1042,7 +1042,7 @@ namespace cryptonote
crypto::public_key subaddress_spendkey;
if (out_can_be_to_acc(view_tag_opt, derivation, output_index))
{
- hwdev.derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey);
+ CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, derivation, output_index, subaddress_spendkey), boost::none, "Failed to derive subaddress public key");
auto found = subaddresses.find(subaddress_spendkey);
if (found != subaddresses.end())
return subaddress_receive_info{ found->second, derivation };
@@ -1054,7 +1054,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(output_index < additional_derivations.size(), boost::none, "wrong number of additional derivations");
if (out_can_be_to_acc(view_tag_opt, additional_derivations[output_index], output_index))
{
- hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey);
+ CHECK_AND_ASSERT_MES(hwdev.derive_subaddress_public_key(out_key, additional_derivations[output_index], output_index, subaddress_spendkey), boost::none, "Failed to derive subaddress public key");
auto found = subaddresses.find(subaddress_spendkey);
if (found != subaddresses.end())
return subaddress_receive_info{ found->second, additional_derivations[output_index] };
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index 51e65dfa5..aa73e998c 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -694,7 +694,8 @@ namespace hw {
log_hexbuffer("derive_subaddress_public_key: [[IN]] pub ", pub_x.data, 32);
log_hexbuffer("derive_subaddress_public_key: [[IN]] derivation", derivation_x.data, 32);
log_message ("derive_subaddress_public_key: [[IN]] index ", std::to_string((int)output_index_x));
- this->controle_device->derive_subaddress_public_key(pub_x, derivation_x,output_index_x,derived_pub_x);
+ if (!this->controle_device->derive_subaddress_public_key(pub_x, derivation_x,output_index_x,derived_pub_x))
+ return false;
log_hexbuffer("derive_subaddress_public_key: [[OUT]] derived_pub", derived_pub_x.data, 32);
#endif
@@ -702,7 +703,8 @@ namespace hw {
//If we are in TRANSACTION_PARSE, the given derivation has been retrieved uncrypted (wihtout the help
//of the device), so continue that way.
MDEBUG( "derive_subaddress_public_key : PARSE mode with known viewkey");
- crypto::derive_subaddress_public_key(pub, derivation, output_index,derived_pub);
+ if (!crypto::derive_subaddress_public_key(pub, derivation, output_index,derived_pub))
+ return false;
} else {
AUTO_LOCK_CMD();
int offset = set_command_header_noopt(INS_DERIVE_SUBADDRESS_PUBLIC_KEY);
@@ -1052,7 +1054,8 @@ namespace hw {
crypto::key_derivation derivation_x;
log_hexbuffer("generate_key_derivation: [[IN]] pub ", pub_x.data, 32);
log_hexbuffer("generate_key_derivation: [[IN]] sec ", sec_x.data, 32);
- this->controle_device->generate_key_derivation(pub_x, sec_x, derivation_x);
+ if (!this->controle_device->generate_key_derivation(pub_x, sec_x, derivation_x))
+ return false;
log_hexbuffer("generate_key_derivation: [[OUT]] derivation", derivation_x.data, 32);
#endif
@@ -1207,7 +1210,8 @@ namespace hw {
log_hexbuffer("derive_public_key: [[IN]] derivation ", derivation_x.data, 32);
log_message ("derive_public_key: [[IN]] output_index", std::to_string(output_index_x));
log_hexbuffer("derive_public_key: [[IN]] pub ", pub_x.data, 32);
- this->controle_device->derive_public_key(derivation_x, output_index_x, pub_x, derived_pub_x);
+ if (!this->controle_device->derive_public_key(derivation_x, output_index_x, pub_x, derived_pub_x))
+ return false;
log_hexbuffer("derive_public_key: [[OUT]] derived_pub ", derived_pub_x.data, 32);
#endif
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 0fe28465f..5304333ff 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -492,7 +492,6 @@ namespace cryptonote
}
CHECK_PAYMENT_MIN1(req, res, COST_PER_GET_INFO, false);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
const bool restricted = m_restricted && ctx;
@@ -598,7 +597,6 @@ namespace cryptonote
}
CHECK_PAYMENT(req, res, 1);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
// quick check for noop
if (!req.block_ids.empty())
@@ -609,7 +607,7 @@ namespace cryptonote
if (last_block_hash == req.block_ids.front())
{
res.start_height = 0;
- res.current_height = last_block_height + 1;
+ res.current_height = m_core.get_current_blockchain_height();
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -730,7 +728,6 @@ namespace cryptonote
res.blocks.clear();
res.blocks.reserve(req.heights.size());
CHECK_PAYMENT_MIN1(req, res, req.heights.size() * COST_PER_BLOCK, false);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
for (uint64_t height : req.heights)
{
block blk;
@@ -1592,7 +1589,6 @@ namespace cryptonote
return r;
CHECK_PAYMENT(req, res, 1);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
@@ -1617,7 +1613,6 @@ namespace cryptonote
return r;
CHECK_PAYMENT(req, res, 1);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
@@ -1720,14 +1715,11 @@ namespace cryptonote
error_resp.message = "Wrong parameters, expected height";
return false;
}
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
uint64_t h = req[0];
- uint64_t blockchain_height = m_core.get_current_blockchain_height();
- if(blockchain_height <= h)
+ if(m_core.get_current_blockchain_height() <= h)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("Requested block height: ") + std::to_string(h) + " greater than current top block height: " + std::to_string(blockchain_height - 1);
- return false;
+ error_resp.message = std::string("Requested block height: ") + std::to_string(h) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
}
res = string_tools::pod_to_hex(m_core.get_block_id_by_height(h));
return true;
@@ -1877,7 +1869,6 @@ namespace cryptonote
return false;
}
}
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
crypto::hash seed_hash, next_seed_hash;
if (!get_block_template(info.address, req.prev_block.empty() ? NULL : &prev_block, blob_reserve, reserved_offset, wdiff, res.height, res.expected_reward, b, res.seed_height, seed_hash, next_seed_hash, error_resp))
return false;
@@ -2351,7 +2342,6 @@ namespace cryptonote
CHECK_CORE_READY();
CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
uint64_t last_block_height;
crypto::hash last_block_hash;
m_core.get_blockchain_top(last_block_height, last_block_hash);
@@ -2392,8 +2382,6 @@ namespace cryptonote
return false;
}
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
-
auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool {
crypto::hash block_hash;
bool hash_parsed = parse_hash256(hash, block_hash);
@@ -2453,6 +2441,13 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADERS_RANGE>(invoke_http_mode::JON_RPC, "getblockheadersrange", req, res, r))
return r;
+ const uint64_t bc_height = m_core.get_current_blockchain_height();
+ if (req.start_height >= bc_height || req.end_height >= bc_height || req.start_height > req.end_height)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
+ error_resp.message = "Invalid start/end heights.";
+ return false;
+ }
const bool restricted = m_restricted && ctx;
if (restricted && req.end_height - req.start_height > RESTRICTED_BLOCK_HEADER_RANGE)
{
@@ -2462,16 +2457,6 @@ namespace cryptonote
}
CHECK_PAYMENT_MIN1(req, res, (req.end_height - req.start_height + 1) * COST_PER_BLOCK_HEADER, false);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
-
- const uint64_t bc_height = m_core.get_current_blockchain_height();
- if (req.start_height >= bc_height || req.end_height >= bc_height || req.start_height > req.end_height)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = "Invalid start/end heights.";
- return false;
- }
-
for (uint64_t h = req.start_height; h <= req.end_height; ++h)
{
crypto::hash block_hash = m_core.get_block_id_by_height(h);
@@ -2516,12 +2501,10 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT>(invoke_http_mode::JON_RPC, "getblockheaderbyheight", req, res, r))
return r;
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
- uint64_t blockchain_height = m_core.get_current_blockchain_height();
- if(blockchain_height <= req.height)
+ if(m_core.get_current_blockchain_height() <= req.height)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(blockchain_height - 1);
+ error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
return false;
}
CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
@@ -2554,7 +2537,6 @@ namespace cryptonote
return r;
CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK, false);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
crypto::hash block_hash;
if (!req.hash.empty())
@@ -2569,11 +2551,10 @@ namespace cryptonote
}
else
{
- uint64_t blockchain_height = m_core.get_current_blockchain_height();
- if(blockchain_height <= req.height)
+ if(m_core.get_current_blockchain_height() <= req.height)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(blockchain_height - 1);
+ error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
return false;
}
block_hash = m_core.get_block_id_by_height(req.height);
@@ -2881,7 +2862,6 @@ namespace cryptonote
bool core_rpc_server::on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
RPC_TRACKER(get_coinbase_tx_sum);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
const uint64_t bc_height = m_core.get_current_blockchain_height();
if (req.height >= bc_height || req.count > bc_height)
{
@@ -2923,7 +2903,6 @@ namespace cryptonote
bool core_rpc_server::on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
RPC_TRACKER(get_alternate_chains);
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
try
{
std::vector<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
@@ -3226,7 +3205,6 @@ namespace cryptonote
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG>(invoke_http_mode::JON_RPC, "get_txpool_backlog", req, res, r))
return r;
- db_rtxn_guard rtxn_guard(&m_core.get_blockchain_storage().get_db());
size_t n_txes = m_core.get_pool_transactions_count();
CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_POOL_STATS * n_txes, false);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index f6e313089..a8f4e5a07 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -5673,14 +5673,18 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block
m_refresh_progress_reporter.update(height, false);
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time)
+void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time)
{
if (m_locked)
return;
+ std::stringstream burn;
+ if (burnt != 0) {
+ burn << " (" << print_money(amount) << " yet " << print_money(burnt) << " was burnt)";
+ }
message_writer(console_color_green, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
- print_money(amount) << ", " <<
+ print_money(amount - burnt) << burn.str() << ", " <<
tr("idx ") << subaddr_index;
const uint64_t warn_height = m_wallet->nettype() == TESTNET ? 1000000 : m_wallet->nettype() == STAGENET ? 50000 : 1650000;
@@ -6960,18 +6964,33 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer(const std::vector<std::string> &args_)
{
+ if (args_.size() < 1)
+ {
+ PRINT_USAGE(USAGE_TRANSFER);
+ return true;
+ }
transfer_main(Transfer, args_, false);
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
{
+ if (args_.size() < 1)
+ {
+ PRINT_USAGE(USAGE_LOCKED_TRANSFER);
+ return true;
+ }
transfer_main(TransferLocked, args_, false);
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
{
+ if (args_.size() < 1)
+ {
+ PRINT_USAGE(USAGE_LOCKED_SWEEP_ALL);
+ return true;
+ }
sweep_main(m_current_subaddress_account, 0, true, args_);
return true;
}
@@ -7670,6 +7689,7 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
if (args_.size() < 1)
{
fail_msg_writer() << tr("missing threshold amount");
+ PRINT_USAGE(USAGE_SWEEP_BELOW);
return true;
}
if (!cryptonote::parse_amount(below, args_[0]))
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 6c4ddd4e7..6a9fa149d 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -346,7 +346,7 @@ namespace cryptonote
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
- virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time);
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time);
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 7cd8656e1..1ee2e20b6 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -154,18 +154,20 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time)
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time)
{
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
LOG_PRINT_L3(__FUNCTION__ << ": money received. height: " << height
<< ", tx: " << tx_hash
- << ", amount: " << print_money(amount)
+ << ", amount: " << print_money(amount - burnt)
+ << ", burnt: " << print_money(burnt)
+ << ", raw_output_value: " << print_money(amount)
<< ", idx: " << subaddr_index);
// do not signal on received tx if wallet is not syncronized completely
if (m_listener && m_wallet->synchronized()) {
- m_listener->moneyReceived(tx_hash, amount);
+ m_listener->moneyReceived(tx_hash, amount - burnt);
m_listener->updated();
}
}
@@ -1280,6 +1282,42 @@ bool WalletImpl::importOutputs(const string &filename)
return true;
}
+bool WalletImpl::scanTransactions(const std::vector<std::string> &txids)
+{
+ if (txids.empty())
+ {
+ setStatusError(string(tr("Failed to scan transactions: no transaction ids provided.")));
+ return false;
+ }
+
+ // Parse and dedup args
+ std::unordered_set<crypto::hash> txids_u;
+ for (const auto &s : txids)
+ {
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(s, txid))
+ {
+ setStatusError(string(tr("Invalid txid specified: ")) + s);
+ return false;
+ }
+ txids_u.insert(txid);
+ }
+ std::vector<crypto::hash> txids_v(txids_u.begin(), txids_u.end());
+
+ try
+ {
+ m_wallet->scan_tx(txids_v);
+ }
+ catch (const std::exception &e)
+ {
+ LOG_ERROR("Failed to scan transaction: " << e.what());
+ setStatusError(string(tr("Failed to scan transaction: ")) + e.what());
+ return false;
+ }
+
+ return true;
+}
+
void WalletImpl::addSubaddressAccount(const std::string& label)
{
m_wallet->add_subaddress_account(label);
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 0e61ee330..018b2a0ed 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -169,6 +169,7 @@ public:
bool importKeyImages(const std::string &filename) override;
bool exportOutputs(const std::string &filename, bool all = false) override;
bool importOutputs(const std::string &filename) override;
+ bool scanTransactions(const std::vector<std::string> &txids) override;
virtual void disposeTransaction(PendingTransaction * t) override;
virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index c6f81f0e4..b67bce60c 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -927,6 +927,13 @@ struct Wallet
*/
virtual bool importOutputs(const std::string &filename) = 0;
+ /*!
+ * \brief scanTransactions - scan a list of transaction ids, this operation may reveal the txids to the remote node and affect your privacy
+ * \param txids - list of transaction ids
+ * \return - true on success
+ */
+ virtual bool scanTransactions(const std::vector<std::string> &txids) = 0;
+
virtual TransactionHistory * history() = 0;
virtual AddressBook * addressBook() = 0;
virtual Subaddress * subaddress() = 0;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index aa95fa01e..ed153d681 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -2209,7 +2209,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time);
+ m_callback->on_money_received(height, txid, tx, td.m_amount, 0, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time);
}
total_received_1 += amount;
notify = true;
@@ -2243,7 +2243,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
tx_money_got_in_outs[tx_scan_info[o].received->index] -= m_transfers[kit->second].amount();
uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
- uint64_t extra_amount = amount - m_transfers[kit->second].amount();
+ uint64_t burnt = m_transfers[kit->second].amount();
+ uint64_t extra_amount = amount - burnt;
if (!pool)
{
transfer_details &td = m_transfers[kit->second];
@@ -2286,7 +2287,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time);
+ m_callback->on_money_received(height, txid, tx, td.m_amount, burnt, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time);
}
total_received_1 += extra_amount;
notify = true;
@@ -3160,14 +3161,18 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
}
}
- // get those txes
- if (!txids.empty())
+ // get_transaction_pool_hashes.bin may return more transactions than we're allowed to request in restricted mode
+ const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp
+ for (size_t offset = 0; offset < txids.size(); offset += SLICE_SIZE)
{
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
- for (const auto &p: txids)
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first));
- MDEBUG("asking for " << txids.size() << " transactions");
+
+ const size_t n_txids = std::min<size_t>(SLICE_SIZE, txids.size() - offset);
+ for (size_t n = offset; n < (offset + n_txids); ++n) {
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txids.at(n).first));
+ }
+ MDEBUG("asking for " << req.txs_hashes.size() << " transactions");
req.decode_as_json = false;
req.prune = true;
@@ -3184,7 +3189,7 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
MDEBUG("Got " << r << " and " << res.status);
if (r && res.status == CORE_RPC_STATUS_OK)
{
- if (res.txs.size() == txids.size())
+ if (res.txs.size() == req.txs_hashes.size())
{
for (const auto &tx_entry: res.txs)
{
@@ -3220,7 +3225,7 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
}
else
{
- LOG_PRINT_L0("Expected " << txids.size() << " tx(es), got " << res.txs.size());
+ LOG_PRINT_L0("Expected " << n_txids << " out of " << txids.size() << " tx(es), got " << res.txs.size());
}
}
else
@@ -3623,32 +3628,7 @@ bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& rece
//----------------------------------------------------------------------------------------------------
bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
{
- uint32_t rpc_version;
- boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
- // no error
- if (!!result)
- {
- // empty string -> not connection
- THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion");
- THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion");
- if (*result != CORE_RPC_STATUS_OK)
- {
- MDEBUG("Cannot determine daemon RPC version, not requesting rct distribution");
- return false;
- }
- }
- else
- {
- if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 19))
- {
- MDEBUG("Daemon is recent enough, requesting rct distribution");
- }
- else
- {
- MDEBUG("Daemon is too old, not requesting rct distribution");
- return false;
- }
- }
+ MDEBUG("Requesting rct distribution");
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response res = AUTO_VAL_INIT(res);
@@ -12188,7 +12168,8 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::key_derivation derivation;
THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(proof.shared_secret, rct::rct2sk(rct::I), derivation), error::wallet_internal_error, "Failed to generate key derivation");
crypto::public_key subaddr_spendkey;
- crypto::derive_subaddress_public_key(output_public_key, derivation, proof.index_in_tx, subaddr_spendkey);
+ THROW_WALLET_EXCEPTION_IF(!crypto::derive_subaddress_public_key(output_public_key, derivation, proof.index_in_tx, subaddr_spendkey),
+ error::wallet_internal_error, "Failed to derive subaddress public key");
THROW_WALLET_EXCEPTION_IF(subaddr_spendkeys.count(subaddr_spendkey) == 0, error::wallet_internal_error,
"The address doesn't seem to have received the fund");
@@ -14144,43 +14125,6 @@ uint64_t wallet2::get_segregation_fork_height() const
if (m_segregation_height > 0)
return m_segregation_height;
- if (m_use_dns && !m_offline)
- {
- // All four MoneroPulse domains have DNSSEC on and valid
- static const std::vector<std::string> dns_urls = {
- "segheights.moneropulse.org",
- "segheights.moneropulse.net",
- "segheights.moneropulse.co",
- "segheights.moneropulse.se"
- };
-
- const uint64_t current_height = get_blockchain_current_height();
- uint64_t best_diff = std::numeric_limits<uint64_t>::max(), best_height = 0;
- std::vector<std::string> records;
- if (tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
- {
- for (const auto& record : records)
- {
- std::vector<std::string> fields;
- boost::split(fields, record, boost::is_any_of(":"));
- if (fields.size() != 2)
- continue;
- uint64_t height;
- if (!string_tools::get_xtype_from_string(height, fields[1]))
- continue;
-
- MINFO("Found segregation height via DNS: " << fields[0] << " fork height at " << height);
- uint64_t diff = height > current_height ? height - current_height : current_height - height;
- if (diff < best_diff)
- {
- best_diff = diff;
- best_height = height;
- }
- }
- if (best_height)
- return best_height;
- }
- }
return SEGREGATION_FORK_HEIGHT;
}
//----------------------------------------------------------------------------------------------------
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index ae9b83a28..aa00d6c03 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -137,7 +137,7 @@ private:
public:
// Full wallet callbacks
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
- virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {}
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, uint64_t burnt, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {}
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
diff --git a/utils/fish/README.md b/utils/fish/README.md
new file mode 100644
index 000000000..7d7c58c28
--- /dev/null
+++ b/utils/fish/README.md
@@ -0,0 +1,2 @@
+## Fish shell completions for Monero
+This folder has basic Fish completions for `monerod`, `monero-wallet-cli`, and `monero-wallet-rpc`. To use them, put those files (or symlink them) inside `~/.config/fish/completions/` or wherever your Fish completion files are (see [https://fishshell.com/docs/current/completions.html#where-to-put-completions](https://fishshell.com/docs/current/completions.html#where-to-put-completions))
diff --git a/utils/fish/monero-wallet-cli.fish b/utils/fish/monero-wallet-cli.fish
new file mode 100644
index 000000000..c9c878dba
--- /dev/null
+++ b/utils/fish/monero-wallet-cli.fish
@@ -0,0 +1,61 @@
+complete -c monero-wallet-cli -f
+
+complete -c monero-wallet-cli -l help -d "Produce help message"
+complete -c monero-wallet-cli -l version -d "Output version information"
+complete -c monero-wallet-cli -l daemon-address -r -d "Use daemon instance at <host>:<port>"
+complete -c monero-wallet-cli -l daemon-host -r -d "Use daemon instance at host <arg> instead of localhost"
+complete -c monero-wallet-cli -l proxy -r -d "[<ip>:]<port> socks proxy to use for daemon connections"
+complete -c monero-wallet-cli -l trusted-daemon -d "Enable commands which rely on a trusted"
+complete -c monero-wallet-cli -l untrusted-daemon -d "Disable⋅commands⋅which⋅rely⋅on⋅a trusted daemon"
+complete -c monero-wallet-cli -l password -r -d "Wallet⋅password⋅(escape/quote⋅as needed)"
+complete -c monero-wallet-cli -l password-file -r -F -d "Wallet⋅password⋅file"
+complete -c monero-wallet-cli -l daemon-port -r -d "Use daemon instance at port <arg> instead of 18081"
+complete -c monero-wallet-cli -l daemon-login -r -d "Specify username[:password] for daemon RPC client"
+complete -c monero-wallet-cli -l daemon-ssl -x -a "enabled disabled autodetect" -d "Enable SSL on daemon RPC connections. Default: autodetect"
+complete -c monero-wallet-cli -l daemon-ssl-private-key -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format private key"
+complete -c monero-wallet-cli -l daemon-ssl-certificate -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format certificate"
+complete -c monero-wallet-cli -l daemon-ssl-ca-certificates -r -F -d "Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."
+complete -c monero-wallet-cli -l daemon-ssl-allowed-fingerprints -r -d "List of valid fingerprints of allowed RPC servers"
+complete -c monero-wallet-cli -l daemon-ssl-allow-any-cert -d "Allow any SSL certificate from the daemon"
+complete -c monero-wallet-cli -l daemon-ssl-allow-chained -d "Allow user (via --daemon-ssl-ca-certifi cates) chain certificates"
+complete -c monero-wallet-cli -l testnet -d "For testnet. Daemon must also be launched with --testnet flag"
+complete -c monero-wallet-cli -l stagenet -d "For stagenet. Daemon must also be launched with --stagenet flag"
+complete -c monero-wallet-cli -l shared-ringdb-dir -r -F -d "Set shared ring database path"
+complete -c monero-wallet-cli -l kdf-rounds -r -d "Number of rounds for the key derivation function. Default: 1"
+complete -c monero-wallet-cli -l bitmessage-address -r -d "Use PyBitmessage instance at URL <arg>. Default: http://localhost:8442/"
+complete -c monero-wallet-cli -l bitmessage-login -r -d "Specify <arg> as username:password for PyBitmessage API. Default: username:password"
+complete -c monero-wallet-cli -l hw-device -r -d "HW device to use"
+complete -c monero-wallet-cli -l hw-device-deriv-path -r -d "HW device wallet derivation path (e.g., SLIP-10)"
+complete -c monero-wallet-cli -l tx-notify -r -d "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash"
+complete -c monero-wallet-cli -l no-dns -d "Do not use DNS"
+complete -c monero-wallet-cli -l offline -d "Do not connect to a daemon, nor use DNS"
+complete -c monero-wallet-cli -l extra-entropy -r -F -d "File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, which typically means more than 256 bits of data)"
+complete -c monero-wallet-cli -l wallet-file -r -F -d "Use wallet <arg>"
+complete -c monero-wallet-cli -l generate-new-wallet -r -F -d "Generate new wallet and save it to <arg>"
+complete -c monero-wallet-cli -l generate-from-device -r -F -d "Generate new wallet from device and save it to <arg>"
+complete -c monero-wallet-cli -l generate-from-view-key -r -d "Generate incoming-only wallet from view key"
+complete -c monero-wallet-cli -l generate-from-spend-key -r -d "Generate deterministic wallet from spend key"
+complete -c monero-wallet-cli -l generate-from-keys -r -d "Generate wallet from private keys"
+complete -c monero-wallet-cli -l generate-from-multisig-keys -r -d "Generate a master wallet from multisig wallet keys"
+complete -c monero-wallet-cli -l generate-from-json -r -k -a "(__fish_complete_suffix .json)" -d "Generate wallet from JSON format file"
+complete -c monero-wallet-cli -l mnemonic-language -r -d "Language for mnemonic"
+complete -c monero-wallet-cli -l command -d ""
+complete -c monero-wallet-cli -l restore-deterministic-wallet -d "Recover wallet using Electrum-style mnemonic seed"
+complete -c monero-wallet-cli -l restore-from-seed -d "alias for --restore-deterministic-wallet"
+complete -c monero-wallet-cli -l restore-multisig-wallet -d "Recover multisig wallet using Electrum-style mnemonic seed"
+complete -c monero-wallet-cli -l non-deterministic -d "Generate non-deterministic view and spend keys"
+complete -c monero-wallet-cli -l electrum-seed -r -d "Specify Electrum seed for wallet recovery/creation"
+complete -c monero-wallet-cli -l allow-mismatched-daemon-version -d "Allow communicating with a daemon that uses a different RPC version"
+complete -c monero-wallet-cli -l restore-height -r -d "Restore from specific blockchain height. Default: 0"
+complete -c monero-wallet-cli -l restore-date -r -d "Restore from estimated blockchain height on specified date"
+complete -c monero-wallet-cli -l do-not-relay -d "The newly created transaction will not be relayed to the monero network"
+complete -c monero-wallet-cli -l create-address-file -d "Create an address file for new wallets"
+complete -c monero-wallet-cli -l subaddress-lookahead -r -d "Set subaddress lookahead sizes to <major>:<minor>"
+complete -c monero-wallet-cli -l use-english-language-names -d "Display English language names"
+complete -c monero-wallet-cli -l rpc-client-secret-key -r -d "Set RPC client secret key for RPC payments"
+complete -c monero-wallet-cli -l log-file -r -F -d "Specify log file"
+complete -c monero-wallet-cli -l log-level -r -a "0 1 2 3 4" -d "0-4 or categories"
+complete -c monero-wallet-cli -l max-log-file-size -r -d "Specify maximum log file size [B]. Default: 104850000"
+complete -c monero-wallet-cli -l max-log-files -r -d "Specify maximum number of rotated log files to be saved (no limit by setting to 0). Default: 50"
+complete -c monero-wallet-cli -l max-concurrency -d "Max number of threads to use for a parallel job. Default: 1"
+complete -c monero-wallet-cli -l config-file -r -F -d "Config file"
diff --git a/utils/fish/monero-wallet-rpc.fish b/utils/fish/monero-wallet-rpc.fish
new file mode 100644
index 000000000..a64e112ef
--- /dev/null
+++ b/utils/fish/monero-wallet-rpc.fish
@@ -0,0 +1,65 @@
+complete -c monero-wallet-rpc -f
+
+complete -c monero-wallet-rpc -l help -d "Produce help message"
+complete -c monero-wallet-rpc -l version -d "Output version information "
+complete -c monero-wallet-rpc -l daemon-address -r -d "Use daemon instance at <host>:<port>"
+complete -c monero-wallet-rpc -l daemon-host -r -d "Use daemon instance at host <arg> instead of localhost"
+complete -c monero-wallet-rpc -l proxy -r -d "[<ip>:]<port> socks proxy to use for daemon connections"
+complete -c monero-wallet-rpc -l trusted-daemon -d "Enable commands which rely on a trusted daemon"
+complete -c monero-wallet-rpc -l untrusted-daemon -d "Disable commands which rely on a trusted daemon"
+complete -c monero-wallet-rpc -l password -r -d "Wallet password (escape/quote as needed)"
+complete -c monero-wallet-rpc -l password-file -r -F -d "Wallet password file"
+complete -c monero-wallet-rpc -l daemon-port -r -d "Use daemon instance at port <arg> instead of 18081. Default: 0"
+complete -c monero-wallet-rpc -l daemon-login -r -d "Specify username[:password] for daemon RPC client"
+complete -c monero-wallet-rpc -l daemon-ssl -x -a "enabled disabled autodetect" -d "Enable SSL on daemon RPC connections. Default: autodetect"
+complete -c monero-wallet-rpc -l daemon-ssl-private-key -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format private key"
+complete -c monero-wallet-rpc -l daemon-ssl-certificate -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format certificate"
+complete -c monero-wallet-rpc -l daemon-ssl-ca-certificates -r -F -d "Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."
+complete -c monero-wallet-rpc -l daemon-ssl-allowed-fingerprints -r -d "List of valid fingerprints of allowed RPC servers"
+complete -c monero-wallet-rpc -l daemon-ssl-allow-any-cert -d "Allow any SSL certificate from the daemon"
+complete -c monero-wallet-rpc -l daemon-ssl-allow-chained -d "Allow user (via --daemon-ssl-ca-certifi cates) chain certificates"
+complete -c monero-wallet-rpc -l testnet -d "For testnet. Daemon must also be launched with --testnet flag"
+complete -c monero-wallet-rpc -l stagenet -d "For stagenet. Daemon must also be launched with --stagenet flag"
+complete -c monero-wallet-rpc -l shared-ringdb-dir -r -F -d "Set shared ring database path"
+complete -c monero-wallet-rpc -l kdf-rounds -r -d "Number of rounds for the key derivation function. Default: 1"
+complete -c monero-wallet-rpc -l bitmessage-address -r -d "Use PyBitmessage instance at URL <arg>. Default: http://localhost:8442/"
+complete -c monero-wallet-rpc -l bitmessage-login -r -d "Specify <arg> as username:password for PyBitmessage API. Default: username:password"
+complete -c monero-wallet-rpc -l hw-device -r -d "HW device to use"
+complete -c monero-wallet-rpc -l hw-device-deriv-path -r -d "HW device wallet derivation path (e.g., SLIP-10)"
+complete -c monero-wallet-rpc -l tx-notify -r -d "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash"
+complete -c monero-wallet-rpc -l no-dns -d "Do not use DNS"
+complete -c monero-wallet-rpc -l offline -d "Do not connect to a daemon, nor use DNS"
+complete -c monero-wallet-rpc -l extra-entropy -r -F -d "File containing extra entropy to initialize the PRNG (any data, aim for 256 bits of entropy to be useful, which typically means more than 256 bits of data)"
+complete -c monero-wallet-rpc -l rpc-bind-port -r -d "Sets bind port for server"
+complete -c monero-wallet-rpc -l disable-rpc-login -d "Disable HTTP authentication for RPC connections served by this process"
+complete -c monero-wallet-rpc -l restricted-rpc -d "Restricts to view-only commands"
+complete -c monero-wallet-rpc -l rpc-bind-ip -r -d "Specify IP to bind RPC server. Default: 127.0.0.1"
+complete -c monero-wallet-rpc -l rpc-bind-ipv6-address -r -d "Specify IPv6 address to bind RPC server. Default: ::1"
+complete -c monero-wallet-rpc -l rpc-restricted-bind-ip -r -d "Specify IP to bind restricted RPC server. Default: 127.0.0.1"
+complete -c monero-wallet-rpc -l rpc-restricted-bind-ipv6-address -r -d "Specify IPv6 address to bind restricted RPC server. Default: ::1"
+complete -c monero-wallet-rpc -l rpc-use-ipv6 -d "Allow IPv6 for RPC"
+complete -c monero-wallet-rpc -l rpc-ignore-ipv4 -d "Ignore unsuccessful IPv4 bind for RPC"
+complete -c monero-wallet-rpc -l rpc-login -r -d "Specify username[:password] required for RPC server"
+complete -c monero-wallet-rpc -l confirm-external-bind -d "Confirm rpc-bind-ip value is NOT a loopback (local) IP"
+complete -c monero-wallet-rpc -l rpc-access-control-origins -r -d "Specify a comma separated list of origins to allow cross origin resource sharing"
+complete -c monero-wallet-rpc -l rpc-ssl -x -a "enabled disabled autodetect" -d "Enable SSL on RPC connections. Default: autodetect"
+complete -c monero-wallet-rpc -l rpc-ssl-private-key -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format private key"
+complete -c monero-wallet-rpc -l rpc-ssl-certificate -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format certificate"
+complete -c monero-wallet-rpc -l rpc-ssl-ca-certificates -r -F -d "Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."
+complete -c monero-wallet-rpc -l rpc-ssl-allowed-fingerprints -r -d "List of certificate fingerprints to allow"
+complete -c monero-wallet-rpc -l rpc-ssl-allow-chained -d "Allow user (via --rpc-ssl-certificates) chain certificates"
+complete -c monero-wallet-rpc -l disable-rpc-ban -d "Do not ban hosts on RPC errors"
+complete -c monero-wallet-rpc -l wallet-file -r -F -d "Use wallet <arg>"
+complete -c monero-wallet-rpc -l generate-from-json -r -k -a "(__fish_complete_suffix .json)" -d "Generate wallet from JSON format file"
+complete -c monero-wallet-rpc -l wallet-dir -r -F -d "Directory for newly created wallets"
+complete -c monero-wallet-rpc -l prompt-for-password -d "Prompts for password when not provided"
+complete -c monero-wallet-rpc -l rpc-client-secret-key -r -d "Set RPC client secret key for RPC payments"
+complete -c monero-wallet-rpc -l detach -d "Run as daemon"
+complete -c monero-wallet-rpc -l pidfile -r -F -d "File path to write the daemon's PID to (optional, requires --detach)"
+complete -c monero-wallet-rpc -l non-interactive -d "Run non-interactive"
+complete -c monero-wallet-rpc -l log-file -r -F -d "Specify log file"
+complete -c monero-wallet-rpc -l log-level -r -a "0 1 2 3 4" -d "0-4 or categories"
+complete -c monero-wallet-rpc -l max-log-file-size -r -d "Specify maximum log file size [B]. Default: 104850000"
+complete -c monero-wallet-rpc -l max-log-files -r -d "Specify maximum number of rotated log files to be saved (no limit by setting to 0). Default: 50"
+complete -c monero-wallet-rpc -l max-concurrency -d "Max number of threads to use for a parallel job. Default: 1"
+complete -c monero-wallet-rpc -l config-file -r -F -d "Config file"
diff --git a/utils/fish/monerod.fish b/utils/fish/monerod.fish
new file mode 100644
index 000000000..d2836a6b2
--- /dev/null
+++ b/utils/fish/monerod.fish
@@ -0,0 +1,113 @@
+complete -c monerod -f
+
+complete -c monerod -l help -d "Produce help message"
+complete -c monerod -l version -d "Output version information"
+complete -c monerod -l os-version -d "OS for which this executable was compiled"
+complete -c monerod -l config-file -r -d "Specify configuration file"
+complete -c monerod -l detach -d "Run as daemon"
+complete -c monerod -l pidfile -r -F -d "File path to write the daemon's PID to (optional, requires --detach)"
+complete -c monerod -l non-interactive -d "Run non-interactive"
+complete -c monerod -l log-file -r -F -d "Specify log file"
+complete -c monerod -l log-level -r -d ""
+complete -c monerod -l max-log-file-size -r -d "Specify maximum log file size [B]. Default: 104850000"
+complete -c monerod -l max-log-files -r -d "Specify maximum number of rotated log files to be saved (no limit by setting to 0). Default: 50"
+complete -c monerod -l max-concurrency -r -d "Max number of threads to use for a parallel job. Default: 0"
+complete -c monerod -l proxy -r -d "Network communication through proxy: <socks-ip:port> i.e. \"127.0.0.1:9050\""
+complete -c monerod -l proxy-allow-dns-leaks -d "Allow DNS leaks outside of proxy"
+complete -c monerod -l public-node -d "Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P"
+complete -c monerod -l zmq-rpc-bind-ip -r -d "IP for ZMQ RPC server to listen on. Default: 127.0.0.1"
+complete -c monerod -l zmq-rpc-bind-port -r -d "Port for ZMQ RPC server to listen on. Default: 18082, 28082 if 'testnet', 38082 if 'stagenet'"
+complete -c monerod -l zmq-pub -r -d "Address for ZMQ pub - tcp://ip:port or ipc://path "
+complete -c monerod -l no-zmq -d "Disable ZMQ RPC server [114/349]"
+complete -c monerod -l data-dir -x -a "(__fish_complete_directories)" -d "Specify data directory"
+complete -c monerod -l test-drop-download -d "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"
+complete -c monerod -l test-drop-download-height -r -d "Like test-drop-download but discards only after around certain height. Default: 0"
+complete -c monerod -l testnet -d "Run on testnet. The wallet must be launched with --testnet flag."
+complete -c monerod -l stagenet -d "Run on stagenet. The wallet must be launched with --stagenet flag."
+complete -c monerod -l regtest -d "Run in a regression testing mode."
+complete -c monerod -l keep-fakechain -d "Don't delete any existing database when in fakechain mode."
+complete -c monerod -l fixed-difficulty -r -d "Fixed difficulty used for testing. Default: 0"
+complete -c monerod -l enforce-dns-checkpointing -d "checkpoints from DNS server will be enforced"
+complete -c monerod -l prep-blocks-threads -r -d "Max number of threads to use when preparing block hashes in groups. Default: 4"
+complete -c monerod -l fast-block-sync -r -d "Sync up most of the way by using embedded, known block hashes. Default: 1"
+complete -c monerod -l show-time-stats -r -d "(=0) Show time-stats when processing blocks/txs and disk synchronization. Default: 0"
+complete -c monerod -l block-sync-size -r -d "(=0) How many blocks to sync at once during chain synchronization (0 = adaptive). Default: 0"
+complete -c monerod -l check-updates -x -a "disabled notify download update" -d "Check for new versions of monero. Default: notify"
+complete -c monerod -l fluffy-blocks -d "Relay blocks as fluffy blocks (obsolete, now default)"
+complete -c monerod -l no-fluffy-blocks -d "Relay blocks as normal blocks"
+complete -c monerod -l test-dbg-lock-sleep -r -d "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."
+complete -c monerod -l offline -d "Do not listen for peers, nor connect to any"
+complete -c monerod -l disable-dns-checkpoints -d "Do not retrieve checkpoints from DNS"
+complete -c monerod -l block-download-max-size -r -d "Set maximum size of block download queue in bytes (0 for default)"
+complete -c monerod -l sync-pruned-blocks -d "Allow syncing from nodes with only pruned blocks"
+complete -c monerod -l max-txpool-weight -r -d "Set maximum txpool weight in bytes. Default: 648000000"
+complete -c monerod -l block-notify -r -d "Run a program for each new block, '%s' will be replaced by the block hash"
+complete -c monerod -l prune-blockchain -d "Prune blockchain"
+complete -c monerod -l reorg-notify -r -d "Run a program for each reorg, '%s' will be replaced by the split height, '%h' will be replaced by the new blockchain height, '%n' will be replaced by the number of new blocks in the new chain, and '%d' will be replaced by the number of blocks discarded from the old chain"
+complete -c monerod -l block-rate-notify -r -d "Run a program when the block rate undergoes large fluctuations. This might be a sign of large amounts of hash rate going on and off the Monero network, and thus be of potential interest in predicting attacks. %t will be replaced by the number of minutes for the observation window, %b by the number of blocks observed within that window, and %e by the number of blocks that was expected in that window. It is suggested that this notification is used to automatically increase the number of confirmations required before a payment is acted upon."
+complete -c monerod -l keep-alt-blocks -d "Keep alternative blocks on restart"
+complete -c monerod -l extra-messages-file -r -F -d "Specify file for extra messages to include into coinbase transactions"
+complete -c monerod -l start-mining -r -d "Specify wallet address to mining for"
+complete -c monerod -l mining-threads -r -d "Specify mining threads count"
+complete -c monerod -l bg-mining-enable -d "Enable background mining"
+complete -c monerod -l bg-mining-ignore-battery -d "If true, assumes plugged in when unable to query system power status"
+complete -c monerod -l bg-mining-min-idle-interval -r -d "Specify min lookback interval in seconds for determining idle state"
+complete -c monerod -l bg-mining-idle-threshold -r -d "Specify minimum avg idle percentage over lookback interval"
+complete -c monerod -l bg-mining-miner-target -r -d "Specify maximum percentage cpu use by miner(s)"
+complete -c monerod -l db-sync-mode -r -d "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[<nblo cks_per_sync>[blocks]|<nbytes_per_sync> [bytes]]. Default: fast:async:250000000bytes"
+complete -c monerod -l db-salvage -d "Try to salvage a blockchain database if it seems corrupted"
+complete -c monerod -l p2p-bind-ip -r -d "Interface for p2p network protocol (IPv4). Default: 0.0.0.0"
+complete -c monerod -l p2p-bind-ipv6-address -r -d "Interface for p2p network protocol (IPv6). Default: ::"
+complete -c monerod -l p2p-bind-port -r -d "Port for p2p network protocol (IPv4). Default: 18080, 28080 if 'testnet', 38080 if 'stagenet'"
+complete -c monerod -l p2p-bind-port-ipv6 -d "Port for p2p network protocol (IPv6). Default: 18080, 28080 if 'testnet', 38080 if 'stagenet'"
+complete -c monerod -l p2p-use-ipv6 -d "Enable IPv6 for p2p"
+complete -c monerod -l p2p-ignore-ipv4 -d "Ignore unsuccessful IPv4 bind for p2p"
+complete -c monerod -l p2p-external-port -r -d "External port for p2p network protocol (if port forwarding used with NAT). Default: 0"
+complete -c monerod -l allow-local-ip -d "Allow local ip add to peer list, mostly in debug purposes"
+complete -c monerod -l add-peer -r -d "Manually add peer to local peerlist"
+complete -c monerod -l add-priority-node -r -d "Specify list of peers to connect to and attempt to keep the connection open"
+complete -c monerod -l add-exclusive-node -r -d "Specify list of peers to connect to only. If this option is given the options add-priority-node and seed-node are ignored"
+complete -c monerod -l seed-node -r -d "Connect to a node to retrieve peer addresses, and disconnect"
+complete -c monerod -l tx-proxy -r -d "Send local txes through proxy: <network-type>,<socks-ip:port>[,max_con nections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""
+complete -c monerod -l anonymous-inbound -r -d "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""
+complete -c monerod -l ban-list -r -F -d "Specify ban list file, one IP address per line"
+complete -c monerod -l hide-my-port -d "Do not announce yourself as peerlist candidate"
+complete -c monerod -l no-sync -d "Don't synchronize the blockchain with other peers"
+complete -c monerod -l enable-dns-blocklist -d "Apply realtime blocklist from DNS"
+complete -c monerod -l no-igd -d "Disable UPnP port mapping"
+complete -c monerod -l igd -r -a "Enabled disabled enabled" -d "UPnP port mapping. Default: delayed"
+complete -c monerod -l out-peers -r -d "Set max number of out peers. Default: -1"
+complete -c monerod -l in-peers -r -d "Set max number of in peers. Default: -1"
+complete -c monerod -l tos-flag -r -d "Set TOS flag. Default: -1"
+complete -c monerod -l limit-rate-up -r -d "Set limit-rate-up [kB/s]. Default: 2048"
+complete -c monerod -l limit-rate-down -r -d "Set limit-rate-down [kB/s]. Default: 8192"
+complete -c monerod -l limit-rate -r -d "Set limit-rate [kB/s]. Default: -1"
+complete -c monerod -l pad-transactions -d "Pad relayed transactions to help defend against traffic volume analysis"
+complete -c monerod -l max-connections-per-ip -r -d "Maximum number of connections allowed from the same IP address. Default: 1"
+complete -c monerod -l rpc-bind-port -r -d "Port for RPC server. Default: 18081, 28081 if 'testnet', 38081 if 'stagenet'"
+complete -c monerod -l rpc-restricted-bind-port -r -d "Port for restricted RPC server"
+complete -c monerod -l restricted-rpc -d "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls"
+complete -c monerod -l bootstrap-daemon-address -r -d "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced. Use 'auto' to enable automatic public nodes discovering and bootstrap daemon switching"
+complete -c monerod -l bootstrap-daemon-login -r -d "Specify username:password for the bootstrap daemon login"
+complete -c monerod -l bootstrap-daemon-proxy -r -d "<ip>:<port> socks proxy to use for bootstrap daemon connections"
+complete -c monerod -l rpc-bind-ip -r -d "Specify IP to bind RPC server. Default: 127.0.0.1"
+complete -c monerod -l rpc-bind-ipv6-address -r -d "Specify IPv6 address to bind RPC server. Default: ::1"
+complete -c monerod -l rpc-restricted-bind-ip -r -d "Specify IP to bind restricted RPC server. Default: 127.0.0.1"
+complete -c monerod -l rpc-restricted-bind-ipv6-address -r -d "Specify IPv6 address to bind restricted RPC server. Default: ::1"
+complete -c monerod -l rpc-use-ipv6 -d "Allow IPv6 for RPC"
+complete -c monerod -l rpc-ignore-ipv4 -d "Ignore unsuccessful IPv4 bind for RPC"
+complete -c monerod -l rpc-login -d "Specify username[:password] required for RPC server"
+complete -c monerod -l confirm-external-bind -d "Confirm rpc-bind-ip value is NOT a loopback (local) IP"
+complete -c monerod -l rpc-access-control-origins -r -d "Specify a comma separated list of origins to allow cross origin resource sharing"
+complete -c monerod -l rpc-ssl -x -a "enabled disabled autodetect" -d "Enable SSL on RPC connections. Default: autodetect"
+complete -c monerod -l rpc-ssl-private-key -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format private key"
+complete -c monerod -l rpc-ssl-certificate -r -k -a "(__fish_complete_suffix .pem)" -d "Path to a PEM format certificate"
+complete -c monerod -l rpc-ssl-ca-certificates -r -F -d "Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."
+complete -c monerod -l rpc-ssl-allowed-fingerprints -r -d "List of certificate fingerprints to allow"
+complete -c monerod -l rpc-ssl-allow-chained -d "Allow user (via --rpc-ssl-certificates) chain certificates"
+complete -c monerod -l disable-rpc-ban -d "Do not ban hosts on RPC errors"
+complete -c monerod -l rpc-ssl-allow-any-cert -d "Allow any peer certificate"
+complete -c monerod -l rpc-payment-address -r -d "Restrict RPC to clients sending micropayment to this address"
+complete -c monerod -l rpc-payment-difficulty -r -d "Restrict RPC to clients sending micropayment at this difficulty. Default: 1000"
+complete -c monerod -l rpc-payment-credits -r -d "Restrict RPC to clients sending micropayment, yields that many credits per payment. Default: 100"
+complete -c monerod -l rpc-payment-allow-free-loopback -d "Allow free access from the loopback address (ie, the local host)"