* Fix building on Ubuntu 18.04 with GCC 7 * Peerplays SON plugin skeleton (#122) * Peerplays SON plugin skeleton * SON tests skeleton * Part two of SON-83 - plugins option in command line and config file (#126) - Empty SON plugin is INACTIVE by default - To enable it, add peerplays_sidechain to plugins section in config file, or use --plugins command line option - Plugin can work with or without witness * SON11 - Add chain extension parameter to set SON count * [SON-107] Merge develop branch to SONs-base (#166) * fix rng and get_winner_numbers implemented * coipied code for bitshares fixing 429 and 433 isuues * ticket_purchase_operation implemented. added lottery_options to asset * lottery end implemented * minor logic changes. added db_api and cli_wallet methods * fix reindex on peerplays network * fix some tests. add gitlab-ci.yml * add pull to gitlab-ci * fix * fix and comment some tests * added owner to lottery_asset_options. commented async call in on_applied_block callback * added get_account_lotteries method to db_api and cli, lottery end_date and ticket_price verification * merge get_account_lotteries branch. fix create_witness test * fix test genesis and end_date verification * fixed indices sorting and lottery end checking by date * update db_version for replay and removed duplicate include files * Added ntp and upgraded boost version * Revert "GPOS protocol" * need to remove backup files * virtual-op-fix for deterministic virtual_op number * Merged beatrice into 5050 * Updated gitmodules, changes to allow voting on lottery fee * Removed submodule libraries/fc * Added libraries/fc * added missing , in types.hpp * Added sweeps parameters to parameter_extension * added missing comma in operations.hpp, small changes to config.hpp * fixed returntype in chain_parameters.hpp * removed sweeps_parameter_extensions * Changed fc library * fixed asset_object * Changed peerplays-fc submodule * Changed fc submodule to ubuntu 18.04 upgrade * Removed submodule libraries/fc * Added fc library back * fix casting in overloaded function * Removed blind_sign and unblind_signature functions * Added new lottery_asset_create_operation * Changed sweeps hardfork time * Removed redundant if from asset_evaluator and fixed db_notify * fixed duplicate code in fee_tests * removed redundant tgenesis file * Enable building on Ubuntu 18.04 using GCC 7 compiler * fix: is_benefactor_reward had the default value of true when not set * Docker file for Ubuntu 18.04 Base image updated to Unbuntu 18.04 Prerequisite list updated Basic configuration updated * Quick fix: Added missing package pkg-config * Docker file updates * 5050 fee update and compilation error fix * Dockerfile, set system locale Prevents locale::facet::_S_create_c_locale name error * Update README.md Fix typo * Update README.md * Changed hardfork time for SWEEPS and Core-429 * revert master changes that were brought in previous commit * Fixed error when account_history_object with id 0 doesnt exist * Fixed error while loading object database * test for zero id object in account history * Reorder operations in Dockerfile, to make image creation faster - Reorder prevents unnecessary building of Boost libraries * Fix for irrelevant signature included issue * fix copyrigth messages order * remove double empty lines * Backport fix for `get_account_history` from https://github.com/bitshares/bitshares-core/pull/628 and add additional account history test case * NTP client back * GRPH-53-Log_format_error * Merge pull request #1036 from jmjatlanta/issue_730 Add fail_reason to proposal_object * Unit test case fixes and prepared SONs base * Use offsetof instead of custom macro * Hide some compiler warnings * Make all the tests compile * Add nullptr check in api.cpp for easier testing * Add test case for broadcast_trx_with_callback API * Unit test case fixes and prepared SONs base * Merge pull request #714 from pmconrad/json_fix JSON fix * Increase max depth for trx confirmation callback * Adapt to variant API with `max_depth` argument * Update fc submodule * Created unit test for #325 * remove needless find() * GRPH-4-CliWallet_crash_ctrlD * fix copyright message * Make all the tests compile * increase delay for node connection * Increase block creation timeout to 2500ms * remove cache from cli get_account * add cli tests framework * Adjust newly merged code to new API * Improved resilience of block database against corruption * Merged changes from Bitshares PR 1036 * GRPH-76 - Short-cut long sequences of missed blocks Fixes database::update_global_dynamic_data to speed up counting missed blocks. (This also fixes a minor issue with counting - the previous algorithm would skip missed blocks for the witness who signed the first block after the gap.) * Moved reindex logic into database / chain_database, make use of additional blocks in block_database Fixed tests wrt db.open * Enable undo + fork database for final blocks in a replay Dont remove blocks from block db when popping blocks, handle edge case in replay wrt fork_db, adapted unit tests * Log starting block number of replay * Prevent unsigned integer underflow * Fixed lock detection * Dont leave _data_dir empty if db is locked * Writing the object_database is now almost atomic * Improved consistency check for block_log * Cut back block_log index file if inconsistent * Fixed undo_database * Added test case for broken merge on empty undo_db * Merge pull request #938 from bitshares/fix-block-storing Store correct block ID when switching forks * exclude second undo_db.enable() call in some cases * Add missing change * change bitshares to core in message * Fixed integer overflow issue * Fix for for history ID mismatch ( Bitshares PR #875 ) * Update the FC submodule with the changes for GRPH-4 * Fix #436 object_database created outside of witness data directory * supplement more comments on database::_opened variable * prevent segfault when destructing application obj * Fixed duplicate ops returned from get_account_history * minor performance improvement * Added comment * Merged Bitshares PR #1462 and compilation fixes * Support/gitlab (#123) * Updated gitlab process * Fix undefined references in cli test * Fixed test failures and compilation issue * Fixed account_history_pagination test * Fix compilation in debug mode * Removed unrelated comment * Skip auth check when pushing self-generated blocks * Extract public keys before pushing a transaction * Dereference chain_database shared_ptr * Updated transaction::signees to mutable and * updated get_signature_keys() to return a const reference, * get_signature_keys() will update signees on first call, * modified test cases and wallet.cpp accordingly, * no longer construct a new signed_transaction object before pushing * Added get_asset_count API * Allow sufficient space for new undo_session * Throw for deep nesting * No longer extract public keys before pushing a trx and removed unused new added constructor and _get_signature_keys() function from signed_transaction struct * Added cli_test to CI * use random port numbers in app_test (#154) * proposal fail_reason bug fixed (#157) * Added Sonarcloud code_quality to CI (#159) * Added sonarcloud analysis (#158) * fix for lottery end * fix declarations * fix declarations * fix boost integer * fix compilation * fix chain tests * fix app_test * try to fix cli test * fix incorrect max_depth param * working cli test * correct fc version * Revert "[SON-107] Merge develop branch to SONs-base (#166)" This reverts commit499e318199. * Fix build error, add missing GRAPHENE_MAX_NESTED_OBJECTS parameter * Plugin description added, SON plugin params example * fix for cli test * SON object, operations, cli_wallet commands and RPC (#160) - create_son, update_son, delete_son, list_sons - get_sons, get_son_by_account, lookup_son_accounts, get_son_count - vote_for_son, update_son_votes - claim_registered_son - get_son in cli_wallet - Updating global_property_object - Decrease SON hardfork time for test purposes - CLI Wallet tests imported from develop branch * fix affiliate tests * SON-108 - Add cli wallet tests for create_son (#174) * SON-108 - Add cli wallet tests for create_son * Add info message at the beginning and end of the SON CLI tests * Minor output message change * Enable Boost test messages in unit tests * [SON-110] get_son cli test (#173) * get_son cli test * update_son cli test * Add cli wallet tests for vote_for_son (#175) * fix insert object processing in indexes, son_delete is working * Fix segfault when using delete_son from cli_wallet (#177) * Fix segfault when using list_sons from cli_wallet (#178) * Add son_delete cli tests (#182) * Add son_delete cli tests * add son vesting config options * add vesting balance type support * add dormant vesting policy for son * add precision to son vesting amount * SON118-Add Budget for SON (#165) * SON118-Add Budget for SON * SON118 - Compilation errors fix * SON118 - Proper commenting around pay_sons function * SON118 - Comment correction, SON statistics object implementation type correction * SON118 - Add missing index init and reflect enums * SON118 - Correcting the indentation * SON118 SON144 - Add unit test, code fixes and resolve failures for existing tests * SON118 SON144 - Removing extra spaces added * abstraction of dormant vesting policy * force son create vesting balance to have dormant policy * remove not needed code from wallet son commands, add delete son test to cli (#181) * Active SONs, list up to 15, order by votes, add test (#185) * Add test for selecting 15 SONs with highest votes * Display up to 15 active SONs, SON ordering by total_votes * fix build error (#191) * fix build error * adapt son_pay_test to dormant vesting policy * [SON-113] Unit test for cli `update_son_votes` (#179) * refactor cli tests * update_son_votes tests * list_sons test * test changes in get_global_properties() result * fix generate_block failure * fix update_son_votes test * improve update_son cli test * fix linking errors * refactor select_top_fifteen_sons test * refactor other son cli tests to use son_test_helper * create_vesting call in wallet_api * test fix * fix create_son in wallet_api and cli tests * SON126 - Witness Proposals to deregister SONs (#192) * SON126 - Witness Proposals to deregister SONs * SON126 - Approval by witness, removal of son_proposal_object, commenting * SON126 - Witness proposal tests and related fixes * SON126 - Proper commenting * fix son_delete_operation reflection * [SON-160] Fix create_vesting wallet_api call (#206) * Fix create_vesting wallet_api call * change type for vesting_type in create_vesting_balance * [SON-113] Fix several issues in update_son_votes call in wallet_api (#208) * do not allow update votes with both empty lists * fix error messages * check number of sons against votes number in account_object * Update error message * list_active_sons api call implementation * unit test for list_active_sons * fix code style * use assert instead of checking condition with low possibility * Fixed betting tests (#217) * Fixed betting tests * Removed comments * removed unrelated parameter description from delete_son * Add Bitcoin network listener to a SON plugin (#196) * Add Bitcoin network listener to a SON plugin * Add default parameters for Peerplays Bitcoin test node * Add Bitcoin block processing * Update source code to last designs * Set default parameters for peerplays_sidechain plugin to Bitcoin test server * WIP: Some Bitcoin transaction processing * [SON-199] Fix unit tests (#233) * fix app_test * fix son_delete_test * Add peerplays account for a SON in a config/command line options (#231) * SON193-SON200- SON Heartbeats and maintenance mode changes (#241) * SON193-SON200- SON Heartbeats and maintenance mode changes * SON193-SON200- SON Heartbeats and maintenance tests * User sidechain address mappings (#240) * WIP: Sidechain objects * Revert "WIP: Sidechain objects" This reverts commit8676940a28. * WIP: User sidechain address mappings * Fix reflection problem * Reflect missing members of sidechain_address_update_operation * Add sidechain address operation tests * Enable RPC calls * Fix build errors due to merge conflict * Fix RPC, add CLI wallet commands for sidechain addresses * Improved peerplays_sidechain_plugin_impl * Remove short param for son-id * Fix crashing errors on bitcoin event received * Code review changes * SON207 - Introduce scheduling for SONs similar to witnesses (#251) * Extend SON objects to contain sidechain public keys (#254) * SON194-SON195 - Report SON Down, addition of SON Account for sidechain consensus (#244) * SON194-SON195 - Addition of SON BTC Account and report son down changes * SON194-SON195 - SON BTC Account errors rectification * SON194-SON195 - Adding Tests * User sidechain address mappings (#240) * WIP: Sidechain objects * Revert "WIP: Sidechain objects" This reverts commit8676940a28. * WIP: User sidechain address mappings * Fix reflection problem * Reflect missing members of sidechain_address_update_operation * Add sidechain address operation tests * Enable RPC calls * Fix build errors due to merge conflict * Fix RPC, add CLI wallet commands for sidechain addresses * Improved peerplays_sidechain_plugin_impl * Remove short param for son-id * Fix crashing errors on bitcoin event received * Code review changes * SON207 - Introduce scheduling for SONs similar to witnesses (#251) * Extend SON objects to contain sidechain public keys (#254) Co-authored-by: obucinac <obucinac@users.noreply.github.com> * SON206 - Plugin SON Heartbeat changes (#250) * SON206 - Plugin SON Heartbeat changes * SON206 - Plugin SON Heartbeat changes, comment removal * SON206 - Plugin SON Heartbeat changes, stub testing and changes * SON206 - Plugin SON Heartbeat changes, removing debugs prints * Wallet recreation on new set of SONs voted in (#256) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * Fix build errors * SON212-SON213 - Add Sidechain Plugin Code to report and approve SON Down proposal (#260) * SON212 - Add Sidechain Plugin Code to report SON Down * SON212-SON213 - Add Sidechain Plugin Code to report SON Down, Approve proposal from sidechain plugin * SON212-SON213 - Fix Build Error (#262) * SON212-SON213 - Fix Build Error * SON212-SON213 - Fix Build Error Add smart_ref definition for linking * Updated gitlab CI to sync submodules (#265) * SON217 - SON Maintenance,Heartbeat state transition changes (#264) * SON217 - SON Maintenance,Heartbeat state transition changes * SON217 - SON Maintenance,Heartbeat state transition changes * [SON-202] Implement cli_wallet commands for maintenance mode (#261) * Add stop_son_maintenance CLI call * fix bug with SON activation * son_maintenance_operation * son_maintenance_operation tests * cli test for son maintenance state * start_son_maintenance CLI call * keep maintenance state during active SON set changes * Quick fix for list_active_sons * SON199 - Fix Unit Test Failure (#268) * Quickfix for update_sidechain_address and delete_sidechain_address cli commands * SON206_Plugin_CrashFix_Reorg - Plugin Changes (#272) * SON206_Plugin_CrashFix_Reorg - Plugin Changes * SON206_Plugin_CrashFix_Reorg - add owner auths to consensus account * SON165 - Keys mapping missing from wallet data (#274) * SON232 - Avoid duplicate proposals from sidechain plugin (#275) * SON233 - Provide correct downtime metrics to user (#278) * son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow <jahr@yandex.ru> * SON214 - Request maintenance wallet commands (#280) * SON wallet transfer object and operations (#279) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commita688bb93edAuthor: obucinac <obucinac@users.noreply.github.com> Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow <jahr@yandex.ru> commit6e61d6b055Author: satyakoneru <satyakoneru.iiith@gmail.com> Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Add is_active_son guards for sidechain events processing Co-authored-by: gladcow <jahr@yandex.ru> * Support multiple SON nodes per software instance (#282) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commita688bb93edAuthor: obucinac <obucinac@users.noreply.github.com> Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow <jahr@yandex.ru> commit6e61d6b055Author: satyakoneru <satyakoneru.iiith@gmail.com> Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Support multiple SON nodes per software instance * Add is_active_son guards for sidechain events processing * Add is_active_son guards, fix sending proposals and aprovals * Managing GRAPHENE_SON_ACCOUNT and issuing assets on Bitcoin deposit * Fix bad param * Fix aprovals on already approved or invalid proposals * Move transfer inside son_wallet_transfer_process_operation * Fix merging issue * Add cmake command line option SUPPORT_MULTIPLE_SONS * Temoprary disable account history tests for tracking accounts Co-authored-by: gladcow <jahr@yandex.ru> * [SON-209] Create P2SH address with custom redeemScript (#271) * Create redeem script for SONs primary wallet * Add importaddress call Allows to watch for related transactions without private keys import * Get UTXO set for watched addresses * createrawtransaction call * signing PW spending transaction * unit test for btc tx serialization * sending PW transfer in test * BIP143 tx signing * use bech32 address format * use single sha256 for lock script * Digest fix * working signing * separate signing * test partially signed PW transfer * add ability to gather signatures before signing (#290) * [SON-242] fix list_active_sons call after deleting an active son (#292) * test to reproduce error in list_active_sons after delete_son * prevent exception in list_active_list * [SON-260] Sidechain Token withdrawal (#286) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commita688bb93edAuthor: obucinac <obucinac@users.noreply.github.com> Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow <jahr@yandex.ru> commit6e61d6b055Author: satyakoneru <satyakoneru.iiith@gmail.com> Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Support multiple SON nodes per software instance * Add is_active_son guards for sidechain events processing * Add is_active_son guards, fix sending proposals and aprovals * Managing GRAPHENE_SON_ACCOUNT and issuing assets on Bitcoin deposit * Fix bad param * Fix aprovals on already approved or invalid proposals * Move transfer inside son_wallet_transfer_process_operation * Fix merging issue * Add cmake command line option SUPPORT_MULTIPLE_SONS * Skeleton of sidechain_net_handler_peerplays * Skeleton of Peerplays network listener * Temoprary disable account history tests for tracking accounts * Full Peerplays listener, use GRAPHENE_SON_ACCOUNT instead son_btc_account * Renaming son_wallet_transfer* to son_wallet_deposit*, introducing son_wallet_withdrawal* * Extend sidechain_address_object to contain withdrawal addresses - Withdrawal address is the address where system will send sidechain currencies * Rename son_wallet_withdrawal* to son_wallet_withdraw* * Some refactoring * Withdrawal refactoring * Withdrawal refactoring Co-authored-by: gladcow <jahr@yandex.ru> * SON261 - Bitcoin deposit, withdrawal, PW transfer (#287) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * son_wallet_object operations * son_wallet_object operations completed, basic tests added * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Send RPC command to bitcoin node to recreate multisig wallet * Wallet recreation by scheduled SON only, some cosmetic refactoring * Wallet recreation by scheduled SON only, some cosmetic refactoring * Updating wallet info through operation instead through database.modify() for persistance * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp Co-Authored-By: gladcow <jahr@yandex.ru> * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * SON wallet transfer object and operations, for tracking assets deposit/withdrawal * Refactor primary wallet recreation * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file * Squashed commit of the following: commita688bb93edAuthor: obucinac <obucinac@users.noreply.github.com> Date: Tue Feb 4 19:31:45 2020 +0100 son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include <graphene/chain/son_wallet_transfer_object.hpp> * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow <jahr@yandex.ru> commit6e61d6b055Author: satyakoneru <satyakoneru.iiith@gmail.com> Date: Tue Feb 4 00:14:39 2020 +1100 SON233 - Provide correct downtime metrics to user (#278) * Remove duplicated item in CMakeLists.txt * Issue tokens to the user who deposited Bitcoin, WIP... * Add son_wallet_transfer_process_operation * Issue tokens to the user who deposited Bitcoin, WIP... * Support multiple SON nodes per software instance * Add is_active_son guards for sidechain events processing * Add is_active_son guards, fix sending proposals and aprovals * Managing GRAPHENE_SON_ACCOUNT and issuing assets on Bitcoin deposit * Fix bad param * Fix aprovals on already approved or invalid proposals * Move transfer inside son_wallet_transfer_process_operation * Fix merging issue * Add cmake command line option SUPPORT_MULTIPLE_SONS * Skeleton of sidechain_net_handler_peerplays * Skeleton of Peerplays network listener * SON261 - Deposit transfer ( user address -> PW ) and Withdrawal transfer ( PW -> user address ) for m-of-n multisig * Temoprary disable account history tests for tracking accounts * Full Peerplays listener, use GRAPHENE_SON_ACCOUNT instead son_btc_account * Renaming son_wallet_transfer* to son_wallet_deposit*, introducing son_wallet_withdrawal* * Extend sidechain_address_object to contain withdrawal addresses - Withdrawal address is the address where system will send sidechain currencies * Rename son_wallet_withdrawal* to son_wallet_withdraw* * Some refactoring * SON261 - Withdrawal transfer ( PW -> user address ), addition of bitcoin public private key to config.ini for multiple sons mode * Withdrawal refactoring * Withdrawal refactoring * SON261 - Fix prepare_tx * SON261 - Add PW->PW Transfer and Code reorg * Fix file permissions Co-authored-by: obucinac <obucinac@users.noreply.github.com> Co-authored-by: gladcow <jahr@yandex.ru> * [SON-264] Integrating deposit/withdrawals with bitcoin transactions (feature/SON-260 + SON261 branches) (#291) * Partial integration done, some Bitcoin RPC refactoring * CLang Format config file * CLang Format config file v2.0 * Fix repeating tasks that should be executed by scheduled SON only * Fix withdrawal * Integrate PW wallet fund moving * Resolve conflicts Co-authored-by: gladcow <jahr@yandex.ru> Co-authored-by: satyakoneru <satyakoneru.iiith@gmail.com> * SON200 - SON Down proposal broken after latest merges (#294) * SON200 - SON Down proposal broken after latest merges * Add the owner weight threshold similar to witnesses and committee accounts * SON269 - Move SON deregistration to Plugin from witness (#298) * SON200 - SON Down proposal broken after latest merges * Add the owner weight threshold similar to witnesses and committee accounts * SON269 - Move SON deregistration to Plugin from witness * Various SON improvements (#297) * Refactor SON processing * Better exposure of sidechain private keys in sidechain handlers * Support non default Bitcoin wallets * Fix crash on config file recreation * clang-format formatting * New Bitcoin wallet related RPC calls * Add missing create_son_deregister_proposals calls * Add missing create_son_deregister_proposals calls * Add loading/unlocking/locking of non-default bitcoin wallet * Bitcon RFC logs improved, proposal aprovement improved * Move signal connection after handlers are created * Merge develop into SONS * SON118 - Add tx sign metrics for SON rewards (#302) * resolved compilation issues and other conflicts * SON202 - Maintenance improvements (#303) * Quickfix, remove dead code, return result from wallet withdraw do_evaluate * SON275 - ZMQ Crash on application exit (#306) * SON275 - ZMQ Crash on application exit * SON275 - Fix Indentation Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * need to assign both name and id to stats id * fix unit test case failures(add gpos vesting before voting) * SON276 - Fix SON proposal exceptions - I (#307) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Add SON statistic for tracking reported sidechain transactions (#308) - Deposit and Withdrawal object extended to contain expected and received transaction reports from SON network - SON statistic object extended to contain total number of sidechain transactions reported by SON network when SON was active and number of transactions reported by single SON when he was active - Code formatting * Allow voting for son, only if GPOS vesting balance available * notifications of SONS should get restrict to sons functionality * update GPOS hardfork date to sons branch * SON127 - Add son parameter extensions to genesis, push proposal fix (#310) * SON276 - Fix SON proposal exceptions - I * SON127 - Add son parameter extensions to genesis, push proposal fix Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * update GPOS HF to fall in before SONS HF, remove check * updated unit test cases to reflect GPOS vesting and update account id's according to sons-account * [SON-24] - SON Rewards missing serialisations and end to end testing (#313) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Revert "Merge develop branch changes(GPOS+graphene updates) into SONs branch" * [SON-122] - SON Statistics improvements and consensus account creation (#318) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Replace raw with psbt transactions to support parital tx signing (#311) * RPC calls for PSBT, raw transactions replaced with PSBT * Fix estimatesmartfeerate, extensive RPC calls logging for debugging purposes * Remove dead code * Partial signing functional for deposit and withdrawal * Fix sidechain_type declarations * Depositing Peerplays asset refactored * Partial signing functional for primary wallet funds moving * Prettier logs * Refactor multiple SON support processing * Serialize field complete from sidechain_transaction_sign_operation * Refactor transaction signing in particular order, BTC only (maybe) need it * Add number of required signatures parameter for addmultisigaddress * Change default bitcoin node parameters * Transaction signing only by scheduled son * Removed scheduling log * Prevent PW funds moving to the same address * Refactor sidechain_transaction_object processing, code cleanup * Remove obsolete tests * Decrease logging * Code readability * When updated, import son wallet bitcoin address to bitcoin wallet * When updated, recreate son wallet bitcoin address on each node * Refactor on_changed_objects, move it into task * Add check to prevent deposit/withdrawal double processing * Improved check for sidechain transaction object creation * Single sidechain transaction signature per block allowed only * Unlock wallet on addmultisigaddress * Import both address and redeem script on primary wallet change, fix some compiler warnings * Fix invalid list of signers for PW funds transfer * [SON-312] Refactor create_son to assign owner account public key as a signing_key remove key derivation from create son (#323) Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> * [SON-271] Merge recent develop branch changes(both GPOS and graphene updates) into SONs branch (#322) * Parallelizing sidechain transaction signing (#319) * [SON-321, SON-323] Deposit/Withdraw object creation refactoring (#320) * Remove proposals for creating deposit and withdrawal objects, strenghten evaluator checks * Only active SON can create the object * Only expected SON can confirm the transaction * Transaction must be initiated (paid) by SON account owner (SON account given in operation) * Transaction confirmation must contain exactly the same data as existing object * Mirror SON owner account weights from son-account.active.account_auths to active SONs * Fix duplicated typedef, peerplays_sidechain::sidechain_type to chain::sidechain_type * Add missing serialized field * [SON-318_SON-319] - Add approval checks for son down, deregister proposals (#321) * [SON-318_SON-319] - Add approval checks for son down and deregister proposals Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Co-authored-by: Srdjan Obucina <obucinac@gmail.com> * [SON-311] Add try_create_son call without explicit deposit params (#324) Co-authored-by: gladcow <s.gladkov@pbsa.info> * Hotfix - Fix build error * Quickfix - change GPOS and SON hardfork times * [SON-332] Check gitlab building process for dirty build (#327) * Fix failing son test, fix data types and check condition * Very clean build on Gitlab * update son-account parameters (#328) * [SON-329] Hotfix - Enable test test_update_dividend_interval * [SON-313] - Limit SON functionality when min no. of sons are not present (#329) * [SON-313] - Limit SON functionality when min no. of sons are not present * Revert SON HF related checks and tests * Remove the capability to process proposals in plugin Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * [SON-307] Create PBTC asset (#326) * SON-297_SON-336 - SON vesting functionality broken after graphene merge (#331) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Hotfix - add initialization values to extension params, remove trailing spaces * [SON-305, SON-308, SON-310] Use BTC asset in bitcoin deposits and withdraws (#332) * [SON-339] - SON Schedule crash (#334) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * [SON-291,SON-328] - SON Configuration invalid, PW creation issues (#335) * [SON-291,SON-328] - SON Configuration invalid, PW creation issues Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * [SON-322, SON-324] Approval checks for processing deposit/withdrawal (#330) * Refactor proposal processing * Added check for approving son_wallet_deposit_process_operation * Added check for approving son_wallet_withdraw_process_operation * Calculating exchange rates fixed * Fix depositing Peerplays assets * [SON-320] Added check for approving son_wallet_update_operation (#336) * [SON-325] Added check for approving sidechain_transaction_create_operation (#337) * [SON-341, SON-342] Fix issue with deposits number (#339) Co-authored-by: gladcow <s.gladkov@pbsa.info> * [SON-344] BTC asset is created with wrong quote asset id, Fixed (#341) * [SON-344] BTC asset is created with wrong quote asset id, Fixed * Respond to code review * [SON-346] Sidechain transaction marked as complete even though current_weight < threshold, Fixed (#342) * [SON-348] Transaction hash not saved in object after Bitcoin transaction is sent (#343) - Fixed - Unused parameters removed * [SON-337] - Prevent update_son_votes without GPOS vesting (#344) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * [SON-353] Refactor PW processing, PW transfer fixed (#347) * Add proposal checks for deposit and withdrawal * Refactor proposal approvement * Fix transaction verification * Remove logs * [SON-354] Fix son_info compare function (#350) * check object's id (#351) Co-authored-by: gladcow <s.gladkov@pbsa.info> * SON Weighted Multi Signature Signing (#349) * Bring in the bitcoin utils code into plugin * Add tx creation, signing and tests * tx deserialization fix * add 10-of-14 multisig address test * Add signing and verification tests and sign_transaction_standalone * Add send_transaction_standalone function * Debug logs and additional tests * Fix for son deletion in the middle * Extend script_builder * Witness script for weighted wallet * btc_weighted_multisig_address implementation * Fix for bad-txns-nonstandard-inputs * Weighted multisignature address test * Create test tx with weighted multisig wallet * Fix the issues with tx signing * End to End test weighted multi sig * 1 or m-of-n deposit address support * Move network_type enum to the base class * btc_one_or_weighted_multisig_address implementation * Simplify redeem script * Fix error in redeem_script * btc_one_or_weighted_multisig_address tests * Refactor sidechain address mapping * CLANG code format * CLANG code format sidechain tests * Integration of deposit and rest of weighted wallets, withdrawal fee fix, whole code refactoring * Move util functions to Utils file * Add proper checks for withdraw fee * Deposit address creation, import deposit/withdraw addresses, some code cleanup Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Co-authored-by: gladcow <s.gladkov@pbsa.info> Co-authored-by: Srdjan Obucina <obucinac@gmail.com> * [SON-349] Delay BTC asset issue/reserve until tx confirmed on sidchain (#348) * Separate transaction settling from deposit/withdrawal processing * Handle peerplays deposits with transaction settling * Remove logs * All dev features enabled/disabled with single flag * Deposit/withdraw process and sidechain transaction creation in single proposal * Hotfix - remove importing sidechain addresses * Hotfix - remove redundant deposit sidechain address recreation * private-key option update * Use decoderawtraction json for proposal approvals (#352) * Use decoderawtraction json for proposal approvals * Use default null string to get first vout Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Prevent incorrect signatures to be added to transaction (#354) * Prevent incorrect signatures to be added to transaction * Check signers list before signing Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Hotfix - use getrawtransaction for approvals and settling (#355) * Revert "Use decoderawtraction json for proposal approvals (#352)" This reverts commitd3385b28cb. * User getrawtransaction for proposal approvals and settling * Code cleanup * [SON-135] Add timelock to user deposit address (#356) * timelocks * timelocked deposit address * test for deposit timelock Co-authored-by: gladcow <s.gladkov@pbsa.info> * Hotfix - fix threshold_weight calculation in redeem scripts * fix broken peerplays_sidechain tests (#357) Co-authored-by: gladcow <s.gladkov@pbsa.info> * Hotfix - Save deposit address redeem and witness script into sidechain address object * [SON-359] - Fix Errors processing to-be-refunded deposits (#358) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * [SON-363] - Remove son deletion (#359) * [SON-363] - Remove son deletion * Fix the tests * [SON-314] - Weighted Rewards and equal weighted son-account (#360) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Bitcoin network type deduction (#361) Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * chore: changed building to debug mode * ci: update .gitlab-ci.yml * ci: update .gitlab-ci.yml * chore: updated Dockerfile with dnsutils * GPOS2 HF - Handle rolling period on missing blocks (#369) * Mainnet chain halt 5050 Issue (#370) * Peerplays Marketplace + NFT (#367) * ppy marketplace 1 - add evaluators and objects * NFT object and basic operations * ci: update .gitlab-ci.yml * ci: update .gitlab-ci.yml * NFT evaluators and basic tests, no evaluator checks * Evaluator checks in place * ppy marketplace 2 - batch sale, offer_object escrow * Database API * Wallet API * NFT metadata implemented * Fix NFT tests * Database API for NFT metadata and enumerables * ppy marketplace 4 - Add tests NFT+Marketplace * ppy marketplace 5 - Add revenue split * ppy marketplace 6 - Remove unnecessary files * ppy marketplace 7 - Add db, wallet changes and some NFT fixes * ppy marketplace 8 - Add pagination for list APIs * New DB API, list all NFTs, list NFTs by owner * Marketplace + NFT + RBAC (#368) * rbac1 - evaluators and op validators added * rbac2 - op_type hf checks * rbac3 - tx auth verify changes * Update .gitlab-ci.yml * rbac4 - basic op tests * rbac5 - clear expired and deleted permission linked auths * rbac6 - more tests * rbac7 - more tests * rbac8 - more tests * rbac9 - wallet and db api changes * rbac10 - db api changes for required signature fetch * rbac11 - add db_api tests * rbac12 - add missing code for key auths Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Co-authored-by: Roshan Syed <roshan.syed.rs@gmail.com> Co-authored-by: sierra19XX <15652887+sierra19XX@users.noreply.github.com> * Fix nft_get_token_uri returning empty string * Fix nft_mint_evaluator to save token_uri * Fix cli_wallet to properly pass metadata id for nft_create * ppy marketplace 9 - FC_REFLECT offer create op * Add stricter checks to NFTs * Unlisting offers, add result in offer history object * Reverting genesis.json wrong commit * Add non-transferable non-sellable properties to NFTs * Review comments - change variable names, use scoped enums * nft_metadata_update changes * NFT HF checks and op fee addition changes * NFT make revenue_split integer from double * revenue_split condition check allow zero or above Co-authored-by: Srdjan Obucina <obucinac@gmail.com> Co-authored-by: Roshan Syed <roshan.syed.rs@gmail.com> Co-authored-by: Satyanarayana Koneru <skoneru@SK-GT.local> Co-authored-by: obucina <11353193+obucina@users.noreply.github.com> Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> * Son deposit address enhancements (#362) * Deposit address enhancements * fix tests Co-authored-by: Koneru Satyanarayana <15652887+satyakoneru@users.noreply.github.com> * Ws updates * Fix for custom operation authority checking (BTS Issue #210) (#382) * Resolve #210: [HF] Check authorities on custom_operation The required_auths field on custom_operation was being ignored during authority checking. This commit causes it to be checked correctly, and adds a unit test verifying as much. * Ref #381: Fixes Build and logic fixes for Pull Request #381 * Ref #381: Fix bad merge During merge conflict resolution, I accidentally broke custom authorities. This fixes it. * compilation fix Co-authored-by: Nathan Hourt <nathan@followmyvote.com> * Cleanup changes for pretier diff * Cleanup changes for prettier diff * NFT Permissions (#380) * Account Roles Permission 1 - Working code with tests * Account Roles Permission 2 - Add marketplace offer/bid tests * Account Roles Permission 3 - Add Op check * Account Roles Permission 4 - Add chain params and limits * Cleanup changes for prettier diff * Fix failing saving_keys_wallet_test * Fix failing saving_keys_wallet_test * Align submodule versions * Add missing break * Increase tests log_level, some cleanup * Decrease log level for tests * Fix block_tests/maintenance_interval test * Fix son_operation_tests/son_pay_test test * Remove base_uri length checks * Fix HF info Co-authored-by: S <obucinac@gmail.com> Co-authored-by: Bobinson K B <bobinson@gmail.com> Co-authored-by: obucinac <obucinac@users.noreply.github.com> Co-authored-by: satyakoneru <satyakoneru.iiith@gmail.com> Co-authored-by: gladcow <jahr@yandex.ru> Co-authored-by: gladcow <s.gladkov@pbsa.info> Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> Co-authored-by: Sandip Patel <sandip@knackroot.com> Co-authored-by: Roshan Syed <r.syed@pbsa.info> Co-authored-by: pbattu123 <p.battu@pbsa.info> Co-authored-by: sierra19XX <15652887+sierra19XX@users.noreply.github.com> Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Co-authored-by: obucina <11353193+obucina@users.noreply.github.com> Co-authored-by: pbattu123 <43043205+pbattu123@users.noreply.github.com> Co-authored-by: Roshan Syed <roshan.syed.rs@gmail.com> Co-authored-by: Satyanarayana Koneru <skoneru@SK-GT.local> Co-authored-by: blockc p <pravin.blockc@gmail.com> Co-authored-by: Nathan Hourt <nathan@followmyvote.com>
3320 lines
130 KiB
C++
3320 lines
130 KiB
C++
/*
|
|
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
|
*
|
|
* The MIT License
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include <graphene/app/database_api.hpp>
|
|
#include <graphene/chain/get_config.hpp>
|
|
#include <graphene/chain/tournament_object.hpp>
|
|
#include <graphene/chain/account_object.hpp>
|
|
#include <graphene/chain/protocol/address.hpp>
|
|
#include <graphene/chain/pts_address.hpp>
|
|
|
|
#include <fc/bloom_filter.hpp>
|
|
#include <fc/smart_ref_impl.hpp>
|
|
|
|
#include <fc/crypto/hex.hpp>
|
|
#include <fc/rpc/api_connection.hpp>
|
|
#include <fc/uint128.hpp>
|
|
|
|
#include <boost/range/iterator_range.hpp>
|
|
#include <boost/rational.hpp>
|
|
#include <boost/multiprecision/cpp_int.hpp>
|
|
|
|
#include <cctype>
|
|
|
|
#include <cfenv>
|
|
#include <iostream>
|
|
|
|
#define GET_REQUIRED_FEES_MAX_RECURSION 4
|
|
|
|
typedef std::map< std::pair<graphene::chain::asset_id_type, graphene::chain::asset_id_type>, std::vector<fc::variant> > market_queue_type;
|
|
|
|
template class fc::api<graphene::app::database_api>;
|
|
|
|
namespace graphene { namespace app {
|
|
|
|
class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|
{
|
|
public:
|
|
database_api_impl( graphene::chain::database& db );
|
|
~database_api_impl();
|
|
|
|
// Objects
|
|
fc::variants get_objects(const vector<object_id_type>& ids)const;
|
|
|
|
// Subscriptions
|
|
void set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create );
|
|
void set_pending_transaction_callback( std::function<void(const variant&)> cb );
|
|
void set_block_applied_callback( std::function<void(const variant& block_id)> cb );
|
|
void cancel_all_subscriptions();
|
|
|
|
// Blocks and transactions
|
|
optional<block_header> get_block_header(uint32_t block_num)const;
|
|
map<uint32_t, optional<block_header>> get_block_header_batch(const vector<uint32_t> block_nums)const;
|
|
optional<signed_block> get_block(uint32_t block_num)const;
|
|
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
|
|
|
|
// Globals
|
|
chain_property_object get_chain_properties()const;
|
|
global_property_object get_global_properties()const;
|
|
fc::variant_object get_config()const;
|
|
chain_id_type get_chain_id()const;
|
|
dynamic_global_property_object get_dynamic_global_properties()const;
|
|
global_betting_statistics_object get_global_betting_statistics() const;
|
|
|
|
// Keys
|
|
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
|
bool is_public_key_registered(string public_key) const;
|
|
|
|
// Accounts
|
|
account_id_type get_account_id_from_string(const std::string& name_or_id)const;
|
|
vector<optional<account_object>> get_accounts(const vector<std::string>& account_names_or_ids)const;
|
|
std::map<string,full_account> get_full_accounts( const vector<string>& names_or_ids, bool subscribe );
|
|
optional<account_object> get_account_by_name( string name )const;
|
|
vector<account_id_type> get_account_references( const std::string account_id_or_name )const;
|
|
vector<optional<account_object>> lookup_account_names(const vector<string>& account_names)const;
|
|
map<string,account_id_type> lookup_accounts(const string& lower_bound_name, uint32_t limit)const;
|
|
uint64_t get_account_count()const;
|
|
|
|
// Balances
|
|
vector<asset> get_account_balances(const std::string& account_name_or_id, const flat_set<asset_id_type>& assets)const;
|
|
vector<balance_object> get_balance_objects( const vector<address>& addrs )const;
|
|
vector<asset> get_vested_balances( const vector<balance_id_type>& objs )const;
|
|
vector<vesting_balance_object> get_vesting_balances( const std::string account_id_or_name )const;
|
|
|
|
// Assets
|
|
asset_id_type get_asset_id_from_string(const std::string& symbol_or_id)const;
|
|
vector<optional<asset_object>> get_assets(const vector<std::string>& asset_symbols_or_ids)const;
|
|
// helper function
|
|
vector<optional<asset_object>> get_assets( const vector<asset_id_type>& asset_ids )const;
|
|
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
|
|
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
|
|
uint64_t get_asset_count()const;
|
|
|
|
// Peerplays
|
|
vector<sport_object> list_sports() const;
|
|
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
|
|
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
|
|
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
|
|
vector<betting_market_object> list_betting_markets(betting_market_group_id_type) const;
|
|
vector<bet_object> get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const;
|
|
vector<bet_object> get_all_unmatched_bets_for_bettor(account_id_type) const;
|
|
|
|
// Lottery Assets
|
|
vector<asset_object> get_lotteries( asset_id_type stop = asset_id_type(),
|
|
unsigned limit = 100,
|
|
asset_id_type start = asset_id_type() )const;
|
|
vector<asset_object> get_account_lotteries( account_id_type issuer,
|
|
asset_id_type stop,
|
|
unsigned limit,
|
|
asset_id_type start )const;
|
|
asset get_lottery_balance( asset_id_type lottery_id )const;
|
|
sweeps_vesting_balance_object get_sweeps_vesting_balance_object( account_id_type account )const;
|
|
asset get_sweeps_vesting_balance_available_for_claim( account_id_type account )const;
|
|
|
|
// Markets / feeds
|
|
vector<limit_order_object> get_limit_orders( const asset_id_type a, const asset_id_type b, const uint32_t limit )const;
|
|
vector<limit_order_object> get_limit_orders( const std::string& a, const std::string& b, const uint32_t limit)const;
|
|
vector<call_order_object> get_call_orders(const std::string& a, uint32_t limit)const;
|
|
vector<force_settlement_object> get_settle_orders(const std::string& a, uint32_t limit)const;
|
|
vector<call_order_object> get_margin_positions( const std::string account_id_or_name )const;
|
|
void subscribe_to_market(std::function<void(const variant&)> callback, const std::string& a, const std::string& b);
|
|
void unsubscribe_from_market(const std::string& a, const std::string& b);
|
|
market_ticker get_ticker( const string& base, const string& quote )const;
|
|
market_volume get_24_volume( const string& base, const string& quote )const;
|
|
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const;
|
|
vector<market_trade> get_trade_history( const string& base, const string& quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit = 100 )const;
|
|
|
|
// Witnesses
|
|
vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;
|
|
fc::optional<witness_object> get_witness_by_account(const std::string account_id_or_name)const;
|
|
map<string, witness_id_type> lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const;
|
|
uint64_t get_witness_count()const;
|
|
|
|
// Committee members
|
|
vector<optional<committee_member_object>> get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const;
|
|
fc::optional<committee_member_object> get_committee_member_by_account(const std::string account_id_or_name)const;
|
|
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
|
|
|
|
// SON members
|
|
vector<optional<son_object>> get_sons(const vector<son_id_type>& son_ids)const;
|
|
fc::optional<son_object> get_son_by_account(account_id_type account)const;
|
|
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
|
|
uint64_t get_son_count()const;
|
|
|
|
// SON wallets
|
|
optional<son_wallet_object> get_active_son_wallet();
|
|
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
|
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
|
|
|
// Sidechain addresses
|
|
vector<optional<sidechain_address_object>> get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const;
|
|
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_account(account_id_type account)const;
|
|
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const;
|
|
fc::optional<sidechain_address_object> get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const;
|
|
uint64_t get_sidechain_addresses_count()const;
|
|
|
|
// Votes
|
|
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
|
|
|
|
// Authority / validation
|
|
std::string get_transaction_hex(const signed_transaction& trx)const;
|
|
set<public_key_type> get_required_signatures( const signed_transaction& trx, const flat_set<public_key_type>& available_keys )const;
|
|
set<public_key_type> get_potential_signatures( const signed_transaction& trx )const;
|
|
set<address> get_potential_address_signatures( const signed_transaction& trx )const;
|
|
bool verify_authority( const signed_transaction& trx )const;
|
|
bool verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& signers )const;
|
|
processed_transaction validate_transaction( const signed_transaction& trx )const;
|
|
vector< fc::variant > get_required_fees( const vector<operation>& ops, const std::string& asset_id_or_symbol )const;
|
|
|
|
// Proposed transactions
|
|
vector<proposal_object> get_proposed_transactions( const std::string account_id_or_name )const;
|
|
|
|
// Blinded balances
|
|
vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;
|
|
|
|
// Tournaments
|
|
vector<tournament_object> get_tournaments_in_state(tournament_state state, uint32_t limit) const;
|
|
vector<tournament_object> get_tournaments(tournament_id_type stop, unsigned limit, tournament_id_type start);
|
|
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state);
|
|
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
|
|
|
|
// gpos
|
|
gpos_info get_gpos_info(const account_id_type account) const;
|
|
|
|
// rbac
|
|
vector<custom_permission_object> get_custom_permissions(const account_id_type account) const;
|
|
fc::optional<custom_permission_object> get_custom_permission_by_name(const account_id_type account, const string& permission_name) const;
|
|
vector<custom_account_authority_object> get_custom_account_authorities(const account_id_type account) const;
|
|
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const;
|
|
vector<custom_account_authority_object> get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const;
|
|
vector<authority> get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const;
|
|
|
|
// NFT
|
|
uint64_t nft_get_balance(const account_id_type owner) const;
|
|
optional<account_id_type> nft_owner_of(const nft_id_type token_id) const;
|
|
optional<account_id_type> nft_get_approved(const nft_id_type token_id) const;
|
|
bool nft_is_approved_for_all(const account_id_type owner, const account_id_type operator_) const;
|
|
string nft_get_name(const nft_metadata_id_type nft_metadata_id) const;
|
|
string nft_get_symbol(const nft_metadata_id_type nft_metadata_id) const;
|
|
string nft_get_token_uri(const nft_id_type token_id) const;
|
|
uint64_t nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const;
|
|
nft_object nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const;
|
|
nft_object nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const;
|
|
vector<nft_object> nft_get_all_tokens() const;
|
|
vector<nft_object> nft_get_tokens_by_owner(const account_id_type owner) const;
|
|
|
|
// Marketplace
|
|
vector<offer_object> list_offers(const offer_id_type lower_id, uint32_t limit) const;
|
|
vector<offer_object> list_sell_offers(const offer_id_type lower_id, uint32_t limit) const;
|
|
vector<offer_object> list_buy_offers(const offer_id_type lower_id, uint32_t limit) const;
|
|
vector<offer_history_object> list_offer_history(const offer_history_id_type lower_id, uint32_t limit) const;
|
|
vector<offer_object> get_offers_by_issuer(const offer_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const;
|
|
vector<offer_object> get_offers_by_item(const offer_id_type lower_id, const nft_id_type item, uint32_t limit) const;
|
|
vector<offer_history_object> get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const;
|
|
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
|
|
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
|
|
|
|
// Account Role
|
|
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
|
|
|
|
//private:
|
|
const account_object* get_account_from_string( const std::string& name_or_id,
|
|
bool throw_if_not_found = true ) const;
|
|
const asset_object* get_asset_from_string( const std::string& symbol_or_id,
|
|
bool throw_if_not_found = true ) const;
|
|
template<typename T>
|
|
void subscribe_to_item( const T& i )const
|
|
{
|
|
auto vec = fc::raw::pack(i);
|
|
if( !_subscribe_callback )
|
|
return;
|
|
|
|
if( !is_subscribed_to_item(i) )
|
|
{
|
|
_subscribe_filter.insert( vec.data(), vec.size() );//(vecconst char*)&i, sizeof(i) );
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
bool is_subscribed_to_item( const T& i )const
|
|
{
|
|
if( !_subscribe_callback )
|
|
return false;
|
|
|
|
return _subscribe_filter.contains( i );
|
|
}
|
|
|
|
bool is_impacted_account( const flat_set<account_id_type>& accounts)
|
|
{
|
|
if( !_subscribed_accounts.size() || !accounts.size() )
|
|
return false;
|
|
|
|
return std::any_of(accounts.begin(), accounts.end(), [this](const account_id_type& account) {
|
|
return _subscribed_accounts.find(account) != _subscribed_accounts.end();
|
|
});
|
|
}
|
|
|
|
template<typename T>
|
|
void enqueue_if_subscribed_to_market(const object* obj, market_queue_type& queue, bool full_object=true)
|
|
{
|
|
const T* order = dynamic_cast<const T*>(obj);
|
|
FC_ASSERT( order != nullptr);
|
|
|
|
auto market = order->get_market();
|
|
|
|
auto sub = _market_subscriptions.find( market );
|
|
if( sub != _market_subscriptions.end() ) {
|
|
queue[market].emplace_back( full_object ? obj->to_variant() : fc::variant(obj->id, 1) );
|
|
}
|
|
}
|
|
|
|
void broadcast_updates( const vector<variant>& updates );
|
|
void broadcast_market_updates( const market_queue_type& queue);
|
|
void handle_object_changed(bool force_notify, bool full_object, const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts, std::function<const object*(object_id_type id)> find_object);
|
|
|
|
/** called every time a block is applied to report the objects that were changed */
|
|
void on_objects_new(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);
|
|
void on_objects_changed(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts);
|
|
void on_objects_removed(const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts);
|
|
void on_applied_block();
|
|
|
|
bool _notify_remove_create = false;
|
|
mutable fc::bloom_filter _subscribe_filter;
|
|
std::set<account_id_type> _subscribed_accounts;
|
|
std::function<void(const fc::variant&)> _subscribe_callback;
|
|
std::function<void(const fc::variant&)> _pending_trx_callback;
|
|
std::function<void(const fc::variant&)> _block_applied_callback;
|
|
|
|
boost::signals2::scoped_connection _new_connection;
|
|
boost::signals2::scoped_connection _change_connection;
|
|
boost::signals2::scoped_connection _removed_connection;
|
|
boost::signals2::scoped_connection _applied_block_connection;
|
|
boost::signals2::scoped_connection _pending_trx_connection;
|
|
map< pair<asset_id_type,asset_id_type>, std::function<void(const variant&)> > _market_subscriptions;
|
|
graphene::chain::database& _db;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Constructors //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
database_api::database_api( graphene::chain::database& db )
|
|
: my( new database_api_impl( db ) ) {}
|
|
|
|
database_api::~database_api() {}
|
|
|
|
database_api_impl::database_api_impl( graphene::chain::database& db ):_db(db)
|
|
{
|
|
wlog("creating database api ${x}", ("x",int64_t(this)) );
|
|
_new_connection = _db.new_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) {
|
|
on_objects_new(ids, impacted_accounts);
|
|
});
|
|
_change_connection = _db.changed_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) {
|
|
on_objects_changed(ids, impacted_accounts);
|
|
});
|
|
_removed_connection = _db.removed_objects.connect([this](const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts) {
|
|
on_objects_removed(ids, objs, impacted_accounts);
|
|
});
|
|
_applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); });
|
|
|
|
_pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){
|
|
if( _pending_trx_callback ) _pending_trx_callback( fc::variant(trx, GRAPHENE_MAX_NESTED_OBJECTS) );
|
|
});
|
|
}
|
|
|
|
database_api_impl::~database_api_impl()
|
|
{
|
|
elog("freeing database api ${x}", ("x",int64_t(this)) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Objects //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
fc::variants database_api::get_objects(const vector<object_id_type>& ids)const
|
|
{
|
|
return my->get_objects( ids );
|
|
}
|
|
|
|
fc::variants database_api_impl::get_objects(const vector<object_id_type>& ids)const
|
|
{
|
|
if( _subscribe_callback ) {
|
|
for( auto id : ids )
|
|
{
|
|
if( id.type() == operation_history_object_type && id.space() == protocol_ids ) continue;
|
|
if( id.type() == impl_account_transaction_history_object_type && id.space() == implementation_ids ) continue;
|
|
|
|
this->subscribe_to_item( id );
|
|
}
|
|
}
|
|
|
|
fc::variants result;
|
|
result.reserve(ids.size());
|
|
|
|
std::transform(ids.begin(), ids.end(), std::back_inserter(result),
|
|
[this](object_id_type id) -> fc::variant {
|
|
if(auto obj = _db.find_object(id))
|
|
return obj->to_variant();
|
|
return {};
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Subscriptions //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void database_api::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
|
|
{
|
|
my->set_subscribe_callback( cb, notify_remove_create );
|
|
}
|
|
|
|
void database_api_impl::set_subscribe_callback( std::function<void(const variant&)> cb, bool notify_remove_create )
|
|
{
|
|
//edump((clear_filter));
|
|
_subscribe_callback = cb;
|
|
_notify_remove_create = notify_remove_create;
|
|
_subscribed_accounts.clear();
|
|
|
|
static fc::bloom_parameters param;
|
|
param.projected_element_count = 10000;
|
|
param.false_positive_probability = 1.0/100;
|
|
param.maximum_size = 1024*8*8*2;
|
|
param.compute_optimal_parameters();
|
|
_subscribe_filter = fc::bloom_filter(param);
|
|
}
|
|
|
|
void database_api::set_pending_transaction_callback( std::function<void(const variant&)> cb )
|
|
{
|
|
my->set_pending_transaction_callback( cb );
|
|
}
|
|
|
|
void database_api_impl::set_pending_transaction_callback( std::function<void(const variant&)> cb )
|
|
{
|
|
_pending_trx_callback = cb;
|
|
}
|
|
|
|
void database_api::set_block_applied_callback( std::function<void(const variant& block_id)> cb )
|
|
{
|
|
my->set_block_applied_callback( cb );
|
|
}
|
|
|
|
void database_api_impl::set_block_applied_callback( std::function<void(const variant& block_id)> cb )
|
|
{
|
|
_block_applied_callback = cb;
|
|
}
|
|
|
|
void database_api::cancel_all_subscriptions()
|
|
{
|
|
my->cancel_all_subscriptions();
|
|
}
|
|
|
|
void database_api_impl::cancel_all_subscriptions()
|
|
{
|
|
set_subscribe_callback( std::function<void(const fc::variant&)>(), true);
|
|
_market_subscriptions.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Blocks and transactions //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
optional<block_header> database_api::get_block_header(uint32_t block_num)const
|
|
{
|
|
return my->get_block_header( block_num );
|
|
}
|
|
|
|
optional<block_header> database_api_impl::get_block_header(uint32_t block_num) const
|
|
{
|
|
auto result = _db.fetch_block_by_number(block_num);
|
|
if(result)
|
|
return *result;
|
|
return {};
|
|
}
|
|
map<uint32_t, optional<block_header>> database_api::get_block_header_batch(const vector<uint32_t> block_nums)const
|
|
{
|
|
return my->get_block_header_batch( block_nums );
|
|
}
|
|
|
|
map<uint32_t, optional<block_header>> database_api_impl::get_block_header_batch(const vector<uint32_t> block_nums) const
|
|
{
|
|
map<uint32_t, optional<block_header>> results;
|
|
for (const uint32_t block_num : block_nums)
|
|
{
|
|
results[block_num] = get_block_header(block_num);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
optional<signed_block> database_api::get_block(uint32_t block_num)const
|
|
{
|
|
return my->get_block( block_num );
|
|
}
|
|
|
|
optional<signed_block> database_api_impl::get_block(uint32_t block_num)const
|
|
{
|
|
return _db.fetch_block_by_number(block_num);
|
|
}
|
|
|
|
processed_transaction database_api::get_transaction( uint32_t block_num, uint32_t trx_in_block )const
|
|
{
|
|
return my->get_transaction( block_num, trx_in_block );
|
|
}
|
|
|
|
optional<signed_transaction> database_api::get_recent_transaction_by_id( const transaction_id_type& id )const
|
|
{
|
|
try {
|
|
return my->_db.get_recent_transaction( id );
|
|
} catch ( ... ) {
|
|
return optional<signed_transaction>();
|
|
}
|
|
}
|
|
|
|
processed_transaction database_api_impl::get_transaction(uint32_t block_num, uint32_t trx_num)const
|
|
{
|
|
auto opt_block = _db.fetch_block_by_number(block_num);
|
|
FC_ASSERT( opt_block );
|
|
FC_ASSERT( opt_block->transactions.size() > trx_num );
|
|
return opt_block->transactions[trx_num];
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Globals //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
chain_property_object database_api::get_chain_properties()const
|
|
{
|
|
return my->get_chain_properties();
|
|
}
|
|
|
|
chain_property_object database_api_impl::get_chain_properties()const
|
|
{
|
|
return _db.get(chain_property_id_type());
|
|
}
|
|
|
|
global_property_object database_api::get_global_properties()const
|
|
{
|
|
return my->get_global_properties();
|
|
}
|
|
|
|
global_property_object database_api_impl::get_global_properties()const
|
|
{
|
|
return _db.get(global_property_id_type());
|
|
}
|
|
|
|
fc::variant_object database_api::get_config()const
|
|
{
|
|
return my->get_config();
|
|
}
|
|
|
|
fc::variant_object database_api_impl::get_config()const
|
|
{
|
|
return graphene::chain::get_config();
|
|
}
|
|
|
|
chain_id_type database_api::get_chain_id()const
|
|
{
|
|
return my->get_chain_id();
|
|
}
|
|
|
|
chain_id_type database_api_impl::get_chain_id()const
|
|
{
|
|
return _db.get_chain_id();
|
|
}
|
|
|
|
dynamic_global_property_object database_api::get_dynamic_global_properties()const
|
|
{
|
|
return my->get_dynamic_global_properties();
|
|
}
|
|
|
|
dynamic_global_property_object database_api_impl::get_dynamic_global_properties()const
|
|
{
|
|
return _db.get(dynamic_global_property_id_type());
|
|
}
|
|
|
|
global_betting_statistics_object database_api::get_global_betting_statistics() const
|
|
{
|
|
return my->get_global_betting_statistics();
|
|
}
|
|
|
|
global_betting_statistics_object database_api_impl::get_global_betting_statistics() const
|
|
{
|
|
return _db.get(global_betting_statistics_id_type());
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Keys //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<vector<account_id_type>> database_api::get_key_references( vector<public_key_type> key )const
|
|
{
|
|
return my->get_key_references( key );
|
|
}
|
|
|
|
/**
|
|
* @return all accounts that referr to the key or account id in their owner or active authorities.
|
|
*/
|
|
vector<vector<account_id_type>> database_api_impl::get_key_references( vector<public_key_type> keys )const
|
|
{
|
|
wdump( (keys) );
|
|
|
|
const auto& idx = _db.get_index_type<account_index>();
|
|
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
|
|
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
|
|
|
vector< vector<account_id_type> > final_result;
|
|
final_result.reserve(keys.size());
|
|
|
|
for( auto& key : keys )
|
|
{
|
|
|
|
address a1( pts_address(key, false, 56) );
|
|
address a2( pts_address(key, true, 56) );
|
|
address a3( pts_address(key, false, 0) );
|
|
address a4( pts_address(key, true, 0) );
|
|
address a5( key );
|
|
|
|
subscribe_to_item( key );
|
|
subscribe_to_item( a1 );
|
|
subscribe_to_item( a2 );
|
|
subscribe_to_item( a3 );
|
|
subscribe_to_item( a4 );
|
|
subscribe_to_item( a5 );
|
|
|
|
vector<account_id_type> result;
|
|
|
|
for( auto& a : {a1,a2,a3,a4,a5} )
|
|
{
|
|
auto itr = refs.account_to_address_memberships.find(a);
|
|
if( itr != refs.account_to_address_memberships.end() )
|
|
{
|
|
result.reserve( result.size() + itr->second.size() );
|
|
for( auto item : itr->second )
|
|
{
|
|
wdump((a)(item)(item(_db).name));
|
|
result.push_back(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto itr = refs.account_to_key_memberships.find(key);
|
|
if( itr != refs.account_to_key_memberships.end() )
|
|
{
|
|
result.reserve( result.size() + itr->second.size() );
|
|
for( auto item : itr->second ) result.push_back(item);
|
|
}
|
|
final_result.emplace_back( std::move(result) );
|
|
}
|
|
|
|
for( auto i : final_result )
|
|
subscribe_to_item(i);
|
|
|
|
return final_result;
|
|
}
|
|
|
|
bool database_api::is_public_key_registered(string public_key) const
|
|
{
|
|
return my->is_public_key_registered(public_key);
|
|
}
|
|
|
|
bool database_api_impl::is_public_key_registered(string public_key) const
|
|
{
|
|
// Short-circuit
|
|
if (public_key.empty()) {
|
|
return false;
|
|
}
|
|
|
|
// Search among all keys using an existing map of *current* account keys
|
|
public_key_type key;
|
|
try {
|
|
key = public_key_type(public_key);
|
|
} catch ( ... ) {
|
|
// An invalid public key was detected
|
|
return false;
|
|
}
|
|
const auto& idx = _db.get_index_type<account_index>();
|
|
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
|
|
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
|
auto itr = refs.account_to_key_memberships.find(key);
|
|
bool is_known = itr != refs.account_to_key_memberships.end();
|
|
|
|
return is_known;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Accounts //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
account_id_type database_api::get_account_id_from_string(const std::string& name_or_id)const
|
|
{
|
|
return my->get_account_from_string( name_or_id )->id;
|
|
}
|
|
|
|
vector<optional<account_object>> database_api::get_accounts(const vector<std::string>& account_names_or_ids)const
|
|
{
|
|
return my->get_accounts( account_names_or_ids );
|
|
}
|
|
|
|
vector<optional<account_object>> database_api_impl::get_accounts(const vector<std::string>& account_names_or_ids)const
|
|
{
|
|
vector<optional<account_object>> result; result.reserve(account_names_or_ids.size());
|
|
std::transform(account_names_or_ids.begin(), account_names_or_ids.end(), std::back_inserter(result),
|
|
[this](std::string id_or_name) -> optional<account_object> {
|
|
const account_object *account = get_account_from_string(id_or_name, false);
|
|
if(account == nullptr)
|
|
return {};
|
|
|
|
subscribe_to_item( account->id );
|
|
return *account;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
std::map<string,full_account> database_api::get_full_accounts( const vector<string>& names_or_ids, bool subscribe )
|
|
{
|
|
return my->get_full_accounts( names_or_ids, subscribe );
|
|
}
|
|
|
|
std::map<std::string, full_account> database_api_impl::get_full_accounts( const vector<std::string>& names_or_ids, bool subscribe)
|
|
{
|
|
const auto& proposal_idx = _db.get_index_type<proposal_index>();
|
|
const auto& pidx = dynamic_cast<const base_primary_index&>(proposal_idx);
|
|
const auto& proposals_by_account = pidx.get_secondary_index<graphene::chain::required_approval_index>();
|
|
|
|
std::map<std::string, full_account> results;
|
|
|
|
for (const std::string& account_name_or_id : names_or_ids)
|
|
{
|
|
const account_object* account = nullptr;
|
|
if (std::isdigit(account_name_or_id[0]))
|
|
account = _db.find(fc::variant(account_name_or_id, 1).as<account_id_type>(1));
|
|
else
|
|
{
|
|
const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
|
|
auto itr = idx.find(account_name_or_id);
|
|
if (itr != idx.end())
|
|
account = &*itr;
|
|
}
|
|
if (account == nullptr)
|
|
continue;
|
|
|
|
if( subscribe )
|
|
{
|
|
FC_ASSERT( std::distance(_subscribed_accounts.begin(), _subscribed_accounts.end()) <= 100 );
|
|
_subscribed_accounts.insert( account->get_id() );
|
|
subscribe_to_item( account->id );
|
|
}
|
|
|
|
full_account acnt;
|
|
acnt.account = *account;
|
|
acnt.statistics = account->statistics(_db);
|
|
acnt.registrar_name = account->registrar(_db).name;
|
|
acnt.referrer_name = account->referrer(_db).name;
|
|
acnt.lifetime_referrer_name = account->lifetime_referrer(_db).name;
|
|
acnt.votes = lookup_vote_ids( vector<vote_id_type>(account->options.votes.begin(),account->options.votes.end()) );
|
|
|
|
if (account->cashback_vb)
|
|
{
|
|
acnt.cashback_balance = account->cashback_balance(_db);
|
|
}
|
|
// Add the account's proposals
|
|
auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id );
|
|
if( required_approvals_itr != proposals_by_account._account_to_proposals.end() )
|
|
{
|
|
acnt.proposals.reserve( required_approvals_itr->second.size() );
|
|
for( auto proposal_id : required_approvals_itr->second )
|
|
acnt.proposals.push_back( proposal_id(_db) );
|
|
}
|
|
|
|
|
|
// Add the account's balances
|
|
const auto& balances = _db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >().get_account_balances( account->id );
|
|
for( const auto balance : balances )
|
|
acnt.balances.emplace_back( *balance.second );
|
|
|
|
// Add the account's vesting balances
|
|
auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>().equal_range(account->id);
|
|
std::for_each(vesting_range.first, vesting_range.second,
|
|
[&acnt](const vesting_balance_object& balance) {
|
|
acnt.vesting_balances.emplace_back(balance);
|
|
});
|
|
|
|
// Add the account's orders
|
|
auto order_range = _db.get_index_type<limit_order_index>().indices().get<by_account>().equal_range(account->id);
|
|
std::for_each(order_range.first, order_range.second,
|
|
[&acnt] (const limit_order_object& order) {
|
|
acnt.limit_orders.emplace_back(order);
|
|
});
|
|
auto call_range = _db.get_index_type<call_order_index>().indices().get<by_account>().equal_range(account->id);
|
|
std::for_each(call_range.first, call_range.second,
|
|
[&acnt] (const call_order_object& call) {
|
|
acnt.call_orders.emplace_back(call);
|
|
});
|
|
auto settle_range = _db.get_index_type<force_settlement_index>().indices().get<by_account>().equal_range(account->id);
|
|
std::for_each(settle_range.first, settle_range.second,
|
|
[&acnt] (const force_settlement_object& settle) {
|
|
acnt.settle_orders.emplace_back(settle);
|
|
});
|
|
|
|
// get assets issued by user
|
|
auto asset_range = _db.get_index_type<asset_index>().indices().get<by_issuer>().equal_range(account->id);
|
|
std::for_each(asset_range.first, asset_range.second,
|
|
[&acnt] (const asset_object& asset) {
|
|
acnt.assets.emplace_back(asset.id);
|
|
});
|
|
|
|
// get withdraws permissions
|
|
auto withdraw_range = _db.get_index_type<withdraw_permission_index>().indices().get<by_from>().equal_range(account->id);
|
|
std::for_each(withdraw_range.first, withdraw_range.second,
|
|
[&acnt] (const withdraw_permission_object& withdraw) {
|
|
acnt.withdraws.emplace_back(withdraw);
|
|
});
|
|
|
|
auto pending_payouts_range =
|
|
_db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>().indices().get<by_account_dividend_payout>().equal_range(boost::make_tuple(account->id));
|
|
|
|
std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments));
|
|
|
|
results[account_name_or_id] = acnt;
|
|
}
|
|
return results;
|
|
}
|
|
|
|
optional<account_object> database_api::get_account_by_name( string name )const
|
|
{
|
|
return my->get_account_by_name( name );
|
|
}
|
|
|
|
optional<account_object> database_api_impl::get_account_by_name( string name )const
|
|
{
|
|
const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
|
|
auto itr = idx.find(name);
|
|
if (itr != idx.end())
|
|
return *itr;
|
|
return optional<account_object>();
|
|
}
|
|
|
|
vector<account_id_type> database_api::get_account_references( const std::string account_id_or_name )const
|
|
{
|
|
return my->get_account_references( account_id_or_name );
|
|
}
|
|
|
|
vector<account_id_type> database_api_impl::get_account_references( const std::string account_id_or_name )const
|
|
{
|
|
const auto& idx = _db.get_index_type<account_index>();
|
|
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
|
|
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
|
const account_id_type account_id = get_account_from_string(account_id_or_name)->id;
|
|
auto itr = refs.account_to_account_memberships.find(account_id);
|
|
vector<account_id_type> result;
|
|
|
|
if( itr != refs.account_to_account_memberships.end() )
|
|
{
|
|
result.reserve( itr->second.size() );
|
|
for( auto item : itr->second ) result.push_back(item);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<optional<account_object>> database_api::lookup_account_names(const vector<string>& account_names)const
|
|
{
|
|
return my->lookup_account_names( account_names );
|
|
}
|
|
|
|
vector<optional<account_object>> database_api_impl::lookup_account_names(const vector<string>& account_names)const
|
|
{
|
|
const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();
|
|
vector<optional<account_object> > result;
|
|
result.reserve(account_names.size());
|
|
std::transform(account_names.begin(), account_names.end(), std::back_inserter(result),
|
|
[&accounts_by_name](const string& name) -> optional<account_object> {
|
|
auto itr = accounts_by_name.find(name);
|
|
return itr == accounts_by_name.end()? optional<account_object>() : *itr;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
map<string,account_id_type> database_api::lookup_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
return my->lookup_accounts( lower_bound_name, limit );
|
|
}
|
|
|
|
map<string,account_id_type> database_api_impl::lookup_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
FC_ASSERT( limit <= 1000 );
|
|
const auto& accounts_by_name = _db.get_index_type<account_index>().indices().get<by_name>();
|
|
map<string,account_id_type> result;
|
|
|
|
for( auto itr = accounts_by_name.lower_bound(lower_bound_name);
|
|
limit-- && itr != accounts_by_name.end();
|
|
++itr )
|
|
{
|
|
result.insert(make_pair(itr->name, itr->get_id()));
|
|
if( limit == 1 )
|
|
subscribe_to_item( itr->get_id() );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint64_t database_api::get_account_count()const
|
|
{
|
|
return my->get_account_count();
|
|
}
|
|
|
|
uint64_t database_api_impl::get_account_count()const
|
|
{
|
|
return _db.get_index_type<account_index>().indices().size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Balances //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<asset> database_api::get_account_balances(const std::string& account_name_or_id, const flat_set<asset_id_type>& assets)const
|
|
{
|
|
return my->get_account_balances( account_name_or_id, assets );
|
|
}
|
|
|
|
vector<asset> database_api_impl::get_account_balances( const std::string& account_name_or_id,
|
|
const flat_set<asset_id_type>& assets)const
|
|
{
|
|
const account_object* account = get_account_from_string(account_name_or_id);
|
|
account_id_type acnt = account->id;
|
|
vector<asset> result;
|
|
if (assets.empty())
|
|
{
|
|
// if the caller passes in an empty list of assets, return balances for all assets the account owns
|
|
const auto& balance_index = _db.get_index_type< primary_index< account_balance_index > >();
|
|
const auto& balances = balance_index.get_secondary_index< balances_by_account_index >().get_account_balances( acnt );
|
|
for( const auto balance : balances )
|
|
result.push_back( balance.second->get_balance() );
|
|
}
|
|
else
|
|
{
|
|
result.reserve(assets.size());
|
|
|
|
std::transform(assets.begin(), assets.end(), std::back_inserter(result),
|
|
[this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<asset> database_api::get_named_account_balances(const std::string& name, const flat_set<asset_id_type>& assets)const
|
|
{
|
|
return my->get_account_balances( name, assets );
|
|
}
|
|
|
|
vector<balance_object> database_api::get_balance_objects( const vector<address>& addrs )const
|
|
{
|
|
return my->get_balance_objects( addrs );
|
|
}
|
|
|
|
vector<balance_object> database_api_impl::get_balance_objects( const vector<address>& addrs )const
|
|
{
|
|
try
|
|
{
|
|
const auto& bal_idx = _db.get_index_type<balance_index>();
|
|
const auto& by_owner_idx = bal_idx.indices().get<by_owner>();
|
|
|
|
vector<balance_object> result;
|
|
|
|
for( const auto& owner : addrs )
|
|
{
|
|
subscribe_to_item( owner );
|
|
auto itr = by_owner_idx.lower_bound( boost::make_tuple( owner, asset_id_type(0) ) );
|
|
while( itr != by_owner_idx.end() && itr->owner == owner )
|
|
{
|
|
result.push_back( *itr );
|
|
++itr;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
FC_CAPTURE_AND_RETHROW( (addrs) )
|
|
}
|
|
|
|
vector<asset> database_api::get_vested_balances( const vector<balance_id_type>& objs )const
|
|
{
|
|
return my->get_vested_balances( objs );
|
|
}
|
|
|
|
vector<asset> database_api_impl::get_vested_balances( const vector<balance_id_type>& objs )const
|
|
{
|
|
try
|
|
{
|
|
vector<asset> result;
|
|
result.reserve( objs.size() );
|
|
auto now = _db.head_block_time();
|
|
for( auto obj : objs )
|
|
result.push_back( obj(_db).available( now ) );
|
|
return result;
|
|
} FC_CAPTURE_AND_RETHROW( (objs) )
|
|
}
|
|
|
|
vector<vesting_balance_object> database_api::get_vesting_balances( const std::string account_id_or_name )const
|
|
{
|
|
return my->get_vesting_balances( account_id_or_name );
|
|
}
|
|
|
|
vector<vesting_balance_object> database_api_impl::get_vesting_balances( const std::string account_id_or_name )const
|
|
{
|
|
try
|
|
{
|
|
const account_id_type account_id = get_account_from_string(account_id_or_name)->id;
|
|
vector<vesting_balance_object> result;
|
|
auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>().equal_range(account_id);
|
|
std::for_each(vesting_range.first, vesting_range.second,
|
|
[&result](const vesting_balance_object& balance) {
|
|
if(balance.balance.amount > 0)
|
|
result.emplace_back(balance);
|
|
});
|
|
return result;
|
|
}
|
|
FC_CAPTURE_AND_RETHROW( (account_id_or_name) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Assets //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
asset_id_type database_api::get_asset_id_from_string(const std::string& symbol_or_id)const
|
|
{
|
|
return my->get_asset_from_string( symbol_or_id )->id;
|
|
}
|
|
|
|
const asset_object* database_api_impl::get_asset_from_string( const std::string& symbol_or_id,
|
|
bool throw_if_not_found ) const
|
|
{
|
|
// TODO cache the result to avoid repeatly fetching from db
|
|
FC_ASSERT( symbol_or_id.size() > 0);
|
|
const asset_object* asset = nullptr;
|
|
if (std::isdigit(symbol_or_id[0]))
|
|
asset = _db.find(fc::variant(symbol_or_id, 1).as<asset_id_type>(1));
|
|
else
|
|
{
|
|
const auto& idx = _db.get_index_type<asset_index>().indices().get<by_symbol>();
|
|
auto itr = idx.find(symbol_or_id);
|
|
if (itr != idx.end())
|
|
asset = &*itr;
|
|
}
|
|
if(throw_if_not_found)
|
|
FC_ASSERT( asset, "no such asset" );
|
|
return asset;
|
|
}
|
|
|
|
vector<optional<asset_object>> database_api::get_assets(const vector<std::string>& asset_symbols_or_ids)const
|
|
{
|
|
return my->get_assets( asset_symbols_or_ids );
|
|
}
|
|
|
|
vector<optional<asset_object>> database_api_impl::get_assets(const vector<std::string>& asset_symbols_or_ids)const
|
|
{
|
|
vector<optional<asset_object>> result; result.reserve(asset_symbols_or_ids.size());
|
|
std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result),
|
|
[this](std::string id_or_name) -> optional<asset_object> {
|
|
const asset_object* asset_obj = get_asset_from_string( id_or_name, false );
|
|
if( asset_obj == nullptr )
|
|
return {};
|
|
subscribe_to_item(asset_obj->id );
|
|
return asset_object( *asset_obj );
|
|
});
|
|
return result;
|
|
}
|
|
|
|
vector<optional<asset_object>> database_api_impl::get_assets(const vector<asset_id_type>& asset_ids)const
|
|
{
|
|
vector<optional<asset_object>> result; result.reserve(asset_ids.size());
|
|
std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result),
|
|
[this](asset_id_type id) -> optional<asset_object> {
|
|
if(auto o = _db.find(id))
|
|
{
|
|
subscribe_to_item( id );
|
|
return *o;
|
|
}
|
|
return {};
|
|
});
|
|
return result;
|
|
}
|
|
|
|
vector<asset_object> database_api::list_assets(const string& lower_bound_symbol, uint32_t limit)const
|
|
{
|
|
return my->list_assets( lower_bound_symbol, limit );
|
|
}
|
|
|
|
vector<asset_object> database_api_impl::list_assets(const string& lower_bound_symbol, uint32_t limit)const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();
|
|
vector<asset_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = assets_by_symbol.lower_bound(lower_bound_symbol);
|
|
|
|
if( lower_bound_symbol == "" )
|
|
itr = assets_by_symbol.begin();
|
|
|
|
while(limit-- && itr != assets_by_symbol.end())
|
|
result.emplace_back(*itr++);
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<optional<asset_object>> database_api::lookup_asset_symbols(const vector<string>& symbols_or_ids)const
|
|
{
|
|
return my->lookup_asset_symbols( symbols_or_ids );
|
|
}
|
|
|
|
vector<optional<asset_object>> database_api_impl::lookup_asset_symbols(const vector<string>& symbols_or_ids)const
|
|
{
|
|
const auto& assets_by_symbol = _db.get_index_type<asset_index>().indices().get<by_symbol>();
|
|
vector<optional<asset_object> > result;
|
|
result.reserve(symbols_or_ids.size());
|
|
std::transform(symbols_or_ids.begin(), symbols_or_ids.end(), std::back_inserter(result),
|
|
[this, &assets_by_symbol](const string& symbol_or_id) -> optional<asset_object> {
|
|
if( !symbol_or_id.empty() && std::isdigit(symbol_or_id[0]) )
|
|
{
|
|
auto ptr = _db.find(variant(symbol_or_id, 1).as<asset_id_type>(1));
|
|
return ptr == nullptr? optional<asset_object>() : *ptr;
|
|
}
|
|
auto itr = assets_by_symbol.find(symbol_or_id);
|
|
return itr == assets_by_symbol.end()? optional<asset_object>() : *itr;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
uint64_t database_api::get_asset_count()const
|
|
{
|
|
return my->get_asset_count();
|
|
}
|
|
|
|
uint64_t database_api_impl::get_asset_count()const
|
|
{
|
|
return _db.get_index_type<asset_index>().indices().size();
|
|
}
|
|
////////////////////
|
|
// Lottery Assets //
|
|
////////////////////
|
|
|
|
|
|
vector<asset_object> database_api::get_lotteries( asset_id_type stop,
|
|
unsigned limit,
|
|
asset_id_type start )const
|
|
{
|
|
return my->get_lotteries( stop, limit, start );
|
|
}
|
|
vector<asset_object> database_api_impl::get_lotteries( asset_id_type stop,
|
|
unsigned limit,
|
|
asset_id_type start )const
|
|
{
|
|
vector<asset_object> result;
|
|
if( limit > 100 ) limit = 100;
|
|
const auto& assets = _db.get_index_type<asset_index>().indices().get<by_lottery>();
|
|
|
|
const auto range = assets.equal_range( boost::make_tuple( true ) );
|
|
for( const auto& a : boost::make_iterator_range( range.first, range.second ) )
|
|
{
|
|
if( start == asset_id_type() || (a.get_id().instance.value <= start.instance.value) )
|
|
result.push_back( a );
|
|
if( a.get_id().instance.value < stop.instance.value || result.size() >= limit )
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
vector<asset_object> database_api::get_account_lotteries( account_id_type issuer,
|
|
asset_id_type stop,
|
|
unsigned limit,
|
|
asset_id_type start )const
|
|
{
|
|
return my->get_account_lotteries( issuer, stop, limit, start );
|
|
}
|
|
|
|
vector<asset_object> database_api_impl::get_account_lotteries( account_id_type issuer,
|
|
asset_id_type stop,
|
|
unsigned limit,
|
|
asset_id_type start )const
|
|
{
|
|
vector<asset_object> result;
|
|
if( limit > 100 ) limit = 100;
|
|
const auto& assets = _db.get_index_type<asset_index>().indices().get<by_lottery_owner>();
|
|
|
|
const auto range = assets.equal_range( boost::make_tuple( true, issuer.instance.value ) );
|
|
for( const auto& a : boost::make_iterator_range( range.first, range.second ) )
|
|
{
|
|
if( start == asset_id_type() || (a.get_id().instance.value <= start.instance.value) )
|
|
result.push_back( a );
|
|
if( a.get_id().instance.value < stop.instance.value || result.size() >= limit )
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
asset database_api::get_lottery_balance( asset_id_type lottery_id )const
|
|
{
|
|
return my->get_lottery_balance( lottery_id );
|
|
}
|
|
|
|
asset database_api_impl::get_lottery_balance( asset_id_type lottery_id )const
|
|
{
|
|
auto lottery_asset = lottery_id( _db );
|
|
FC_ASSERT( lottery_asset.is_lottery() );
|
|
return _db.get_balance( lottery_id );
|
|
}
|
|
|
|
sweeps_vesting_balance_object database_api::get_sweeps_vesting_balance_object( account_id_type account )const
|
|
{
|
|
return my->get_sweeps_vesting_balance_object( account );
|
|
}
|
|
|
|
sweeps_vesting_balance_object database_api_impl::get_sweeps_vesting_balance_object( account_id_type account )const
|
|
{
|
|
const auto& vesting_idx = _db.get_index_type<sweeps_vesting_balance_index>().indices().get<by_owner>();
|
|
auto account_balance = vesting_idx.find(account);
|
|
FC_ASSERT( account_balance != vesting_idx.end(), "NO SWEEPS VESTING BALANCE" );
|
|
return *account_balance;
|
|
}
|
|
|
|
asset database_api::get_sweeps_vesting_balance_available_for_claim( account_id_type account )const
|
|
{
|
|
return my->get_sweeps_vesting_balance_available_for_claim( account );
|
|
}
|
|
|
|
asset database_api_impl::get_sweeps_vesting_balance_available_for_claim( account_id_type account )const
|
|
{
|
|
const auto& vesting_idx = _db.get_index_type<sweeps_vesting_balance_index>().indices().get<by_owner>();
|
|
auto account_balance = vesting_idx.find(account);
|
|
FC_ASSERT( account_balance != vesting_idx.end(), "NO SWEEPS VESTING BALANCE" );
|
|
return account_balance->available_for_claim();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Peerplays //
|
|
//////////////////////////////////////////////////////////////////////
|
|
vector<sport_object> database_api::list_sports() const
|
|
{
|
|
return my->list_sports();
|
|
}
|
|
|
|
vector<sport_object> database_api_impl::list_sports() const
|
|
{
|
|
const auto& sport_object_idx = _db.get_index_type<sport_object_index>().indices().get<by_id>();
|
|
return boost::copy_range<vector<sport_object> >(sport_object_idx);
|
|
}
|
|
|
|
vector<event_group_object> database_api::list_event_groups(sport_id_type sport_id) const
|
|
{
|
|
return my->list_event_groups(sport_id);
|
|
}
|
|
|
|
vector<event_group_object> database_api_impl::list_event_groups(sport_id_type sport_id) const
|
|
{
|
|
const auto& event_group_idx = _db.get_index_type<event_group_object_index>().indices().get<by_sport_id>();
|
|
return boost::copy_range<vector<event_group_object> >(event_group_idx.equal_range(sport_id));
|
|
}
|
|
|
|
vector<event_object> database_api::list_events_in_group(event_group_id_type event_group_id) const
|
|
{
|
|
return my->list_events_in_group(event_group_id);
|
|
}
|
|
|
|
vector<event_object> database_api_impl::list_events_in_group(event_group_id_type event_group_id) const
|
|
{
|
|
const auto& event_idx = _db.get_index_type<event_object_index>().indices().get<by_event_group_id>();
|
|
return boost::copy_range<vector<event_object> >(event_idx.equal_range(event_group_id));
|
|
}
|
|
|
|
vector<betting_market_group_object> database_api::list_betting_market_groups(event_id_type event_id) const
|
|
{
|
|
return my->list_betting_market_groups(event_id);
|
|
}
|
|
|
|
vector<betting_market_group_object> database_api_impl::list_betting_market_groups(event_id_type event_id) const
|
|
{
|
|
const auto& betting_market_group_idx = _db.get_index_type<betting_market_group_object_index>().indices().get<by_event_id>();
|
|
return boost::copy_range<vector<betting_market_group_object> >(betting_market_group_idx.equal_range(event_id));
|
|
}
|
|
|
|
vector<betting_market_object> database_api::list_betting_markets(betting_market_group_id_type betting_market_group_id) const
|
|
{
|
|
return my->list_betting_markets(betting_market_group_id);
|
|
}
|
|
|
|
vector<betting_market_object> database_api_impl::list_betting_markets(betting_market_group_id_type betting_market_group_id) const
|
|
{
|
|
const auto& betting_market_idx = _db.get_index_type<betting_market_object_index>().indices().get<by_betting_market_group_id>();
|
|
return boost::copy_range<vector<betting_market_object> >(betting_market_idx.equal_range(betting_market_group_id));
|
|
}
|
|
|
|
vector<bet_object> database_api::get_unmatched_bets_for_bettor(betting_market_id_type betting_market_id, account_id_type bettor_id) const
|
|
{
|
|
return my->get_unmatched_bets_for_bettor(betting_market_id, bettor_id);
|
|
}
|
|
|
|
vector<bet_object> database_api_impl::get_unmatched_bets_for_bettor(betting_market_id_type betting_market_id, account_id_type bettor_id) const
|
|
{
|
|
const auto& bet_idx = _db.get_index_type<bet_object_index>().indices().get<by_bettor_and_odds>();
|
|
return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id, betting_market_id)));
|
|
}
|
|
|
|
vector<bet_object> database_api::get_all_unmatched_bets_for_bettor(account_id_type bettor_id) const
|
|
{
|
|
return my->get_all_unmatched_bets_for_bettor(bettor_id);
|
|
}
|
|
|
|
vector<bet_object> database_api_impl::get_all_unmatched_bets_for_bettor(account_id_type bettor_id) const
|
|
{
|
|
const auto& bet_idx = _db.get_index_type<bet_object_index>().indices().get<by_bettor_and_odds>();
|
|
return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id)));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Markets / feeds //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<limit_order_object> database_api::get_limit_orders(const std::string& a, const std::string& b, const uint32_t limit)const
|
|
{
|
|
return my->get_limit_orders( a, b, limit );
|
|
}
|
|
|
|
/**
|
|
* @return the limit orders for both sides of the book for the two assets specified up to limit number on each side.
|
|
*/
|
|
vector<limit_order_object> database_api_impl::get_limit_orders(const std::string& a, const std::string& b, const uint32_t limit)const
|
|
{
|
|
const asset_id_type asset_a_id = get_asset_from_string(a)->id;
|
|
const asset_id_type asset_b_id = get_asset_from_string(b)->id;
|
|
|
|
return get_limit_orders(asset_a_id, asset_b_id, limit);
|
|
}
|
|
|
|
vector<limit_order_object> database_api_impl::get_limit_orders( const asset_id_type a, const asset_id_type b,
|
|
const uint32_t limit )const
|
|
{
|
|
const auto& limit_order_idx = _db.get_index_type<limit_order_index>();
|
|
const auto& limit_price_idx = limit_order_idx.indices().get<by_price>();
|
|
|
|
vector<limit_order_object> result;
|
|
result.reserve(limit*2);
|
|
|
|
uint32_t count = 0;
|
|
auto limit_itr = limit_price_idx.lower_bound(price::max(a,b));
|
|
auto limit_end = limit_price_idx.upper_bound(price::min(a,b));
|
|
while(limit_itr != limit_end && count < limit)
|
|
{
|
|
result.push_back(*limit_itr);
|
|
++limit_itr;
|
|
++count;
|
|
}
|
|
count = 0;
|
|
limit_itr = limit_price_idx.lower_bound(price::max(b,a));
|
|
limit_end = limit_price_idx.upper_bound(price::min(b,a));
|
|
while(limit_itr != limit_end && count < limit)
|
|
{
|
|
result.push_back(*limit_itr);
|
|
++limit_itr;
|
|
++count;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<call_order_object> database_api::get_call_orders(const std::string& a, uint32_t limit)const
|
|
{
|
|
return my->get_call_orders( a, limit );
|
|
}
|
|
|
|
vector<call_order_object> database_api_impl::get_call_orders(const std::string& a, uint32_t limit)const
|
|
{
|
|
const auto& call_index = _db.get_index_type<call_order_index>().indices().get<by_price>();
|
|
const asset_object* mia = get_asset_from_string(a);
|
|
price index_price = price::min(mia->bitasset_data(_db).options.short_backing_asset, mia->get_id());
|
|
|
|
return vector<call_order_object>(call_index.lower_bound(index_price.min()),
|
|
call_index.lower_bound(index_price.max()));
|
|
}
|
|
|
|
vector<force_settlement_object> database_api::get_settle_orders(const std::string& a, uint32_t limit)const
|
|
{
|
|
return my->get_settle_orders( a, limit );
|
|
}
|
|
|
|
vector<force_settlement_object> database_api_impl::get_settle_orders(const std::string& a, uint32_t limit)const
|
|
{
|
|
const auto& settle_index = _db.get_index_type<force_settlement_index>().indices().get<by_expiration>();
|
|
const asset_object* mia = get_asset_from_string(a);
|
|
return vector<force_settlement_object>(settle_index.lower_bound(mia->get_id()),
|
|
settle_index.upper_bound(mia->get_id()));
|
|
}
|
|
|
|
vector<call_order_object> database_api::get_margin_positions( const std::string account_id_or_name )const
|
|
{
|
|
return my->get_margin_positions( account_id_or_name );
|
|
}
|
|
|
|
vector<call_order_object> database_api_impl::get_margin_positions( const std::string account_id_or_name )const
|
|
{
|
|
try
|
|
{
|
|
const auto& idx = _db.get_index_type<call_order_index>();
|
|
const auto& aidx = idx.indices().get<by_account>();
|
|
const account_id_type id = get_account_from_string(account_id_or_name)->id;
|
|
auto start = aidx.lower_bound( boost::make_tuple( id, asset_id_type(0) ) );
|
|
auto end = aidx.lower_bound( boost::make_tuple( id+1, asset_id_type(0) ) );
|
|
vector<call_order_object> result;
|
|
while( start != end )
|
|
{
|
|
result.push_back(*start);
|
|
++start;
|
|
}
|
|
return result;
|
|
} FC_CAPTURE_AND_RETHROW( (account_id_or_name) )
|
|
}
|
|
|
|
void database_api::subscribe_to_market(std::function<void(const variant&)> callback, const std::string& a, const std::string& b)
|
|
{
|
|
my->subscribe_to_market( callback, a, b );
|
|
}
|
|
|
|
void database_api_impl::subscribe_to_market(std::function<void(const variant&)> callback, const std::string& a, const std::string& b)
|
|
{
|
|
auto asset_a_id = get_asset_from_string(a)->id;
|
|
auto asset_b_id = get_asset_from_string(b)->id;
|
|
|
|
if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id);
|
|
FC_ASSERT(asset_a_id != asset_b_id);
|
|
_market_subscriptions[ std::make_pair(asset_a_id,asset_b_id) ] = callback;
|
|
}
|
|
|
|
void database_api::unsubscribe_from_market(const std::string& a, const std::string& b)
|
|
{
|
|
my->unsubscribe_from_market( a, b );
|
|
}
|
|
|
|
void database_api_impl::unsubscribe_from_market(const std::string& a, const std::string& b)
|
|
{
|
|
auto asset_a_id = get_asset_from_string(a)->id;
|
|
auto asset_b_id = get_asset_from_string(b)->id;
|
|
|
|
if(asset_a_id > asset_b_id) std::swap(asset_a_id,asset_b_id);
|
|
FC_ASSERT(asset_a_id != asset_b_id);
|
|
_market_subscriptions.erase(std::make_pair(asset_a_id,asset_b_id));
|
|
}
|
|
|
|
market_ticker database_api::get_ticker( const string& base, const string& quote )const
|
|
{
|
|
return my->get_ticker( base, quote );
|
|
}
|
|
|
|
market_ticker database_api_impl::get_ticker( const string& base, const string& quote )const
|
|
{
|
|
const auto assets = lookup_asset_symbols( {base, quote} );
|
|
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
|
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
|
|
|
market_ticker result;
|
|
result.base = base;
|
|
result.quote = quote;
|
|
result.latest = 0;
|
|
result.lowest_ask = 0;
|
|
result.highest_bid = 0;
|
|
result.percent_change = 0;
|
|
result.base_volume = 0;
|
|
result.quote_volume = 0;
|
|
|
|
try {
|
|
const fc::time_point_sec now = fc::time_point::now();
|
|
const fc::time_point_sec yesterday = fc::time_point_sec( now.sec_since_epoch() - 86400 );
|
|
const auto batch_size = 100;
|
|
|
|
vector<market_trade> trades = get_trade_history( base, quote, now, yesterday, batch_size );
|
|
if( !trades.empty() )
|
|
{
|
|
result.latest = trades[0].price;
|
|
|
|
while( !trades.empty() )
|
|
{
|
|
for( const market_trade& t: trades )
|
|
{
|
|
result.base_volume += t.value;
|
|
result.quote_volume += t.amount;
|
|
}
|
|
|
|
trades = get_trade_history( base, quote, trades.back().date, yesterday, batch_size );
|
|
}
|
|
|
|
const auto last_trade_yesterday = get_trade_history( base, quote, yesterday, fc::time_point_sec(), 1 );
|
|
if( !last_trade_yesterday.empty() )
|
|
{
|
|
const auto price_yesterday = last_trade_yesterday[0].price;
|
|
result.percent_change = ( (result.latest / price_yesterday) - 1 ) * 100;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const auto last_trade = get_trade_history( base, quote, now, fc::time_point_sec(), 1 );
|
|
if( !last_trade.empty() )
|
|
result.latest = last_trade[0].price;
|
|
}
|
|
|
|
const auto orders = get_order_book( base, quote, 1 );
|
|
if( !orders.asks.empty() ) result.lowest_ask = orders.asks[0].price;
|
|
if( !orders.bids.empty() ) result.highest_bid = orders.bids[0].price;
|
|
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
|
|
|
|
return result;
|
|
}
|
|
|
|
market_volume database_api::get_24_volume( const string& base, const string& quote )const
|
|
{
|
|
return my->get_24_volume( base, quote );
|
|
}
|
|
|
|
market_volume database_api_impl::get_24_volume( const string& base, const string& quote )const
|
|
{
|
|
const auto ticker = get_ticker( base, quote );
|
|
|
|
market_volume result;
|
|
result.base = ticker.base;
|
|
result.quote = ticker.quote;
|
|
result.base_volume = ticker.base_volume;
|
|
result.quote_volume = ticker.quote_volume;
|
|
|
|
return result;
|
|
}
|
|
|
|
order_book database_api::get_order_book( const string& base, const string& quote, unsigned limit )const
|
|
{
|
|
return my->get_order_book( base, quote, limit);
|
|
}
|
|
|
|
order_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const
|
|
{
|
|
using boost::multiprecision::uint128_t;
|
|
FC_ASSERT( limit <= 50 );
|
|
|
|
order_book result;
|
|
result.base = base;
|
|
result.quote = quote;
|
|
|
|
auto assets = lookup_asset_symbols( {base, quote} );
|
|
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
|
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
|
|
|
auto base_id = assets[0]->id;
|
|
auto quote_id = assets[1]->id;
|
|
auto orders = get_limit_orders( base_id, quote_id, limit );
|
|
|
|
|
|
auto asset_to_real = [&]( const asset& a, int p ) { return double(a.amount.value)/pow( 10, p ); };
|
|
auto price_to_real = [&]( const price& p )
|
|
{
|
|
if( p.base.asset_id == base_id )
|
|
return asset_to_real( p.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision );
|
|
else
|
|
return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision );
|
|
};
|
|
|
|
for( const auto& o : orders )
|
|
{
|
|
if( o.sell_price.base.asset_id == base_id )
|
|
{
|
|
order ord;
|
|
ord.price = price_to_real( o.sell_price );
|
|
ord.quote = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[1]->precision );
|
|
ord.base = asset_to_real( o.for_sale, assets[0]->precision );
|
|
result.bids.push_back( ord );
|
|
}
|
|
else
|
|
{
|
|
order ord;
|
|
ord.price = price_to_real( o.sell_price );
|
|
ord.quote = asset_to_real( o.for_sale, assets[1]->precision );
|
|
ord.base = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[0]->precision );
|
|
result.asks.push_back( ord );
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<market_trade> database_api::get_trade_history( const string& base,
|
|
const string& quote,
|
|
fc::time_point_sec start,
|
|
fc::time_point_sec stop,
|
|
unsigned limit )const
|
|
{
|
|
return my->get_trade_history( base, quote, start, stop, limit );
|
|
}
|
|
|
|
vector<market_trade> database_api_impl::get_trade_history( const string& base,
|
|
const string& quote,
|
|
fc::time_point_sec start,
|
|
fc::time_point_sec stop,
|
|
unsigned limit )const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
|
|
auto assets = lookup_asset_symbols( {base, quote} );
|
|
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
|
|
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
|
|
|
|
auto base_id = assets[0]->id;
|
|
auto quote_id = assets[1]->id;
|
|
|
|
if( base_id > quote_id ) std::swap( base_id, quote_id );
|
|
const auto& history_idx = _db.get_index_type<graphene::market_history::history_index>().indices().get<by_key>();
|
|
history_key hkey;
|
|
hkey.base = base_id;
|
|
hkey.quote = quote_id;
|
|
hkey.sequence = std::numeric_limits<int64_t>::min();
|
|
|
|
auto price_to_real = [&]( const share_type a, int p ) { return double( a.value ) / pow( 10, p ); };
|
|
|
|
if ( start.sec_since_epoch() == 0 )
|
|
start = fc::time_point_sec( fc::time_point::now() );
|
|
|
|
uint32_t count = 0;
|
|
auto itr = history_idx.lower_bound( hkey );
|
|
vector<market_trade> result;
|
|
|
|
while( itr != history_idx.end() && count < limit && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) )
|
|
{
|
|
if( itr->time < start )
|
|
{
|
|
market_trade trade;
|
|
|
|
if( assets[0]->id == itr->op.receives.asset_id )
|
|
{
|
|
trade.amount = price_to_real( itr->op.pays.amount, assets[1]->precision );
|
|
trade.value = price_to_real( itr->op.receives.amount, assets[0]->precision );
|
|
}
|
|
else
|
|
{
|
|
trade.amount = price_to_real( itr->op.receives.amount, assets[1]->precision );
|
|
trade.value = price_to_real( itr->op.pays.amount, assets[0]->precision );
|
|
}
|
|
|
|
trade.date = itr->time;
|
|
trade.price = trade.value / trade.amount;
|
|
|
|
result.push_back( trade );
|
|
++count;
|
|
}
|
|
|
|
// Trades are tracked in each direction.
|
|
++itr;
|
|
++itr;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Witnesses //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<optional<witness_object>> database_api::get_witnesses(const vector<witness_id_type>& witness_ids)const
|
|
{
|
|
return my->get_witnesses( witness_ids );
|
|
}
|
|
|
|
vector<worker_object> database_api::get_workers_by_account(const std::string account_id_or_name)const
|
|
{
|
|
const auto& idx = my->_db.get_index_type<worker_index>().indices().get<by_account>();
|
|
const account_id_type account = my->get_account_from_string(account_id_or_name)->id;
|
|
auto itr = idx.find(account);
|
|
vector<worker_object> result;
|
|
|
|
if( itr != idx.end() && itr->worker_account == account )
|
|
{
|
|
result.emplace_back( *itr );
|
|
++itr;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
vector<optional<witness_object>> database_api_impl::get_witnesses(const vector<witness_id_type>& witness_ids)const
|
|
{
|
|
vector<optional<witness_object>> result; result.reserve(witness_ids.size());
|
|
std::transform(witness_ids.begin(), witness_ids.end(), std::back_inserter(result),
|
|
[this](witness_id_type id) -> optional<witness_object> {
|
|
if(auto o = _db.find(id))
|
|
return *o;
|
|
return {};
|
|
});
|
|
return result;
|
|
}
|
|
|
|
fc::optional<witness_object> database_api::get_witness_by_account(const std::string account_id_or_name)const
|
|
{
|
|
return my->get_witness_by_account( account_id_or_name );
|
|
}
|
|
|
|
fc::optional<witness_object> database_api_impl::get_witness_by_account(const std::string account_id_or_name) const
|
|
{
|
|
const auto& idx = _db.get_index_type<witness_index>().indices().get<by_account>();
|
|
const account_id_type account = get_account_from_string(account_id_or_name)->id;
|
|
auto itr = idx.find(account);
|
|
if( itr != idx.end() )
|
|
return *itr;
|
|
return {};
|
|
}
|
|
|
|
map<string, witness_id_type> database_api::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
return my->lookup_witness_accounts( lower_bound_name, limit );
|
|
}
|
|
|
|
map<string, witness_id_type> database_api_impl::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
FC_ASSERT( limit <= 1000 );
|
|
const auto& witnesses_by_id = _db.get_index_type<witness_index>().indices().get<by_id>();
|
|
|
|
// we want to order witnesses by account name, but that name is in the account object
|
|
// so the witness_index doesn't have a quick way to access it.
|
|
// get all the names and look them all up, sort them, then figure out what
|
|
// records to return. This could be optimized, but we expect the
|
|
// number of witnesses to be few and the frequency of calls to be rare
|
|
std::map<std::string, witness_id_type> witnesses_by_account_name;
|
|
for (const witness_object& witness : witnesses_by_id)
|
|
if (auto account_iter = _db.find(witness.witness_account))
|
|
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
|
|
witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id));
|
|
|
|
auto end_iter = witnesses_by_account_name.begin();
|
|
while (end_iter != witnesses_by_account_name.end() && limit--)
|
|
++end_iter;
|
|
witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end());
|
|
return witnesses_by_account_name;
|
|
}
|
|
|
|
uint64_t database_api::get_witness_count()const
|
|
{
|
|
return my->get_witness_count();
|
|
}
|
|
|
|
uint64_t database_api_impl::get_witness_count()const
|
|
{
|
|
return _db.get_index_type<witness_index>().indices().size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Committee members //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<optional<committee_member_object>> database_api::get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const
|
|
{
|
|
return my->get_committee_members( committee_member_ids );
|
|
}
|
|
|
|
vector<optional<committee_member_object>> database_api_impl::get_committee_members(const vector<committee_member_id_type>& committee_member_ids)const
|
|
{
|
|
vector<optional<committee_member_object>> result; result.reserve(committee_member_ids.size());
|
|
std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result),
|
|
[this](committee_member_id_type id) -> optional<committee_member_object> {
|
|
if(auto o = _db.find(id))
|
|
return *o;
|
|
return {};
|
|
});
|
|
return result;
|
|
}
|
|
|
|
fc::optional<committee_member_object> database_api::get_committee_member_by_account(const std::string account_id_or_name)const
|
|
{
|
|
return my->get_committee_member_by_account( account_id_or_name );
|
|
}
|
|
|
|
fc::optional<committee_member_object> database_api_impl::get_committee_member_by_account(const std::string account_id_or_name) const
|
|
{
|
|
const auto& idx = _db.get_index_type<committee_member_index>().indices().get<by_account>();
|
|
const account_id_type account = get_account_from_string(account_id_or_name)->id;
|
|
auto itr = idx.find(account);
|
|
if( itr != idx.end() )
|
|
return *itr;
|
|
return {};
|
|
}
|
|
|
|
map<string, committee_member_id_type> database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
return my->lookup_committee_member_accounts( lower_bound_name, limit );
|
|
}
|
|
|
|
map<string, committee_member_id_type> database_api_impl::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
FC_ASSERT( limit <= 1000 );
|
|
const auto& committee_members_by_id = _db.get_index_type<committee_member_index>().indices().get<by_id>();
|
|
|
|
// we want to order committee_members by account name, but that name is in the account object
|
|
// so the committee_member_index doesn't have a quick way to access it.
|
|
// get all the names and look them all up, sort them, then figure out what
|
|
// records to return. This could be optimized, but we expect the
|
|
// number of committee_members to be few and the frequency of calls to be rare
|
|
std::map<std::string, committee_member_id_type> committee_members_by_account_name;
|
|
for (const committee_member_object& committee_member : committee_members_by_id)
|
|
if (auto account_iter = _db.find(committee_member.committee_member_account))
|
|
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
|
|
committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id));
|
|
|
|
auto end_iter = committee_members_by_account_name.begin();
|
|
while (end_iter != committee_members_by_account_name.end() && limit--)
|
|
++end_iter;
|
|
committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end());
|
|
return committee_members_by_account_name;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// SON members //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<optional<son_object>> database_api::get_sons(const vector<son_id_type>& son_ids)const
|
|
{
|
|
return my->get_sons( son_ids );
|
|
}
|
|
|
|
vector<optional<son_object>> database_api_impl::get_sons(const vector<son_id_type>& son_ids)const
|
|
{
|
|
vector<optional<son_object>> result; result.reserve(son_ids.size());
|
|
std::transform(son_ids.begin(), son_ids.end(), std::back_inserter(result),
|
|
[this](son_id_type id) -> optional<son_object> {
|
|
if(auto o = _db.find(id))
|
|
return *o;
|
|
return {};
|
|
});
|
|
return result;
|
|
}
|
|
|
|
fc::optional<son_object> database_api::get_son_by_account(account_id_type account)const
|
|
{
|
|
return my->get_son_by_account( account );
|
|
}
|
|
|
|
fc::optional<son_object> database_api_impl::get_son_by_account(account_id_type account) const
|
|
{
|
|
const auto& idx = _db.get_index_type<son_index>().indices().get<by_account>();
|
|
auto itr = idx.find(account);
|
|
if( itr != idx.end() )
|
|
return *itr;
|
|
return {};
|
|
}
|
|
|
|
map<string, son_id_type> database_api::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
return my->lookup_son_accounts( lower_bound_name, limit );
|
|
}
|
|
|
|
map<string, son_id_type> database_api_impl::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const
|
|
{
|
|
FC_ASSERT( limit <= 1000 );
|
|
const auto& sons_by_id = _db.get_index_type<son_index>().indices().get<by_id>();
|
|
|
|
// we want to order sons by account name, but that name is in the account object
|
|
// so the son_index doesn't have a quick way to access it.
|
|
// get all the names and look them all up, sort them, then figure out what
|
|
// records to return. This could be optimized, but we expect the
|
|
// number of witnesses to be few and the frequency of calls to be rare
|
|
std::map<std::string, son_id_type> sons_by_account_name;
|
|
for (const son_object& son : sons_by_id)
|
|
if (auto account_iter = _db.find(son.son_account))
|
|
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
|
|
sons_by_account_name.insert(std::make_pair(account_iter->name, son.id));
|
|
|
|
auto end_iter = sons_by_account_name.begin();
|
|
while (end_iter != sons_by_account_name.end() && limit--)
|
|
++end_iter;
|
|
sons_by_account_name.erase(end_iter, sons_by_account_name.end());
|
|
return sons_by_account_name;
|
|
}
|
|
|
|
uint64_t database_api::get_son_count()const
|
|
{
|
|
return my->get_son_count();
|
|
}
|
|
|
|
uint64_t database_api_impl::get_son_count()const
|
|
{
|
|
return _db.get_index_type<son_index>().indices().size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// SON Wallets //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
optional<son_wallet_object> database_api::get_active_son_wallet()
|
|
{
|
|
return my->get_active_son_wallet();
|
|
}
|
|
|
|
optional<son_wallet_object> database_api_impl::get_active_son_wallet()
|
|
{
|
|
const auto& idx = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
|
auto obj = idx.rbegin();
|
|
if (obj != idx.rend()) {
|
|
return *obj;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
optional<son_wallet_object> database_api::get_son_wallet_by_time_point(time_point_sec time_point)
|
|
{
|
|
return my->get_son_wallet_by_time_point(time_point);
|
|
}
|
|
|
|
optional<son_wallet_object> database_api_impl::get_son_wallet_by_time_point(time_point_sec time_point)
|
|
{
|
|
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
|
for (const son_wallet_object& swo : son_wallets_by_id) {
|
|
if ((time_point >= swo.valid_from) && (time_point < swo.expires))
|
|
return swo;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
vector<optional<son_wallet_object>> database_api::get_son_wallets(uint32_t limit)
|
|
{
|
|
return my->get_son_wallets(limit);
|
|
}
|
|
|
|
vector<optional<son_wallet_object>> database_api_impl::get_son_wallets(uint32_t limit)
|
|
{
|
|
FC_ASSERT( limit <= 1000 );
|
|
vector<optional<son_wallet_object>> result;
|
|
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
|
for (const son_wallet_object& swo : son_wallets_by_id)
|
|
result.push_back(swo);
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Sidechain Accounts //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const
|
|
{
|
|
return my->get_sidechain_addresses( sidechain_address_ids );
|
|
}
|
|
|
|
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const
|
|
{
|
|
vector<optional<sidechain_address_object>> result; result.reserve(sidechain_address_ids.size());
|
|
std::transform(sidechain_address_ids.begin(), sidechain_address_ids.end(), std::back_inserter(result),
|
|
[this](sidechain_address_id_type id) -> optional<sidechain_address_object> {
|
|
if(auto o = _db.find(id))
|
|
return *o;
|
|
return {};
|
|
});
|
|
return result;
|
|
}
|
|
|
|
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses_by_account(account_id_type account)const
|
|
{
|
|
return my->get_sidechain_addresses_by_account( account );
|
|
}
|
|
|
|
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses_by_account(account_id_type account)const
|
|
{
|
|
vector<optional<sidechain_address_object>> result;
|
|
const auto& sidechain_addresses_range = _db.get_index_type<sidechain_address_index>().indices().get<by_account>().equal_range(account);
|
|
std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second,
|
|
[&result] (const sidechain_address_object& sao) {
|
|
if( sao.expires == time_point_sec::maximum() )
|
|
result.push_back(sao);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const
|
|
{
|
|
return my->get_sidechain_addresses_by_sidechain( sidechain );
|
|
}
|
|
|
|
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const
|
|
{
|
|
vector<optional<sidechain_address_object>> result;
|
|
const auto& sidechain_addresses_range = _db.get_index_type<sidechain_address_index>().indices().get<by_sidechain>().equal_range(sidechain);
|
|
std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second,
|
|
[&result] (const sidechain_address_object& sao) {
|
|
if( sao.expires == time_point_sec::maximum() )
|
|
result.push_back(sao);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
fc::optional<sidechain_address_object> database_api::get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const
|
|
{
|
|
return my->get_sidechain_address_by_account_and_sidechain( account, sidechain );
|
|
}
|
|
|
|
fc::optional<sidechain_address_object> database_api_impl::get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const
|
|
{
|
|
const auto& idx = _db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain_and_expires>();
|
|
auto itr = idx.find( boost::make_tuple( account, sidechain, time_point_sec::maximum() ) );
|
|
if( itr != idx.end() )
|
|
return *itr;
|
|
return {};
|
|
}
|
|
|
|
uint64_t database_api::get_sidechain_addresses_count()const
|
|
{
|
|
return my->get_sidechain_addresses_count();
|
|
}
|
|
|
|
uint64_t database_api_impl::get_sidechain_addresses_count()const
|
|
{
|
|
return _db.get_index_type<sidechain_address_index>().indices().size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Votes //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<variant> database_api::lookup_vote_ids( const vector<vote_id_type>& votes )const
|
|
{
|
|
return my->lookup_vote_ids( votes );
|
|
}
|
|
|
|
vector<variant> database_api_impl::lookup_vote_ids( const vector<vote_id_type>& votes )const
|
|
{
|
|
FC_ASSERT( votes.size() < 1000, "Only 1000 votes can be queried at a time" );
|
|
|
|
const auto& witness_idx = _db.get_index_type<witness_index>().indices().get<by_vote_id>();
|
|
const auto& committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>();
|
|
const auto& for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>();
|
|
const auto& against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
|
|
const auto& son_idx = _db.get_index_type<son_index>().indices().get<by_vote_id>();
|
|
|
|
vector<variant> result;
|
|
result.reserve( votes.size() );
|
|
for( auto id : votes )
|
|
{
|
|
switch( id.type() )
|
|
{
|
|
case vote_id_type::committee:
|
|
{
|
|
auto itr = committee_idx.find( id );
|
|
if( itr != committee_idx.end() )
|
|
result.emplace_back( variant( *itr, 1 ) );
|
|
else
|
|
result.emplace_back( variant() );
|
|
break;
|
|
}
|
|
case vote_id_type::witness:
|
|
{
|
|
auto itr = witness_idx.find( id );
|
|
if( itr != witness_idx.end() )
|
|
result.emplace_back( variant( *itr, 1 ) );
|
|
else
|
|
result.emplace_back( variant() );
|
|
break;
|
|
}
|
|
case vote_id_type::worker:
|
|
{
|
|
auto itr = for_worker_idx.find( id );
|
|
if( itr != for_worker_idx.end() ) {
|
|
result.emplace_back( variant( *itr, 1 ) );
|
|
}
|
|
else {
|
|
auto itr = against_worker_idx.find( id );
|
|
if( itr != against_worker_idx.end() ) {
|
|
result.emplace_back( variant( *itr, 1 ) );
|
|
}
|
|
else {
|
|
result.emplace_back( variant() );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case vote_id_type::son:
|
|
{
|
|
auto itr = son_idx.find( id );
|
|
if( itr != son_idx.end() )
|
|
result.emplace_back( variant( *itr, 1 ) );
|
|
else
|
|
result.emplace_back( variant() );
|
|
break;
|
|
}
|
|
|
|
case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings
|
|
default:
|
|
FC_CAPTURE_AND_THROW( fc::out_of_range_exception, (id) );
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Authority / validation //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
std::string database_api::get_transaction_hex(const signed_transaction& trx)const
|
|
{
|
|
return my->get_transaction_hex( trx );
|
|
}
|
|
|
|
std::string database_api_impl::get_transaction_hex(const signed_transaction& trx)const
|
|
{
|
|
return fc::to_hex(fc::raw::pack(trx));
|
|
}
|
|
|
|
set<public_key_type> database_api::get_required_signatures( const signed_transaction& trx, const flat_set<public_key_type>& available_keys )const
|
|
{
|
|
return my->get_required_signatures( trx, available_keys );
|
|
}
|
|
|
|
set<public_key_type> database_api_impl::get_required_signatures( const signed_transaction& trx, const flat_set<public_key_type>& available_keys )const
|
|
{
|
|
wdump((trx)(available_keys));
|
|
auto result = trx.get_required_signatures( _db.get_chain_id(),
|
|
available_keys,
|
|
[&]( account_id_type id ){ return &id(_db).active; },
|
|
[&]( account_id_type id ){ return &id(_db).owner; },
|
|
[&]( account_id_type id, const operation& op ) {
|
|
return _db.get_account_custom_authorities(id, op);
|
|
},
|
|
_db.get_global_properties().parameters.max_authority_depth );
|
|
wdump((result));
|
|
return result;
|
|
}
|
|
|
|
set<public_key_type> database_api::get_potential_signatures( const signed_transaction& trx )const
|
|
{
|
|
return my->get_potential_signatures( trx );
|
|
}
|
|
set<address> database_api::get_potential_address_signatures( const signed_transaction& trx )const
|
|
{
|
|
return my->get_potential_address_signatures( trx );
|
|
}
|
|
|
|
set<public_key_type> database_api_impl::get_potential_signatures( const signed_transaction& trx )const
|
|
{
|
|
wdump((trx));
|
|
set<public_key_type> result;
|
|
trx.get_required_signatures(
|
|
_db.get_chain_id(),
|
|
flat_set<public_key_type>(),
|
|
[&]( account_id_type id )
|
|
{
|
|
const auto& auth = id(_db).active;
|
|
for( const auto& k : auth.get_keys() )
|
|
result.insert(k);
|
|
return &auth;
|
|
},
|
|
[&]( account_id_type id )
|
|
{
|
|
const auto& auth = id(_db).owner;
|
|
for( const auto& k : auth.get_keys() )
|
|
result.insert(k);
|
|
return &auth;
|
|
},
|
|
[&]( account_id_type id, const operation& op ) {
|
|
vector<authority> custom_auths = _db.get_account_custom_authorities(id, op);
|
|
for (const auto& cauth: custom_auths)
|
|
{
|
|
for (const auto& k : cauth.get_keys())
|
|
{
|
|
result.insert(k);
|
|
}
|
|
}
|
|
return custom_auths;
|
|
},
|
|
_db.get_global_properties().parameters.max_authority_depth
|
|
);
|
|
|
|
wdump((result));
|
|
return result;
|
|
}
|
|
|
|
set<address> database_api_impl::get_potential_address_signatures( const signed_transaction& trx )const
|
|
{
|
|
set<address> result;
|
|
trx.get_required_signatures(
|
|
_db.get_chain_id(),
|
|
flat_set<public_key_type>(),
|
|
[&]( account_id_type id )
|
|
{
|
|
const auto& auth = id(_db).active;
|
|
for( const auto& k : auth.get_addresses() )
|
|
result.insert(k);
|
|
return &auth;
|
|
},
|
|
[&]( account_id_type id )
|
|
{
|
|
const auto& auth = id(_db).owner;
|
|
for( const auto& k : auth.get_addresses() )
|
|
result.insert(k);
|
|
return &auth;
|
|
},
|
|
[&]( account_id_type id, const operation& op ) {
|
|
return _db.get_account_custom_authorities(id, op);
|
|
},
|
|
_db.get_global_properties().parameters.max_authority_depth
|
|
);
|
|
return result;
|
|
}
|
|
|
|
bool database_api::verify_authority( const signed_transaction& trx )const
|
|
{
|
|
return my->verify_authority( trx );
|
|
}
|
|
|
|
bool database_api_impl::verify_authority( const signed_transaction& trx )const
|
|
{
|
|
trx.verify_authority( _db.get_chain_id(),
|
|
[this]( account_id_type id ){ return &id(_db).active; },
|
|
[this]( account_id_type id ){ return &id(_db).owner; },
|
|
[this]( account_id_type id, const operation& op ) {
|
|
return _db.get_account_custom_authorities(id, op); },
|
|
_db.get_global_properties().parameters.max_authority_depth );
|
|
return true;
|
|
}
|
|
|
|
bool database_api::verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& signers )const
|
|
{
|
|
return my->verify_account_authority( name_or_id, signers );
|
|
}
|
|
|
|
bool database_api_impl::verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& keys )const
|
|
{
|
|
FC_ASSERT( name_or_id.size() > 0);
|
|
const account_object* account = nullptr;
|
|
if (std::isdigit(name_or_id[0]))
|
|
account = _db.find(fc::variant(name_or_id, 1).as<account_id_type>(1));
|
|
else
|
|
{
|
|
const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
|
|
auto itr = idx.find(name_or_id);
|
|
if (itr != idx.end())
|
|
account = &*itr;
|
|
}
|
|
FC_ASSERT( account, "no such account" );
|
|
|
|
|
|
/// reuse trx.verify_authority by creating a dummy transfer
|
|
signed_transaction trx;
|
|
transfer_operation op;
|
|
op.from = account->id;
|
|
trx.operations.emplace_back(op);
|
|
|
|
return verify_authority( trx );
|
|
}
|
|
|
|
processed_transaction database_api::validate_transaction( const signed_transaction& trx )const
|
|
{
|
|
return my->validate_transaction( trx );
|
|
}
|
|
|
|
processed_transaction database_api_impl::validate_transaction( const signed_transaction& trx )const
|
|
{
|
|
return _db.validate_transaction(trx);
|
|
}
|
|
|
|
vector< fc::variant > database_api::get_required_fees( const vector<operation>& ops, const std::string& asset_id_or_symbol )const
|
|
{
|
|
return my->get_required_fees( ops, asset_id_or_symbol );
|
|
}
|
|
|
|
/**
|
|
* Container method for mutually recursive functions used to
|
|
* implement get_required_fees() with potentially nested proposals.
|
|
*/
|
|
struct get_required_fees_helper
|
|
{
|
|
get_required_fees_helper(
|
|
const fee_schedule& _current_fee_schedule,
|
|
const price& _core_exchange_rate,
|
|
uint32_t _max_recursion
|
|
)
|
|
: current_fee_schedule(_current_fee_schedule),
|
|
core_exchange_rate(_core_exchange_rate),
|
|
max_recursion(_max_recursion)
|
|
{}
|
|
|
|
fc::variant set_op_fees( operation& op )
|
|
{
|
|
if( op.which() == operation::tag<proposal_create_operation>::value )
|
|
{
|
|
return set_proposal_create_op_fees( op );
|
|
}
|
|
else
|
|
{
|
|
asset fee = current_fee_schedule.set_fee( op, core_exchange_rate );
|
|
fc::variant result;
|
|
fc::to_variant( fee, result, GRAPHENE_NET_MAX_NESTED_OBJECTS );
|
|
return result;
|
|
}
|
|
}
|
|
|
|
fc::variant set_proposal_create_op_fees( operation& proposal_create_op )
|
|
{
|
|
proposal_create_operation& op = proposal_create_op.get<proposal_create_operation>();
|
|
std::pair< asset, fc::variants > result;
|
|
for( op_wrapper& prop_op : op.proposed_ops )
|
|
{
|
|
FC_ASSERT( current_recursion < max_recursion );
|
|
++current_recursion;
|
|
result.second.push_back( set_op_fees( prop_op.op ) );
|
|
--current_recursion;
|
|
}
|
|
// we need to do this on the boxed version, which is why we use
|
|
// two mutually recursive functions instead of a visitor
|
|
result.first = current_fee_schedule.set_fee( proposal_create_op, core_exchange_rate );
|
|
fc::variant vresult;
|
|
fc::to_variant( result, vresult, GRAPHENE_NET_MAX_NESTED_OBJECTS );
|
|
return vresult;
|
|
}
|
|
|
|
const fee_schedule& current_fee_schedule;
|
|
const price& core_exchange_rate;
|
|
uint32_t max_recursion;
|
|
uint32_t current_recursion = 0;
|
|
};
|
|
|
|
vector< fc::variant > database_api_impl::get_required_fees( const vector<operation>& ops, const std::string& asset_id_or_symbol )const
|
|
{
|
|
vector< operation > _ops = ops;
|
|
//
|
|
// we copy the ops because we need to mutate an operation to reliably
|
|
// determine its fee, see #435
|
|
//
|
|
|
|
vector< fc::variant > result;
|
|
result.reserve(ops.size());
|
|
const asset_object& a = *get_asset_from_string(asset_id_or_symbol);
|
|
get_required_fees_helper helper(
|
|
_db.current_fee_schedule(),
|
|
a.options.core_exchange_rate,
|
|
GET_REQUIRED_FEES_MAX_RECURSION );
|
|
for( operation& op : _ops )
|
|
{
|
|
result.push_back( helper.set_op_fees( op ) );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Proposed transactions //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<proposal_object> database_api::get_proposed_transactions( const std::string account_id_or_name )const
|
|
{
|
|
return my->get_proposed_transactions( account_id_or_name );
|
|
}
|
|
|
|
/** TODO: add secondary index that will accelerate this process */
|
|
vector<proposal_object> database_api_impl::get_proposed_transactions( const std::string account_id_or_name )const
|
|
{
|
|
const auto& idx = _db.get_index_type<proposal_index>();
|
|
vector<proposal_object> result;
|
|
const account_id_type id = get_account_from_string(account_id_or_name)->id;
|
|
|
|
idx.inspect_all_objects( [&](const object& obj){
|
|
const proposal_object& p = static_cast<const proposal_object&>(obj);
|
|
if( p.required_active_approvals.find( id ) != p.required_active_approvals.end() )
|
|
result.push_back(p);
|
|
else if ( p.required_owner_approvals.find( id ) != p.required_owner_approvals.end() )
|
|
result.push_back(p);
|
|
else if ( p.available_active_approvals.find( id ) != p.available_active_approvals.end() )
|
|
result.push_back(p);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Blinded balances //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<blinded_balance_object> database_api::get_blinded_balances( const flat_set<commitment_type>& commitments )const
|
|
{
|
|
return my->get_blinded_balances( commitments );
|
|
}
|
|
|
|
vector<blinded_balance_object> database_api_impl::get_blinded_balances( const flat_set<commitment_type>& commitments )const
|
|
{
|
|
vector<blinded_balance_object> result; result.reserve(commitments.size());
|
|
const auto& bal_idx = _db.get_index_type<blinded_balance_index>();
|
|
const auto& by_commitment_idx = bal_idx.indices().get<by_commitment>();
|
|
for( const auto& c : commitments )
|
|
{
|
|
auto itr = by_commitment_idx.find( c );
|
|
if( itr != by_commitment_idx.end() )
|
|
result.push_back( *itr );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Tournament methods //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
vector<tournament_object> database_api::get_tournaments_in_state(tournament_state state, uint32_t limit) const
|
|
{
|
|
return my->get_tournaments_in_state(state, limit);
|
|
}
|
|
|
|
vector<tournament_object> database_api_impl::get_tournaments_in_state(tournament_state state, uint32_t limit) const
|
|
{
|
|
vector<tournament_object> result;
|
|
const auto& registration_deadline_index = _db.get_index_type<tournament_index>().indices().get<by_registration_deadline>();
|
|
const auto range = registration_deadline_index.equal_range(boost::make_tuple(state));
|
|
for (const tournament_object& tournament_obj : boost::make_iterator_range(range.first, range.second))
|
|
{
|
|
result.emplace_back(tournament_obj);
|
|
subscribe_to_item( tournament_obj.id );
|
|
|
|
if (result.size() >= limit)
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<tournament_object> database_api::get_tournaments(tournament_id_type stop,
|
|
unsigned limit,
|
|
tournament_id_type start)
|
|
{
|
|
return my->get_tournaments(stop, limit, start);
|
|
}
|
|
|
|
vector<tournament_object> database_api_impl::get_tournaments(tournament_id_type stop,
|
|
unsigned limit,
|
|
tournament_id_type start)
|
|
{
|
|
vector<tournament_object> result;
|
|
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
|
for (auto elem: tournament_idx) {
|
|
if( result.size() >= limit ) break;
|
|
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
|
|
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()))
|
|
result.push_back( elem );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
vector<tournament_object> database_api::get_tournaments_by_state(tournament_id_type stop,
|
|
unsigned limit,
|
|
tournament_id_type start,
|
|
tournament_state state)
|
|
{
|
|
return my->get_tournaments_by_state(stop, limit, start, state);
|
|
}
|
|
|
|
vector<tournament_object> database_api_impl::get_tournaments_by_state(tournament_id_type stop,
|
|
unsigned limit,
|
|
tournament_id_type start,
|
|
tournament_state state)
|
|
{
|
|
vector<tournament_object> result;
|
|
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
|
for (auto elem: tournament_idx) {
|
|
if( result.size() >= limit ) break;
|
|
if( ( (elem.get_id().instance.value <= start.instance.value) || start == tournament_id_type()) &&
|
|
( (elem.get_id().instance.value >= stop.instance.value) || stop == tournament_id_type()) &&
|
|
elem.get_state() == state )
|
|
result.push_back( elem );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const account_object* database_api_impl::get_account_from_string( const std::string& name_or_id,
|
|
bool throw_if_not_found ) const
|
|
{
|
|
// TODO cache the result to avoid repeatly fetching from db
|
|
FC_ASSERT( name_or_id.size() > 0);
|
|
const account_object* account = nullptr;
|
|
if (std::isdigit(name_or_id[0]))
|
|
account = _db.find(fc::variant(name_or_id, 1).as<account_id_type>(1));
|
|
else
|
|
{
|
|
const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
|
|
auto itr = idx.find(name_or_id);
|
|
if (itr != idx.end())
|
|
account = &*itr;
|
|
}
|
|
if(throw_if_not_found)
|
|
FC_ASSERT( account, "no such account" );
|
|
return account;
|
|
}
|
|
|
|
vector<tournament_id_type> database_api::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const
|
|
{
|
|
return my->get_registered_tournaments(account_filter, limit);
|
|
}
|
|
|
|
vector<tournament_id_type> database_api_impl::get_registered_tournaments(account_id_type account_filter, uint32_t limit) const
|
|
{
|
|
const auto& tournament_details_idx = _db.get_index_type<tournament_details_index>();
|
|
const auto& tournament_details_primary_idx = dynamic_cast<const primary_index<tournament_details_index>&>(tournament_details_idx);
|
|
const auto& players_idx = tournament_details_primary_idx.get_secondary_index<graphene::chain::tournament_players_index>();
|
|
|
|
vector<tournament_id_type> tournament_ids = players_idx.get_registered_tournaments_for_account(account_filter);
|
|
if (tournament_ids.size() >= limit)
|
|
tournament_ids.resize(limit);
|
|
return tournament_ids;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// GPOS methods //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
graphene::app::gpos_info database_api::get_gpos_info(const account_id_type account) const
|
|
{
|
|
return my->get_gpos_info(account);
|
|
|
|
}
|
|
|
|
graphene::app::gpos_info database_api_impl::get_gpos_info(const account_id_type account) const
|
|
{
|
|
FC_ASSERT( _db.head_block_time() > HARDFORK_GPOS_TIME); //Can be deleted after GPOS hardfork time
|
|
gpos_info result;
|
|
|
|
result.vesting_factor = _db.calculate_vesting_factor(account(_db));
|
|
result.current_subperiod = _db.get_gpos_current_subperiod();
|
|
result.last_voted_time = account(_db).statistics(_db).last_vote_time;
|
|
|
|
const auto& dividend_data = asset_id_type()(_db).dividend_data(_db);
|
|
const account_object& dividend_distribution_account = dividend_data.dividend_distribution_account(_db);
|
|
result.award = _db.get_balance(dividend_distribution_account, asset_id_type()(_db));
|
|
|
|
share_type total_amount;
|
|
auto balance_type = vesting_balance_type::gpos;
|
|
#ifdef USE_VESTING_OBJECT_BY_ASSET_BALANCE_INDEX
|
|
// get only once a collection of accounts that hold nonzero vesting balances of the dividend asset
|
|
auto vesting_balances_begin =
|
|
vesting_index.indices().get<by_asset_balance>().lower_bound(boost::make_tuple(asset_id_type(), balance_type));
|
|
auto vesting_balances_end =
|
|
vesting_index.indices().get<by_asset_balance>().upper_bound(boost::make_tuple(asset_id_type(), balance_type, share_type()));
|
|
|
|
for (const vesting_balance_object& vesting_balance_obj : boost::make_iterator_range(vesting_balances_begin, vesting_balances_end))
|
|
{
|
|
total_amount += vesting_balance_obj.balance.amount;
|
|
}
|
|
#else
|
|
const vesting_balance_index& vesting_index = _db.get_index_type<vesting_balance_index>();
|
|
const auto& vesting_balances = vesting_index.indices().get<by_id>();
|
|
for (const vesting_balance_object& vesting_balance_obj : vesting_balances)
|
|
{
|
|
if (vesting_balance_obj.balance.asset_id == asset_id_type() && vesting_balance_obj.balance_type == balance_type)
|
|
{
|
|
total_amount += vesting_balance_obj.balance.amount;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
vector<vesting_balance_object> account_vbos;
|
|
const time_point_sec now = _db.head_block_time();
|
|
auto vesting_range = _db.get_index_type<vesting_balance_index>().indices().get<by_account>().equal_range(account);
|
|
std::for_each(vesting_range.first, vesting_range.second,
|
|
[&account_vbos, now](const vesting_balance_object& balance) {
|
|
if(balance.balance.amount > 0 && balance.balance_type == vesting_balance_type::gpos
|
|
&& balance.balance.asset_id == asset_id_type())
|
|
account_vbos.emplace_back(balance);
|
|
});
|
|
|
|
share_type allowed_withdraw_amount = 0, account_vested_balance = 0;
|
|
|
|
for (const vesting_balance_object& vesting_balance_obj : account_vbos)
|
|
{
|
|
account_vested_balance += vesting_balance_obj.balance.amount;
|
|
if(vesting_balance_obj.is_withdraw_allowed(_db.head_block_time(), vesting_balance_obj.balance.amount))
|
|
allowed_withdraw_amount += vesting_balance_obj.balance.amount;
|
|
}
|
|
|
|
result.total_amount = total_amount;
|
|
result.allowed_withdraw_amount = allowed_withdraw_amount;
|
|
result.account_vested_balance = account_vested_balance;
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// RBAC methods //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
vector<custom_permission_object> database_api::get_custom_permissions(const account_id_type account) const
|
|
{
|
|
return my->get_custom_permissions(account);
|
|
}
|
|
|
|
vector<custom_permission_object> database_api_impl::get_custom_permissions(const account_id_type account) const
|
|
{
|
|
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
|
|
auto prange = pindex.equal_range(boost::make_tuple(account));
|
|
vector<custom_permission_object> custom_permissions;
|
|
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
|
|
{
|
|
custom_permissions.push_back(pobj);
|
|
}
|
|
return custom_permissions;
|
|
}
|
|
|
|
fc::optional<custom_permission_object> database_api::get_custom_permission_by_name(const account_id_type account, const string& permission_name) const
|
|
{
|
|
return my->get_custom_permission_by_name(account, permission_name);
|
|
}
|
|
|
|
fc::optional<custom_permission_object> database_api_impl::get_custom_permission_by_name(const account_id_type account, const string& permission_name) const
|
|
{
|
|
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
|
|
auto prange = pindex.equal_range(boost::make_tuple(account, permission_name));
|
|
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
|
|
{
|
|
return pobj;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// NFT methods //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
uint64_t database_api::nft_get_balance(const account_id_type owner) const
|
|
{
|
|
return my->nft_get_balance(owner);
|
|
}
|
|
|
|
uint64_t database_api_impl::nft_get_balance(const account_id_type owner) const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
|
|
const auto &idx_nft_range = idx_nft.equal_range(owner);
|
|
return std::distance(idx_nft_range.first, idx_nft_range.second);
|
|
}
|
|
|
|
optional<account_id_type> database_api::nft_owner_of(const nft_id_type token_id) const
|
|
{
|
|
return my->nft_owner_of(token_id);
|
|
}
|
|
|
|
optional<account_id_type> database_api_impl::nft_owner_of(const nft_id_type token_id) const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
|
|
auto itr_nft = idx_nft.find(token_id);
|
|
if (itr_nft != idx_nft.end()) {
|
|
return itr_nft->owner;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
optional<account_id_type> database_api::nft_get_approved(const nft_id_type token_id) const
|
|
{
|
|
return my->nft_get_approved(token_id);
|
|
}
|
|
|
|
optional<account_id_type> database_api_impl::nft_get_approved(const nft_id_type token_id) const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
|
|
auto itr_nft = idx_nft.find(token_id);
|
|
if (itr_nft != idx_nft.end()) {
|
|
return itr_nft->approved;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
bool database_api::nft_is_approved_for_all(const account_id_type owner, const account_id_type operator_) const
|
|
{
|
|
return my->nft_is_approved_for_all(owner, operator_);
|
|
}
|
|
|
|
bool database_api_impl::nft_is_approved_for_all(const account_id_type owner, const account_id_type operator_) const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
|
|
const auto &idx_nft_range = idx_nft.equal_range(owner);
|
|
if (std::distance(idx_nft_range.first, idx_nft_range.second) == 0) {
|
|
return false;
|
|
}
|
|
bool result = true;
|
|
std::for_each(idx_nft_range.first, idx_nft_range.second, [&](const nft_object &obj) {
|
|
result = result && (obj.approved == operator_);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
string database_api::nft_get_name(const nft_metadata_id_type nft_metadata_id) const
|
|
{
|
|
return my->nft_get_name(nft_metadata_id);
|
|
}
|
|
|
|
string database_api_impl::nft_get_name(const nft_metadata_id_type nft_metadata_id) const
|
|
{
|
|
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
|
auto itr_nft_md = idx_nft_md.find(nft_metadata_id);
|
|
if (itr_nft_md != idx_nft_md.end()) {
|
|
return itr_nft_md->name;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
string database_api::nft_get_symbol(const nft_metadata_id_type nft_metadata_id) const
|
|
{
|
|
return my->nft_get_symbol(nft_metadata_id);
|
|
}
|
|
|
|
string database_api_impl::nft_get_symbol(const nft_metadata_id_type nft_metadata_id) const
|
|
{
|
|
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
|
auto itr_nft_md = idx_nft_md.find(nft_metadata_id);
|
|
if (itr_nft_md != idx_nft_md.end()) {
|
|
return itr_nft_md->symbol;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
string database_api::nft_get_token_uri(const nft_id_type token_id) const
|
|
{
|
|
return my->nft_get_token_uri(token_id);
|
|
}
|
|
|
|
string database_api_impl::nft_get_token_uri(const nft_id_type token_id) const
|
|
{
|
|
string result = "";
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
|
|
auto itr_nft = idx_nft.find(token_id);
|
|
if (itr_nft != idx_nft.end()) {
|
|
result = itr_nft->token_uri;
|
|
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
|
auto itr_nft_md = idx_nft_md.find(itr_nft->nft_metadata_id);
|
|
if (itr_nft_md != idx_nft_md.end()) {
|
|
result = itr_nft_md->base_uri + itr_nft->token_uri;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint64_t database_api::nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const
|
|
{
|
|
return my->nft_get_total_supply(nft_metadata_id);
|
|
}
|
|
|
|
uint64_t database_api_impl::nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const
|
|
{
|
|
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
|
return idx_nft_md.size();
|
|
}
|
|
|
|
nft_object database_api::nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const
|
|
{
|
|
return my->nft_token_by_index(nft_metadata_id, token_idx);
|
|
}
|
|
|
|
nft_object database_api_impl::nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_metadata>();
|
|
auto idx_nft_range = idx_nft.equal_range(nft_metadata_id);
|
|
uint64_t tmp_idx = token_idx;
|
|
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) {
|
|
if (tmp_idx == 0) {
|
|
return *itr;
|
|
}
|
|
tmp_idx = tmp_idx - 1;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
nft_object database_api::nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const
|
|
{
|
|
return my->nft_token_of_owner_by_index(nft_metadata_id, owner, token_idx);
|
|
}
|
|
|
|
nft_object database_api_impl::nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_metadata_and_owner>();
|
|
auto idx_nft_range = idx_nft.equal_range(std::make_tuple(nft_metadata_id, owner));
|
|
uint64_t tmp_idx = token_idx;
|
|
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) {
|
|
if (tmp_idx == 0) {
|
|
return *itr;
|
|
}
|
|
tmp_idx = tmp_idx - 1;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
vector<nft_object> database_api::nft_get_all_tokens() const
|
|
{
|
|
return my->nft_get_all_tokens();
|
|
}
|
|
|
|
vector<nft_object> database_api_impl::nft_get_all_tokens() const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_id>();
|
|
vector<nft_object> result;
|
|
for (auto itr = idx_nft.begin(); itr != idx_nft.end(); ++itr) {
|
|
result.push_back(*itr);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<nft_object> database_api::nft_get_tokens_by_owner(const account_id_type owner) const
|
|
{
|
|
return my->nft_get_tokens_by_owner(owner);
|
|
}
|
|
|
|
vector<nft_object> database_api_impl::nft_get_tokens_by_owner(const account_id_type owner) const
|
|
{
|
|
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_owner>();
|
|
auto idx_nft_range = idx_nft.equal_range(owner);
|
|
vector<nft_object> result;
|
|
for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) {
|
|
result.push_back(*itr);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<custom_account_authority_object> database_api::get_custom_account_authorities(const account_id_type account) const
|
|
{
|
|
return my->get_custom_account_authorities(account);
|
|
}
|
|
|
|
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities(const account_id_type account) const
|
|
{
|
|
const auto& pindex = _db.get_index_type<custom_permission_index>().indices().get<by_account_and_permission>();
|
|
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
|
|
vector<custom_account_authority_object> custom_account_auths;
|
|
auto prange = pindex.equal_range(boost::make_tuple(account));
|
|
for(const custom_permission_object& pobj : boost::make_iterator_range(prange.first, prange.second))
|
|
{
|
|
auto crange = cindex.equal_range(boost::make_tuple(pobj.id));
|
|
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
|
|
{
|
|
custom_account_auths.push_back(cobj);
|
|
}
|
|
}
|
|
return custom_account_auths;
|
|
}
|
|
|
|
vector<custom_account_authority_object> database_api::get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const
|
|
{
|
|
return my->get_custom_account_authorities_by_permission_id(permission_id);
|
|
}
|
|
|
|
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities_by_permission_id(const custom_permission_id_type permission_id) const
|
|
{
|
|
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
|
|
vector<custom_account_authority_object> custom_account_auths;
|
|
auto crange = cindex.equal_range(boost::make_tuple(permission_id));
|
|
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
|
|
{
|
|
custom_account_auths.push_back(cobj);
|
|
}
|
|
return custom_account_auths;
|
|
}
|
|
|
|
vector<custom_account_authority_object> database_api::get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const
|
|
{
|
|
return my->get_custom_account_authorities_by_permission_name(account, permission_name);
|
|
}
|
|
|
|
vector<custom_account_authority_object> database_api_impl::get_custom_account_authorities_by_permission_name(const account_id_type account, const string& permission_name) const
|
|
{
|
|
vector<custom_account_authority_object> custom_account_auths;
|
|
fc::optional<custom_permission_object> pobj = get_custom_permission_by_name(account, permission_name);
|
|
if(!pobj)
|
|
{
|
|
return custom_account_auths;
|
|
}
|
|
const auto& cindex = _db.get_index_type<custom_account_authority_index>().indices().get<by_permission_and_op>();
|
|
auto crange = cindex.equal_range(boost::make_tuple(pobj->id));
|
|
for(const custom_account_authority_object& cobj : boost::make_iterator_range(crange.first, crange.second))
|
|
{
|
|
custom_account_auths.push_back(cobj);
|
|
}
|
|
return custom_account_auths;
|
|
}
|
|
|
|
vector<authority> database_api::get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const
|
|
{
|
|
return my->get_active_custom_account_authorities_by_operation(account, operation_type);
|
|
}
|
|
|
|
vector<authority> database_api_impl::get_active_custom_account_authorities_by_operation(const account_id_type account, int operation_type) const
|
|
{
|
|
operation op;
|
|
op.set_which(operation_type);
|
|
return _db.get_account_custom_authorities(account, op);
|
|
}
|
|
|
|
// Marketplace
|
|
vector<offer_object> database_api::list_offers(const offer_id_type lower_id, uint32_t limit) const
|
|
{
|
|
return my->list_offers(lower_id, limit);
|
|
}
|
|
|
|
vector<offer_object> database_api_impl::list_offers(const offer_id_type lower_id, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
|
|
vector<offer_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = offers_idx.lower_bound(lower_id);
|
|
|
|
while(limit-- && itr != offers_idx.end())
|
|
result.emplace_back(*itr++);
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<offer_object> database_api::list_sell_offers(const offer_id_type lower_id, uint32_t limit) const
|
|
{
|
|
return my->list_sell_offers(lower_id, limit);
|
|
}
|
|
|
|
vector<offer_object> database_api_impl::list_sell_offers(const offer_id_type lower_id, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
|
|
vector<offer_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = offers_idx.lower_bound(lower_id);
|
|
|
|
while(limit && itr != offers_idx.end())
|
|
{
|
|
if(itr->buying_item == false)
|
|
{
|
|
result.emplace_back(*itr);
|
|
limit--;
|
|
}
|
|
itr++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<offer_object> database_api::list_buy_offers(const offer_id_type lower_id, uint32_t limit) const
|
|
{
|
|
return my->list_buy_offers(lower_id, limit);
|
|
}
|
|
|
|
vector<offer_object> database_api_impl::list_buy_offers(const offer_id_type lower_id, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
|
|
vector<offer_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = offers_idx.lower_bound(lower_id);
|
|
|
|
while(limit && itr != offers_idx.end())
|
|
{
|
|
if(itr->buying_item == true)
|
|
{
|
|
result.emplace_back(*itr);
|
|
limit--;
|
|
}
|
|
itr++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<offer_history_object> database_api::list_offer_history(const offer_history_id_type lower_id, uint32_t limit) const
|
|
{
|
|
return my->list_offer_history(lower_id, limit);
|
|
}
|
|
|
|
vector<offer_history_object> database_api_impl::list_offer_history(const offer_history_id_type lower_id, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
|
|
vector<offer_history_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = oh_idx.lower_bound(lower_id);
|
|
|
|
while(limit-- && itr != oh_idx.end())
|
|
result.emplace_back(*itr++);
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<offer_object> database_api::get_offers_by_issuer(const offer_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
|
|
{
|
|
return my->get_offers_by_issuer(lower_id, issuer_account_id, limit);
|
|
}
|
|
|
|
vector<offer_object> database_api_impl::get_offers_by_issuer(const offer_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
|
|
vector<offer_object> result;
|
|
result.reserve(limit);
|
|
auto itr = offers_idx.lower_bound(lower_id);
|
|
while(limit && itr != offers_idx.end())
|
|
{
|
|
if(itr->issuer == issuer_account_id)
|
|
{
|
|
result.emplace_back(*itr);
|
|
limit--;
|
|
}
|
|
itr++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<offer_object> database_api::get_offers_by_item(const offer_id_type lower_id, const nft_id_type item, uint32_t limit) const
|
|
{
|
|
return my->get_offers_by_item(lower_id, item, limit);
|
|
}
|
|
|
|
vector<offer_object> database_api_impl::get_offers_by_item(const offer_id_type lower_id, const nft_id_type item, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& offers_idx = _db.get_index_type<offer_index>().indices().get<by_id>();
|
|
vector<offer_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = offers_idx.lower_bound(lower_id);
|
|
while(limit && itr != offers_idx.end())
|
|
{
|
|
if(itr->item_ids.find(item) != itr->item_ids.end())
|
|
{
|
|
result.emplace_back(*itr);
|
|
limit--;
|
|
}
|
|
itr++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<offer_history_object> database_api::get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
|
|
{
|
|
return my->get_offer_history_by_issuer(lower_id, issuer_account_id, limit);
|
|
}
|
|
|
|
vector<offer_history_object> database_api::get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const
|
|
{
|
|
return my->get_offer_history_by_item(lower_id, item, limit);
|
|
}
|
|
|
|
vector<offer_history_object> database_api::get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const
|
|
{
|
|
return my->get_offer_history_by_bidder(lower_id, bidder_account_id, limit);
|
|
}
|
|
|
|
vector<offer_history_object> database_api_impl::get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
|
|
vector<offer_history_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = oh_idx.lower_bound(lower_id);
|
|
|
|
while(limit && itr != oh_idx.end())
|
|
{
|
|
if(itr->issuer == issuer_account_id)
|
|
{
|
|
result.emplace_back(*itr);
|
|
limit--;
|
|
}
|
|
itr++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vector<offer_history_object> database_api_impl::get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
|
|
vector<offer_history_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = oh_idx.lower_bound(lower_id);
|
|
|
|
while(limit && itr != oh_idx.end())
|
|
{
|
|
if(itr->item_ids.find(item) != itr->item_ids.end())
|
|
{
|
|
result.emplace_back(*itr);
|
|
limit--;
|
|
}
|
|
itr++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<offer_history_object> database_api_impl::get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const
|
|
{
|
|
FC_ASSERT( limit <= 100 );
|
|
const auto& oh_idx = _db.get_index_type<offer_history_index>().indices().get<by_id>();
|
|
vector<offer_history_object> result;
|
|
result.reserve(limit);
|
|
|
|
auto itr = oh_idx.lower_bound(lower_id);
|
|
|
|
while(limit && itr != oh_idx.end())
|
|
{
|
|
if(itr->bidder && *itr->bidder == bidder_account_id)
|
|
{
|
|
result.emplace_back(*itr);
|
|
limit--;
|
|
}
|
|
itr++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<account_role_object> database_api::get_account_roles_by_owner(account_id_type owner) const
|
|
{
|
|
return my->get_account_roles_by_owner(owner);
|
|
}
|
|
|
|
vector<account_role_object> database_api_impl::get_account_roles_by_owner(account_id_type owner) const
|
|
{
|
|
const auto &idx_aro = _db.get_index_type<account_role_index>().indices().get<by_owner>();
|
|
auto idx_aro_range = idx_aro.equal_range(owner);
|
|
vector<account_role_object> result;
|
|
for (auto itr = idx_aro_range.first; itr != idx_aro_range.second; ++itr)
|
|
{
|
|
result.push_back(*itr);
|
|
}
|
|
return result;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Private methods //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void database_api_impl::broadcast_updates( const vector<variant>& updates )
|
|
{
|
|
if( updates.size() && _subscribe_callback ) {
|
|
auto capture_this = shared_from_this();
|
|
fc::async([capture_this,updates](){
|
|
if(capture_this->_subscribe_callback)
|
|
capture_this->_subscribe_callback( fc::variant(updates) );
|
|
});
|
|
}
|
|
}
|
|
|
|
void database_api_impl::broadcast_market_updates( const market_queue_type& queue)
|
|
{
|
|
if( queue.size() )
|
|
{
|
|
auto capture_this = shared_from_this();
|
|
fc::async([capture_this, this, queue](){
|
|
for( const auto& item : queue )
|
|
{
|
|
auto sub = _market_subscriptions.find(item.first);
|
|
if( sub != _market_subscriptions.end() )
|
|
sub->second( fc::variant(item.second ) );
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void database_api_impl::on_objects_removed( const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts)
|
|
{
|
|
handle_object_changed(_notify_remove_create, false, ids, impacted_accounts,
|
|
[objs](object_id_type id) -> const object* {
|
|
auto it = std::find_if(
|
|
objs.begin(), objs.end(),
|
|
[id](const object* o) {return o != nullptr && o->id == id;});
|
|
|
|
if (it != objs.end())
|
|
return *it;
|
|
|
|
return nullptr;
|
|
}
|
|
);
|
|
}
|
|
|
|
void database_api_impl::on_objects_new(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts)
|
|
{
|
|
handle_object_changed(_notify_remove_create, true, ids, impacted_accounts,
|
|
std::bind(&object_database::find_object, &_db, std::placeholders::_1)
|
|
);
|
|
}
|
|
|
|
void database_api_impl::on_objects_changed(const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts)
|
|
{
|
|
handle_object_changed(false, true, ids, impacted_accounts,
|
|
std::bind(&object_database::find_object, &_db, std::placeholders::_1)
|
|
);
|
|
}
|
|
|
|
void database_api_impl::handle_object_changed(bool force_notify, bool full_object, const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts, std::function<const object*(object_id_type id)> find_object)
|
|
{
|
|
if( _subscribe_callback )
|
|
{
|
|
vector<variant> updates;
|
|
|
|
for(auto id : ids)
|
|
{
|
|
if( force_notify || is_subscribed_to_item(id) || is_impacted_account(impacted_accounts) )
|
|
{
|
|
if( full_object )
|
|
{
|
|
auto obj = find_object(id);
|
|
if( obj )
|
|
{
|
|
updates.emplace_back( obj->to_variant() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
updates.emplace_back( fc::variant( id, 1 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
broadcast_updates(updates);
|
|
}
|
|
|
|
if( _market_subscriptions.size() )
|
|
{
|
|
market_queue_type broadcast_queue;
|
|
/// pushing the future back / popping the prior future if it is complete.
|
|
/// if a connection hangs then this could get backed up and result in
|
|
/// a failure to exit cleanly.
|
|
//fc::async([capture_this,this,updates,market_broadcast_queue](){
|
|
//if( _subscribe_callback )
|
|
// _subscribe_callback( updates );
|
|
|
|
for(auto id : ids)
|
|
{
|
|
if( id.is<call_order_object>() )
|
|
{
|
|
enqueue_if_subscribed_to_market<call_order_object>( find_object(id), broadcast_queue, full_object );
|
|
}
|
|
else if( id.is<limit_order_object>() )
|
|
{
|
|
enqueue_if_subscribed_to_market<limit_order_object>( find_object(id), broadcast_queue, full_object );
|
|
}
|
|
}
|
|
|
|
broadcast_market_updates(broadcast_queue);
|
|
}
|
|
}
|
|
|
|
/** note: this method cannot yield because it is called in the middle of
|
|
* apply a block.
|
|
*/
|
|
void database_api_impl::on_applied_block()
|
|
{
|
|
if (_block_applied_callback)
|
|
{
|
|
auto capture_this = shared_from_this();
|
|
block_id_type block_id = _db.head_block_id();
|
|
fc::async([this,capture_this,block_id](){
|
|
_block_applied_callback(fc::variant(block_id, 1));
|
|
});
|
|
}
|
|
|
|
if(_market_subscriptions.size() == 0)
|
|
return;
|
|
|
|
const auto& ops = _db.get_applied_operations();
|
|
map< std::pair<asset_id_type,asset_id_type>, vector<pair<operation, operation_result>> > subscribed_markets_ops;
|
|
for(const optional< operation_history_object >& o_op : ops)
|
|
{
|
|
if( !o_op.valid() )
|
|
continue;
|
|
const operation_history_object& op = *o_op;
|
|
|
|
std::pair<asset_id_type,asset_id_type> market;
|
|
switch(op.op.which())
|
|
{
|
|
/* This is sent via the object_changed callback
|
|
case operation::tag<limit_order_create_operation>::value:
|
|
market = op.op.get<limit_order_create_operation>().get_market();
|
|
break;
|
|
*/
|
|
case operation::tag<fill_order_operation>::value:
|
|
market = op.op.get<fill_order_operation>().get_market();
|
|
break;
|
|
/*
|
|
case operation::tag<limit_order_cancel_operation>::value:
|
|
*/
|
|
default: break;
|
|
}
|
|
if(_market_subscriptions.count(market))
|
|
subscribed_markets_ops[market].push_back(std::make_pair(op.op, op.result));
|
|
}
|
|
/// we need to ensure the database_api is not deleted for the life of the async operation
|
|
auto capture_this = shared_from_this();
|
|
fc::async([this,capture_this,subscribed_markets_ops](){
|
|
for(auto item : subscribed_markets_ops)
|
|
{
|
|
auto itr = _market_subscriptions.find(item.first);
|
|
if(itr != _market_subscriptions.end())
|
|
itr->second(fc::variant(item.second, GRAPHENE_NET_MAX_NESTED_OBJECTS));
|
|
}
|
|
});
|
|
}
|
|
|
|
} } // graphene::app
|