This fixes a few issues surrounding required contributions:
- #1210 broke reserved registrations (including the operator stake)
because it wasn't properly recognizing a contribution into a reserved
slot.
- As part of the above, the overstaking protection now applies to the
reserved amount + any free contribution room. (So, if you have a
reserved spot of 8000 and there is 2000 free, but someone else fills
1000 of it before your stake gets mined, any stake above 9090 (9000 +
1%) will fall through without being locked up.
- The required contribution calculation was only taking into account
contributions but not unfilled, reserved contributions (issue #1137).
This fixes it to count unfilled, reserved spots as contributions for
the purposes of calculating the minimum contribution. This is applied
in the wallet immediately, but for the chain it is not enforced until
HF16.
- Eliminates some potential edge cases if a stake contains multiple
stake outputs. The wallet won't generate these, but they are
allowed on chain and could result in an incompletely staked service
node with no contributor slots remaining. The minimum contribution
math effectively assumes a single output for calculating the minimum
contribution (but if we have multiple outputs we would chew up two
slots). Disallow such stakes entirely beginning in HF16.
- Operator stake included in the registration tx had some weird edge
cases. The wallet wouldn't produce these, but they should be
disallowed at the blockchain level:
- multi-output stakes were allowed (as above).
- the registration stake only had to be >= 25%, even if the operator
reserved a larger amount.
- the registration stake could be for someone *other* than the
operator.
Beginning in HF16 the stake included in the registration must be >=
the operator-reserved amount, and must be staked by the operator
wallet, and must be a single output.
This changes the credit rules for recommission: instead of being
restored with no credit at all, with this you get restored with 2 blocks
of credit lost (up to all of your credit) for each block of decommission
time you had.
For example, if you had 1000 blocks of credit and are decommissioned for
150 blocks, you'll get recommissioned with 700 blocks of credit. If you
were decommissioned for 500 or more blocks then you'll get
decommissioned with 0 blocks of credit.
This applies the effects immediately rather than waiting for the hard
fork. Because this isn't a consensus-breaking change it seems easier to
not worry about the HF rules. The consequences are that the rules that
get applied to determining how much credit you have will depend on the
nodes involved in testing up until the hard fork: you *may* be subject
to the harsher v7 rules, or you may fall under v8 rules if enough of the
testing quorum has upgraded, but you'll never end up worse than the v7
rules.
This prevents staking transactions from being accepted if they overstake
the available contribution room by more than 1%. This is to prevent a
case that has happened a few times where there are competing partial
stakes submitted for the same SN at the same time (i.e. before a block
gets mined with the stakes). For example:
- Operator registers service node with 30% contribution
- Staker 1 submits stake with 40% contribution
- Staker 2 submits stake with 60% contribution
The wallet avoids stake 2 if the 40% has been accepted into a block, but
doesn't if it is still in the mempool. Later, when the contributions
get mined, both stakes are admitted because whichever one goes first
doesn't complete the stake, and the second one is still valid (since
there is a spot, and since it contributes >= the required amount).
Whichever stake gets added to a block second, however, will only be
counted as a contribution of the available amount. So, for example, if
stake 1 gets added first and then stake 2 gets added you'll end up with
an active service node of:
- operator has 30% contributed and locked
- staker 1 has 40% contributed and locked
- staker 2 has 30% contributed but 60% locked.
This commit adds an upper bound for an acceptable stake that is 101% of
the available contribution room so that, in the above situation,
whichever stake gets added first will be a contribution and the second
one will fall through as an ordinary transaction back to the staker's
wallet so that the staker the re-contribute the proper amount.
- pessimizing move in wallet2 prevents copy ellision
- various for loops were creating copies (clang-10 now warns about
this). Mostly this is because they had the type wrong when looping
through a map: the iterator type of a `map<K, V>` is `pair<const K,
V>` not `pair<K, V>`. Replaced them with C++17:
for (const auto& [key, val] : var)
which is so much nicer.
- cryptonote::core did not have a virtual destructor, but had virtual
methods (causing both a warning, and likely a crash if we ever have
something inheriting from it held in a unique_ptr<core>).
- core() constructor still had explicit even though it lost the single
argument.
- test code class had a `final` destructor but wasn't marked final. (It
also has a billion superfluous `virtual` declarations but I left them
in place because it's just test code).
The tx extra getting/setting API is a little non-DRY, so this dries it
out with some small changes:
- Added get_field_from_tx_extra that takes a specific tx_extra lvalue
reference and fetches it. This eliminates a bunch of nearly duplicate
code that calls parse - find - return.
- Add three new functions to add tagged data:
- add_tagged_data_to_tx_extra is a raw function that takes a
tag and string_view and appends it to tx_extra
- add_tx_extra<T>(tx_extra, val) lets you copy `val` directly as a
`T`. `val` does *not* have to be the same as `T` (`T` is not
deduced) but rather just has to have the same size. This makes it
very easy to add simple tx_extra values such as `tx_extra_pub_key`
from just a pub key without needing to wrap it first:
add_tx_extra<tx_extra_pub_key>(tx.extra, regular_old_pubkey);
- add_tx_extra<T>(tx, val) - very light wrapper around the above that
takes a transaction lvalue directly (rather than the extra vector)
for slight convenience.
These additions allow removing several now-unneeded methods and simplify
some code around the tx extra handling.
This commit also converts the TX_EXTRA_* macros into constexprs.
A huge amount of this is repetitive:
- `boost::get<T>(variant)` becomes `std::get<T>(variant)`
- `boost::get<T>(variant_ptr)` becomes `std::get_if<T>(variant_ptr)`
- `variant.type() == typeid(T)` becomes `std::holds_alternative<T>(variant)`
There are also some simplifications to visitors using simpler stl
visitors, or (simpler still) generic lambdas as visitors.
Also adds boost serialization serializers for std::variant and
std::optional.
Switch loki dev branch to C++17 compilation, and update the code with
various C++17 niceties.
- stop including the (deprecated) lokimq/string_view.h header and
instead switch everything to use std::string_view and `""sv` instead of
`""_sv`.
- std::string_view is much nicer than epee::span, so updated various
loki-specific code to use it instead.
- made epee "portable storage" serialization accept a std::string_view
instead of const lvalue std::string so that we can avoid copying.
- switched from mapbox::variant to std::variant
- use `auto [a, b] = whatever()` instead of `T1 a; T2 b; std::tie(a, b)
= whatever()` in a couple places (in the wallet code).
- switch to std::lock(...) instead of boost::lock(...) for simultaneous
lock acquisition. boost::lock() won't compile in C++17 mode when given
locks of different types.
- removed various pre-C++17 workarounds, e.g. for fold expressions,
unused argument attributes, and byte-spannable object detection.
- class template deduction means lock types no longer have to specify
the mutex, so `std::unique_lock<std::mutex> lock{mutex}` can become
`std::unique_lock lock{mutex}`. This will make switching any mutex
types (e.g. from boost to std mutexes) far easier as you just have to
update the type in the header and everything should work. This also
makes the tools::unique_lock and tools::shared_lock methods redundant
(which were a sort of poor-mans-pre-C++17 way to eliminate the
redundancy) so they are now gone and replaced with direct unique_lock or
shared_lock constructions.
- Redid the LNS validation using a string_view; instead of using raw
char pointers the code now uses a string view and chops off parts of the
view as it validates. So, for instance, it starts with "abcd.loki",
validates the ".loki" and chops the view to "abcd", then validates the
first character and chops to "bcd", validates the last and chops to
"bc", then can just check everything remaining for is-valid-middle-char.
- LNS validation gained a couple minor validation checks in the process:
- slightly tightened the requirement on lokinet addresses to require
that the last character of the mapped address is 'y' or 'o' (the
last base32z char holds only one significant bit).
- In parse_owner_to_generic_owner made sure that the owner value has
the correct size (otherwise we could up end not filling or
overfilling the pubkey buffer).
- Replaced base32z/base64/hex conversions with lokimq's versions which
have a nicer interface, are better optimized, and don't depend on epee.
* fix segfault when exiting cli wallet due to running poll thread
* fix whitespace
* style
* support for amount of blacklisted stakes in wallets
* bump version number for blacklist entries with amounts
* default initialize ints in key_image_blacklist_entry
* whitespace
High-level details:
This redesigns the RPC layer to make it much easier to work with,
decouples it from an embedded HTTP server, and gets the vast majority of
the RPC serialization and dispatch code out of a very commonly included
header.
There is unfortunately rather a lot of interconnected code here that
cannot be easily separated out into separate commits. The full details
of what happens here are as follows:
Major details:
- All of the RPC code is now in a `cryptonote::rpc` namespace; this
renames quite a bit to be less verbose: e.g. CORE_RPC_STATUS_OK
becomes `rpc::STATUS_OK`, and `cryptonote::COMMAND_RPC_SOME_LONG_NAME`
becomes `rpc::SOME_LONG_NAME` (or just SOME_LONG_NAME for code already
working in the `rpc` namespace).
- `core_rpc_server` is now completely decoupled from providing any
request protocol: it is now *just* the core RPC call handler.
- The HTTP RPC interface now lives in a new rpc/http_server.h; this code
handles listening for HTTP requests and dispatching them to
core_rpc_server, then sending the results back to the caller.
- There is similarly a rpc/lmq_server.h for LMQ RPC code; more details
on this (and other LMQ specifics) below.
- RPC implementing code now returns the response object and throws when
things go wrong which simplifies much of the rpc error handling. They
can throw anything; generic exceptions get logged and a generic
"internal error" message gets returned to the caller, but there is
also an `rpc_error` class to return an error code and message used by
some json-rpc commands.
- RPC implementing functions now overload `core_rpc_server::invoke`
following the pattern:
RPC_BLAH_BLAH::response core_rpc_server::invoke(RPC_BLAH_BLAH::request&& req, rpc_context context);
This overloading makes the code vastly simpler: all instantiations are
now done with a small amount of generic instantiation code in a single
.cpp rather than needing to go to hell and back with a nest of epee
macros in a core header.
- each RPC endpoint is now defined by the RPC types themselves,
including its accessible names and permissions, in
core_rpc_server_commands_defs.h:
- every RPC structure now has a static `names()` function that returns
the names by which the end point is accessible. (The first one is
the primary, the others are for deprecated aliases).
- RPC command wrappers define their permissions and type by inheriting
from special tag classes:
- rpc::RPC_COMMAND is a basic, admin-only, JSON command, available
via JSON RPC. *All* JSON commands are now available via JSON RPC,
instead of the previous mix of some being at /foo and others at
/json_rpc. (Ones that were previously at /foo are still there for
backwards compatibility; see `rpc::LEGACY` below).
- rpc::PUBLIC specifies that the command should be available via a
restricted RPC connection.
- rpc::BINARY specifies that the command is not JSON, but rather is
accessible as /name and takes and returns values in the magic epee
binary "portable storage" (lol) data format.
- rpc::LEGACY specifies that the command should be available via the
non-json-rpc interface at `/name` for backwards compatibility (in
addition to the JSON-RPC interface).
- some epee serialization got unwrapped and de-templatized so that it
can be moved into a .cpp file with just declarations in the .h. (This
makes a *huge* difference for core_rpc_server_commands_defs.h and for
every compilation unit that includes it which previously had to
compile all the serialization code and then throw all by one copy away
at link time). This required some new macros so as to not break a ton
of places that will use the old way putting everything in the headers;
The RPC code uses this as does a few other places; there are comments
in contrib/epee/include/serialization/keyvalue_serialization.h as to
how to use it.
- Detemplatized a bunch of epee/storages code. Most of it should have
have been using templates at all (because it can only ever be called
with one type!), and now it isn't. This broke some things that didn't
properly compile because of missing headers or (in one case) a messed
up circular dependency.
- Significantly simplified a bunch of over-templatized serialization
code.
- All RPC serialization definitions is now out of
core_rpc_server_commands_defs.h and into a single .cpp file
(core_rpc_server_commands_defs.cpp).
- core RPC no longer uses the disgusting
BEGIN_URI_MAP2/MAP_URI_BLAH_BLAH macros. This was a terrible design
that forced slamming tons of code into a common header that didn't
need to be there.
- epee::struct_init is gone. It was a horrible hack that instiated
multiple templates just so the coder could be so lazy and write
`some_type var;` instead of properly value initializing with
`some_type var{};`.
- Removed a bunch of useless crap from epee. In particular, forcing
extra template instantiations all over the place in order to nest
return objects inside JSON RPC values is no longer needed, as are a
bunch of stuff related to the above de-macroization of the code.
- get_all_service_nodes, get_service_nodes, and get_n_service_nodes are
now combined into a single `get_service_nodes` (with deprecated
aliases for the others), which eliminates a fair amount of
duplication. The biggest obstacle here was getting the requested
fields reference passed through: this is now done by a new ability to
stash a context in the serialization object that can be retrieved by a
sub-serialized type.
LMQ-specifics:
- The LokiMQ instance moves into `cryptonote::core` rather than being
inside cryptonote_protocol. Currently the instance is used both for
qnet and rpc calls (and so needs to be in a common place), but I also
intend future PRs to use the batching code for job processing
(replacing the current threaded job queue).
- rpc/lmq_server.h handles the actual LMQ-request-to-core-RPC glue.
Unlike http_server it isn't technically running the whole LMQ stack
from here, but the parallel name with http_server seemed appropriate.
- All RPC endpoints are supported by LMQ under the same names as defined
generically, but prefixed with `rpc.` for public commands and `admin.`
for restricted ones.
- service node keys are now always available, even when not running in
`--service-node` mode: this is because we want the x25519 key for
being able to offer CURVE encryption for lmq RPC end-points, and
because it doesn't hurt to have them available all the time. In the
RPC layer this is now called "get_service_keys" (with
"get_service_node_key" as an alias) since they aren't strictly only
for service nodes. This also means code needs to check
m_service_node, and not m_service_node_keys, to tell if it is running
as a service node. (This is also easier to notice because
m_service_node_keys got renamed to `m_service_keys`).
- Added block and mempool monitoring LMQ RPC endpoints: `sub.block` and
`sub.mempool` subscribes the connection for new block and new mempool
TX notifications. The latter can notify on just blink txes, or all
new mempool txes (but only new ones -- txes dumped from a block don't
trigger it). The client gets pushed a [`notify.block`, `height`,
`hash`] or [`notify.tx`, `txhash`, `blob`] message when something
arrives.
Minor details:
- rpc::version_t is now a {major,minor} pair. Forcing everyone to pack
and unpack a uint32_t was gross.
- Changed some macros to constexprs (e.g. CORE_RPC_ERROR_CODE_...).
(This immediately revealed a couple of bugs in the RPC code that was
assigning CORE_RPC_ERROR_CODE_... to a string, and it worked because
the macro allows implicit conversion to a char).
- De-templatizing useless templates in epee (i.e. a bunch of templated
types that were never invoked with different types) revealed a painful
circular dependency between epee and non-epee code for tor_address and
i2p_address. This crap is now handled in a suitably named
`net/epee_network_address_hack.cpp` hack because it really isn't
trivial to extricate this mess.
- Removed `epee/include/serialization/serialize_base.h`. Amazingly the
code somehow still all works perfectly with this previously vital
header removed.
- Removed bitrotted, unused epee "crypted_storage" and
"gzipped_inmemstorage" code.
- Replaced a bunch of epee::misc_utils::auto_scope_leave_caller with
LOKI_DEFERs. The epee version involves quite a bit more instantiation
and is ugly as sin. Also made the `loki::defer` class invokable for
some edge cases that need calling before destruction in particular
conditions.
- Moved the systemd code around; it makes much more sense to do the
systemd started notification as in daemon.cpp as late as possible
rather than in core (when we can still have startup failures, e.g. if
the RPC layer can't start).
- Made the systemd short status string available in the get_info RPC
(and no longer require building with systemd).
- during startup, print (only) the x25519 when not in SN mode, and
continue to print all three when in SN mode.
- DRYed out some RPC implementation code (such as set_limit)
- Made wallet_rpc stop using a raw m_wallet pointer
Call service_node_lists's block added hook function manually instead of
hooking the hook. There's a common operation between the 2 subsystems,
Loki Name Service and Service Node List, in which they generate state
based off incoming blocks from the blockchain.
Upon profiling, the slowest factor of this is retrieving the blocks from
the DB in batches of a 1000 rather than the processing of the blocks in
the subsystems. So flatten out the hierarchy so that we can retrieve the
batch and simultaneously process the blocks in both subsystems on the
same fetch request.
A complete removal of the hook system so we have more flexibility at the
callsite can be done in a later PR, to avoid too many unneccesary
changes in the LNS PR.
x25519 -> pubkey cache entries weren't being updated on new but
unchanged proofs, so would eventually expire and cause SNs to stop
authenticating new quorumnet connections as being from SNs.
The MacOSX 10.11 SDK we use is broken AF: it lies about supporting
C++14, but really only upgraded the headers but not the library itself,
so using std::shared_timed_mutex just results in a linking failure.
Upgrading the SDK is a huge pain (I tried, failed, and gave up), so for
now temporarily switch to boost::shared_mutex until we sort out the
macOS build disaster.
The m_blockchain lock added in #975 was causing deadlocks because we
have ordered `m_blockchain -> ... -> m_sn_mutex` lock sequences but
`proof.store()` was adding a `m_sn_mutex -> ... -> m_blockchain`
sequence when called when receiving an uptime proof. Fix it by also
taking out an m_blockchain lock where we take out the `m_sn_mutex`.
auto locks = tools::unique_locks(mutex1, mutex2, ...);
gives you a tuple of unique_locks and obtains the locks atomically.
auto lock = tools::unique_lock(lock1);
is essentially the same as:
std::unique_lock<decltype(lock1)> lock{lock1};
but less ugly (and extends nicely to the plural version).
Apparently it's not safe to use "transactions" in the LMDB without first
taking out an exclusive lock on the Blockchain class. Transactions
apparently aren't actually transactions, they are just fatal errors that
forever deadlock the database if you try to take two write transactions
at once.
Work around this terrible design by adding the appropriate magic
incantation.
This would wipe out proof data if the rescan crossed a registration, and
wipe out the timestamp if it crossed a decommission. We don't want to
do either when rescanning: our latest proof data would have already been
reset the first time we cross the registration/decomm transactions.
This extracts uptime proof data entirely from service node states,
instead storing (some) proof data as its own first-class object in the
code and backed by the database. We now persistently store:
- timestamp
- version
- ip & ports
- ed25519 pubkey
and update it every time a proof is received. Upon restart, we load all
the proofs from the database, which means we no longer lose last proof
received times on restart, and always have the most recently received ip
and port information (rather than only having whatever it was in the
most recently stored state, which could be ~12 blocks ago). It also
means we don't need to spam the network with proofs for up to half an
hour after a restart because we now know when we last sent (and
received) our own proof before restarting.
This separation required some restructuring: most notably changing
`state_t` to be constructed with a `service_node_list` pointer, which it
needs both directly and for BlockchainDB access. Related to this is
also eliminating the separate BlockchainDB pointer stored in
service_node_list in favour of just accessing it via m_blockchain (which
now has a new `has_db()` method to detect when it has been initialized).
Looking up by height currently goes via a mostly-empty implicitly
created state_t, which is wasteful but was necessary with C++11. C++14
`std::set`s let us avoid this by making `state_t` and heights directly
comparable for set lookups.
- Fix assert to use version_t::_count in service_node_list.cpp
- Incoporate staking changes from LNS which revamp construct tx to
derive more of the parameters from hf_version and type. This removes
extraneous parameters that can be derived elsewhere.
Also delegate setting loki_construct_tx_params into wallet2 atleast,
so that callers into the wallet logic, like rpc server, simplewallet
don't need bend backwards to get the HF version whereas the wallet has
dedicated functions for determining the HF.
- Don't use a trusty distro to load an ubuntu docker of a bionic image
which builds a bunch of dependencies that are already available as
system packages.
- Replace arm64 cross compiled build with native full build
- Remove armhf build; it's possible but not that simple and not that
useful for every build.
- Add s390x (big endian) and pp64le builds. These and the arm64 build
are built partially with -j1 and partially with -j2 because the build
environment for the non-amd64 arches only has 4GB memory, which is
barely enough for wallet2.cpp by itself.
- Build on bionic and xenial
- Add a clang build
- Add a debug build
- Build tests on main bionic build (don't run them; we can explore that
later)
- Do macos build on macos, and build on 10.13 and 10.14 (with the
intention of updating the latter to 10.15 once available on travis);
cross-compiling it from linux was stupid.
If you accept a block on the main chain due to an alternative quorum,
eventually that alternative quorum will get pruned as it corresponds to an
alternative block and not the current block.
This should not be accepted via the main chain, but instead the block
should be passed through the alt chain block added hook.
These are quite serious: if they occur there's a good chance that the
local SN state data is wrong. (At default logging they aren't printed
at all, and really should be).
This also fixes and adds more detail in the the "height is not stored"
error.
These are quite serious: if they occur there's a good chance that the
local SN state data is wrong. (At default logging they aren't printed
at all, and really should be).
This also fixes and adds more detail in the the "height is not stored"
error.
This adds a thread-local, pre-seeded rng at `tools::rng` (to avoid the
multiple places we are creating + seeding such an RNG currently).
This also moves the portable uniform value and generic shuffle code
there as well as neither function is specific to service nodes and this
seems a logical place for them.
We don't impose any alignment on hashable types, but this means the
hashing function is doing invalid misaligned access when converting to a
size_t. This aligns all of the primitive data types (crypto::hash,
public keys, etc.) to the same alignment as size_t.
That cascades into a few places in epee which only allow byte spanning
types that have byte alignment when what it really requires is just that
the type has no padding. In C++17 this is exactly the purpose of
std::has_unique_object_representations, but that isn't available (or
even implementable) in C++14 so add specializations for the type that
need it to tell epee that we know those types are properly packed and
that it can safely use them as bytes.
Related to this, monero/epee also misuses `is_standard_layout` when the
purpose is actually `is_trivially_copyable`, so fixed that too. (You
need the latter but don't need the former for a type to be safely
memcpy'able; the only purpose of `is_standard_layout` is when you need
to be sure your structs are compatible with C structs which is
irrelevant here).
This is the bulk of the work for blink. There is two pieces yet to come
which will follow shortly, which are: the p2p communication of blink
transactions (which needs to be fully synchronized, not just shared,
unlike regular mempool txes); and an implementation of fee burning.
Blink approval, multi-quorum signing, cli wallet and node support for
submission denial are all implemented here.
This overhauls and fixes various parts of the SNNetwork interface to fix
some issues (particularly around non-SN communication with SNs, which
wasn't working).
There are also a few sundry FIXME's and TODO's of other minor details
that will follow shortly under cleanup/testing/etc.
The owning pointer is stashed in a unique_ptr so that it gets properly
destroyed; everything else gets a plain pointer to it.
(Ideally it would be std::optional instead, but that's not until C++17)
Currently we store it as various different things: 3 separate ints, 2
u16s, 3 separate u16s, and a vector of u16s. This unifies all version
values to a `std::array<uint16_t,3>`.
- LOKI_VERSION_{MAJOR,MINOR,PATCH} are now just LOKI_VERSION
- The previous LOKI_VERSION (C-string of the version) is now renamed
LOKI_VERSION_STR
A related change included here is that the restricted RPC now returns
the major version in the get_info rpc call instead of an empty string
(e.g. "5" instead of ""). There is almost certainly enough difference
in the RPC results to distinguish major versions already so this doesn't
seem like it actually leaks anything significant.
This adds vote relaying via quorumnet.
- The main glue between existing code and quorumnet code is in
cryptonote_protocol/quorumnet.{h,cpp}
- Uses function pointers assigned at initialization to call into the
glue without making cn_core depend on p2p
- Adds ed25519 and quorumnet port to uptime proofs and serialization.
- Added a second uptime proof signature using the ed25519 key to that
SNs are proving they actually have it (otherwise they could spoof
someone else's pubkey).
- Adds quorumnet port, defaulting to zmq rpc port + 1. quorumnet
listens on the p2p IPv4 address (but broadcasts the `public_ip` to the
network).
- Derives x25519 when received or deserialized.
- Converted service_info_info::version_t into a scoped enum (with the
same underlying type).
- Added contribution_t::version_t scoped enum. It was using
service_info_info::version for a 0 value which seemed rather wrong.
Random small details:
- Renamed internal `arg_sn_bind_port` for consistency
- Moved default stagenet ports from 38153-38155 to 38056-38058, and add the default stagenet
quorumnet port at 38059 (this keeps the range contiguous; otherwise the next stagenet default port
would collide with the testnet p2p port).
This allows a `"if_block_not_equal": "hash"` parameter to be given to
`get_n_service_nodes` which, if the given value matches the current
top block hash, skips building and returning a reply.
This is primarily aimed at lokinet which polls fairly frequenty for an
update but where the vast majority of those polls contain no new
information.
It also removes the get_all_service_nodes_public_keys RPC request
completely as lokinet was the only consumer of it (particularly unlikely
that anyone else was using this because it was returning the keys
base32z-encoded).
Neither of these have a place in modern C++11; boost::value_initialized
is entirely superseded by `Type var{};` which does value initialization
(or default construction if a default constructor is defined). More
problematically, each `boost::value_initialized<T>` requires
instantiation of another wrapping templated type which is a pointless
price to pay the compiler in C++11 or newer.
Also removed is the AUTO_VAL_INIT macro (which is just a simple macro
around constructing a boost::value_initialized<T>).
BOOST_FOREACH is a similarly massive pile of code to implement
C++11-style for-each loops. (And bizarrely it *doesn't* appear to fall
back to C++ for-each loops even when under a C++11 compiler!)
This removes both entirely from the codebase.
If major/minor are uint8_t then printing them tries to print characters
rather than numbers. Changing them to uint16_t seems the easiest fix
(and agrees with the type we use for storing version parts in proofs).
This required a bit of reworking - without the duplication we only have
a const iterator so can't reuse it later for a non-const iterator, so
store the found contributor by index instead.
This generates a ed25519 keypair (and from it derives a x25519 keypair)
and broadcasts the ed25519 pubkey in HF13 uptime proofs.
This auxiliary key will be used both inside lokid (starting in HF14) in
places like the upcoming quorumnet code where we need a standard
pub/priv keypair that is usable in external tools (e.g. sodium) without
having to reimplement the incompatible (though still 25519-based) Monero
pubkey format.
This pulls it back into HF13 from the quorumnet code because the
generation code is ready now, and because there may be opportunities to
use this outside of lokid (e.g. in the storage server and in lokinet)
before HF14. Broadcasting it earlier also allows us to be ready to go
as soon as HF14 hits rather than having to wait for every node to have
sent a post-HF14 block uptime proof.
For a similar reason this adds a placeholder for the quorumnet port in
the uptime proof: currently the value is just set to 0 and ignored, but
allowing it to be passed will allow upgraded loki 6.x nodes to start
sending it to each other without having to wait for the fork height so
that they can start using it immediately when HF14 begins.
This puts the SN pubkey/privkey into a single struct (which have
ed25519/x25519 keys added to it in the future), which simplifies various
places to just pass the struct rather than storing and passing the
pubkey and privkey separately.
- Replace a bunch of repetitive proof rejection rules with a macro +
HF requirement version loop.
- Remove mostly unused MAX_KEY_IMAGES_PER_CONTRIBUTOR variable. It has
always been set to 1 (and so doesn't actually do anything). If we
wanted to increase it at this point we'd have to add multiple
HF-guarded versions of it and HF-version guard the code anyway, so
seems simpler to just drop it.
- Simplify the max contributor/max contribution logic and merge it in
with the existing contributor code (this is made easier by the above
dropped variable).
* Add a 1 atomic unit fudge factor on reward calculations
If the thread's rounding mode is different (because RandomX changed it
in the current thread or in the thread that generated the block) then
the reward calculation can be up to 1 atomic unit off. This lets those
pass rather than rejecting the block.
(I tested LOKI's reward calculations up to height 10M to verify that the
maximum error in reward values is never off by more than 1).
* Remove partial_block_reward
This variable is not actually used anywhere.
* Add 1ULP tolerance for SN reward checks
* Rewrite confusing logic and error
- fix the broken error message which was printing "base(base+fees)" instead
of "total(base+fees)".
- improve the error message wording to actually indicate what the values
signify.
- don't use base_reward as a temporary value (the intermediate value
assigned to it is completely replaced later anyway).
- use some better variable names to describe what is happening here
* Add check for overflow
This is so that we can validly print an error in quorum_cop on the
heights that are actually not being stored in the daemon VS a service
node network that has insufficient nodes to form the quorum.
Update core test to enforce this constraint
* Add temporary HF12/13 code to switch to validators for checkpointing
* Don't use target height for verifying vote age
You wouldn't have the quorum calculated to verify the vote in the first place.
* Update core tests to use validators for checkpoints
If a peer views the destination peer as not synchronizing, then the
destination peer should just accept the uptime proof, rather than accept
it and then conditionally relay it depending on whether or not you are
synchronizing at the point of attempting to relay (which you could
transition into synchronizing state interim between accepting and
attempting to relay the proof).
Otherwise we get into a ping-pong situation as follows
Node1 sends uptime ->
Node2 receives uptime and relays it back to Node1 for acknowledgement ->
Node1 receives it, handle_uptime_proof returns true to acknowledge ->
Node1 tries to resend to the same peers again
Instead, if we receive our own uptime proof, then acknowledge but don't
send on. If the we are missing an uptime proof it will have been
submitted automatically by the daemon itself instead of using my own
proof relayed by other nodes.
Remove using crypto, using cryptonote, using epee in chaingen
Add loki prefix to data structures, cleanup warts
Remove the need for get_static_keys(), remove useless functions and
attempt to remove some pointless function overloading that puts too many
levels of indirection and makes it harder to follow code execution.
Rehaul tests further, persist test generator when replaying through core
The class used to generate the testing events to replay through core is
now preserved. This subtle change simplifies a big chunk of core testing
in that we can remove the need for using callbacks to track events in
the events vector.
Add preliminary checkpoint/alt service node tests
Allow inlining of fail cases for test conditions
Add more checkpoint core tests
add_block in generator is parameterised with checkpoints
Add test to check chain with equal checkpoints
Add test for chain reorging when recieving enough votes for checkpoint
Move service node list methods to state_t methods
Add querying state from alt blocks and put key image parsing into function
Incorporate hash into state_t to find alt states
Add a way to query alternate testing quorums
Rebase cleanup
only_serialize_quorums should set only_store_quorums flag
Simplify detach logic since we don't need constants anymore
Move state storage comment to ::store
Fix off by one in detach on recent states
If we are re-deriving states we still also need the historical quorums
such that we can process incoming blocks with state changes. Without it,
state changes will be ignored and skipped causing inconsistent state in
the service node list.
This mandates a rescan and stores the most recent in state_ts, and only
just quorums for states preceeding 10k intervals. We can no longer store
the 1st most recent state in the DB as that will be missing quorums as
well for similar reason when rederiving on start-up.
When a node gets recommissioned in 4.0.4 we reset its timestamp to the
current time to delay obligations checks for newly recommissioned nodes,
but this reset caused problems:
- the code runs not only when a fresh block is received, but also when
syncing or rescanning, and so time(NULL) gets used to update the
node's timestamp even if it is an old record, and since proof info is
shared across states, the affects the current state.
- as a result of the above, a just-rescanned node that has been
decommissioned at some point in the past will think it has just sent a
proof, and so won't send any proofs for an hour.
- A just-rescanned node won't accept or relay any proofs for any node
that was recommissioned in its scan for the first half an hour, but this
lack of relaying can cause chaos in getting uptime proofs out across the
network, especially while we still have 4.0.3 nodes that need it.
To address the first issue, this switches the recommissioning to use the
block timestamp rather than the current timestamp. This *will* be
slightly delayed in the case of current blocks (since a block timestamp
is the time the pool *started* working on the block, which is generally
the time the previous block was found on the network), but even with an
exceptionally long block delay (e.g. 20 minutes) we are still fending
off obligations checks for 1h40m.
That would partially fix issues 2 and 3, but we actually don't want a
recommissioning to look like a received uptime proof for a couple
reasons:
- When we haven't actually received an uptime proof it's confusing to
report that we have (at the recommission time) and may mask an
underlying issue of a node that isn't actually sending proofs for some
reason (which might be more common for a node that has just been
decommissioned/recommissioned). There's also a related weird state
here for nodes that have come on recently: they think the SN is
active, but have 0's for IP and storage server port.
- 4.0.3 nodes don't get the updated timestamp and so really need the
proof to come through even when the 4.0.4 nodes don't think it's
important/acceptable.
So to also fix these, this commits adds an "effective_timestamp" to the
proof info: if it is larger than the actual timestamp field, we use it
instead of the actual one for obligations checking. On a recommission,
we update only the effective field so that we can delay obligations
checking for a couple of hours without delaying actual proofs info going
over the network.
If we are re-deriving states we still also need the historical quorums
such that we can process incoming blocks with state changes. Without it,
state changes will be ignored and skipped causing inconsistent state in
the service node list.
This mandates a rescan and stores the most recent in state_ts, and only
just quorums for states preceeding 10k intervals. We can no longer store
the 1st most recent state in the DB as that will be missing quorums as
well for similar reason when rederiving on start-up.
We can't delete all state change TX's on blockchain increment incase the
state change came from an alternative block. We must keep them around
even if they would conflict with another state change because if we
switch chains, they need to be present for the block to still be
considered valid.
Add a .size() check when checking for is_migrated_from_v403 because when
reinitialise is true, m_state_history does not necessarily have any
elements (in tests atleast).
This converts the stored service_node_info value into a
`shared_ptr<const service_node_info>` rather than a plain
`service_node_info`. This yields a huge performance benefit by
significantly eliminating the vast majority of service_node_info
construction, destruction, and copying.
Most of the time when we copy a service_node_info nothing in it has
changed, which means we're storing exactly the same thing; this means an
extra construction for every SN info on every block *and* an extra
destruction when we cull old stored history. By using a
shared_ptr, the vast majority of those constructions and destructions
are eliminated.
The immediately previous commit (upon which this one builds) already
reduced a full rescan from 180s to 171s; this commit further reduces
that time to 104s, or about 42% reduced from the rescan time required
before this pair of commits. (All timings are from the dev.lokinet.org
box, tested over multiple runs with the entire lmdb cached in memory).
With the shared_ptr approach, we only make a copy when a change is
actually needed: because of infrequent (at the per-SN level) events like
a state_change, received reward, contribution, etc. The contained
reference is deliberately `const` so that values are not changeable;
there's a new function that does an explicit copying duplication,
returning the new non-const and storing the const ref in the shared
pointer.
Related to this is a small change (and fix) to how proof info and
public_ip/storage_port are stored: rather than store the values in the
service_node_info struct itself, they now gets stored in a shared_ptr
inside the service_node_info that intentionally gets shared among all
copies of the service_node_info (that is, a SN info copy deliberately
copies the pointer rather than the values). This also moves the ip/port
values into the proof struct, since that seemed much easier than
maintaining a separate shared_ptr for each value.
Previously, because these were stored as values in the service_node_info
they would actually get rolled back in the event of a reorg, but that
seems highly undesirable: you would end up rolling back to the old
values of the uptime proof and ip address (for example), but that should
not happen: those values are not dependent on the blockchain and so
should not be affected by a reorg/rollback. With this change they
aren't since there is only one actual proof stored.
Note that the shared storage here only applies to in-memory states;
states loaded from the db will still be duplicated.
This commit makes various simplifications and optimizations, mainly in
the service node list code.
All in all, this shaves about 5% of the CPU time used for re-processing
the entire service node list.
In particular:
- changed m_state_history from a std::vector to a std::set that sorts on
height. This is responsible for the bulk of the CPU reduction by
significant reducing the amount of work required for checkpoint
culling, which has to shuffle a lot of `state_t`s around when removing
from the midde of a vector.
- the above also allows replacing the binary-search `std::lower_bound`
complexity with a much simpler `find()`.
- since the history is now always sorted, removed the error messages
related to sorting that either can't happen (on store) or don't matter
(on load).
- Added some converting constructors to simplify code (for example, a
`state_t` can now be very usefully constructed from an r-value
`state_serialized`).
- Many construct + moves (and a couple construct + copy) were replaced
with in-place constructions.
- removed some unused variables
Ordinarily on shut down we would discard the quorum states that are
converted and serialised once on initial start up with the new software.
If this is lost, we would not be able to process incoming votes or any
state changes and essentially halt without forcing a way to rescan the
service node list back to a consistent state.
* Update state transition check to account for height and universally set timestamp on recommission
Reject invalidated state changes by their height after HF13
* Prune invalidated state changes on blockchain increment
Simplify check_tx_inputs for state_changes by using service node list
Instead of querying the last 60 historical blocks for every transaction,
use the service node list and determine the state of the service node
and if it can transition to its new state.
We also now enforce at hardfork 13, that the network cannot commit
transactions to the network if they would have been invalidated by
a newer state change that already is already on the blockchain.
This is backwards compatible all the way back to hardfork 9.
Greatly simplify state change tx pruning on block added
Use the new stricter rules for pruning state changes in the txpool
We can do so because pruning the TX pool won't cause issues at the
protocol level, but the more people we can upgrade the better network
behaviour we get in terms of propagating more intrinsically correct
ordering of state changes to other peers.
* Don't generate state changes if not valid, disallow voting if node is non-votable
* Store the oldest state in the MAX_SHORT_TERM_STATE_HISTORY window
Rederive the rest on startup, this way we only need to store states for
10k intervals + 1 state every block temporarily removing the overhead of
having to serialise 30+(every 10k interval) states every block which was
taking upto 15ms~ per block on mainnet.
* Store quorums into the state class
This is setup work to allow only storing one service node list state and
rederiving the rest of the state at runtime in memory without having to
waste space storing the extra 360 quorums every block which is the
biggest bottleneck with syncing the blockchain adding around ~15ms per
block after Service Nodes are activated.
* Derive quorums using correct state, record duration of scan
* Don't fail load if quorums empty, save progress on rescan
Storing quorums in quorum states is now optional because quorums are
stored with the state objects. Quorums are only stored in the dedicated
data structure if you requested to store quorum history so for most
users this will be empty.
* Store state upto quorum lifetime to match previous behaviour
* Make store every 10k states more robust
* Corectly use lower bound to find quorum
* Code review
Increase the range to mitigate frequent rescanning when reorganising,
technically with checkpointing we only need up to the 2nd oldest
checkpoint, but I'm sure there's some case we're forgetting that would
benefit from a larger rollback range.
Also considering there were only 160 events in the month with a mass
deregistration- keeping 2 days worth of rollbacks around is not going to
be too expensive.
Start storing service node list state for past blocks
Separate quorums and reduce storage to 1 days worth
Don't use pointer, declare m_state by value
Fix rebase to dev
Fix double serialise of service node state and expensive blob copy
std::move quorums into storage, reserve vector size
Don't +1 checkpoint finality height
Code review
Code review 2
* Add disabled-by-default quorum storage support
This adds support for storing N blocks of recent expired quorum state
history in lokid (and dumping to/from the lmdb).
This isn't useful for regular nodes (and that's why we don't store it)
but is incredibly useful for the block explorer to be able to report
*which* node got deregistered/decommissioned/etc. by a given
state_change tx.
* Fix quorum states copy
As recent bugs have demonstrated, requiring the caller to have offset
the quorum by 11 is highly error prone. This commit moves the offset
into `get_testing_quorum` itself so that any caller asking for the
quorum for vote height 12345 just asks for it rather than having to
remember to subtract 11 to get at the correct generating block.
Because of how the serialization code works, these gets stored as
quorums with empty members, but we don't want them at all so just skip
loading them when the height says it should be empty.
This was causing mass checkpoint deregistrations for missed votes
because 3/4 of the checkpointing "quorums" being identified were not
(and not supposed to be) voted on.
* Only dupe check state changes against the latest valid change
* Check the service node info for dupe state change
* Gate dupe state changes behind HF12
* Actually properly gate dupe state change and revert breaking changes
* Use is_decommissioned() to get service node state, change msg log level
service_node_list::store() was called on destruction, but destruction
can happen before it is set up with the database pointer (hence the
segfault). Fix it to just silently return if not set up yet.
* Add deregistration of checkpoints by checking how many votes are missed
Move uptime proofs and add checkpoint counts in the service_node_list
because we typically prune uptime proofs by time, but it seems we want
to switch to a model where we persist proof data until the node expires
otherwise currently we would prune uptime entries and potentially our
checkpoint vote counts which would cause premature deregistration as the
expected vote counts start mismatching with the number of received
votes.
* Revise deregistration
* Fix test breakages
* uint16_t for port, remove debug false, min votes to 2 in integration mode
* Fix integration build
This adds a new obligations quorum vote "ip_change_penalty" that gets
triggered if the quorum has received multiple IPs advertised in uptime
proofs from a service node in the past 24 hours. Upon reception of such
a transaction the SN gets bumped to the bottom of the reward list.
The vast majority of the time `is_active()` is what we actually want.
Most of these are just a name change, but there is also one important
fix here to the next-winner list which needs to use active, not
fully-funded.
* core: do not commit half constructed batch db txn
* Add defer macro
* Revert dumb extra copy/move change
* Fix pop_blocks not calling hooks, fix BaseTestDB missing prototypes
* Merge ServiceNodeCheckpointing5 branch, syncing and integration fixes
* Update tests to compile with relaxed-registration changes
* Get back to feature parity pre-relaxed registration changes
* Remove debug changes noticed in code review and some small bugs
The replaces the deregistration mechanism with a new state change
mechanism (beginning at the v12 fork) which can change a service node's
network status via three potential values (and is extensible in the
future to handle more):
- deregistered -- this is the same as the existing deregistration; the
SN is instantly removed from the SN list.
- decommissioned -- this is a sort of temporary deregistration: your SN
remains in the service node list, but is removed from the rewards list
and from any network duties.
- recommissioned -- this tx is sent by a quorum if they observe a
decommissioned SN sending uptime proofs again. Upon reception, the SN
is reactivated and put on the end of the reward list.
Since this is broadening the quorum use, this also renames the relevant
quorum to a "obligations" quorum (since it validates SN obligations),
while the transactions are "state_change" transactions (since they
change the state of a registered SN).
The new parameters added to service_node_rules.h control how this works:
// Service node decommissioning: as service nodes stay up they earn "credits" (measured in blocks)
// towards a future outage. A new service node starts out with INITIAL_CREDIT, and then builds up
// CREDIT_PER_DAY for each day the service node remains active up to a maximum of
// DECOMMISSION_MAX_CREDIT.
//
// If a service node stops sending uptime proofs, a quorum will consider whether the service node
// has built up enough credits (at least MINIMUM): if so, instead of submitting a deregistration,
// it instead submits a decommission. This removes the service node from the list of active
// service nodes both for rewards and for any active network duties. If the service node comes
// back online (i.e. starts sending the required performance proofs again) before the credits run
// out then a quorum will reinstate the service node using a recommission transaction, which adds
// the service node back to the bottom of the service node reward list, and resets its accumulated
// credits to 0. If it does not come back online within the required number of blocks (i.e. the
// accumulated credit at the point of decommissioning) then a quorum will send a permanent
// deregistration transaction to the network, starting a 30-day deregistration count down.
This commit currently includes values (which are not necessarily
finalized):
- 8 hours (240 blocks) of credit required for activation of a
decommission (rather than a deregister)
- 0 initial credits at registration
- a maximum of 24 hours (720 blocks) of credits
- credits accumulate at a rate that you hit 24 hours of credits after 30
days of operation.
Miscellaneous other details of this PR:
- a new TX extra tag is used for the state change (including
deregistrations). The old extra tag has no version or type tag, so
couldn't be reused. The data in the new tag is slightly more
efficiently packed than the old deregistration transaction, so it gets
used for deregistrations (starting at the v12 fork) as well.
- Correct validator/worker selection required generalizing the shuffle
function to be able to shuffle just part of a vector. This lets us
stick any down service nodes at the end of the potential list, then
select validators by only shuffling the part of the index vector that
contains active service indices. Once the validators are selected, the
remainder of the list (this time including decommissioned SN indices) is
shuffled to select quorum workers to check, thus allowing decommisioned
nodes to be randomly included in the nodes to check without being
selected as a validator.
- Swarm recalculation was not quite right: swarms were recalculated on
SN registrations, even if those registrations were include shared node
registrations, but *not* recalculated on stakes. Starting with the
upgrade this behaviour is fixed (swarms aren't actually used currently
and aren't consensus-relevant so recalculating early won't hurt
anything).
- Details on decomm/dereg are added to RPC info and print_sn/print_sn_status
- Slightly improves the % of reward output in the print_sn output by
rounding it to two digits, and reserves space in the output string to
avoid excessive reallocations.
- Adds various debugging at higher debug levels to quorum voting (into
all of voting itself, vote transmission, and vote reception).
- Reset service node list internal data structure version to 0. The SN
list has to be rescanned anyway at upgrade (its size has changed), so we
might as well reset the version and remove the version-dependent
serialization code. (Note that the affected code here is for SN states
in lmdb storage, not for SN-to-SN communication serialization).
This converts the transaction type and version to scoped enum, giving
type safety and making the tx type assignment less error prone because
there is no implicit conversion or comparison with raw integers that has
to be worried about.
This ends up converting any use of `cryptonote::transaction::type_xyz`
to `cryptonote::transaction::txtype::xyz`. For version, names like
`transaction::version_v4` become `cryptonote::txversion::v4_tx_types`.
This also allows/includes various other simplifications related to or
enabled by this change:
- handle `is_deregister` dynamically in serialization code (setting
`type::standard` or `type::deregister` rather than using a
version-determined union)
- `get_type()` is no longer needed with the above change: it is now
much simpler to directly access `type` which will always have the
correct value (even for v2 or v3 transaction types). And though there
was an assertion on the enum value, `get_type()` was being used only
sporadically: many places accessed `.type` directly.
- the old unscoped enum didn't have a type but was assumed castable
to/from `uint16_t`, which technically meant there was potential
undefined behaviour when deserializing any type values >= 8.
- tx type range checks weren't being done in all serialization paths;
they are now. Because `get_type()` was not used everywhere (lots of
places simply accessed `.type` directory) these might not have been
caught.
- `set_type()` is not needed; it was only being used in a single place
(wallet2.cpp) and only for v4 txes, so the version protection code was
never doing anything.
- added a std::ostream << operator for the enum types so that they can be
output with `<< tx_type <<` rather than needing to wrap it in
`type_to_string(tx_type)` everywhere. For the versions, you get the
annotated version string (e.g. 4_tx_types) rather than just the number
4.
* Initial updates to allow syncing of checkpoints in protocol_handler
* Handle checkpoints in prepare_handle_incoming_blocks
* Debug changes for testing, cancel changes later
* Add checkpoint pruning code
* Reduce DB checkpoint accesses, sync votes for up to 60 blocks
* Remove duplicate lifetime variable
* Move parsing checkpoints to above early return for consistency
* Various integration test fixes
- Don't try participate in quorums at startup if you are not a service node
- Add lock for when potentially writing to the DB
- Pre-existing batch can be open whilst updating checkpoint so can't use
DB guards
- Temporarily emit the ban message using msgwriter instead of logs
* Integration mode bug fixes
- Always deserialize empty quorums so get_testing_quorum only fails when
an actual critical error has occurred
- Cache the last culled height so we don't needlessly query the DB over
the same heights trying to delete already-deleted checkpoints
- Submit votes when new blocks arrive for more reliable vote
transportation
* Undo debug changes for testing
* Update incorrect DB message and stale comment
* Incorporate service node ip address into uptime proofs; expose them using rpc
* Check that storage server port is specified in service-node mode
* Remove problematic const, rename argument name for storage port, update comments
* Validate ip address when receive uptime proof
* Better argument names and descriptions
* Unify checkpointing and uptime quorums
* Begin making checkpoints cull old votes/checkpoints
* Begin rehaul of service node code out of core, to assist checkpoints
* Begin overhaul of votes to move resposibility into quorum_cop
* Update testing suite to work with the new system
* Remove vote culling from checkpoints and into voting_pool
* Fix bugs making integration deregistration fail
* Votes don't always specify an index in the validators
* Update tests for validator index member change
* Rename deregister to voting, fix subtle hashing bug
Update the deregister hash derivation to use uint32_t as originally set
not uint64_t otherwise this affects the result and produces different
results.
* Remove un-needed nettype from vote pool
* PR review, use <algorithms>
* Rename uptime_deregister/uptime quorums to just deregister quorums
* Remove unused add_deregister_vote, move side effect out of macro
* unit tests and core tests for new queueless swarm assignment logic
* new bufferless swarm assignment logic
* new lines
* Address reviews
* Improve node stealing logic
* unit tests for additional functions
* get_excess_pool to return early if the threshold is lower than MIN_SWARM_SIZE
* Address reviews
* another round of reviews
* Move new_swarm_vector inside loop
* Rebase changes: moved get_new_swarm_id to service_node_swarm.cpp
* reserve vector size for all_ids in get_new_swarm_id
* Add the minimal fix for prepare registration failing by dust amounts
* Add dust redistribution at final step
* Comment and clean up useless lines
* Add fudge factor for dust amounts
* Avoid possible overflow with portions if contributor 1 is insufficient
* Cleanup and undoing some protocol breakages
* Simplify expiration of nodes
* Request unlock schedules entire node for expiration
* Fix off by one in expiring nodes
* Undo expiring code for pre v10 nodes
* Fix RPC returning register as unlock height and not checking 0
* Rename key image unlock height const
* Undo testnet hardfork debug changes
* Remove is_type for get_type, fix missing var rename
* Move serialisable data into public namespace
* Serialise tx types properly
* Fix typo in no service node known msg
* Code review
* Fix == to >= on serialising tx type
* Code review 2
* Fix tests and key image unlock
* Add command to print locked key images
* Update ui to display lock stakes, query in print cmd blacklist
* Modify print stakes to be less slow
* Remove autostaking code
* Refactor staking into sweep functions
It appears staking was derived off stake_main written separately at
implementation at the beginning. This merges them back into a common
code path, after removing autostake there's only some minor differences.
It also makes sure that any changes to sweeping upstream are going to be
considered in the staking process which we want.
* Display unlock height for stakes
* Begin creating output blacklist
* Make blacklist output a migration step
* Implement get_output_blacklist for lmdb
* In wallet output selection ignore blacklisted outputs
* Apply blacklisted outputs to output selection
* Fix broken tests, switch key image unlock
* Fix broken unit_tests
* Begin change to limit locked key images to 4 globally
* Revamp prepare registration for new min contribution rules
* Fix up old back case in prepare registration
* Remove debug code
* Cleanup debug code and some unecessary changes
* Fix migration step on mainnet db
* Fix blacklist outputs for pre-existing DB's
* Remove irrelevant note
* Tweak scanning addresses for locked stakes
Since we only now allow contributions from the primary address we can
skip checking all subaddress + lookahead to speed up wallet scanning
* Define macro for SCNu64 for Mingw
* Fix failure on empty DB
* Add missing error msg, remove contributor from stake
* Improve staking messages
* Flush prompt to always display
* Return the msg from stake failure and fix stake parsing error
* Tweak fork rules for smaller bulletproofs
* Tweak pooled nodes minimum amounts
* Fix crash on exit, there's no need to store on destructor
Since all information about service nodes is derived from the blockchain
and we store state every time we receive a block, storing in the
destructor is redundant as there is no new information to store.
* Make prompt be consistent with CLI
* Check max number of key images from per user to node
* Implement error message on get_output_blacklist failure
* Remove resolved TODO's/comments
* Handle infinite staking in print_sn
* Atoi->strtol, fix prepare_registration, virtual override, stale msgs
* Remove dead branches in hot-path check_tx_inputs
Also renames #define for mixins to better match naming convention
* Shuffle around some more code into common branches
* Fix min/max tx version rules, since there 1 tx v2 on v9 fork
* First draft infinite staking implementation
* Actually generate the right key image and expire appropriately
* Add framework to lock key images after expiry
* Return locked key images for nodes, add request unlock option
* Introduce transaction types for key image unlock
* Update validation steps to accept tx types, key_image_unlock
* Add mapping for lockable key images to amounts
* Change inconsistent naming scheme of contributors
* Create key image unlock transaction type and process it
* Update tx params to allow v4 types and as a result construct_tx*
* Fix some serialisation issues not sending all the information
* Fix dupe tx extra tag causing incorrect deserialisation
* Add warning comments
* Fix key image unlocks parsing error
* Simplify key image proof checks
* Fix rebase errors
* Correctly calculate the key image unlock times
* Blacklist key image on deregistration
* Serialise key image blacklist
* Rollback blacklisted key images
* Fix expiry logic error
* Disallow requesting stake unlock if already unlocked client side
* Add double spend checks for key image unlocks
* Rename get_staking_requirement_lock_blocks
To staking_initial_num_lock_blocks
* Begin modifying output selection to not use locked outputs
* Modify output selection to avoid locked/blacklisted key images
* Cleanup and undoing some protocol breakages
* Simplify expiration of nodes
* Request unlock schedules entire node for expiration
* Fix off by one in expiring nodes
* Undo expiring code for pre v10 nodes
* Fix RPC returning register as unlock height and not checking 0
* Rename key image unlock height const
* Undo testnet hardfork debug changes
* Remove is_type for get_type, fix missing var rename
* Move serialisable data into public namespace
* Serialise tx types properly
* Fix typo in no service node known msg
* Code review
* Fix == to >= on serialising tx type
* Code review 2
* Fix tests and key image unlock
* Add additional test, fix assert
* Remove debug code in wallet
* Fix merge dev problem
* create get_service_node_list rpc call
currently does nothing, just a shell (that compiles)
* implement get_all_service_node_keys rpc call
* change get_all_service_node_keys rpc to use json rpc
also change the result to be vector of hex strings rather than binary keys
* Make nodes be plural, add hex to base32z for keys
Base32z for Lokinet internal usage
* Add option to return fully funded service nodes only
* Add nullptr check for conversion
* Add assert for incorrect usage of to base32z
* Allow `prepare_registration` to work on command-line
This allows you to use `lokid prepare_registration` to generate a
registration command via RPC commands to the lokid, which is
particularly useful when the lokid is running non-interactively as a
system service.
Without this it is necessary to stop the lokid service, start it
manually, run the prepare_registration, then stop lokid and restart it
via the system service.
This also rewrites the description of the prepare_registration command to
remove the reference to saving to disk.
* make getting registration command more user-friendly via rpc
* better error message for get registration command rpc
* move get_service_node_registration_cmd into prepare_registration
* show error message on unsuccessful registration in simplewallet as well