// Copyright (c) 2018, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // 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. #pragma once #include #include #include #include #include #include "common/expect.h" #include "crypto/crypto.h" #include "db/account.h" #include "db/data.h" #include "fwd.h" #include "lmdb/transaction.h" #include "lmdb/key_stream.h" #include "lmdb/value_stream.h" namespace cryptonote { class checkpoints; } namespace lws { namespace db { namespace cursor { MONERO_CURSOR(accounts); MONERO_CURSOR(outputs); MONERO_CURSOR(spends); MONERO_CURSOR(images); MONERO_CURSOR(requests); MONERO_CURSOR(subaddress_ranges); MONERO_CURSOR(subaddress_indexes); MONERO_CURSOR(blocks); MONERO_CURSOR(pow); MONERO_CURSOR(accounts_by_address); MONERO_CURSOR(accounts_by_height); MONERO_CURSOR(webhooks); MONERO_CURSOR(events); } struct storage_internal; struct reader_internal { cursor::blocks blocks_cur; cursor::accounts_by_address accounts_ba_cur; cursor::accounts_by_height accounts_bh_cur; }; struct pow_window { std::vector pow_timestamps; //!< for pow calculation std::vector cumulative_diffs; std::vector median_timestamps; //!< for timestamp check }; //! Wrapper for LMDB read access to on-disk storage of light-weight server data. class storage_reader { std::shared_ptr db; lmdb::read_txn txn; reader_internal curs; public: storage_reader(std::shared_ptr db, lmdb::read_txn txn) noexcept : db(std::move(db)), txn(std::move(txn)), curs{} {} storage_reader(storage_reader&&) = default; storage_reader(storage_reader const&) = delete; ~storage_reader() noexcept; storage_reader& operator=(storage_reader&&) = default; storage_reader& operator=(storage_reader const&) = delete; //! \return Last known block. expect get_last_block() noexcept; //! \return Last known pow block. expect get_last_pow_block() noexcept; //! \return "Our" block hash at `height`. expect get_block_hash(const block_id height) noexcept; //! \return List for `GetHashesFast` to sync blockchain with daemon. expect> get_chain_sync(); //! \return List for GetBlocksFast` to sync blockchain+pow with daemon expect> get_pow_sync(); //! \return Objects for use with cryptonote::next_difficulty and median timestamp check expect get_pow_window(block_id last); //! \return All registered `account`s. expect> get_accounts(cursor::accounts cur = nullptr) noexcept; //! \return All `account`s currently in `status` or `lmdb::error(MDB_NOT_FOUND)`. expect> get_accounts(account_status status, cursor::accounts cur = nullptr) noexcept; //! \return Info for account `id` iff it has `status`. expect get_account(const account_status status, const account_id id) noexcept; //! \return Info related to `address`. expect> get_account(account_address const& address) noexcept; //! \return All outputs received by `id`. expect> get_outputs(account_id id, cursor::outputs cur = nullptr) noexcept; //! \return All potential spends by `id`. expect> get_spends(account_id id, cursor::spends cur = nullptr) noexcept; //! \return All key images associated with `id`. expect> get_images(output_id id, cursor::images cur = nullptr) noexcept; //! \return All `request_info`s. expect> get_requests(cursor::requests cur = nullptr) noexcept; //! \return A specific request from `address` of `type`. expect get_request(request type, account_address const& address, cursor::requests cur = nullptr) noexcept; //! \return All subaddresses activated for account `id`. expect> get_subaddresses(account_id id, cursor::subaddress_ranges cur = nullptr) noexcept; //! \return A specific subaddress index expect find_subaddress(account_id id, crypto::public_key const& spend_public, cursor::subaddress_indexes& cur) noexcept; //! \return All webhook values associated with user `key` and `payment_id`. expect> find_webhook(webhook_key const& key, crypto::hash8 const& payment_id, cursor::webhooks cur = nullptr); //! \return All webhooks in the DB expect>>> get_webhooks(cursor::webhooks cur = nullptr); //! Dump the contents of the database in JSON format to `out`. expect json_debug(std::ostream& out, bool show_keys); //! \return Read txn that can be re-used via `storage::start_read`. lmdb::suspended_txn finish_read() noexcept; }; //! Wrapper for LMDB on-disk storage of light-weight server data. class storage { std::shared_ptr db; storage(std::shared_ptr db) noexcept : db(std::move(db)) {} public: //! \return A single instance of compiled-in checkpoints for lws static cryptonote::checkpoints const& get_checkpoints(); //! \return Last hard-coded block checkpoint static block_info get_last_checkpoint(); /*! Open a light_wallet_server LDMB database. \param path Directory for LMDB storage \param create_queue_max Maximum number of create account requests allowed. \throw std::system_error on any LMDB error (all treated as fatal). \throw std::bad_alloc If `std::shared_ptr` fails to allocate. \return A ready light-wallet server database. */ static storage open(const char* path, unsigned create_queue_max); storage(storage&&) = default; storage(storage const&) = delete; ~storage() noexcept; storage& operator=(storage&&) = default; storage& operator=(storage const&) = delete; //! \return A copy of the LMDB environment, but not reusable txn/cursors. storage clone() const noexcept; //! Rollback chain and accounts to `height`. expect rollback(block_id height); /*! Sync the local blockchain with a remote version. Pops user txes if reorg detected. \param height The height of the element in `hashes` \param hashes List of blockchain hashes starting at `height`. \return True if the local blockchain is correctly synced. */ expect sync_chain(block_id height, epee::span hashes); expect sync_pow(block_id height, epee::span hashes, epee::span pow); //! Bump the last access time of `address` to the current time. expect update_access_time(account_address const& address) noexcept; //! Change state of `address` to `status`. \return Updated `addresses`. expect> change_status(account_status status, epee::span addresses); //! Add an account, for immediate inclusion in the active list. expect add_account(account_address const& address, crypto::secret_key const& key, account_flags flags = account_flags::default_account) noexcept; //! Reset `addresses` to `height` for scanning. expect> rescan(block_id height, epee::span addresses); //! Add an account for later approval. For use with the login endpoint. expect> creation_request(account_address const& address, crypto::secret_key const& key, account_flags flags) noexcept; /*! Request lock height of an existing account. No effect if the `start_height` is already older. */ expect import_request(account_address const& address, block_id height) noexcept; //! Accept requests by `addresses` of type `req`. \return Accepted addresses. expect> accept_requests(request req, epee::span addresses); //! Reject requests by `addresses` of type `req`. \return Rejected addresses. expect> reject_requests(request req, epee::span addresses); /*! Updates the status of user accounts, even if inactive or hidden. Duplicate receives or spends provided in `accts` are silently ignored. If a gap in `height` vs the stored account record is detected, the entire update will fail. \param height The first hash in `chain` is at this height. \param chain List of block hashes that `accts` were scanned against. \param accts Updated to `height + chain.size()` scan height. \return True iff LMDB successfully committed the update. */ expect>> update(block_id height, epee::span chain, epee::span accts, epee::span pow); /*! Adds subaddresses to an account. Upon success, an account will immediately begin tracking them in the scanner. \param id of the account to associate new indexes \param addresss of the account (needed to generate subaddress publc key) \param view_key of the account (needed to generate subaddress public key) \param subaddrs Range of subaddress indexes that need to be added to the database. Indexes _may_ overlap with existing indexes. \param max_subaddresses The maximum number of subaddresses allowed per account. \return The new ranges of subaddress indexes added to the database (whereas `subaddrs` may overlap with existing indexes). */ expect> upsert_subaddresses(account_id id, const account_address& address, const crypto::secret_key& view_key, std::vector subaddrs, std::uint32_t max_subaddresses); /*! Add webhook to be tracked in the database. The webhook will "call" the specified URL with JSON/msgpack information when the event occurs. \param type The webhook event type to be tracked by the DB. \param address is required for `type == tx_confirmation`, and is not not needed for all other types. \param event Additional information for the webhook. A valid "http" or "https" URL must be provided (or else error). All other information is optional. */ expect add_webhook(webhook_type type, const boost::optional& address, const webhook_value& event); /*! Delete all webhooks associated with every value in `addresses`. This is likely only valid for `tx_confirmation` event types. */ expect clear_webhooks(epee::span addressses); //! Delete all webhooks associated with every value in `ids` expect clear_webhooks(std::vector ids); //! `txn` must have come from a previous call on the same thread. expect start_read(lmdb::suspended_txn txn = nullptr) const; }; } // db } // lws